diff options
-rw-r--r-- | include/tinyalsa/asoundlib.h | 18 | ||||
-rw-r--r-- | mixer.c | 8 | ||||
-rw-r--r-- | pcm.c | 37 | ||||
-rw-r--r-- | tinycap.c | 43 | ||||
-rw-r--r-- | tinymix.c | 85 |
5 files changed, 160 insertions, 31 deletions
diff --git a/include/tinyalsa/asoundlib.h b/include/tinyalsa/asoundlib.h index f578c4d..e9861e3 100644 --- a/include/tinyalsa/asoundlib.h +++ b/include/tinyalsa/asoundlib.h @@ -55,15 +55,12 @@ struct pcm; * second call to pcm_write will attempt to * restart the stream. */ +#define PCM_MONOTONIC 0x00000008 /* see pcm_get_htimestamp */ /* PCM runtime states */ -#define PCM_STATE_OPEN 0 -#define PCM_STATE_SETUP 1 -#define PCM_STATE_PREPARED 2 -#define PCM_STATE_RUNNING 3 +#define PCM_STATE_RUNNING 3 #define PCM_STATE_XRUN 4 #define PCM_STATE_DRAINING 5 -#define PCM_STATE_PAUSED 6 #define PCM_STATE_SUSPENDED 7 #define PCM_STATE_DISCONNECTED 8 @@ -143,10 +140,6 @@ unsigned int pcm_params_get_min(struct pcm_params *pcm_params, unsigned int pcm_params_get_max(struct pcm_params *pcm_params, enum pcm_param param); -/* Set and get config */ -int pcm_get_config(struct pcm *pcm, struct pcm_config *config); -int pcm_set_config(struct pcm *pcm, struct pcm_config *config); - /* Returns a human readable reason for the last error */ const char *pcm_get_error(struct pcm *pcm); @@ -162,10 +155,9 @@ unsigned int pcm_get_buffer_size(struct pcm *pcm); unsigned int pcm_frames_to_bytes(struct pcm *pcm, unsigned int frames); unsigned int pcm_bytes_to_frames(struct pcm *pcm, unsigned int bytes); -/* Returns the pcm latency in ms */ -unsigned int pcm_get_latency(struct pcm *pcm); - /* Returns available frames in pcm buffer and corresponding time stamp. + * The clock is CLOCK_MONOTONIC if flag PCM_MONOTONIC was specified in pcm_open, + * otherwise the clock is CLOCK_REALTIME. * For an input stream, frames available are frames ready for the * application to read. * For an output stream, frames available are the number of empty frames available @@ -190,6 +182,8 @@ int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset, unsigned int *frames); int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames); +/* Prepare the PCM substream to be triggerable */ +int pcm_prepare(struct pcm *pcm); /* Start and stop a PCM channel that doesn't transfer data */ int pcm_start(struct pcm *pcm); int pcm_stop(struct pcm *pcm); @@ -383,12 +383,20 @@ int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value) if (ret < 0) return ret; + if (value > mixer_ctl_get_range_max(ctl)) + value = mixer_ctl_get_range_max(ctl); + switch (ctl->info->type) { case SNDRV_CTL_ELEM_TYPE_BOOLEAN: ev.value.integer.value[id] = !!value; break; case SNDRV_CTL_ELEM_TYPE_INTEGER: + if ((value < mixer_ctl_get_range_min(ctl)) || + (value > mixer_ctl_get_range_max(ctl))) { + return -EINVAL; + } + ev.value.integer.value[id] = value; break; @@ -159,6 +159,7 @@ struct pcm { int fd; unsigned int flags; int running:1; + int prepared:1; int underruns; unsigned int buffer_size; unsigned int boundary; @@ -392,14 +393,16 @@ int pcm_write(struct pcm *pcm, const void *data, unsigned int count) for (;;) { if (!pcm->running) { - if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE)) - return oops(pcm, errno, "cannot prepare channel"); + int prepare_error = pcm_prepare(pcm); + if (prepare_error) + return prepare_error; if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) return oops(pcm, errno, "cannot write initial data"); pcm->running = 1; return 0; } if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) { + pcm->prepared = 0; pcm->running = 0; if (errno == EPIPE) { /* we failed to make our window -- try to restart if we are @@ -435,6 +438,7 @@ int pcm_read(struct pcm *pcm, void *data, unsigned int count) } } if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) { + pcm->prepared = 0; pcm->running = 0; if (errno == EPIPE) { /* we failed to make our window -- try to restart */ @@ -588,6 +592,7 @@ int pcm_close(struct pcm *pcm) if (pcm->fd >= 0) close(pcm->fd); + pcm->prepared = 0; pcm->running = 0; pcm->buffer_size = 0; pcm->fd = -1; @@ -725,6 +730,17 @@ struct pcm *pcm_open(unsigned int card, unsigned int device, goto fail; } +#ifdef SNDRV_PCM_IOCTL_TTSTAMP + if (pcm->flags & PCM_MONOTONIC) { + int arg = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC; + rc = ioctl(pcm->fd, SNDRV_PCM_IOCTL_TTSTAMP, &arg); + if (rc < 0) { + oops(pcm, rc, "cannot set timestamp type"); + goto fail; + } + } +#endif + pcm->underruns = 0; return pcm; @@ -742,11 +758,24 @@ int pcm_is_ready(struct pcm *pcm) return pcm->fd >= 0; } -int pcm_start(struct pcm *pcm) +int pcm_prepare(struct pcm *pcm) { + if (pcm->prepared) + return 0; + if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE) < 0) return oops(pcm, errno, "cannot prepare channel"); + pcm->prepared = 1; + return 0; +} + +int pcm_start(struct pcm *pcm) +{ + int prepare_error = pcm_prepare(pcm); + if (prepare_error) + return prepare_error; + if (pcm->flags & PCM_MMAP) pcm_sync_ptr(pcm, 0); @@ -762,6 +791,7 @@ int pcm_stop(struct pcm *pcm) if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP) < 0) return oops(pcm, errno, "cannot stop channel"); + pcm->prepared = 0; pcm->running = 0; return 0; } @@ -942,6 +972,7 @@ int pcm_mmap_transfer(struct pcm *pcm, const void *buffer, unsigned int bytes) err = pcm_wait(pcm, time); if (err < 0) { + pcm->prepared = 0; pcm->running = 0; fprintf(stderr, "wait error: hw 0x%x app 0x%x avail 0x%x\n", (unsigned int)pcm->mmap_status->hw_ptr, @@ -57,6 +57,7 @@ struct wav_header { }; int capturing = 1; +int prinfo = 1; unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels, unsigned int rate, @@ -81,17 +82,25 @@ int main(int argc, char **argv) unsigned int period_size = 1024; unsigned int period_count = 4; enum pcm_format format; + int no_header = 0; if (argc < 2) { - fprintf(stderr, "Usage: %s file.wav [-D card] [-d device] [-c channels] " - "[-r rate] [-b bits] [-p period_size] [-n n_periods]\n", argv[0]); + fprintf(stderr, "Usage: %s {file.wav | --} [-D card] [-d device] [-c channels] " + "[-r rate] [-b bits] [-p period_size] [-n n_periods]\n\n" + "Use -- for filename to send raw PCM to stdout\n", argv[0]); return 1; } - file = fopen(argv[1], "wb"); - if (!file) { - fprintf(stderr, "Unable to create file '%s'\n", argv[1]); - return 1; + if (strcmp(argv[1],"--") == 0) { + file = stdout; + prinfo = 0; + no_header = 1; + } else { + file = fopen(argv[1], "wb"); + if (!file) { + fprintf(stderr, "Unable to create file '%s'\n", argv[1]); + return 1; + } } /* parse command line arguments */ @@ -160,20 +169,26 @@ int main(int argc, char **argv) header.data_id = ID_DATA; /* leave enough room for header */ - fseek(file, sizeof(struct wav_header), SEEK_SET); + if (!no_header) { + fseek(file, sizeof(struct wav_header), SEEK_SET); + } /* install signal handler and begin capturing */ signal(SIGINT, sigint_handler); frames = capture_sample(file, card, device, header.num_channels, header.sample_rate, format, period_size, period_count); - printf("Captured %d frames\n", frames); + if (prinfo) { + printf("Captured %d frames\n", frames); + } /* write header now all information is known */ - header.data_sz = frames * header.block_align; - header.riff_sz = header.data_sz + sizeof(header) - 8; - fseek(file, 0, SEEK_SET); - fwrite(&header, sizeof(struct wav_header), 1, file); + if (!no_header) { + header.data_sz = frames * header.block_align; + header.riff_sz = header.data_sz + sizeof(header) - 8; + fseek(file, 0, SEEK_SET); + fwrite(&header, sizeof(struct wav_header), 1, file); + } fclose(file); @@ -216,8 +231,10 @@ unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device, return 0; } - printf("Capturing sample: %u ch, %u hz, %u bit\n", channels, rate, + if (prinfo) { + printf("Capturing sample: %u ch, %u hz, %u bit\n", channels, rate, pcm_format_to_bits(format)); + } while (capturing && !pcm_read(pcm, buffer, size)) { if (fwrite(buffer, 1, size, file) != size) { @@ -27,10 +27,13 @@ */ #include <tinyalsa/asoundlib.h> +#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> +#include <limits.h> +#include <errno.h> static void tinymix_list_controls(struct mixer *mixer); static void tinymix_detail_control(struct mixer *mixer, const char *control, @@ -127,6 +130,9 @@ static void tinymix_detail_control(struct mixer *mixer, const char *control, unsigned int num_values; unsigned int i; int min, max; + int ret; + char buf[512] = { 0 }; + size_t len; if (isdigit(control[0])) ctl = mixer_get_ctl(mixer, atoi(control)); @@ -141,6 +147,19 @@ static void tinymix_detail_control(struct mixer *mixer, const char *control, type = mixer_ctl_get_type(ctl); num_values = mixer_ctl_get_num_values(ctl); + if (type == MIXER_CTL_TYPE_BYTE) { + len = num_values; + if (len > sizeof(buf)) { + fprintf(stderr, "Truncating get to %zu bytes\n", sizeof(buf)); + len = sizeof(buf); + } + ret = mixer_ctl_get_array(ctl, buf, len); + if (ret < 0) { + fprintf(stderr, "Failed to mixer_ctl_get_array\n"); + return; + } + } + if (print_all) printf("%s:", mixer_ctl_get_name(ctl)); @@ -156,8 +175,8 @@ static void tinymix_detail_control(struct mixer *mixer, const char *control, case MIXER_CTL_TYPE_ENUM: tinymix_print_enum(ctl, print_all); break; - case MIXER_CTL_TYPE_BYTE: - printf(" 0x%02x", mixer_ctl_get_value(ctl, i)); + case MIXER_CTL_TYPE_BYTE: + printf("%02x", buf[i]); break; default: printf(" unknown"); @@ -175,6 +194,61 @@ static void tinymix_detail_control(struct mixer *mixer, const char *control, printf("\n"); } +static void tinymix_set_byte_ctl(struct mixer_ctl *ctl, const char *control, + char **values, unsigned int num_values) +{ + int ret; + char buf[512] = { 0 }; + char *end; + int i; + long n; + + if (num_values > sizeof(buf)) { + fprintf(stderr, "Truncating set to %zu bytes\n", sizeof(buf)); + num_values = sizeof(buf); + } + + for (i = 0; i < num_values; i++) { + errno = 0; + n = strtol(values[i], &end, 0); + if (*end) { + fprintf(stderr, "%s not an integer\n", values[i]); + exit(EXIT_FAILURE); + } + if (errno) { + fprintf(stderr, "strtol: %s: %s\n", values[i], + strerror(errno)); + exit(EXIT_FAILURE); + } + if (n < 0 || n > 0xff) { + fprintf(stderr, "%s should be between [0, 0xff]\n", + values[i]); + exit(EXIT_FAILURE); + } + buf[i] = n; + } + + ret = mixer_ctl_set_array(ctl, buf, num_values); + if (ret < 0) { + fprintf(stderr, "Failed to set binary control\n"); + exit(EXIT_FAILURE); + } +} + +static int is_int(char *value) +{ + char* end; + long int result; + + errno = 0; + result = strtol(value, &end, 10); + + if (result == LONG_MIN || result == LONG_MAX) + return 0; + + return errno == 0 && *end == '\0'; +} + static void tinymix_set_value(struct mixer *mixer, const char *control, char **values, unsigned int num_values) { @@ -196,7 +270,12 @@ static void tinymix_set_value(struct mixer *mixer, const char *control, type = mixer_ctl_get_type(ctl); num_ctl_values = mixer_ctl_get_num_values(ctl); - if (isdigit(values[0][0])) { + if (type == MIXER_CTL_TYPE_BYTE) { + tinymix_set_byte_ctl(ctl, control, values, num_values); + return; + } + + if (is_int(values[0])) { if (num_values == 1) { /* Set all values the same */ int value = atoi(values[0]); |