aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk18
-rw-r--r--Makefile46
-rw-r--r--include/sound/asound.h1
-rw-r--r--include/tinyalsa/asoundlib.h40
-rw-r--r--mixer.c176
-rw-r--r--pcm.c122
-rw-r--r--tinycap.c64
-rw-r--r--tinymix.c105
-rw-r--r--tinypcminfo.c108
-rw-r--r--tinyplay.c94
-rw-r--r--tinywavinfo.c214
11 files changed, 835 insertions, 153 deletions
diff --git a/Android.mk b/Android.mk
index 3ebd972..0d58556 100644
--- a/Android.mk
+++ b/Android.mk
@@ -21,6 +21,24 @@ include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_C_INCLUDES:= external/tinyalsa/include
+LOCAL_SRC_FILES:= tinywavinfo.c
+LOCAL_MODULE := tinywavinfo
+LOCAL_SHARED_LIBRARIES:= libcutils libutils libm
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_EXECUTABLE)
+
+ifeq ($(HOST_OS), linux)
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES:= external/tinyalsa/include
+LOCAL_SRC_FILES:= tinywavinfo.c
+LOCAL_MODULE := tinywavinfo
+LOCAL_STATIC_LIBRARIES:= libcutils libutils
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_HOST_EXECUTABLE)
+endif
+
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES:= external/tinyalsa/include
LOCAL_SRC_FILES:= tinycap.c
LOCAL_MODULE := tinycap
LOCAL_SHARED_LIBRARIES:= libcutils libutils libtinyalsa
diff --git a/Makefile b/Makefile
index f172a13..d895384 100644
--- a/Makefile
+++ b/Makefile
@@ -1,29 +1,45 @@
-CFLAGS = -c -fPIC -Wall
+CFLAGS ?= -Wall
+LDFLAGS ?=
INC = include
OBJECTS = mixer.o pcm.o
-LIB = libtinyalsa.so
+LIB = libtinyalsa.a
+SHLIB = libtinyalsa.so
CROSS_COMPILE =
+PREFIX = /usr/local
-all: $(LIB) tinyplay tinycap tinymix tinypcminfo
+.PHONY: all
+all: $(LIB) $(SHLIB) tinyplay tinycap tinymix tinypcminfo
-tinyplay: $(LIB) tinyplay.o
- $(CROSS_COMPILE)gcc tinyplay.o -L. -ltinyalsa -o tinyplay
+tinyplay: $(SHLIB) tinyplay.o
+ $(CROSS_COMPILE)$(CC) $(LDFLAGS) tinyplay.o -L. -ltinyalsa -o tinyplay
-tinycap: $(LIB) tinycap.o
- $(CROSS_COMPILE)gcc tinycap.o -L. -ltinyalsa -o tinycap
+tinycap: $(SHLIB) tinycap.o
+ $(CROSS_COMPILE)$(CC) $(LDFLAGS) tinycap.o -L. -ltinyalsa -o tinycap
-tinymix: $(LIB) tinymix.o
- $(CROSS_COMPILE)gcc tinymix.o -L. -ltinyalsa -o tinymix
+tinymix: $(SHLIB) tinymix.o
+ $(CROSS_COMPILE)$(CC) $(LDFLAGS) tinymix.o -L. -ltinyalsa -o tinymix
-tinypcminfo: $(LIB) tinypcminfo.o
- $(CROSS_COMPILE)gcc tinypcminfo.o -L. -ltinyalsa -o tinypcminfo
+tinypcminfo: $(SHLIB) tinypcminfo.o
+ $(CROSS_COMPILE)$(CC) $(LDFLAGS) tinypcminfo.o -L. -ltinyalsa -o tinypcminfo
+
+$(SHLIB): $(OBJECTS)
+ $(CROSS_COMPILE)$(CC) $(LDFLAGS) -shared $(OBJECTS) -o $(SHLIB)
$(LIB): $(OBJECTS)
- $(CROSS_COMPILE)gcc -shared $(OBJECTS) -o $(LIB)
+ $(CROSS_COMPILE)$(AR) rcs $@ $^
-.c.o:
- $(CROSS_COMPILE)gcc $(CFLAGS) $< -I$(INC)
+%.o: %.c
+ $(CROSS_COMPILE)$(CC) $(CFLAGS) -fPIC -c $^ -I$(INC) -o $@
+.PHONY: clean
clean:
- -rm $(LIB) $(OBJECTS) tinyplay.o tinyplay tinycap.o tinycap \
+ -rm $(LIB) $(SHLIB) $(OBJECTS) tinyplay.o tinyplay tinycap.o tinycap \
tinymix.o tinymix tinypcminfo.o tinypcminfo
+
+.PHONY: install
+install: $(LIB) $(SHLIB)
+ cp -u $(SHLIB) $(PREFIX)/lib/$(SHLIB)
+ cp -u $(LIB) $(PREFIX)/lib/$(LIB)
+ mkdir -p $(PREFIX)/include/tinyalsa
+ cp -u $(INC)/tinyalsa/asoundlib.h $(PREFIX)/include/tinyalsa/asoundlib.h
+
diff --git a/include/sound/asound.h b/include/sound/asound.h
index a041628..7c6de81 100644
--- a/include/sound/asound.h
+++ b/include/sound/asound.h
@@ -12,6 +12,7 @@
#ifndef __SOUND_ASOUND_H
#define __SOUND_ASOUND_H
+#include <time.h>
#include <linux/types.h>
#define SNDRV_PROTOCOL_VERSION(major, minor, subminor) (((major)<<16)|((minor)<<8)|(subminor))
diff --git a/include/tinyalsa/asoundlib.h b/include/tinyalsa/asoundlib.h
index 873b9c9..8c215ce 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,14 +145,16 @@ 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 the file descriptor associated with the pcm */
+int pcm_get_file_descriptor(struct pcm *pcm);
/* Returns a human readable reason for the last error */
const char *pcm_get_error(struct pcm *pcm);
@@ -162,10 +171,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
@@ -174,6 +182,9 @@ unsigned int pcm_get_latency(struct pcm *pcm);
int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail,
struct timespec *tstamp);
+/* Returns the subdevice on which the pcm has been opened */
+unsigned int pcm_get_subdevice(struct pcm *pcm);
+
/* Write data to the fifo.
* Will start playback on the first write or on a write that
* occurs after a fifo underrun.
@@ -185,10 +196,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);
@@ -197,6 +211,9 @@ int pcm_stop(struct pcm *pcm);
int pcm_wait(struct pcm *pcm, int timeout);
+/* Get the pcm delay */
+long pcm_get_delay(struct pcm *pcm);
+
/*
* MIXER API
*/
@@ -220,6 +237,7 @@ struct mixer_ctl *mixer_get_ctl_by_name_and_index(struct mixer *mixer,
unsigned int index);
/* Get info about mixer controls */
+unsigned int mixer_ctl_get_id(struct mixer_ctl *ctl);
const char *mixer_ctl_get_name(struct mixer_ctl *ctl);
enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl);
const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl);
diff --git a/mixer.c b/mixer.c
index afe9d7f..ac294ca 100644
--- a/mixer.c
+++ b/mixer.c
@@ -28,11 +28,14 @@
#include <stdio.h>
#include <stdlib.h>
+#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
+#include <limits.h>
+#include <time.h>
#include <sys/ioctl.h>
@@ -46,14 +49,13 @@
struct mixer_ctl {
struct mixer *mixer;
- struct snd_ctl_elem_info *info;
+ struct snd_ctl_elem_info info;
char **ename;
};
struct mixer {
int fd;
struct snd_ctl_card_info card_info;
- struct snd_ctl_elem_info *elem_info;
struct mixer_ctl *ctl;
unsigned int count;
};
@@ -71,7 +73,7 @@ void mixer_close(struct mixer *mixer)
if (mixer->ctl) {
for (n = 0; n < mixer->count; n++) {
if (mixer->ctl[n].ename) {
- unsigned int max = mixer->ctl[n].info->value.enumerated.items;
+ unsigned int max = mixer->ctl[n].info.value.enumerated.items;
for (m = 0; m < max; m++)
free(mixer->ctl[n].ename[m]);
free(mixer->ctl[n].ename);
@@ -80,9 +82,6 @@ void mixer_close(struct mixer *mixer)
free(mixer->ctl);
}
- if (mixer->elem_info)
- free(mixer->elem_info);
-
free(mixer);
/* TODO: verify frees */
@@ -112,8 +111,7 @@ struct mixer *mixer_open(unsigned int card)
goto fail;
mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
- mixer->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
- if (!mixer->ctl || !mixer->elem_info)
+ if (!mixer->ctl)
goto fail;
if (ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info) < 0)
@@ -131,11 +129,10 @@ struct mixer *mixer_open(unsigned int card)
goto fail;
for (n = 0; n < mixer->count; n++) {
- struct snd_ctl_elem_info *ei = mixer->elem_info + n;
+ struct snd_ctl_elem_info *ei = &mixer->ctl[n].info;
ei->id.numid = eid[n].numid;
if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
goto fail;
- mixer->ctl[n].info = ei;
mixer->ctl[n].mixer = mixer;
if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
char **enames = calloc(ei->value.enumerated.items, sizeof(char*));
@@ -200,12 +197,15 @@ struct mixer_ctl *mixer_get_ctl_by_name_and_index(struct mixer *mixer,
unsigned int index)
{
unsigned int n;
+ struct mixer_ctl *ctl;
if (!mixer)
return NULL;
+ ctl = mixer->ctl;
+
for (n = 0; n < mixer->count; n++)
- if (!strcmp(name, (char*) mixer->elem_info[n].id.name))
+ if (!strcmp(name, (char*) ctl[n].info.id.name))
if (index-- == 0)
return mixer->ctl + n;
@@ -217,12 +217,23 @@ void mixer_ctl_update(struct mixer_ctl *ctl)
ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info);
}
+unsigned int mixer_ctl_get_id(struct mixer_ctl *ctl)
+{
+ if (!ctl)
+ return UINT_MAX;
+
+ /* numid values start at 1, return a 0-base value that
+ * can be passed to mixer_get_ctl()
+ */
+ return ctl->info.id.numid - 1;
+}
+
const char *mixer_ctl_get_name(struct mixer_ctl *ctl)
{
if (!ctl)
return NULL;
- return (const char *)ctl->info->id.name;
+ return (const char *)ctl->info.id.name;
}
enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl)
@@ -230,7 +241,7 @@ enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl)
if (!ctl)
return MIXER_CTL_TYPE_UNKNOWN;
- switch (ctl->info->type) {
+ switch (ctl->info.type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return MIXER_CTL_TYPE_BOOL;
case SNDRV_CTL_ELEM_TYPE_INTEGER: return MIXER_CTL_TYPE_INT;
case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return MIXER_CTL_TYPE_ENUM;
@@ -246,7 +257,7 @@ const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl)
if (!ctl)
return "";
- switch (ctl->info->type) {
+ switch (ctl->info.type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL";
case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT";
case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";
@@ -262,19 +273,16 @@ unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl)
if (!ctl)
return 0;
- return ctl->info->count;
+ return ctl->info.count;
}
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;
}
@@ -291,18 +299,18 @@ static int int_to_percent(struct snd_ctl_elem_info *ei, int value)
int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id)
{
- if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
+ if (!ctl || (ctl->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER))
return -EINVAL;
- return int_to_percent(ctl->info, mixer_ctl_get_value(ctl, id));
+ return int_to_percent(&ctl->info, mixer_ctl_get_value(ctl, id));
}
int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent)
{
- if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
+ if (!ctl || (ctl->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER))
return -EINVAL;
- return mixer_ctl_set_value(ctl, id, percent_to_int(ctl->info, percent));
+ return mixer_ctl_set_value(ctl, id, percent_to_int(&ctl->info, percent));
}
int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id)
@@ -310,16 +318,16 @@ int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id)
struct snd_ctl_elem_value ev;
int ret;
- if (!ctl || (id >= ctl->info->count))
+ if (!ctl || (id >= ctl->info.count))
return -EINVAL;
memset(&ev, 0, sizeof(ev));
- ev.id.numid = ctl->info->id.numid;
+ 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) {
+ switch (ctl->info.type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
return !!ev.value.integer.value[id];
@@ -342,31 +350,55 @@ 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;
- if (!ctl || (count > ctl->info->count) || !count || !array)
+ if (!ctl || (count > ctl->info.count) || !count || !array)
return -EINVAL;
memset(&ev, 0, sizeof(ev));
- ev.id.numid = ctl->info->id.numid;
+ 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) {
+ 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;
+
+ if (count > SIZE_MAX - sizeof(*tlv))
+ return -EINVAL;
+ tlv = calloc(1, sizeof(*tlv) + count);
+ if (!tlv)
+ return -ENOMEM;
+ 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;
@@ -382,21 +414,26 @@ int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
struct snd_ctl_elem_value ev;
int ret;
- if (!ctl || (id >= ctl->info->count))
+ if (!ctl || (id >= ctl->info.count))
return -EINVAL;
memset(&ev, 0, sizeof(ev));
- ev.id.numid = ctl->info->id.numid;
+ 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) {
+ 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;
@@ -404,6 +441,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;
}
@@ -417,13 +458,13 @@ int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
size_t size;
void *dest;
- if (!ctl || (count > ctl->info->count) || !count || !array)
+ if (!ctl || (count > ctl->info.count) || !count || !array)
return -EINVAL;
memset(&ev, 0, sizeof(ev));
- ev.id.numid = ctl->info->id.numid;
+ ev.id.numid = ctl->info.id.numid;
- switch (ctl->info->type) {
+ switch (ctl->info.type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
case SNDRV_CTL_ELEM_TYPE_INTEGER:
size = sizeof(ev.value.integer.value[0]);
@@ -431,8 +472,27 @@ 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;
+ if (count > SIZE_MAX - sizeof(*tlv))
+ return -EINVAL;
+ tlv = calloc(1, sizeof(*tlv) + count);
+ if (!tlv)
+ return -ENOMEM;
+ 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:
@@ -446,18 +506,18 @@ int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
int mixer_ctl_get_range_min(struct mixer_ctl *ctl)
{
- if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
+ if (!ctl || (ctl->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER))
return -EINVAL;
- return ctl->info->value.integer.min;
+ return ctl->info.value.integer.min;
}
int mixer_ctl_get_range_max(struct mixer_ctl *ctl)
{
- if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
+ if (!ctl || (ctl->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER))
return -EINVAL;
- return ctl->info->value.integer.max;
+ return ctl->info.value.integer.max;
}
unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl)
@@ -465,14 +525,14 @@ unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl)
if (!ctl)
return 0;
- return ctl->info->value.enumerated.items;
+ return ctl->info.value.enumerated.items;
}
const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
unsigned int enum_id)
{
- if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) ||
- (enum_id >= ctl->info->value.enumerated.items))
+ if (!ctl || (ctl->info.type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) ||
+ (enum_id >= ctl->info.value.enumerated.items))
return NULL;
return (const char *)ctl->ename[enum_id];
@@ -484,15 +544,15 @@ int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
struct snd_ctl_elem_value ev;
int ret;
- if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED))
+ if (!ctl || (ctl->info.type != SNDRV_CTL_ELEM_TYPE_ENUMERATED))
return -EINVAL;
- num_enums = ctl->info->value.enumerated.items;
+ num_enums = ctl->info.value.enumerated.items;
for (i = 0; i < num_enums; i++) {
if (!strcmp(string, ctl->ename[i])) {
memset(&ev, 0, sizeof(ev));
ev.value.enumerated.item[0] = i;
- ev.id.numid = ctl->info->id.numid;
+ ev.id.numid = ctl->info.id.numid;
ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
if (ret < 0)
return ret;
diff --git a/pcm.c b/pcm.c
index ca3eb3e..c1f2212 100644
--- a/pcm.c
+++ b/pcm.c
@@ -38,6 +38,7 @@
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/time.h>
+#include <time.h>
#include <limits.h>
#include <linux/ioctl.h>
@@ -159,6 +160,7 @@ struct pcm {
int fd;
unsigned int flags;
int running:1;
+ int prepared:1;
int underruns;
unsigned int buffer_size;
unsigned int boundary;
@@ -169,6 +171,8 @@ struct pcm {
struct snd_pcm_sync_ptr *sync_ptr;
void *mmap_buffer;
unsigned int noirq_frames_per_msec;
+ long pcm_delay;
+ unsigned int subdevice;
};
unsigned int pcm_get_buffer_size(struct pcm *pcm)
@@ -176,11 +180,21 @@ unsigned int pcm_get_buffer_size(struct pcm *pcm)
return pcm->buffer_size;
}
+int pcm_get_file_descriptor(struct pcm *pcm)
+{
+ return pcm->fd;
+}
+
const char* pcm_get_error(struct pcm *pcm)
{
return pcm->error;
}
+unsigned int pcm_get_subdevice(struct pcm *pcm)
+{
+ return pcm->subdevice;
+}
+
static int oops(struct pcm *pcm, int e, const char *fmt, ...)
{
va_list ap;
@@ -300,7 +314,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 +322,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 +343,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 +406,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 +451,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 +517,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 +565,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 +628,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;
@@ -619,6 +666,7 @@ struct pcm *pcm_open(unsigned int card, unsigned int device,
oops(pcm, errno, "cannot get info");
goto fail_close;
}
+ pcm->subdevice = info.subdevice;
param_init(&params);
param_set_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT,
@@ -719,6 +767,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 +795,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 +828,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;
}
@@ -768,7 +841,7 @@ static inline int pcm_mmap_playback_avail(struct pcm *pcm)
if (avail < 0)
avail += pcm->boundary;
- else if (avail > (int)pcm->boundary)
+ else if (avail >= (int)pcm->boundary)
avail -= pcm->boundary;
return avail;
@@ -859,7 +932,7 @@ int pcm_wait(struct pcm *pcm, int timeout)
int err;
pfd.fd = pcm->fd;
- pfd.events = POLLOUT | POLLERR | POLLNVAL;
+ pfd.events = POLLIN | POLLOUT | POLLERR | POLLNVAL;
do {
/* let's wait for avail or timeout */
@@ -894,7 +967,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 +1009,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 +1029,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 +1044,27 @@ 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);
+}
+
+long pcm_get_delay(struct pcm *pcm)
+{
+ if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DELAY, &pcm->pcm_delay) < 0)
+ return -1;
+
+ return pcm->pcm_delay;
+}
diff --git a/tinycap.c b/tinycap.c
index 7429750..4e0e651 100644
--- a/tinycap.c
+++ b/tinycap.c
@@ -57,11 +57,12 @@ 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,
enum pcm_format format, unsigned int period_size,
- unsigned int period_count);
+ unsigned int period_count, unsigned int capture_time);
void sigint_handler(int sig)
{
@@ -80,18 +81,27 @@ int main(int argc, char **argv)
unsigned int frames;
unsigned int period_size = 1024;
unsigned int period_count = 4;
+ unsigned int capture_time = 0;
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] [-t time_in_seconds]\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 */
@@ -125,6 +135,10 @@ int main(int argc, char **argv)
argv++;
if (*argv)
period_count = atoi(*argv);
+ } else if (strcmp(*argv, "-t") == 0) {
+ argv++;
+ if (*argv)
+ capture_time = atoi(*argv);
}
if (*argv)
argv++;
@@ -150,7 +164,7 @@ int main(int argc, char **argv)
format = PCM_FORMAT_S16_LE;
break;
default:
- fprintf(stderr, "%d bits is not supported.\n", bits);
+ fprintf(stderr, "%u bits is not supported.\n", bits);
return 1;
}
@@ -160,20 +174,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);
+ period_size, period_count, capture_time);
+ if (prinfo) {
+ printf("Captured %u 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);
@@ -183,7 +203,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,
enum pcm_format format, unsigned int period_size,
- unsigned int period_count)
+ unsigned int period_count, unsigned int capture_time)
{
struct pcm_config config;
struct pcm *pcm;
@@ -191,6 +211,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;
@@ -210,16 +231,23 @@ unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
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);
+ fprintf(stderr, "Unable to allocate %u bytes\n", size);
free(buffer);
pcm_close(pcm);
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 (capture_time > 0 &&
+ ((bytes_read + size) > pcm_frames_to_bytes(pcm, capture_time * rate))) {
+ size = pcm_frames_to_bytes(pcm, capture_time * rate) - bytes_read;
+ capturing = 0;
+ }
if (fwrite(buffer, 1, size, file) != size) {
fprintf(stderr,"Error capturing sample\n");
break;
diff --git a/tinymix.c b/tinymix.c
index afc2fee..5966df9 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,
@@ -87,7 +90,7 @@ static void tinymix_list_controls(struct mixer *mixer)
num_ctls = mixer_get_num_ctls(mixer);
- printf("Number of controls: %d\n", num_ctls);
+ printf("Number of controls: %u\n", num_ctls);
printf("ctl\ttype\tnum\t%-40s value\n", "name");
for (i = 0; i < num_ctls; i++) {
@@ -96,7 +99,7 @@ static void tinymix_list_controls(struct mixer *mixer)
name = mixer_ctl_get_name(ctl);
type = mixer_ctl_get_type_string(ctl);
num_values = mixer_ctl_get_num_values(ctl);
- printf("%d\t%s\t%d\t%-40s", i, type, num_values, name);
+ printf("%u\t%s\t%u\t%-40s", i, type, num_values, name);
tinymix_detail_control(mixer, name, 0);
}
}
@@ -127,6 +130,8 @@ 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;
if (isdigit(control[0]))
ctl = mixer_get_ctl(mixer, atoi(control));
@@ -141,6 +146,21 @@ 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) && (num_values > 0)) {
+ buf = calloc(1, num_values);
+ if (buf == NULL) {
+ fprintf(stderr, "Failed to alloc mem for bytes %u\n", num_values);
+ return;
+ }
+
+ ret = mixer_ctl_get_array(ctl, buf, num_values);
+ 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 +176,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 +192,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 %u\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 +282,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]);
@@ -211,13 +302,13 @@ static void tinymix_set_value(struct mixer *mixer, const char *control,
/* Set multiple values */
if (num_values > num_ctl_values) {
fprintf(stderr,
- "Error: %d values given, but control only takes %d\n",
+ "Error: %u values given, but control only takes %u\n",
num_values, num_ctl_values);
return;
}
for (i = 0; i < num_values; i++) {
if (mixer_ctl_set_value(ctl, i, atoi(values[i]))) {
- fprintf(stderr, "Error: invalid value for index %d\n", i);
+ fprintf(stderr, "Error: invalid value for index %u\n", i);
return;
}
}
diff --git a/tinypcminfo.c b/tinypcminfo.c
index 3282186..4eb0afa 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;
@@ -59,10 +125,11 @@ int main(int argc, char **argv)
argv++;
}
- printf("Info for card %d, device %d:\n", card, device);
+ printf("Info for card %u, device %u:\n", card, device);
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..920b0e8 100644
--- a/tinyplay.c
+++ b/tinyplay.c
@@ -81,12 +81,18 @@ int main(int argc, char **argv)
unsigned int card = 0;
unsigned int period_size = 1024;
unsigned int period_count = 4;
+ unsigned int channels = 2;
+ unsigned int rate = 48000;
+ unsigned int bits = 16;
+ unsigned int is_raw = 0; /* Default wav file */
char *filename;
int more_chunks = 1;
if (argc < 2) {
- fprintf(stderr, "Usage: %s file.wav [-D card] [-d device] [-p period_size]"
+ fprintf(stderr, "Usage1: %s file.wav [-D card] [-d device] [-p period_size]"
" [-n n_periods] \n", argv[0]);
+ fprintf(stderr, "Usage2: %s file.raw [-D card] [-d device] [-p period_size] "
+ "[-n n_periods] [-c channels] [-r rate] [-b bits] -i raw \n", argv[0]);
return 1;
}
@@ -97,34 +103,6 @@ int main(int argc, char **argv)
return 1;
}
- fread(&riff_wave_header, sizeof(riff_wave_header), 1, file);
- if ((riff_wave_header.riff_id != ID_RIFF) ||
- (riff_wave_header.wave_id != ID_WAVE)) {
- fprintf(stderr, "Error: '%s' is not a riff/wave file\n", filename);
- fclose(file);
- return 1;
- }
-
- do {
- fread(&chunk_header, sizeof(chunk_header), 1, file);
-
- switch (chunk_header.id) {
- case ID_FMT:
- fread(&chunk_fmt, sizeof(chunk_fmt), 1, file);
- /* If the format header is larger, skip the rest */
- if (chunk_header.sz > sizeof(chunk_fmt))
- fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR);
- break;
- case ID_DATA:
- /* Stop looking for chunks */
- more_chunks = 0;
- break;
- default:
- /* Unknown chunk, skip bytes */
- fseek(file, chunk_header.sz, SEEK_CUR);
- }
- } while (more_chunks);
-
/* parse command line arguments */
argv += 2;
while (*argv) {
@@ -148,12 +126,65 @@ int main(int argc, char **argv)
if (*argv)
card = atoi(*argv);
}
+ if (strcmp(*argv, "-c") == 0) {
+ argv++;
+ if (*argv)
+ channels = atoi(*argv);
+ }
+ if (strcmp(*argv, "-r") == 0) {
+ argv++;
+ if (*argv)
+ rate = atoi(*argv);
+ }
+ if (strcmp(*argv, "-b") == 0) {
+ argv++;
+ if (*argv)
+ bits = atoi(*argv);
+ }
+ if (strcmp(*argv, "-i") == 0) {
+ argv++;
+ if (*argv) {
+ if (strcasecmp(*argv, "raw") == 0) {
+ is_raw = 1;
+ }
+ }
+ }
if (*argv)
argv++;
}
- play_sample(file, card, device, chunk_fmt.num_channels, chunk_fmt.sample_rate,
- chunk_fmt.bits_per_sample, period_size, period_count);
+ if ( !is_raw ) {
+ fread(&riff_wave_header, sizeof(riff_wave_header), 1, file);
+ if ((riff_wave_header.riff_id != ID_RIFF) ||
+ (riff_wave_header.wave_id != ID_WAVE)) {
+ fprintf(stderr, "Error: '%s' is not a riff/wave file\n", filename);
+ fclose(file);
+ return 1;
+ }
+ do {
+ fread(&chunk_header, sizeof(chunk_header), 1, file);
+ switch (chunk_header.id) {
+ case ID_FMT:
+ fread(&chunk_fmt, sizeof(chunk_fmt), 1, file);
+ /* If the format header is larger, skip the rest */
+ if (chunk_header.sz > sizeof(chunk_fmt))
+ fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR);
+ break;
+ case ID_DATA:
+ /* Stop looking for chunks */
+ more_chunks = 0;
+ break;
+ default:
+ /* Unknown chunk, skip bytes */
+ fseek(file, chunk_header.sz, SEEK_CUR);
+ }
+ } while (more_chunks);
+ channels = chunk_fmt.num_channels;
+ rate = chunk_fmt.sample_rate;
+ bits = chunk_fmt.bits_per_sample;
+ }
+
+ play_sample(file, card, device, channels, rate, bits, period_size, period_count);
fclose(file);
@@ -218,6 +249,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;
diff --git a/tinywavinfo.c b/tinywavinfo.c
new file mode 100644
index 0000000..99ee5da
--- /dev/null
+++ b/tinywavinfo.c
@@ -0,0 +1,214 @@
+/* tinywavinfo.c
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of The Android Open Source Project nor the names of
+** its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+** DAMAGE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <signal.h>
+#include <math.h>
+#include <malloc.h>
+
+#define ID_RIFF 0x46464952
+#define ID_WAVE 0x45564157
+#define ID_FMT 0x20746d66
+#define ID_DATA 0x61746164
+
+struct riff_wave_header {
+ uint32_t riff_id;
+ uint32_t riff_sz;
+ uint32_t wave_id;
+};
+
+struct chunk_header {
+ uint32_t id;
+ uint32_t sz;
+};
+
+struct chunk_fmt {
+ uint16_t audio_format;
+ uint16_t num_channels;
+ uint32_t sample_rate;
+ uint32_t byte_rate;
+ uint16_t block_align;
+ uint16_t bits_per_sample;
+};
+
+static int close = 0;
+
+void analyse_sample(FILE *file, unsigned int channels, unsigned int bits,
+ unsigned int data_chunk_size);
+
+void stream_close(int sig)
+{
+ /* allow the stream to be closed gracefully */
+ signal(sig, SIG_IGN);
+ close = 1;
+}
+
+int main(int argc, char **argv)
+{
+ FILE *file;
+ struct riff_wave_header riff_wave_header;
+ struct chunk_header chunk_header;
+ struct chunk_fmt chunk_fmt;
+ char *filename;
+ int more_chunks = 1;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s file.wav \n", argv[0]);
+ return 1;
+ }
+
+ filename = argv[1];
+ file = fopen(filename, "rb");
+ if (!file) {
+ fprintf(stderr, "Unable to open file '%s'\n", filename);
+ return 1;
+ }
+
+ fread(&riff_wave_header, sizeof(riff_wave_header), 1, file);
+ if ((riff_wave_header.riff_id != ID_RIFF) ||
+ (riff_wave_header.wave_id != ID_WAVE)) {
+ fprintf(stderr, "Error: '%s' is not a riff/wave file\n", filename);
+ fclose(file);
+ return 1;
+ }
+
+ do {
+ fread(&chunk_header, sizeof(chunk_header), 1, file);
+
+ switch (chunk_header.id) {
+ case ID_FMT:
+ fread(&chunk_fmt, sizeof(chunk_fmt), 1, file);
+ /* If the format header is larger, skip the rest */
+ if (chunk_header.sz > sizeof(chunk_fmt))
+ fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR);
+ break;
+ case ID_DATA:
+ /* Stop looking for chunks */
+ more_chunks = 0;
+ break;
+ default:
+ /* Unknown chunk, skip bytes */
+ fseek(file, chunk_header.sz, SEEK_CUR);
+ }
+ } while (more_chunks);
+
+ printf("Input File : %s \n", filename);
+ printf("Channels : %u \n", chunk_fmt.num_channels);
+ printf("Sample Rate : %u \n", chunk_fmt.sample_rate);
+ printf("Bits per sample : %u \n\n", chunk_fmt.bits_per_sample);
+
+ analyse_sample(file, chunk_fmt.num_channels, chunk_fmt.bits_per_sample,
+ chunk_header.sz);
+
+ fclose(file);
+
+ return 0;
+}
+
+void analyse_sample(FILE *file, unsigned int channels, unsigned int bits,
+ unsigned int data_chunk_size)
+{
+ void *buffer;
+ int size;
+ int num_read;
+ int i;
+ unsigned int ch;
+ int frame_size = 1024;
+ unsigned int byte_align = 0;
+ float *power;
+ int total_sample_per_channel;
+ float normalization_factor;
+
+ if (bits == 32)
+ byte_align = 4;
+ else if (bits == 16)
+ byte_align = 2;
+
+ normalization_factor = (float)pow(2.0, (bits-1));
+
+ size = channels * byte_align * frame_size;
+ buffer = memalign(byte_align, size);
+
+ if (!buffer) {
+ fprintf(stderr, "Unable to allocate %d bytes\n", size);
+ free(buffer);
+ return;
+ }
+
+ power = (float *) calloc(channels, sizeof(float));
+
+ total_sample_per_channel = data_chunk_size / (channels * byte_align);
+
+ /* catch ctrl-c to shutdown cleanly */
+ signal(SIGINT, stream_close);
+
+ do {
+ num_read = fread(buffer, 1, size, file);
+ if (num_read > 0) {
+ if (2 == byte_align) {
+ short *buffer_ptr = (short *)buffer;
+ for (i = 0; i < num_read; i += channels) {
+ for (ch = 0; ch < channels; ch++) {
+ int temp = *buffer_ptr++;
+ /* Signal Normalization */
+ float f = (float) temp / normalization_factor;
+ *(power + ch) += (float) (f * f);
+ }
+ }
+ }
+ if (4 == byte_align) {
+ int *buffer_ptr = (int *)buffer;
+ for (i = 0; i < num_read; i += channels) {
+ for (ch = 0; ch < channels; ch++) {
+ int temp = *buffer_ptr++;
+ /* Signal Normalization */
+ float f = (float) temp / normalization_factor;
+ *(power + ch) += (float) (f * f);
+ }
+ }
+ }
+ }
+ }while (!close && num_read > 0);
+
+ for (ch = 0; ch < channels; ch++) {
+ float average_power = 10 * log10((*(power + ch)) / total_sample_per_channel);
+ if(isinf (average_power)) {
+ printf("Channel [%2u] Average Power : NO signal or ZERO signal\n", ch);
+ } else {
+ printf("Channel [%2u] Average Power : %.2f dB\n", ch, average_power);
+ }
+ }
+
+ free(buffer);
+ free(power);
+
+}
+