aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/tinyalsa/asoundlib.h48
-rw-r--r--mixer.c103
-rw-r--r--pcm.c99
-rw-r--r--tinycap.c76
-rw-r--r--tinymix.c111
-rw-r--r--tinypcminfo.c106
6 files changed, 467 insertions, 76 deletions
diff --git a/include/tinyalsa/asoundlib.h b/include/tinyalsa/asoundlib.h
index ddd7a01..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,27 +145,32 @@ 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);
+/* 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);
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
@@ -178,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);
@@ -201,6 +216,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);
@@ -215,6 +233,12 @@ 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);
diff --git a/mixer.c b/mixer.c
index 7419d3f..73ae6da 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)
@@ -245,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;
}
@@ -320,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;
@@ -330,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;
@@ -375,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;
@@ -382,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;
}
@@ -409,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 fd0242a..1a5d84b 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:
@@ -299,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);
@@ -307,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;
@@ -322,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);
@@ -385,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
@@ -428,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 */
@@ -493,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;
@@ -535,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)
{
@@ -581,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;
@@ -718,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;
@@ -735,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);
@@ -755,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;
}
@@ -893,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;
@@ -935,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,
@@ -954,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,
@@ -969,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 90b37cd..180a2dd 100644
--- a/tinycap.c
+++ b/tinycap.c
@@ -57,10 +57,11 @@ 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,
- 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,17 +81,26 @@ int main(int argc, char **argv)
unsigned int frames;
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 */
@@ -137,26 +147,48 @@ 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;
/* 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, header.bits_per_sample,
+ 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);
@@ -165,7 +197,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;
@@ -179,10 +211,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;
@@ -203,7 +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, bits);
+ 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) {
@@ -215,6 +247,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..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,
@@ -61,14 +64,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 +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));
@@ -138,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));
@@ -153,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");
@@ -169,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)
{
@@ -193,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);