aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/tinyalsa/asoundlib.h32
-rw-r--r--mixer.c75
-rw-r--r--pcm.c96
-rw-r--r--tinycap.c44
-rw-r--r--tinymix.c100
-rw-r--r--tinypcminfo.c106
-rw-r--r--tinyplay.c1
7 files changed, 399 insertions, 55 deletions
diff --git a/include/tinyalsa/asoundlib.h b/include/tinyalsa/asoundlib.h
index 3d20b94..a85e810 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
@@ -77,6 +74,11 @@ enum pcm_format {
PCM_FORMAT_MAX,
};
+/* Bitmask has 256 bits (32 bytes) in asound.h */
+struct pcm_mask {
+ unsigned int bits[32 / sizeof(unsigned int)];
+};
+
/* Configuration for a stream */
struct pcm_config {
unsigned int channels;
@@ -101,6 +103,11 @@ struct pcm_config {
/* PCM parameters */
enum pcm_param
{
+ /* mask parameters */
+ PCM_PARAM_ACCESS,
+ PCM_PARAM_FORMAT,
+ PCM_PARAM_SUBFORMAT,
+ /* interval parameters */
PCM_PARAM_SAMPLE_BITS,
PCM_PARAM_FRAME_BITS,
PCM_PARAM_CHANNELS,
@@ -138,15 +145,14 @@ int pcm_is_ready(struct pcm *pcm);
struct pcm_params *pcm_params_get(unsigned int card, unsigned int device,
unsigned int flags);
void pcm_params_free(struct pcm_params *pcm_params);
+
+struct pcm_mask *pcm_params_get_mask(struct pcm_params *pcm_params,
+ enum pcm_param param);
unsigned int pcm_params_get_min(struct pcm_params *pcm_params,
enum pcm_param param);
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 +168,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
@@ -185,10 +190,13 @@ int pcm_read(struct pcm *pcm, void *data, unsigned int count);
* mmap() support.
*/
int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count);
+int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count);
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);
diff --git a/mixer.c b/mixer.c
index 4568cca..73ae6da 100644
--- a/mixer.c
+++ b/mixer.c
@@ -259,14 +259,11 @@ unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl)
static int percent_to_int(struct snd_ctl_elem_info *ei, int percent)
{
- int range;
-
- if (percent > 100)
- percent = 100;
- else if (percent < 0)
- percent = 0;
+ if ((percent > 100) || (percent < 0)) {
+ return -EINVAL;
+ }
- range = (ei->value.integer.max - ei->value.integer.min);
+ int range = (ei->value.integer.max - ei->value.integer.min);
return ei->value.integer.min + (range * percent) / 100;
}
@@ -334,7 +331,7 @@ int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id)
int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count)
{
struct snd_ctl_elem_value ev;
- int ret;
+ int ret = 0;
size_t size;
void *source;
@@ -344,21 +341,41 @@ int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count)
memset(&ev, 0, sizeof(ev));
ev.id.numid = ctl->info->id.numid;
- ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
- if (ret < 0)
- return ret;
-
switch (ctl->info->type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
+ if (ret < 0)
+ return ret;
size = sizeof(ev.value.integer.value[0]);
source = ev.value.integer.value;
break;
case SNDRV_CTL_ELEM_TYPE_BYTES:
- size = sizeof(ev.value.bytes.data[0]);
- source = ev.value.bytes.data;
- break;
+ /* check if this is new bytes TLV */
+ if (ctl->info->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {
+ struct snd_ctl_tlv *tlv;
+ int ret;
+
+ tlv = calloc(1, sizeof(*tlv) + count);
+ tlv->numid = ctl->info->id.numid;
+ tlv->length = count;
+ ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_READ, tlv);
+
+ source = tlv->tlv;
+ memcpy(array, source, count);
+
+ free(tlv);
+
+ return ret;
+ } else {
+ ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
+ if (ret < 0)
+ return ret;
+ size = sizeof(ev.value.bytes.data[0]);
+ source = ev.value.bytes.data;
+ break;
+ }
default:
return -EINVAL;
@@ -389,6 +406,11 @@ int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int 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;
@@ -396,6 +418,10 @@ int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
ev.value.enumerated.item[id] = value;
break;
+ case SNDRV_CTL_ELEM_TYPE_BYTES:
+ ev.value.bytes.data[id] = value;
+ break;
+
default:
return -EINVAL;
}
@@ -423,8 +449,23 @@ int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
break;
case SNDRV_CTL_ELEM_TYPE_BYTES:
- size = sizeof(ev.value.bytes.data[0]);
- dest = ev.value.bytes.data;
+ /* check if this is new bytes TLV */
+ if (ctl->info->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {
+ struct snd_ctl_tlv *tlv;
+ int ret = 0;
+ tlv = calloc(1, sizeof(*tlv) + count);
+ tlv->numid = ctl->info->id.numid;
+ tlv->length = count;
+ memcpy(tlv->tlv, array, count);
+
+ ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_WRITE, tlv);
+ free(tlv);
+
+ return ret;
+ } else {
+ size = sizeof(ev.value.bytes.data[0]);
+ dest = ev.value.bytes.data;
+ }
break;
default:
diff --git a/pcm.c b/pcm.c
index 66c6635..a260bfe 100644
--- a/pcm.c
+++ b/pcm.c
@@ -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;
@@ -300,7 +301,7 @@ static void pcm_hw_munmap_status(struct pcm *pcm) {
}
static int pcm_areas_copy(struct pcm *pcm, unsigned int pcm_offset,
- const char *src, unsigned int src_offset,
+ char *buf, unsigned int src_offset,
unsigned int frames)
{
int size_bytes = pcm_frames_to_bytes(pcm, frames);
@@ -308,12 +309,18 @@ static int pcm_areas_copy(struct pcm *pcm, unsigned int pcm_offset,
int src_offset_bytes = pcm_frames_to_bytes(pcm, src_offset);
/* interleaved only atm */
- memcpy((char*)pcm->mmap_buffer + pcm_offset_bytes,
- src + src_offset_bytes, size_bytes);
+ if (pcm->flags & PCM_IN)
+ memcpy(buf + src_offset_bytes,
+ (char*)pcm->mmap_buffer + pcm_offset_bytes,
+ size_bytes);
+ else
+ memcpy((char*)pcm->mmap_buffer + pcm_offset_bytes,
+ buf + src_offset_bytes,
+ size_bytes);
return 0;
}
-static int pcm_mmap_write_areas(struct pcm *pcm, const char *src,
+static int pcm_mmap_transfer_areas(struct pcm *pcm, char *buf,
unsigned int offset, unsigned int size)
{
void *pcm_areas;
@@ -323,7 +330,7 @@ static int pcm_mmap_write_areas(struct pcm *pcm, const char *src,
while (size > 0) {
frames = size;
pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
- pcm_areas_copy(pcm, pcm_offset, src, offset, frames);
+ pcm_areas_copy(pcm, pcm_offset, buf, offset, frames);
commit = pcm_mmap_commit(pcm, pcm_offset, frames);
if (commit < 0) {
oops(pcm, commit, "failed to commit %d frames\n", frames);
@@ -386,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
@@ -429,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 */
@@ -494,6 +504,12 @@ void pcm_params_free(struct pcm_params *pcm_params)
static int pcm_param_to_alsa(enum pcm_param param)
{
switch (param) {
+ case PCM_PARAM_ACCESS:
+ return SNDRV_PCM_HW_PARAM_ACCESS;
+ case PCM_PARAM_FORMAT:
+ return SNDRV_PCM_HW_PARAM_FORMAT;
+ case PCM_PARAM_SUBFORMAT:
+ return SNDRV_PCM_HW_PARAM_SUBFORMAT;
case PCM_PARAM_SAMPLE_BITS:
return SNDRV_PCM_HW_PARAM_SAMPLE_BITS;
break;
@@ -536,6 +552,23 @@ static int pcm_param_to_alsa(enum pcm_param param)
}
}
+struct pcm_mask *pcm_params_get_mask(struct pcm_params *pcm_params,
+ enum pcm_param param)
+{
+ int p;
+ struct snd_pcm_hw_params *params = (struct snd_pcm_hw_params *)pcm_params;
+ if (params == NULL) {
+ return NULL;
+ }
+
+ p = pcm_param_to_alsa(param);
+ if (p < 0 || !param_is_mask(p)) {
+ return NULL;
+ }
+
+ return (struct pcm_mask *)param_to_mask(params, p);
+}
+
unsigned int pcm_params_get_min(struct pcm_params *pcm_params,
enum pcm_param param)
{
@@ -582,6 +615,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;
@@ -719,6 +753,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;
@@ -736,11 +781,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);
@@ -756,6 +814,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;
}
@@ -894,7 +953,7 @@ int pcm_wait(struct pcm *pcm, int timeout)
return 1;
}
-int pcm_mmap_write(struct pcm *pcm, const void *buffer, unsigned int bytes)
+int pcm_mmap_transfer(struct pcm *pcm, const void *buffer, unsigned int bytes)
{
int err = 0, frames, avail;
unsigned int offset = 0, count;
@@ -936,6 +995,7 @@ int pcm_mmap_write(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,
@@ -955,7 +1015,7 @@ int pcm_mmap_write(struct pcm *pcm, const void *buffer, unsigned int bytes)
break;
/* copy frames from buffer */
- frames = pcm_mmap_write_areas(pcm, buffer, offset, frames);
+ frames = pcm_mmap_transfer_areas(pcm, (void *)buffer, offset, frames);
if (frames < 0) {
fprintf(stderr, "write error: hw 0x%x app 0x%x avail 0x%x\n",
(unsigned int)pcm->mmap_status->hw_ptr,
@@ -970,3 +1030,19 @@ int pcm_mmap_write(struct pcm *pcm, const void *buffer, unsigned int bytes)
return 0;
}
+
+int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count)
+{
+ if ((~pcm->flags) & (PCM_OUT | PCM_MMAP))
+ return -ENOSYS;
+
+ return pcm_mmap_transfer(pcm, (void *)data, count);
+}
+
+int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count)
+{
+ if ((~pcm->flags) & (PCM_IN | PCM_MMAP))
+ return -ENOSYS;
+
+ return pcm_mmap_transfer(pcm, data, count);
+}
diff --git a/tinycap.c b/tinycap.c
index 7429750..180a2dd 100644
--- a/tinycap.c
+++ b/tinycap.c
@@ -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);
@@ -191,6 +206,7 @@ unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
unsigned int size;
unsigned int bytes_read = 0;
+ memset(&config, 0, sizeof(config));
config.channels = channels;
config.rate = rate;
config.period_size = period_size;
@@ -216,8 +232,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) {
diff --git a/tinymix.c b/tinymix.c
index afc2fee..71dd206 100644
--- a/tinymix.c
+++ b/tinymix.c
@@ -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 = NULL;
+ size_t len;
if (isdigit(control[0]))
ctl = mixer_get_ctl(mixer, atoi(control));
@@ -141,6 +147,23 @@ 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) {
+
+ buf = calloc(1, num_values);
+ if (buf == NULL) {
+ fprintf(stderr, "Failed to alloc mem for bytes %d\n", num_values);
+ return;
+ }
+
+ len = num_values;
+ ret = mixer_ctl_get_array(ctl, buf, len);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to mixer_ctl_get_array\n");
+ free(buf);
+ return;
+ }
+ }
+
if (print_all)
printf("%s:", mixer_ctl_get_name(ctl));
@@ -156,8 +179,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");
@@ -172,9 +195,75 @@ static void tinymix_detail_control(struct mixer *mixer, const char *control,
printf(" (range %d->%d)", min, max);
}
}
+
+ free(buf);
+
printf("\n");
}
+static void tinymix_set_byte_ctl(struct mixer_ctl *ctl,
+ char **values, unsigned int num_values)
+{
+ int ret;
+ char *buf;
+ char *end;
+ unsigned int i;
+ long n;
+
+ buf = calloc(1, num_values);
+ if (buf == NULL) {
+ fprintf(stderr, "set_byte_ctl: Failed to alloc mem for bytes %d\n", num_values);
+ exit(EXIT_FAILURE);
+ }
+
+ 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]);
+ goto fail;
+ }
+ if (errno) {
+ fprintf(stderr, "strtol: %s: %s\n", values[i],
+ strerror(errno));
+ goto fail;
+ }
+ if (n < 0 || n > 0xff) {
+ fprintf(stderr, "%s should be between [0, 0xff]\n",
+ values[i]);
+ goto fail;
+ }
+ buf[i] = n;
+ }
+
+ ret = mixer_ctl_set_array(ctl, buf, num_values);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to set binary control\n");
+ goto fail;
+ }
+
+ free(buf);
+ return;
+
+fail:
+ free(buf);
+ 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 +285,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, values, num_values);
+ return;
+ }
+
+ if (is_int(values[0])) {
if (num_values == 1) {
/* Set all values the same */
int value = atoi(values[0]);
diff --git a/tinypcminfo.c b/tinypcminfo.c
index 3282186..99eec34 100644
--- a/tinypcminfo.c
+++ b/tinypcminfo.c
@@ -31,6 +31,72 @@
#include <stdlib.h>
#include <string.h>
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
+#endif
+
+/* The format_lookup is in order of SNDRV_PCM_FORMAT_##index and
+ * matches the grouping in sound/asound.h. Note this is not
+ * continuous and has an empty gap from (25 - 30).
+ */
+static const char *format_lookup[] = {
+ /*[0] =*/ "S8",
+ "U8",
+ "S16_LE",
+ "S16_BE",
+ "U16_LE",
+ "U16_BE",
+ "S24_LE",
+ "S24_BE",
+ "U24_LE",
+ "U24_BE",
+ "S32_LE",
+ "S32_BE",
+ "U32_LE",
+ "U32_BE",
+ "FLOAT_LE",
+ "FLOAT_BE",
+ "FLOAT64_LE",
+ "FLOAT64_BE",
+ "IEC958_SUBFRAME_LE",
+ "IEC958_SUBFRAME_BE",
+ "MU_LAW",
+ "A_LAW",
+ "IMA_ADPCM",
+ "MPEG",
+ /*[24] =*/ "GSM",
+ [31] = "SPECIAL",
+ "S24_3LE",
+ "S24_3BE",
+ "U24_3LE",
+ "U24_3BE",
+ "S20_3LE",
+ "S20_3BE",
+ "U20_3LE",
+ "U20_3BE",
+ "S18_3LE",
+ "S18_3BE",
+ "U18_3LE",
+ /*[43] =*/ "U18_3BE",
+#if 0
+ /* recent additions, may not be present on local asound.h */
+ "G723_24",
+ "G723_24_1B",
+ "G723_40",
+ "G723_40_1B",
+ "DSD_U8",
+ "DSD_U16_LE",
+#endif
+};
+
+/* Returns a human readable name for the format associated with bit_index,
+ * NULL if bit_index is not known.
+ */
+static inline const char *pcm_get_format_name(unsigned bit_index)
+{
+ return bit_index < ARRAY_SIZE(format_lookup) ? format_lookup[bit_index] : NULL;
+}
+
int main(int argc, char **argv)
{
unsigned int device = 0;
@@ -63,6 +129,7 @@ int main(int argc, char **argv)
for (i = 0; i < 2; i++) {
struct pcm_params *params;
+ struct pcm_mask *m;
unsigned int min;
unsigned int max;
@@ -74,6 +141,45 @@ int main(int argc, char **argv)
continue;
}
+ m = pcm_params_get_mask(params, PCM_PARAM_ACCESS);
+ if (m) { /* bitmask, refer to SNDRV_PCM_ACCESS_*, generally interleaved */
+ printf(" Access:\t%#08x\n", m->bits[0]);
+ }
+ m = pcm_params_get_mask(params, PCM_PARAM_FORMAT);
+ if (m) { /* bitmask, refer to: SNDRV_PCM_FORMAT_* */
+ unsigned j, k, count = 0;
+ const unsigned bitcount = sizeof(m->bits[0]) * 8;
+
+ /* we only check first two format masks (out of 8) - others are zero. */
+ printf(" Format[0]:\t%#08x\n", m->bits[0]);
+ printf(" Format[1]:\t%#08x\n", m->bits[1]);
+
+ /* print friendly format names, if they exist */
+ for (k = 0; k < 2; ++k) {
+ for (j = 0; j < bitcount; ++j) {
+ const char *name;
+
+ if (m->bits[k] & (1 << j)) {
+ name = pcm_get_format_name(j + k*bitcount);
+ if (name) {
+ if (count++ == 0) {
+ printf(" Format Name:\t");
+ } else {
+ printf (", ");
+ }
+ printf("%s", name);
+ }
+ }
+ }
+ }
+ if (count) {
+ printf("\n");
+ }
+ }
+ m = pcm_params_get_mask(params, PCM_PARAM_SUBFORMAT);
+ if (m) { /* bitmask, should be 1: SNDRV_PCM_SUBFORMAT_STD */
+ printf(" Subformat:\t%#08x\n", m->bits[0]);
+ }
min = pcm_params_get_min(params, PCM_PARAM_RATE);
max = pcm_params_get_max(params, PCM_PARAM_RATE);
printf(" Rate:\tmin=%uHz\tmax=%uHz\n", min, max);
diff --git a/tinyplay.c b/tinyplay.c
index f4fac9f..88c54ae 100644
--- a/tinyplay.c
+++ b/tinyplay.c
@@ -218,6 +218,7 @@ void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned in
int size;
int num_read;
+ memset(&config, 0, sizeof(config));
config.channels = channels;
config.rate = rate;
config.period_size = period_size;