1#ifndef _GNU_SOURCE 2#define _GNU_SOURCE 3#endif 4 5#include <assert.h> 6#include <inttypes.h> 7#include <time.h> 8#include <unistd.h> 9#include <pthread.h> 10 11#include <alsa/asoundlib.h> 12 13#define SAMPLE_RATE 44100 14#define CHANNELS 2 15 16static uint64_t timespec_us(const struct timespec *ts) { 17 return 18 ts->tv_sec * 1000000LLU + 19 ts->tv_nsec / 1000LLU; 20} 21 22int main(int argc, char *argv[]) { 23 const char *dev; 24 int r, cap, count = 0; 25 snd_pcm_hw_params_t *hwparams; 26 snd_pcm_sw_params_t *swparams; 27 snd_pcm_status_t *status; 28 snd_pcm_t *pcm; 29 unsigned rate = SAMPLE_RATE; 30 unsigned periods = 2; 31 snd_pcm_uframes_t boundary, buffer_size = SAMPLE_RATE/10; /* 100s */ 32 int dir = 1; 33 int fillrate; 34 struct timespec start, last_timestamp = { 0, 0 }; 35 uint64_t start_us, last_us = 0; 36 snd_pcm_sframes_t last_avail = 0, last_delay = 0; 37 struct pollfd *pollfds; 38 int n_pollfd; 39 int64_t sample_count = 0; 40 uint16_t *samples; 41 struct sched_param sp; 42 43 r = -1; 44#ifdef _POSIX_PRIORITY_SCHEDULING 45 sp.sched_priority = 5; 46 r = pthread_setschedparam(pthread_self(), SCHED_RR, &sp); 47#endif 48 if (r) 49 printf("Could not get RT prio. :(\n"); 50 51 snd_pcm_hw_params_alloca(&hwparams); 52 snd_pcm_sw_params_alloca(&swparams); 53 snd_pcm_status_alloca(&status); 54 55 r = clock_gettime(CLOCK_MONOTONIC, &start); 56 assert(r == 0); 57 58 start_us = timespec_us(&start); 59 60 dev = argc > 1 ? argv[1] : "front:0"; 61 cap = argc > 2 ? atoi(argv[2]) : 0; 62 fillrate = argc > 3 ? atoi(argv[3]) : 1; 63 assert(fillrate > 0); 64 65 samples = calloc(fillrate, CHANNELS*sizeof(uint16_t)); 66 assert(samples); 67 68 if (cap == 0) 69 r = snd_pcm_open(&pcm, dev, SND_PCM_STREAM_PLAYBACK, 0); 70 else 71 r = snd_pcm_open(&pcm, dev, SND_PCM_STREAM_CAPTURE, 0); 72 assert(r == 0); 73 74 r = snd_pcm_hw_params_any(pcm, hwparams); 75 assert(r == 0); 76 77 r = snd_pcm_hw_params_set_rate_resample(pcm, hwparams, 0); 78 assert(r == 0); 79 80 r = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); 81 assert(r == 0); 82 83 r = snd_pcm_hw_params_set_format(pcm, hwparams, SND_PCM_FORMAT_S16_LE); 84 assert(r == 0); 85 86 r = snd_pcm_hw_params_set_rate_near(pcm, hwparams, &rate, NULL); 87 assert(r == 0); 88 89 r = snd_pcm_hw_params_set_channels(pcm, hwparams, CHANNELS); 90 assert(r == 0); 91 92 r = snd_pcm_hw_params_set_periods_integer(pcm, hwparams); 93 assert(r == 0); 94 95 r = snd_pcm_hw_params_set_periods_near(pcm, hwparams, &periods, &dir); 96 assert(r == 0); 97 98 r = snd_pcm_hw_params_set_buffer_size_near(pcm, hwparams, &buffer_size); 99 assert(r == 0); 100 101 r = snd_pcm_hw_params(pcm, hwparams); 102 assert(r == 0); 103 104 r = snd_pcm_hw_params_current(pcm, hwparams); 105 assert(r == 0); 106 107 r = snd_pcm_sw_params_current(pcm, swparams); 108 assert(r == 0); 109 110 if (cap == 0) 111 r = snd_pcm_sw_params_set_avail_min(pcm, swparams, 1); 112 else 113 r = snd_pcm_sw_params_set_avail_min(pcm, swparams, 0); 114 assert(r == 0); 115 116 r = snd_pcm_sw_params_set_period_event(pcm, swparams, 0); 117 assert(r == 0); 118 119 r = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size); 120 assert(r == 0); 121 r = snd_pcm_sw_params_set_start_threshold(pcm, swparams, buffer_size - (buffer_size % fillrate)); 122 assert(r == 0); 123 124 r = snd_pcm_sw_params_get_boundary(swparams, &boundary); 125 assert(r == 0); 126 r = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, boundary); 127 assert(r == 0); 128 129 r = snd_pcm_sw_params_set_tstamp_mode(pcm, swparams, SND_PCM_TSTAMP_ENABLE); 130 assert(r == 0); 131 132 r = snd_pcm_sw_params(pcm, swparams); 133 assert(r == 0); 134 135 r = snd_pcm_sw_params_current(pcm, swparams); 136 assert(r == 0); 137 138/* assert(snd_pcm_hw_params_is_monotonic(hwparams) > 0); */ 139 140 n_pollfd = snd_pcm_poll_descriptors_count(pcm); 141 assert(n_pollfd > 0); 142 143 pollfds = malloc(sizeof(struct pollfd) * n_pollfd); 144 assert(pollfds); 145 146 r = snd_pcm_poll_descriptors(pcm, pollfds, n_pollfd); 147 assert(r == n_pollfd); 148 149 printf("Starting. Buffer size is %u frames\n", (unsigned int) buffer_size); 150 151 if (cap) { 152 r = snd_pcm_start(pcm); 153 assert(r == 0); 154 } 155 156 for (;;) { 157 snd_pcm_sframes_t avail, delay; 158 struct timespec now, timestamp; 159 unsigned short revents; 160 int handled = 0; 161 uint64_t now_us, timestamp_us; 162 snd_pcm_state_t state; 163 unsigned long long pos; 164 165 r = poll(pollfds, n_pollfd, 0); 166 assert(r >= 0); 167 168 r = snd_pcm_poll_descriptors_revents(pcm, pollfds, n_pollfd, &revents); 169 assert(r == 0); 170 171 if (cap == 0) 172 assert((revents & ~POLLOUT) == 0); 173 else 174 assert((revents & ~POLLIN) == 0); 175 176 avail = snd_pcm_avail(pcm); 177 assert(avail >= 0); 178 179 r = snd_pcm_status(pcm, status); 180 assert(r == 0); 181 182 /* This assertion fails from time to time. ALSA seems to be broken */ 183/* assert(avail == (snd_pcm_sframes_t) snd_pcm_status_get_avail(status)); */ 184/* printf("%lu %lu\n", (unsigned long) avail, (unsigned long) snd_pcm_status_get_avail(status)); */ 185 186 snd_pcm_status_get_htstamp(status, ×tamp); 187 delay = snd_pcm_status_get_delay(status); 188 state = snd_pcm_status_get_state(status); 189 190 r = clock_gettime(CLOCK_MONOTONIC, &now); 191 assert(r == 0); 192 193 assert(!revents || avail > 0); 194 195 if ((!cap && (avail >= fillrate)) || (cap && (unsigned)avail >= buffer_size)) { 196 snd_pcm_sframes_t sframes; 197 198 if (cap == 0) 199 sframes = snd_pcm_writei(pcm, samples, fillrate); 200 else 201 sframes = snd_pcm_readi(pcm, samples, fillrate); 202 assert(sframes == fillrate); 203 204 handled = fillrate; 205 sample_count += fillrate; 206 } 207 208 if (!handled && 209 memcmp(×tamp, &last_timestamp, sizeof(timestamp)) == 0 && 210 avail == last_avail && 211 delay == last_delay) { 212 /* This is boring */ 213 continue; 214 } 215 216 now_us = timespec_us(&now); 217 timestamp_us = timespec_us(×tamp); 218 219 if (cap == 0) 220 pos = (unsigned long long) ((sample_count - handled - delay) * 1000000LU / SAMPLE_RATE); 221 else 222 pos = (unsigned long long) ((sample_count - handled + delay) * 1000000LU / SAMPLE_RATE); 223 224 if (count++ % 50 == 0) 225 printf("Elapsed\tCPU\tALSA\tPos\tSamples\tavail\tdelay\trevents\thandled\tstate\n"); 226 227 printf("%llu\t%llu\t%llu\t%llu\t%llu\t%li\t%li\t%i\t%i\t%i\n", 228 (unsigned long long) (now_us - last_us), 229 (unsigned long long) (now_us - start_us), 230 (unsigned long long) (timestamp_us ? timestamp_us - start_us : 0), 231 pos, 232 (unsigned long long) sample_count, 233 (signed long) avail, 234 (signed long) delay, 235 revents, 236 handled, 237 state); 238 239 if (cap == 0) 240 /** When this assert is hit, most likely something bad 241 * happened, i.e. the avail jumped suddenly. */ 242 assert((unsigned) avail <= buffer_size); 243 244 last_avail = avail; 245 last_delay = delay; 246 last_timestamp = timestamp; 247 last_us = now_us; 248 } 249 250 return 0; 251} 252