aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/tinyalsa/asoundlib.h22
-rw-r--r--mixer.c78
-rw-r--r--pcm.c29
-rw-r--r--tinycap.c37
-rw-r--r--tinymix.c78
5 files changed, 206 insertions, 38 deletions
diff --git a/include/tinyalsa/asoundlib.h b/include/tinyalsa/asoundlib.h
index 42f5348..01a6303 100644
--- a/include/tinyalsa/asoundlib.h
+++ b/include/tinyalsa/asoundlib.h
@@ -142,6 +142,13 @@ unsigned int pcm_params_get_max(struct pcm_params *pcm_params,
/* Returns a human readable reason for the last error */
const char *pcm_get_error(struct pcm *pcm);
+/* Returns the sample size in bits for a PCM format.
+ * As with ALSA formats, this is the storage size for the format, whereas the
+ * format represents the number of significant bits. For example,
+ * PCM_FORMAT_S24_LE uses 32 bits of storage.
+ */
+unsigned int pcm_format_to_bits(enum pcm_format format);
+
/* Returns the buffer size (int frames) that should be used for pcm_write. */
unsigned int pcm_get_buffer_size(struct pcm *pcm);
unsigned int pcm_frames_to_bytes(struct pcm *pcm, unsigned int frames);
@@ -171,6 +178,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);
@@ -190,6 +199,9 @@ struct mixer_ctl;
struct mixer *mixer_open(unsigned int card);
void mixer_close(struct mixer *mixer);
+/* Get info about a mixer */
+const char *mixer_get_name(struct mixer *mixer);
+
/* Obtain mixer controls */
unsigned int mixer_get_num_ctls(struct mixer *mixer);
struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id);
@@ -204,14 +216,20 @@ unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl);
const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
unsigned int enum_id);
+/* Some sound cards update their controls due to external events,
+ * such as HDMI EDID byte data changing when an HDMI cable is
+ * connected. This API allows the count of elements to be updated.
+ */
+void mixer_ctl_update(struct mixer_ctl *ctl);
+
/* Set and get mixer controls */
int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id);
int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent);
int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id);
-int mixer_ctl_get_bytes(struct mixer_ctl *ctl, void *data, size_t len);
+int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count);
int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value);
-int mixer_ctl_set_bytes(struct mixer_ctl *ctl, const void *data, size_t len);
+int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count);
int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string);
/* Determe range of integer mixer controls */
diff --git a/mixer.c b/mixer.c
index f52bca9..4568cca 100644
--- a/mixer.c
+++ b/mixer.c
@@ -52,7 +52,8 @@ struct mixer_ctl {
struct mixer {
int fd;
- struct snd_ctl_elem_info *info;
+ struct snd_ctl_card_info card_info;
+ struct snd_ctl_elem_info *elem_info;
struct mixer_ctl *ctl;
unsigned int count;
};
@@ -79,8 +80,8 @@ void mixer_close(struct mixer *mixer)
free(mixer->ctl);
}
- if (mixer->info)
- free(mixer->info);
+ if (mixer->elem_info)
+ free(mixer->elem_info);
free(mixer);
@@ -111,8 +112,11 @@ struct mixer *mixer_open(unsigned int card)
goto fail;
mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
- mixer->info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
- if (!mixer->ctl || !mixer->info)
+ mixer->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
+ if (!mixer->ctl || !mixer->elem_info)
+ goto fail;
+
+ if (ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info) < 0)
goto fail;
eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));
@@ -127,7 +131,7 @@ struct mixer *mixer_open(unsigned int card)
goto fail;
for (n = 0; n < mixer->count; n++) {
- struct snd_ctl_elem_info *ei = mixer->info + n;
+ struct snd_ctl_elem_info *ei = mixer->elem_info + n;
ei->id.numid = eid[n].numid;
if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
goto fail;
@@ -165,6 +169,11 @@ fail:
return 0;
}
+const char *mixer_get_name(struct mixer *mixer)
+{
+ return (const char *)mixer->card_info.name;
+}
+
unsigned int mixer_get_num_ctls(struct mixer *mixer)
{
if (!mixer)
@@ -189,12 +198,17 @@ struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name)
return NULL;
for (n = 0; n < mixer->count; n++)
- if (!strcmp(name, (char*) mixer->info[n].id.name))
+ if (!strcmp(name, (char*) mixer->elem_info[n].id.name))
return mixer->ctl + n;
return NULL;
}
+void mixer_ctl_update(struct mixer_ctl *ctl)
+{
+ ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info);
+}
+
const char *mixer_ctl_get_name(struct mixer_ctl *ctl)
{
if (!ctl)
@@ -317,13 +331,14 @@ int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id)
return 0;
}
-int mixer_ctl_get_bytes(struct mixer_ctl *ctl, void *data, size_t len)
+int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count)
{
struct snd_ctl_elem_value ev;
int ret;
+ size_t size;
+ void *source;
- if (!ctl || (len > ctl->info->count) || !len || !data ||
- (ctl->info->type != SNDRV_CTL_ELEM_TYPE_BYTES))
+ if (!ctl || (count > ctl->info->count) || !count || !array)
return -EINVAL;
memset(&ev, 0, sizeof(ev));
@@ -333,7 +348,23 @@ int mixer_ctl_get_bytes(struct mixer_ctl *ctl, void *data, size_t len)
if (ret < 0)
return ret;
- memcpy(data, ev.value.bytes.data, len);
+ switch (ctl->info->type) {
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ 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;
+
+ default:
+ return -EINVAL;
+ }
+
+ memcpy(array, source, size * count);
return 0;
}
@@ -372,18 +403,35 @@ int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
}
-int mixer_ctl_set_bytes(struct mixer_ctl *ctl, const void *data, size_t len)
+int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
{
struct snd_ctl_elem_value ev;
+ size_t size;
+ void *dest;
- if (!ctl || (len > ctl->info->count) || !len || !data ||
- (ctl->info->type != SNDRV_CTL_ELEM_TYPE_BYTES))
+ if (!ctl || (count > ctl->info->count) || !count || !array)
return -EINVAL;
memset(&ev, 0, sizeof(ev));
ev.id.numid = ctl->info->id.numid;
- memcpy(ev.value.bytes.data, data, len);
+ switch (ctl->info->type) {
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ size = sizeof(ev.value.integer.value[0]);
+ dest = ev.value.integer.value;
+ break;
+
+ case SNDRV_CTL_ELEM_TYPE_BYTES:
+ size = sizeof(ev.value.bytes.data[0]);
+ dest = ev.value.bytes.data;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ memcpy(dest, array, size * count);
return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
}
diff --git a/pcm.c b/pcm.c
index fd0242a..b081e87 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;
@@ -212,10 +213,11 @@ static unsigned int pcm_format_to_alsa(enum pcm_format format)
};
}
-static unsigned int pcm_format_to_bits(enum pcm_format format)
+unsigned int pcm_format_to_bits(enum pcm_format format)
{
switch (format) {
case PCM_FORMAT_S32_LE:
+ case PCM_FORMAT_S24_LE:
return 32;
default:
case PCM_FORMAT_S16_LE:
@@ -385,14 +387,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
@@ -428,6 +432,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 */
@@ -581,6 +586,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;
@@ -735,11 +741,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);
@@ -755,6 +774,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;
}
@@ -935,6 +955,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,
diff --git a/tinycap.c b/tinycap.c
index 8c9fcfb..7429750 100644
--- a/tinycap.c
+++ b/tinycap.c
@@ -60,7 +60,7 @@ int capturing = 1;
unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
unsigned int channels, unsigned int rate,
- unsigned int bits, unsigned int period_size,
+ enum pcm_format format, unsigned int period_size,
unsigned int period_count);
void sigint_handler(int sig)
@@ -80,6 +80,7 @@ int main(int argc, char **argv)
unsigned int frames;
unsigned int period_size = 1024;
unsigned int period_count = 4;
+ enum pcm_format format;
if (argc < 2) {
fprintf(stderr, "Usage: %s file.wav [-D card] [-d device] [-c channels] "
@@ -137,7 +138,23 @@ int main(int argc, char **argv)
header.audio_format = FORMAT_PCM;
header.num_channels = channels;
header.sample_rate = rate;
- header.bits_per_sample = bits;
+
+ switch (bits) {
+ case 32:
+ format = PCM_FORMAT_S32_LE;
+ break;
+ case 24:
+ format = PCM_FORMAT_S24_LE;
+ break;
+ case 16:
+ format = PCM_FORMAT_S16_LE;
+ break;
+ default:
+ fprintf(stderr, "%d bits is not supported.\n", bits);
+ return 1;
+ }
+
+ header.bits_per_sample = pcm_format_to_bits(format);
header.byte_rate = (header.bits_per_sample / 8) * channels * rate;
header.block_align = channels * (header.bits_per_sample / 8);
header.data_id = ID_DATA;
@@ -148,7 +165,7 @@ int main(int argc, char **argv)
/* install signal handler and begin capturing */
signal(SIGINT, sigint_handler);
frames = capture_sample(file, card, device, header.num_channels,
- header.sample_rate, header.bits_per_sample,
+ header.sample_rate, format,
period_size, period_count);
printf("Captured %d frames\n", frames);
@@ -165,7 +182,7 @@ int main(int argc, char **argv)
unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
unsigned int channels, unsigned int rate,
- unsigned int bits, unsigned int period_size,
+ enum pcm_format format, unsigned int period_size,
unsigned int period_count)
{
struct pcm_config config;
@@ -178,10 +195,7 @@ unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
config.rate = rate;
config.period_size = period_size;
config.period_count = period_count;
- if (bits == 32)
- config.format = PCM_FORMAT_S32_LE;
- else if (bits == 16)
- config.format = PCM_FORMAT_S16_LE;
+ config.format = format;
config.start_threshold = 0;
config.stop_threshold = 0;
config.silence_threshold = 0;
@@ -193,7 +207,7 @@ unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
return 0;
}
- size = pcm_get_buffer_size(pcm);
+ size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
buffer = malloc(size);
if (!buffer) {
fprintf(stderr, "Unable to allocate %d bytes\n", size);
@@ -202,7 +216,8 @@ 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, bits);
+ 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) {
@@ -214,6 +229,6 @@ unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
free(buffer);
pcm_close(pcm);
- return bytes_read / ((bits / 8) * channels);
+ return pcm_bytes_to_frames(pcm, bytes_read);
}
diff --git a/tinymix.c b/tinymix.c
index 962450e..68f9495 100644
--- a/tinymix.c
+++ b/tinymix.c
@@ -27,6 +27,7 @@
*/
#include <tinyalsa/asoundlib.h>
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
@@ -61,14 +62,17 @@ int main(int argc, char **argv)
return EXIT_FAILURE;
}
- if (argc == 1)
+
+ if (argc == 1) {
+ printf("Mixer name: '%s'\n", mixer_get_name(mixer));
tinymix_list_controls(mixer);
- else if (argc == 2)
+ } else if (argc == 2) {
tinymix_detail_control(mixer, argv[1], 1);
- else if (argc >= 3)
+ } else if (argc >= 3) {
tinymix_set_value(mixer, argv[1], &argv[2], argc - 2);
- else
+ } else {
printf("Usage: tinymix [-D card] [control id] [value to set]\n");
+ }
mixer_close(mixer);
@@ -124,6 +128,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));
@@ -138,6 +145,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));
@@ -153,8 +173,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,6 +192,47 @@ 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 void tinymix_set_value(struct mixer *mixer, const char *control,
char **values, unsigned int num_values)
{
@@ -193,6 +254,11 @@ 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 (type == MIXER_CTL_TYPE_BYTE) {
+ tinymix_set_byte_ctl(ctl, control, values, num_values);
+ return;
+ }
+
if (isdigit(values[0][0])) {
if (num_values == 1) {
/* Set all values the same */