/* * This extra small demo sends silence to your speakers and tests the precision of the reported position. */ #include #include #include #include #include #include #include #include const char* device = "hw:0"; const int channels = 2; const snd_pcm_sframes_t period_size = 1024; const int periods = 4; const int rate = 48000; static uint64_t timespec_us(const struct timespec *ts) { return ts->tv_sec * 1000000LLU + ts->tv_nsec / 1000LLU; } int main(int argc, char* argv[]) { int err; int failed = 0; unsigned int j; short *silence; snd_pcm_sframes_t avail = -1; snd_pcm_uframes_t min_period_size; snd_pcm_uframes_t boundary; int dir; /*struct timespec start, last_timestamp = { 0, 0 };*/ struct timespec start, now, timestamp, last_timestamp = { 0, 0 }; uint64_t start_us, now_us, last_us = 0, start_timestamp_us, timestamp_us, last_timestamp_us=0; int r; /*snd_pcm_t *pcm;*/ snd_pcm_status_t *status; snd_pcm_t *handle; snd_output_t *out = NULL; snd_pcm_hw_params_t *params; snd_pcm_sw_params_t *swparams; snd_pcm_hw_params_alloca(¶ms); snd_pcm_sw_params_alloca(&swparams); snd_output_stdio_attach(&out, stderr, 0); silence = calloc(period_size * periods, sizeof(short) * channels); if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { fprintf(stderr, "Playback open error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } failed = (err = snd_pcm_hw_params_any(handle, params)) < 0; failed = failed || (err = snd_pcm_hw_params_set_rate_resample(handle, params, 1)) < 0; failed = failed || (err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0; failed = failed || (err = snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16)) < 0; failed = failed || (err = snd_pcm_hw_params_set_channels(handle, params, channels)) < 0; failed = failed || (err = snd_pcm_hw_params_set_rate(handle, params, rate, 0)) < 0; if (failed) { fprintf(stderr, "Playback hwparams (access & format) error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } err = snd_pcm_hw_params_get_period_size_min(params, &min_period_size, &dir); if (err < 0) { fprintf(stderr, "Cannot get minimum period size: %s\n", snd_strerror(err)); } fprintf(stderr, "min_period_size: %d frames, dir: %d\n", (int)min_period_size, dir); failed = (err = snd_pcm_hw_params_set_period_size(handle, params, period_size, 0)) < 0; failed = failed || (err = snd_pcm_hw_params_set_periods(handle, params, periods, 0)) < 0; failed = failed || (err = snd_pcm_hw_params(handle, params)) < 0; if (failed) { fprintf(stderr, "Playback hwparams (period size & periods) error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } err = snd_pcm_hw_params_get_fifo_size(params); if (err < 0) { fprintf(stderr, "Playback hwparams: can't get FIFO size, error: %s\n", snd_strerror(err)); } else { fprintf(stderr, "Playback hwparams: FIFO size is %d\n", err); } failed = (err = snd_pcm_sw_params_current(handle, swparams)) < 0; failed = failed || (err = snd_pcm_sw_params_set_start_threshold(handle, swparams, period_size)) < 0; failed = failed || (err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size)) < 0; failed = failed || (err = snd_pcm_sw_params(handle, swparams)) < 0; if (failed) { fprintf(stderr, "Playback swparams error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } snd_pcm_dump(handle, out); fprintf(stderr, "Playing silence\n"); fflush(stderr); memset(silence, 0, period_size * periods * sizeof(short) * channels); err = snd_pcm_writei(handle, silence, period_size * periods); if (err < 0) { fprintf(stderr, "Playback error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } j = 0; snd_pcm_status_alloca(&status); r = clock_gettime(CLOCK_MONOTONIC, &start); start_us = timespec_us(&start); /*printf("start_us=%llu\n", (unsigned long long) start_us);*/ r = snd_pcm_status(handle, status); snd_pcm_status_get_htstamp(status, ×tamp); start_timestamp_us = timespec_us(×tamp); while (1) { /*struct timespec now, timestamp;*/ /*uint64_t now_us, timestamp_us;*/ /*r = snd_pcm_status(pcm, status);*/ r = snd_pcm_status(handle, status); snd_pcm_status_get_htstamp(status, ×tamp); snd_pcm_sframes_t avail1 = snd_pcm_avail(handle); r = clock_gettime(CLOCK_MONOTONIC, &now); now_us = timespec_us(&now); /*printf("start_us=%llu\t now_us=%llu\t last_us=%llu\n", (unsigned long long) start_us, (unsigned long long) now_us, (unsigned long long) last_us);*/ timestamp_us = timespec_us(×tamp); /*printf("start_timestamp_us=%llu\t timestamp_us=%llu\t last_timestamp_us=%llu\n)", (unsigned long long) start_timestamp_us, (unsigned long long) timestamp_us, (unsigned long long) last_timestamp_us);*/ if (avail1 < 0) break; if (avail != avail1) { fprintf(stderr, "now_us-last_us=%llu\t now_us-start_us=%llu\t time_us-start_us=%llu\t time_us-last_time=%llu\t time_us-start_time=%llu\t Available: %d\t loop iteration: %d\n", (unsigned long long) (now_us - last_us), (unsigned long long) (now_us - start_us), (unsigned long long) (timestamp_us - start_us), (unsigned long long) (timestamp_us - last_timestamp_us), (unsigned long long) (timestamp_us - start_timestamp_us), (int)avail1, j); /*(unsigned long long) (timestamp_us ? timestamp_us - start_us : 0),*/ } avail = avail1; j++; last_timestamp_us = timestamp_us; last_us = now_us; } snd_pcm_drop(handle); snd_pcm_close(handle); free(silence); return 0; }