Project import
diff --git a/audio-tool/Android.mk b/audio-tool/Android.mk
new file mode 100644
index 0000000..c6106e7
--- /dev/null
+++ b/audio-tool/Android.mk
@@ -0,0 +1,108 @@
+#
+# Copyright (c) 2015 Nest Labs, Inc.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+
+# This makefile uses 2 code generators:
+#
+# 1. gengetopt - this needs to be installed on the host.
+# 2. audio-tool-build-generate-wave-table - this is built as part of
+# the project.
+#
+# This makefile is a little confusing because it must handle this.
+# For the wave tables, all the source files are properly generated in
+# the intermediates directory. However, for gengetopt, the cleanest
+# solution is to generate the files in the source directory instead of
+# the intermediates directory, because it avoids having to
+# -I. (i.e. the android top-of-tree).
+
+LIBAUDIO_TOOL_INTERMEDIATES := $(TARGET_OUT_INTERMEDIATES)/STATIC_LIBRARIES/libaudio-tool_intermediates
+AUDIO_TOOL_INTERMEDIATES := $(TARGET_OUT_INTERMEDIATES)/EXECUTABLES/audio-tool_intermediates
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := audio-tool-build-generate-wave-table
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_SRC_FILES := generate-wave-table.c
+include $(BUILD_HOST_EXECUTABLE)
+
+$(LIBAUDIO_TOOL_INTERMEDIATES):
+ mkdir -p $(LIBAUDIO_TOOL_INTERMEDIATES)
+
+$(LIBAUDIO_TOOL_INTERMEDIATES)/table_%.c: $(HOST_OUT_EXECUTABLES)/audio-tool-build-generate-wave-table | $(LIBAUDIO_TOOL_INTERMEDIATES)
+ audio-tool-build-generate-wave-table $(basename $*) 2048 S16 > $@
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libaudio-tool
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \
+ $(LOCAL_PATH) \
+ $(LIBAUDIO_TOOL_INTERMEDIATES)
+LOCAL_CFLAGS += -Wno-unused-parameter
+LOCAL_ADDITIONAL_DEPENDENCIES := \
+ $(LIBAUDIO_TOOL_INTERMEDIATES)/table_square.c \
+ $(LIBAUDIO_TOOL_INTERMEDIATES)/table_sine.c \
+ $(LIBAUDIO_TOOL_INTERMEDIATES)/table_triangle.c \
+ $(LIBAUDIO_TOOL_INTERMEDIATES)/table_sawtooth.c
+LOCAL_SRC_FILES := \
+ tinyplay.c \
+ tinycap.c \
+ tinymix.c \
+ mixer.c \
+ pcm.c \
+ pulse-generator.c \
+ tone-generator.c \
+ oscillator-table.c \
+ save.c \
+ restore.c \
+ mixer_cache.c \
+ module.c \
+ defaults.c \
+ alsa-control.c \
+ config_cmd.c\
+ wav_chan_splitter.c \
+ rms.c
+include $(BUILD_STATIC_LIBRARY)
+
+# also generates cmdline.h
+AUDIO_TOOL_SRC_PATH := $(value LOCAL_PATH)
+$(AUDIO_TOOL_SRC_PATH)/cmdline.c: GENGETOPT_OUTPUT_DIR := $(AUDIO_TOOL_SRC_PATH)
+$(AUDIO_TOOL_SRC_PATH)/cmdline.c: ABS_PATH_TO_GGO := $(abspath $(LOCAL_PATH)/cmdline.ggo)
+$(AUDIO_TOOL_SRC_PATH)/cmdline.c: $(LOCAL_PATH)/cmdline.ggo
+ cd $(GENGETOPT_OUTPUT_DIR) && gengetopt --input=$(ABS_PATH_TO_GGO) --file-name=cmdline --unamed-opts
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := audio-tool
+LOCAL_MODULE_TAGS := debug
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \
+ $(LOCAL_PATH) \
+ $(AUDIO_TOOL_INTERMEDIATES)
+LOCAL_CFLAGS := \
+ -DVERSION_STR="\"1.1-rc1\"" \
+ -DCMDLINE_PARSER_VERSION="\"1.1-rc1\"" \
+ -DVERSION=0x0100F0
+LOCAL_ADDITIONAL_DEPENDENCIES := $(AUDIO_TOOL_SRC_PATH)/cmdline.c
+LOCAL_SRC_FILES := \
+ main.c \
+ config.c \
+ cmdline.c
+LOCAL_STATIC_LIBRARIES := libaudio-tool
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := audio-config
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_SRC_FILES := audio-config
+include $(BUILD_PREBUILT)
diff --git a/audio-tool/LICENSE b/audio-tool/LICENSE
new file mode 100644
index 0000000..e85388e
--- /dev/null
+++ b/audio-tool/LICENSE
@@ -0,0 +1,16 @@
+#
+# Copyright (c) 2015 Nest Labs, Inc.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
diff --git a/audio-tool/Makefile b/audio-tool/Makefile
new file mode 100644
index 0000000..a995dba
--- /dev/null
+++ b/audio-tool/Makefile
@@ -0,0 +1,131 @@
+INC = include
+CFLAGS = -g -O0 -I$(INC) -Wall
+LIB = libtinyalsa.a
+LINKOPTS := -static
+
+# Versions are 1.0, 1.0.1, 1.0.2, 1.1, 1.1.1, etc.
+VERSION_STR := 1.1-rc1
+# Version string bytes: 0xMMmmPP (MM=major, mm=minor, PP=patch)
+VERSION := 0x0100F0
+
+HOSTCC := gcc
+HOSTCXX := g++
+HOSTAR := ar
+HOSTLINKOPTS :=
+HOSTCFLAGS := $(HOSTLINKOPTS) $(CFLAGS)
+HOSTLDFLAGS :=
+HOSTLDLIBS :=
+HOSTGENGETOPT := gengetopt
+
+# If you set ARCH to anything, then CROSS_COMPILE will be used.
+ifneq ($(ARCH),)
+ TARGET_CROSS_COMPILE := $(CROSS_COMPILE)
+else
+ TARGET_CROSS_COMPILE :=
+endif
+TARGETCC := $(TARGET_CROSS_COMPILE)gcc
+TARGETCXX := $(TARGET_CROSS_COMPILE)g++
+TARGETAR := $(TARGET_CROSS_COMPILE)ar
+TARGETLINKOPTS := -static
+TARGETCFLAGS := $(TARGETLINKOPTS) $(CFLAGS) \
+ -DVERSION_STR="\"$(VERSION_STR)\"" \
+ -DCMDLINE_PARSER_VERSION="\"$(VERSION_STR)\"" \
+ -DVERSION=$(VERSION) \
+
+TARGETLDFLAGS :=
+TARGETLDLIBS := $(LIB) -lm -lrt
+
+TARGETS := $(LIB) \
+ audio-tool \
+
+LIB_OBJECTS = \
+ tinyplay.o \
+ tinycap.o \
+ tinymix.o \
+ mixer.o \
+ pcm.o \
+ pulse-generator.o \
+ tone-generator.o \
+ oscillator-table.o \
+ save.o \
+ restore.o \
+ mixer_cache.o \
+ module.o \
+ defaults.o \
+ alsa-control.o \
+ config_cmd.o \
+
+MODULES = \
+ card-omap-abe.o \
+
+all: $(TARGETS)
+
+audio-tool: main.o config.o cmdline.o $(LIB) $(MODULES)
+ $(TARGETCC) $(TARGETCFLAGS) $(TARGETLDFLAGS) -o $@ $^ $(TARGETLDLIBS)
+
+config.o: config.c cmdline.h
+ $(TARGETCC) $(TARGETCFLAGS) -c -o $@ $<
+
+cmdline.h: cmdline.c
+
+cmdline.c: cmdline.ggo
+ $(HOSTGENGETOPT) --input=$< --file-name=cmdline --unamed-opts
+
+tinymix: tinymix.o $(LIB)
+ $(TARGETCC) $(TARGETCFLAGS) $(TARGETLDFLAGS) -o $@ $^ $(TARGETLDLIBS)
+
+pulse-generator: pulse-generator.o $(LIB)
+ $(TARGETCC) $(TARGETCFLAGS) $(TARGETLDFLAGS) -o $@ $^ $(TARGETLDLIBS)
+
+$(LIB): $(LIB_OBJECTS)
+ $(TARGETAR) rc $@ $^
+
+.c.o:
+ $(TARGETCC) $(TARGETCFLAGS) -c $<
+
+clean: clean_tone_generator
+ -rm -f *.o $(TARGETS)
+ -rm -f cmdline.c cmdline.h
+
+card-omap45.o: card-omap45.c card-omap-common-4-5.h
+ $(TARGETCC) $(TARGETCFLAGS) -o $@ -c $<
+
+card-sdp4430.o: card-sdp4430.c card-omap-common-4-5.h
+ $(TARGETCC) $(TARGETCFLAGS) -o $@ -c $<
+
+##############################################
+##
+## TONE GENERATOR
+##
+##############################################
+
+TONEGEN_CFLAGS := $(TARGETCFLAGS)
+TONEGEN_LDFLAGS := $(LDFLAGS)
+TONEGEN_LDLIBS := $(LDLIBS) $(LIB) -lm
+
+TONEGEN_WAVE_LENGTH = 2048
+TONEGEN_WAVE_FORMAT = S16
+TONEGEN_EXE_TARGETS = generate-wave-table \
+ tone-generator
+TONEGEN_TABLE_TARGETS = table_square.c \
+ table_sine.c \
+ table_triangle.c \
+ table_sawtooth.c
+
+clean_tone_generator:
+ -rm -f $(TONEGEN_EXE_TARGETS) *.o
+ -rm -f $(TONEGEN_TABLE_TARGETS)
+
+# This program is intended as a build tool, so it's compiled for the host.
+generate-wave-table: generate-wave-table.o
+ $(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^ $(HOSTLDLIBS) -lm
+
+# This program is intended as a build tool, so it's compiled for the host.
+generate-wave-table.o: generate-wave-table.c
+ $(HOSTCC) -c $(HOSTCFLAGS) $(HOSTCFLAGS) -o $@ $^
+
+table_%.c: generate-wave-table
+ ./generate-wave-table $* $(TONEGEN_WAVE_LENGTH) $(TONEGEN_WAVE_FORMAT) > $@
+
+tone-generator.o: tone-generator.c $(TONEGEN_TABLE_TARGETS)
+ $(TARGETCC) -c $(TONEGEN_CPPFLAGS) $(TONEGEN_CFLAGS) -o $@ $<
diff --git a/audio-tool/README b/audio-tool/README
new file mode 100644
index 0000000..f112d13
--- /dev/null
+++ b/audio-tool/README
@@ -0,0 +1,80 @@
+OMAP audio-tool
+---------------
+
+The audio-tool is a swiss-army knife for testing and using audio on
+OMAP. It includes the ability to play and record simple wave files,
+play test tones and impulses, and manipulate the ALSA mixer.
+
+There also includes a "legacy mode" for backwards-compatibility with
+the tinyalsa utils.
+
+MANIFEST
+--------
+
+As stated before, the goal is to have ONE binary, 'audio-tool'. But
+for now we have:
+
+TARGET BINARIES:
+
+ audio-tool - The main audio-tool tool.
+
+HOST BINARIES:
+
+ generate-wave-tables - Generates C-source output with look-up tables
+ for various waveforms.
+
+BUILDING
+--------
+
+The Makefile builds statically linked binaries for the target
+platform. The binaries for the host platform are usually dynamically
+linked.
+
+To build for the host PC, do this:
+
+ $ make
+
+To build for the target device, do this:
+
+ $ export CROSS_COMPILE=/path/to/arm-none-linux-gnueabi-
+ $ make ARCH=arm
+
+...and push the binaries to the device.
+
+LEGACY MODE
+-----------
+
+The audio-tool may be used as a drop-in replacement for the tinyalsa
+utils by creating symlinks to audio-tool. In this mode, it accepts
+the exact same command-line syntax as those tools. To use this mode,
+then you need to do this on your target device.
+
+ # which audio-tool
+ /usr/bin/audio-tool
+ # ln -s audio-tool /usr/bin/tinyplay
+ # ln -s audio-tool /usr/bin/tinycap
+ # ln -s audio-tool /usr/bin/tinymix
+
+You can then use those tools as you always have (with some of the
+enhanced parameters that the audio team has been using interally, such
+as the '-t duration' parameter.
+
+LICENSING
+---------
+
+BSD (3-clause)
+
+IDEAS AND FUTURE DIRECTIONS
+---------------------------
+
+* On unit tests, android's test tool looks for the last output
+ line instead of the OS return code for pass/fail. Make sure
+ that we're compatible with that.
+
+* Add support for using the mmap() API in the tests and/or
+ play/cap.
+
+* Support UCM
+
+* Test multi-port configurations (e.g. play a system tone during
+ mm playback. Capture/playback at the same time over misc. ports.
diff --git a/audio-tool/alsa-control.c b/audio-tool/alsa-control.c
new file mode 100644
index 0000000..17a7dd2
--- /dev/null
+++ b/audio-tool/alsa-control.c
@@ -0,0 +1,232 @@
+/*
+ * alsa-control.c
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ * 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 Texas Instruments Incorporated 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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 "alsa-control.h"
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#include <sound/asound.h>
+
+#define log_fmt(x) x
+#define LOGE(msg, ...) fprintf(stderr, log_fmt(msg) "\n", ##__VA_ARGS__)
+#ifdef LOG_NDEBUG
+#define LOGV(msg, ...) fprintf(stderr, log_fmt(msg) "\n", ##__VA_ARGS__)
+#else
+#define LOGV(msg, ...) ((void)0)
+#endif
+
+/* \brief Returns the path to a card's control device (/dev/snd/controlC#)
+ *
+ * \param card - The index of the sound card.
+ * \param dest - Pointer to a string buffer.
+ * \param dest_len - Maximum size of memory at dest. Should be larger
+ * than sizeof(SND_CONTROL_TEMPLATE) by a few bytes (16 to 32 bytes).
+ *
+ * \return 0 on succes. Negative error code on failure.
+ *
+ * -EINVAL: The destination string was not large enough.
+ */
+static int card_control_path(int card, char *dest, size_t dest_len)
+{
+ snprintf(dest, dest_len, SND_CONTROL_TEMPLATE, card);
+ return 0;
+}
+
+/* \brief returns the maximum number of cards that could be plugged in.
+ *
+ * \return The maximum number of cards that could be plugged in. This
+ * will be one more than the maximum index.
+ */
+int ah_card_max_count()
+{
+ /* XXX This is being set arbitrarily. Need to ask kernel. */
+ return AH_MAX_CARD_COUNT;
+}
+
+/* \brief returns the number of cards currently registered with ALSA
+ *
+ * \return The number of cards registered with ALSA. If there are no
+ * cards returns a zero. Note that in some cases you may have a card
+ * number greater than the return of this function. (E.g. if there
+ * were 4 cards and cards 1 and 2 were unplugged.
+ */
+int ah_card_count()
+{
+ DIR *dir;
+ struct dirent *de;
+ int count = 0;
+
+ dir = opendir(ALSA_DEVICE_DIRECTORY);
+ if (dir == 0)
+ return 0;
+
+ do {
+ de = readdir(dir);
+ if (!de)
+ break;
+ if (0 == strncmp(SND_CONTROL_FILE_PREFIX, de->d_name, strlen(SND_CONTROL_FILE_PREFIX)))
+ ++count;
+ } while(1);
+
+ closedir(dir);
+
+ if (count > ah_card_max_count())
+ count = ah_card_max_count();
+
+ return count;
+}
+
+/* \brief Returns the name of sound card (by number)
+ *
+ * \param card - The sound card index.
+ * \param str - Pointer to location where string will be written.
+ * \param strlen - Amound of memory available at str
+ *
+ * \return 0 on success. A negative error code on error.
+ */
+int ah_card_get_name(int card, char *str, size_t strlen)
+{
+ snd_ctl_card_info_t info;
+ int fd, rv = 0;
+
+ fd = ah_control_open(card, O_RDONLY);
+ if (fd < 0) {
+ LOGE("ah_control_open() failed");
+ rv = -errno;
+ goto end;
+ }
+
+ rv = ah_card_get_info(fd, &info);
+ if (rv) {
+ LOGE("error %d in getting card info", errno);
+ rv = -errno;
+ goto card_err;
+ }
+ LOGV("card %d info->name = %s", card, info.id);
+ strncpy(str, (char*)info.id, strlen);
+card_err:
+ ah_control_close(fd);
+end:
+ return rv;
+}
+
+/* \brief Return the index of the card with id name, if exists.
+ *
+ * \param name - name of card that is being searched for.
+ *
+ * \return - The card index, or a negative error code:
+ *
+ * ENODEV - Device does not exits.
+ */
+int ah_card_find_by_name(const char* name)
+{
+ char cur[256] = "";
+ int cards = ah_card_count();
+ int k;
+ int match = 0;
+ LOGV("%s() looking for %s", __func__, name);
+ for (k = 0 ; k < cards ; ++k) {
+ ah_card_get_name(k, cur, sizeof(cur));
+ LOGV("Comparing to '%s'", cur);
+ if ( 0 == strcmp(cur, name) ) {
+ match = 1;
+ break;
+ }
+ }
+
+ if (match)
+ return k;
+
+ return -ENODEV;
+}
+
+/* \brief Grabs the card info structure from the kernel.
+ *
+ * \param card - The sound card index
+ * \param info - Pointer to card info structure. The kernel's
+ * structure will be copied to this memory location.
+ *
+ * \return 0 on success. Otherwise a negative error code.
+ * Error codes are the same as may be returned by open(2) and
+ * ioctl(2).
+ */
+int ah_card_get_info(int fd, snd_ctl_card_info_t *info)
+{
+ int e, rv = 0;
+ e = ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, info);
+ if(e)
+ rv = -errno;
+ return rv;
+}
+
+/* \brief Open up the control device for the specified card.
+ *
+ * \param card - The index of the card to use.
+ * \param mode - The mode with which to open the card. Same as for
+ * open(2).
+ *
+ * Note: In fact, this function is really just a front from open(2).
+ *
+ * \return A file descriptor (int) for the device on success. On
+ * error, returns the same errors as open(2).
+ */
+int ah_control_open(int card, int mode)
+{
+ char control_fn[sizeof(SND_CONTROL_TEMPLATE) + 32] = "";
+ int fd;
+ card_control_path(card, control_fn, sizeof(control_fn));
+ LOGV("Trying to open %s (card = %d)", control_fn, card);
+ fd = open(control_fn, mode);
+ return fd;
+}
+
+/* \brief Close the control device.
+ *
+ * \param fd - File descriptor to control device.
+ *
+ * \return 0 on success. Returns same errors as close(2)
+ */
+int ah_control_close(int fd)
+{
+ return close(fd);
+}
diff --git a/audio-tool/alsa-control.h b/audio-tool/alsa-control.h
new file mode 100644
index 0000000..cae80ec
--- /dev/null
+++ b/audio-tool/alsa-control.h
@@ -0,0 +1,65 @@
+/*
+ * alsa-control.h
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ * 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 Texas Instruments Incorporated 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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.
+ *
+ */
+
+#ifndef __ALSA_CONTROL_H__
+#define __ALSA_CONTROL_H__
+
+#include "alsa-global.h"
+
+#include <string.h>
+#include <time.h>
+/*#include <linux/time.h>*/
+#include <sound/asound.h>
+
+/* Typedefs for kernel structs. See <sound/asound.h>
+ */
+typedef struct snd_ctl_card_info snd_ctl_card_info_t;
+typedef struct snd_ctl_elem_id snd_ctl_elem_id_t;
+typedef struct snd_ctl_elem_info snd_ctl_elem_info_t;
+typedef struct snd_ctl_elem_list snd_ctl_elem_list_t;
+typedef struct snd_ctl_elem_value snd_ctl_elem_value_t;
+
+int ah_card_max_count();
+int ah_card_count();
+int ah_card_get_name(int card, char *str, size_t strlen);
+int ah_card_find_by_name(const char* name);
+
+int ah_control_open(int card, int mode);
+int ah_control_close(int fd);
+int ah_card_get_info(int fd, snd_ctl_card_info_t *info);
+
+
+#endif /* __ALSA_CONTROL_H__ */
diff --git a/audio-tool/alsa-global.h b/audio-tool/alsa-global.h
new file mode 100644
index 0000000..b156fac
--- /dev/null
+++ b/audio-tool/alsa-global.h
@@ -0,0 +1,47 @@
+/*
+ * alsa-global.h
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ * 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 Texas Instruments Incorporated 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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.
+ *
+ */
+
+#ifndef __ALSA_GLOBAL_H__
+#define __ALSA_GLOBAL_H__
+
+#define ALSA_DEVICE_DIRECTORY "/dev/snd"
+#define SND_CONTROL_FILE_PREFIX "controlC"
+#define SND_CONTROL_PREFIX ALSA_DEVICE_DIRECTORY "/" SND_CONTROL_FILE_PREFIX
+#define SND_CONTROL_TEMPLATE SND_CONTROL_PREFIX "%d"
+
+#define AH_MAX_CARD_COUNT 32
+
+#endif /* __ALSA_GLOBAL_H__ */
diff --git a/audio-tool/audio-config b/audio-tool/audio-config
new file mode 100755
index 0000000..016a9d5
--- /dev/null
+++ b/audio-tool/audio-config
@@ -0,0 +1,162 @@
+#!/system/bin/sh
+#
+# Copyright (c) 2015 Nest Labs, Inc.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+set -x
+
+AUDIO_TOOL=/system/bin/audio-tool
+TINYMIX="${AUDIO_TOOL} mix"
+
+die() {
+ echo "ERROR: $@" 1>&2
+ exit 0
+}
+
+set_defaults() {
+ # QCOM has 1000 mixer controls, and they are fragile. Merely observing
+ # some of them causes crashes. Therefore we focus on a subset of controls
+ # that we manipulate.
+
+ ${TINYMIX} 'QUIN_MI2S_RX Audio Mixer MultiMedia1' 0 || die "failed to set mixer value"
+ ${TINYMIX} 'AIF1_CAP Mixer SLIM TX1' 0 || die "failed to set mixer value"
+ ${TINYMIX} 'AIF1_CAP Mixer SLIM TX2' 0 || die "failed to set mixer value"
+ ${TINYMIX} 'AIF1_CAP Mixer SLIM TX3' 0 || die "failed to set mixer value"
+ ${TINYMIX} 'AIF1_CAP Mixer SLIM TX4' 0 || die "failed to set mixer value"
+ ${TINYMIX} 'DEC1 MUX' ZERO || die "failed to set mixer value"
+ ${TINYMIX} 'DEC1 Volume' 84 || die "failed to set mixer value"
+ ${TINYMIX} 'DEC2 MUX' ZERO || die "failed to set mixer value"
+ ${TINYMIX} 'DEC3 MUX' ZERO || die "failed to set mixer value"
+ ${TINYMIX} 'DEC4 MUX' ZERO || die "failed to set mixer value"
+ ${TINYMIX} 'MultiMedia1 Mixer SLIM_0_TX' 0 || die "failed to set mixer value"
+ ${TINYMIX} 'SLIM TX1 MUX' ZERO || die "failed to set mixer value"
+ ${TINYMIX} 'SLIM TX2 MUX' ZERO || die "failed to set mixer value"
+ ${TINYMIX} 'SLIM TX3 MUX' ZERO || die "failed to set mixer value"
+ ${TINYMIX} 'SLIM TX4 MUX' ZERO || die "failed to set mixer value"
+ ${TINYMIX} 'SLIM_0_TX Channels' One || die "failed to set mixer value"
+}
+
+config_single_mic_recording() {
+ ${TINYMIX} 'MultiMedia1 Mixer SLIM_0_TX' 1 || die "failed to set mixer value"
+ ${TINYMIX} 'SLIM_0_TX Channels' One || die "failed to set mixer value"
+ ${TINYMIX} 'AIF1_CAP Mixer SLIM TX1' 1 || die "failed to set mixer value"
+ ${TINYMIX} 'SLIM TX1 MUX' DEC1 || die "failed to set mixer value"
+ ${TINYMIX} 'DEC1 MUX' DMIC1 || die "failed to set mixer value"
+ ${TINYMIX} 'DEC1 Volume' 68 || die "failed to set mixer value"
+}
+
+config_single_other_mic_recording() {
+ ${TINYMIX} 'MultiMedia1 Mixer SLIM_0_TX' 1 || die "failed to set mixer value"
+ ${TINYMIX} 'SLIM_0_TX Channels' One || die "failed to set mixer value"
+ ${TINYMIX} 'AIF1_CAP Mixer SLIM TX3' 1 || die "failed to set mixer value"
+ ${TINYMIX} 'SLIM TX3 MUX' DEC3 || die "failed to set mixer value"
+ ${TINYMIX} 'DEC3 MUX' DMIC3 || die "failed to set mixer value"
+ ${TINYMIX} 'DEC3 Volume' 68 || die "failed to set mixer value"
+}
+
+config_four_mic_recording() {
+ ${TINYMIX} 'MultiMedia1 Mixer SLIM_0_TX' 1 || die "failed to set mixer value"
+ ${TINYMIX} 'AIF1_CAP Mixer SLIM TX1' 1 || die "failed to set mixer value"
+ ${TINYMIX} 'AIF1_CAP Mixer SLIM TX2' 1 || die "failed to set mixer value"
+ ${TINYMIX} 'AIF1_CAP Mixer SLIM TX3' 1 || die "failed to set mixer value"
+ ${TINYMIX} 'AIF1_CAP Mixer SLIM TX4' 1 || die "failed to set mixer value"
+ ${TINYMIX} 'SLIM_0_TX Channels' Four || die "failed to set mixer value"
+ ${TINYMIX} 'SLIM TX1 MUX' DEC1 || die "failed to set mixer value"
+ ${TINYMIX} 'DEC1 MUX' DMIC1 || die "failed to set mixer value"
+ ${TINYMIX} 'SLIM TX2 MUX' DEC2 || die "failed to set mixer value"
+ ${TINYMIX} 'DEC2 MUX' DMIC2 || die "failed to set mixer value"
+ ${TINYMIX} 'SLIM TX3 MUX' DEC3 || die "failed to set mixer value"
+ ${TINYMIX} 'DEC3 MUX' DMIC3 || die "failed to set mixer value"
+ ${TINYMIX} 'SLIM TX4 MUX' DEC4 || die "failed to set mixer value"
+ ${TINYMIX} 'DEC4 MUX' DMIC4 || die "failed to set mixer value"
+ if [ ! -z "$1" ]; then
+ if [ $1 -gt 83 ] && [ $1 -lt 125 ] ; then
+ ${TINYMIX} 'DEC1 Volume' $1 || die "failed to set mixer value"
+ ${TINYMIX} 'DEC2 Volume' $1 || die "failed to set mixer value"
+ ${TINYMIX} 'DEC3 Volume' $1 || die "failed to set mixer value"
+ ${TINYMIX} 'DEC4 Volume' $1 || die "failed to set mixer value"
+ fi
+ fi
+}
+
+config_speaker_playback() {
+ ${TINYMIX} 'QUIN_MI2S_RX Audio Mixer MultiMedia1' 1 || die "failed to set mixer value"
+ if [ ! -z "$1" ]; then
+ if [ $1 -gt 0 ] && [ $1 -lt 512 ] ; then
+ ${TINYMIX} 'Speaker Driver Playback Volume' $1 || die "failed to set mixer value"
+ fi
+ fi
+}
+
+config_loopback() {
+ config_four_mic_recording $1
+ config_speaker_playback $2
+}
+
+usage() {
+ cat <<EOF
+usage: audio-config <USE-CASE>
+
+Where <USE-CASE> may be one of:
+
+ default, d, dflt - set mixers to what are (normally) the boot-up
+ default values.
+
+ 1mic, 1 - configure for single DMIC recording.
+
+ 4mic, 4 - configure for 4x DMIC recording.
+
+ loopback, l - configure for 4x DMIC + Playback.
+
+ playback, p - configure for playback on speaker driver.
+
+EOF
+}
+
+if [ $# -eq 0 ] ; then
+ usage
+ exit 0
+fi
+
+if [ $# -gt 3 ] ; then
+ usage
+ die "Too many argument given"
+fi
+
+set_defaults
+
+case $1 in
+default|d|dflt)
+ ;;
+1mic|1)
+ config_single_mic_recording
+ ;;
+4mic|4)
+ config_four_mic_recording $2
+ ;;
+playback|p)
+ config_speaker_playback $2
+ ;;
+loopback|l)
+ config_loopback $2 $3
+ ;;
+alt)
+ config_single_other_mic_recording
+ ;;
+*)
+ usage
+ die "Invalid/unsupported argument"
+ ;;
+esac
diff --git a/audio-tool/card-omap-abe.c b/audio-tool/card-omap-abe.c
new file mode 100644
index 0000000..eb8fd85
--- /dev/null
+++ b/audio-tool/card-omap-abe.c
@@ -0,0 +1,1977 @@
+/*
+ * card-omap-common-4-5.h
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ * 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 Texas Instruments Incorporated 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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.
+ *
+ */
+#ifndef __AUDIO_TOOL_CARD_OMAP_COMMON_4_5_H__
+#define __AUDIO_TOOL_CARD_OMAP_COMMON_4_5_H__
+#else
+#error This file should be treated as code and not as a header.
+#endif
+
+/*
+ * Code common between cards SDP4430 and OMAP45
+ *
+ * When matching ABE versions, the ABE FW version is attached.
+ *
+ * Symbols are not versioned until versioning is required.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <tinyalsa/asoundlib.h>
+
+#include "module.h"
+#include "alsa-control.h"
+#include "mixer_cache.h"
+
+static struct audio_tool_mixer_control_info g_defaults_common[] = {
+ {
+ .id = -1,
+ .name = "DL1 Equalizer",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "Flat response",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "DL2 Left Equalizer",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "High-pass 0dB",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "DL2 Right Equalizer",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "High-pass 0dB",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "Sidetone Equalizer",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "Flat response",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "AMIC Equalizer",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "High-pass 0dB",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "DMIC Equalizer",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "High-pass 0dB",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "DL1 Media Playback Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 1,
+ .value.integer = {
+ 118,
+ },
+ },
+ {
+ .id = -1,
+ .name = "DL1 Tones Playback Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "DL1 Voice Playback Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 1,
+ .value.integer = {
+ 120,
+ },
+ },
+ {
+ .id = -1,
+ .name = "DL1 Capture Playback Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "VXREC Media Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "VXREC Tones Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "VXREC Voice DL Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "VXREC Voice UL Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "AUDUL Media Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "AUDUL Tones Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "AUDUL Voice UL Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 1,
+ .value.integer = {
+ 120,
+ },
+ },
+ {
+ .id = -1,
+ .name = "AUDUL Voice DL Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "SDT UL Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 1,
+ .value.integer = {
+ 101,
+ },
+ },
+ {
+ .id = -1,
+ .name = "SDT DL Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 1,
+ .value.integer = {
+ 120,
+ },
+ },
+ {
+ .id = -1,
+ .name = "DMIC1 UL Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 2,
+ .value.integer = {
+ 120,
+ 120,
+ },
+ },
+ {
+ .id = -1,
+ .name = "DMIC2 UL Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 2,
+ .value.integer = {
+ 120,
+ 120,
+ },
+ },
+ {
+ .id = -1,
+ .name = "DMIC3 UL Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 2,
+ .value.integer = {
+ 120,
+ 120,
+ },
+ },
+ {
+ .id = -1,
+ .name = "AMIC UL Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 2,
+ .value.integer = {
+ 120,
+ 120,
+ },
+ },
+ {
+ .id = -1,
+ .name = "BT UL Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 2,
+ .value.integer = {
+ 120,
+ 120,
+ },
+ },
+ {
+ .id = -1,
+ .name = "DL1 Mono Mixer",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "AUDUL Mono Mixer",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "DL1 MM_EXT Switch",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "DL1 BT_VX Switch",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "DL1 PDM Switch",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "Sidetone Mixer Capture",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "Sidetone Mixer Playback",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 1,
+ },
+ },
+ {
+ .id = -1,
+ .name = "Capture Mixer Tones",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "Capture Mixer Voice Playback",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "Capture Mixer Voice Capture",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "Capture Mixer Media Playback",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "Voice Capture Mixer Tones Playback",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "Voice Capture Mixer Media Playback",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "Voice Capture Mixer Capture",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "DL1 Mixer Tones",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "DL1 Mixer Voice",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "DL1 Mixer Capture",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "DL1 Mixer Multimedia",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "MUX_VX1",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "None",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "MUX_VX0",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "None",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "MUX_UL11",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "None",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "MUX_UL10",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "None",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "MUX_UL07",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "None",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "MUX_UL06",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "None",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "MUX_UL05",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "None",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "MUX_UL04",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "None",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "MUX_UL03",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "None",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "MUX_UL02",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "None",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "MUX_UL01",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "None",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "MUX_UL00",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "None",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "Capture Preamplifier Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 2,
+ .value.integer = {
+ 1,
+ 1,
+ },
+ },
+ {
+ .id = -1,
+ .name = "Capture Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 2,
+ .value.integer = {
+ 4,
+ 4,
+ },
+ },
+ {
+ .id = -1,
+ .name = "Aux FM Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 2,
+ .value.integer = {
+ 3,
+ 3,
+ },
+ },
+ {
+ .id = -1,
+ .name = "Headset Playback Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 2,
+ .value.integer = {
+ 15,
+ 15,
+ },
+ },
+ {
+ .id = -1,
+ .name = "Handsfree Playback Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 2,
+ .value.integer = {
+ 26,
+ 26,
+ },
+ },
+ {
+ .id = -1,
+ .name = "Earphone Playback Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 1,
+ .value.integer = {
+ 14,
+ },
+ },
+ {
+ .id = -1,
+ .name = "Headset Power Mode",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "Low-Power",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "Earphone Playback Switch",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "Headset Right Playback",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "Off",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "Headset Left Playback",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "Off",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "Handsfree Right Playback",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "HF DAC",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "Handsfree Left Playback",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "HF DAC",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "Analog Right Capture Route",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "Off",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "Analog Left Capture Route",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "Off",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "TWL6040 Power Mode",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "Low-Power",
+ "",
+ "",
+ "",
+ },
+ },
+};
+
+/* The AUX switch hasn't really been associated to an ABE FW version,
+ * so we check for it specifically.
+ */
+static struct audio_tool_mixer_control_info g_defaults_aux_switch[] = {
+ {
+ .id = -1,
+ .name = "Aux Left Playback Switch",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "Aux Right Playback Switch",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+};
+
+static struct audio_tool_mixer_control_info g_defaults_0951[] = {
+ {
+ .id = -1,
+ .name = "DL2 Media Playback Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 1,
+ .value.integer = {
+ 118,
+ },
+ },
+ {
+ .id = -1,
+ .name = "DL2 Tones Playback Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "DL2 Voice Playback Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 1,
+ .value.integer = {
+ 120,
+ },
+ },
+ {
+ .id = -1,
+ .name = "DL2 Capture Playback Volume",
+ .type = MIXER_CTL_TYPE_INT,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "DL2 Mono Mixer",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "DL2 Mixer Tones",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "DL2 Mixer Voice",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "DL2 Mixer Capture",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "DL2 Mixer Multimedia",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 1,
+ },
+ },
+};
+
+static struct audio_tool_mixer_control_info g_defaults_0956[] = {
+ {
+ .id = -1,
+ .name = "DL1 PDM_DL2 Switch",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 1,
+ },
+ },
+ {
+ .id = -1,
+ .name = "PLL Selection",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "Low-Power",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "AUXR Playback Switch",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "AUXL Playback Switch",
+ .type = MIXER_CTL_TYPE_BOOL,
+ .num_values = 1,
+ .value.integer = {
+ 0,
+ },
+ },
+ {
+ .id = -1,
+ .name = "Vibra Right Playback",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "Input FF",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ .id = -1,
+ .name = "Vibra Left Playback",
+ .type = MIXER_CTL_TYPE_ENUM,
+ .num_values = 1,
+ .value.enumerated = {
+ "Input FF",
+ "",
+ "",
+ "",
+ },
+ },
+};
+
+#define ABE_API_NULL 0L
+#define ABE_API_0951 951L
+#define ABE_API_0956 956L
+
+static int g_abe_api = ABE_API_NULL;
+
+static struct audio_tool_mixer_cache g_card_mix_defaults = {
+ .count = 0,
+ .ctrls = 0,
+};
+
+static char* g_playback_frontends[] = {
+ "Multimedia",
+ "Voice",
+ "Tones",
+ "MultimediaLP",
+ 0,
+};
+
+static char* g_playback_backends[] = {
+ "Headset",
+ "Handsfree",
+ "Earpiece",
+ "Bluetooth",
+ 0,
+};
+
+static char *g_capture_frontends[] = {
+ "Multimedia",
+ "Multimedia2",
+ "Voice",
+ 0,
+};
+
+static char *g_capture_backends[] = {
+ "HeadsetMic",
+ "OnboardMic",
+ "Aux/FM",
+ "DMic0",
+ "DMic1",
+ "DMic2",
+ "Bluetooth",
+ "Echo",
+ "VoiceRec",
+ 0,
+};
+
+struct route_setting
+{
+ char *ctl_name;
+ int intval;
+ char *strval;
+};
+
+#define RS_INT(str, val) { .ctl_name = str, .intval = val }
+#define RS_ENUM(str, val) { .ctl_name = str, .strval = val }
+#define RS_NULL { 0, 0, 0, }
+
+/***************************************************************************
+ * PLAYBACK ROUTE SETTINGS
+ ***************************************************************************
+ */
+
+/* For Frontend := { Multimedia, MultimediaLP }
+ * For Backend := { Headset, Earpiece, Bluetooth }
+ */
+static struct route_setting g_playback_multimedia_accessory_mix[] = {
+ RS_INT("Sidetone Mixer Playback", 1),
+ RS_INT("SDT DL Volume", 120),
+ RS_INT("DL1 Mixer Multimedia", 1),
+ RS_INT("DL1 Media Playback Volume", 118),
+ RS_NULL,
+};
+
+/* For Frontend := { Multimedia, MultimediaLP }
+ * For Backend := { Handsfree }
+ */
+static struct route_setting *g_playback_multimedia_handsfree_mix = 0;
+static struct route_setting g_playback_multimedia_handsfree_mix_0951[] = {
+ RS_INT("DL2 Mixer Multimedia", 1),
+ RS_INT("DL2 Media Playback Volume", 118),
+ RS_NULL,
+};
+static struct route_setting g_playback_multimedia_handsfree_mix_0956[] = {
+ RS_INT("Sidetone Mixer Playback", 1),
+ RS_INT("SDT DL Volume", 120),
+ RS_INT("DL1 Mixer Multimedia", 1),
+ RS_INT("DL1 Media Playback Volume", 118),
+ RS_NULL,
+};
+
+/* For Frontend := { Voice }
+ * For Backend := { Headset, Earpiece, Bluetooth }
+ */
+static struct route_setting g_playback_voice_accessory_mix[] = {
+ RS_INT("Sidetone Mixer Playback", 1),
+ RS_INT("SDT DL Volume", 120),
+ RS_INT("DL1 Mixer Voice", 1),
+ RS_INT("DL1 Voice Playback Volume", 118),
+ RS_NULL,
+};
+
+/* For Frontend := { Voice }
+ * For Backend := { Handsfree }
+ */
+static struct route_setting *g_playback_voice_handsfree_mix = 0;
+static struct route_setting g_playback_voice_handsfree_mix_0951[] = {
+ RS_INT("DL2 Mixer Voice", 1),
+ RS_INT("DL2 Voice Playback Volume", 118),
+ RS_NULL,
+};
+static struct route_setting g_playback_voice_handsfree_mix_0956[] = {
+ RS_INT("Sidetone Mixer Playback", 1),
+ RS_INT("SDT DL Volume", 120),
+ RS_INT("DL1 Mixer Voice", 1),
+ RS_INT("DL1 Voice Playback Volume", 118),
+ RS_NULL,
+};
+
+/* For Frontend := { Tones }
+ * For Backend := { Headset, Earpiece, Bluetooth }
+ */
+static struct route_setting g_playback_tones_accessory_mix[] = {
+ RS_INT("Sidetone Mixer Playback", 1),
+ RS_INT("SDT DL Volume", 120),
+ RS_INT("DL1 Mixer Tones", 1),
+ RS_INT("DL1 Tones Playback Volume", 118),
+ RS_NULL,
+};
+
+/* For Frontend := { Tones }
+ * For Backend := { Handsfree }
+ */
+static struct route_setting *g_playback_tones_handsfree_mix = 0;
+static struct route_setting g_playback_tones_handsfree_mix_0951[] = {
+ RS_INT("DL2 Mixer Tones", 1),
+ RS_INT("DL2 Tones Playback Volume", 118),
+ RS_NULL,
+};
+static struct route_setting g_playback_tones_handsfree_mix_0956[] = {
+ RS_INT("Sidetone Mixer Playback", 1),
+ RS_INT("SDT DL Volume", 120),
+ RS_INT("DL1 Mixer Tones", 1),
+ RS_INT("DL1 Tones Playback Volume", 118),
+ RS_NULL,
+};
+
+static struct route_setting g_playback_be_headset_mix[] = {
+ RS_INT("DL1 PDM Switch", 1),
+ RS_ENUM("Headset Left Playback", "HS DAC"),
+ RS_ENUM("Headset Right Playback", "HS DAC"),
+ RS_INT("Headset Playback Volume", 13),
+ RS_NULL,
+};
+
+static struct route_setting *g_playback_be_earpiece_mix = 0;
+static struct route_setting g_playback_be_earpiece_mix_0951[] = {
+ RS_INT("DL1 PDM Switch", 1),
+ RS_INT("Earphone Playback Switch", 1),
+ RS_INT("Earphone Playback Volume", 13),
+ RS_NULL,
+};
+static struct route_setting g_playback_be_earpiece_mix_0956[] = {
+ RS_INT("DL1 PDM Switch", 1),
+ RS_INT("Earphone Playback Switch", 1),
+ RS_INT("Earphone Playback Volume", 13),
+ RS_NULL,
+};
+
+static struct route_setting g_playback_be_bluetooth_mix[] = {
+ RS_INT("DL1 BT_VX Switch", 1),
+ RS_INT("BT UL Volume", 120),
+ RS_NULL,
+};
+
+static struct route_setting *g_playback_be_handsfree_mix = 0;
+static struct route_setting g_playback_be_handsfree_mix_0951[] = {
+ RS_ENUM("Handsfree Left Playback", "HF DAC"),
+ RS_ENUM("Handsfree Right Playback", "HF DAC"),
+ RS_INT("Handsfree Playback Volume", 23),
+ RS_NULL,
+};
+static struct route_setting g_playback_be_handsfree_mix_0956[] = {
+ RS_INT("DL1 PDM_DL2 Switch", 1),
+ RS_ENUM("Handsfree Left Playback", "HF DAC"),
+ RS_ENUM("Handsfree Right Playback", "HF DAC"),
+ RS_INT("Handsfree Playback Volume", 23),
+ RS_NULL,
+};
+
+/***************************************************************************
+ * CAPTURE ROUTE SETTINGS
+ ***************************************************************************
+ */
+
+static struct route_setting g_capture_multimedia_amic_mix[] = {
+ RS_ENUM("MUX_UL00", "AMic0"),
+ RS_ENUM("MUX_UL01", "AMic1"),
+ RS_ENUM("MUX_UL02", "None"),
+ RS_ENUM("MUX_UL03", "None"),
+ RS_ENUM("MUX_UL04", "None"),
+ RS_ENUM("MUX_UL05", "None"),
+ RS_ENUM("MUX_UL06", "None"),
+ RS_ENUM("MUX_UL07", "None"),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia_dmic0_mix[] = {
+ RS_ENUM("MUX_UL00", "DMic0L"),
+ RS_ENUM("MUX_UL01", "DMic0R"),
+ RS_ENUM("MUX_UL02", "None"),
+ RS_ENUM("MUX_UL03", "None"),
+ RS_ENUM("MUX_UL04", "None"),
+ RS_ENUM("MUX_UL05", "None"),
+ RS_ENUM("MUX_UL06", "None"),
+ RS_ENUM("MUX_UL07", "None"),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia_dmic1_mix[] = {
+ RS_ENUM("MUX_UL00", "DMic1L"),
+ RS_ENUM("MUX_UL01", "DMic1R"),
+ RS_ENUM("MUX_UL02", "None"),
+ RS_ENUM("MUX_UL03", "None"),
+ RS_ENUM("MUX_UL04", "None"),
+ RS_ENUM("MUX_UL05", "None"),
+ RS_ENUM("MUX_UL06", "None"),
+ RS_ENUM("MUX_UL07", "None"),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia_dmic2_mix[] = {
+ RS_ENUM("MUX_UL00", "DMic2L"),
+ RS_ENUM("MUX_UL01", "DMic2R"),
+ RS_ENUM("MUX_UL02", "None"),
+ RS_ENUM("MUX_UL03", "None"),
+ RS_ENUM("MUX_UL04", "None"),
+ RS_ENUM("MUX_UL05", "None"),
+ RS_ENUM("MUX_UL06", "None"),
+ RS_ENUM("MUX_UL07", "None"),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia_bluetooth_mix[] = {
+ RS_ENUM("MUX_UL00", "BT Left"),
+ RS_ENUM("MUX_UL01", "BT Right"),
+ RS_ENUM("MUX_UL02", "None"),
+ RS_ENUM("MUX_UL03", "None"),
+ RS_ENUM("MUX_UL04", "None"),
+ RS_ENUM("MUX_UL05", "None"),
+ RS_ENUM("MUX_UL06", "None"),
+ RS_ENUM("MUX_UL07", "None"),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia_echo_mix[] = {
+ RS_ENUM("MUX_UL00", "Echo Left"),
+ RS_ENUM("MUX_UL01", "Echo Right"),
+ RS_ENUM("MUX_UL02", "None"),
+ RS_ENUM("MUX_UL03", "None"),
+ RS_ENUM("MUX_UL04", "None"),
+ RS_ENUM("MUX_UL05", "None"),
+ RS_ENUM("MUX_UL06", "None"),
+ RS_ENUM("MUX_UL07", "None"),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia_vxrec_mix[] = {
+ RS_ENUM("MUX_UL00", "VX Left"),
+ RS_ENUM("MUX_UL01", "VX Right"),
+ RS_ENUM("MUX_UL02", "None"),
+ RS_ENUM("MUX_UL03", "None"),
+ RS_ENUM("MUX_UL04", "None"),
+ RS_ENUM("MUX_UL05", "None"),
+ RS_ENUM("MUX_UL06", "None"),
+ RS_ENUM("MUX_UL07", "None"),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia2_amic_mix[] = {
+ RS_ENUM("MUX_UL10", "AMic0"),
+ RS_ENUM("MUX_UL11", "AMic1"),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia2_dmic0_mix[] = {
+ RS_ENUM("MUX_UL10", "DMic0L"),
+ RS_ENUM("MUX_UL11", "DMic0R"),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia2_dmic1_mix[] = {
+ RS_ENUM("MUX_UL10", "DMic1L"),
+ RS_ENUM("MUX_UL11", "DMic1R"),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia2_dmic2_mix[] = {
+ RS_ENUM("MUX_UL10", "DMic2L"),
+ RS_ENUM("MUX_UL11", "DMic2R"),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia2_bluetooth_mix[] = {
+ RS_ENUM("MUX_UL10", "BT Left"),
+ RS_ENUM("MUX_UL11", "BT Right"),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia2_echo_mix[] = {
+ RS_ENUM("MUX_UL10", "Echo Left"),
+ RS_ENUM("MUX_UL11", "Echo Right"),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_multimedia2_vxrec_mix[] = {
+ RS_ENUM("MUX_UL10", "VX Left"),
+ RS_ENUM("MUX_UL11", "VX Right"),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_voice_amic_mix[] = {
+ RS_INT("Voice Capture Mixer Capture", 1),
+ RS_ENUM("MUX_VX0", "AMic0"),
+ RS_ENUM("MUX_VX1", "AMic1"),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_voice_dmic0_mix[] = {
+ RS_INT("Voice Capture Mixer Capture", 1),
+ RS_ENUM("MUX_VX0", "DMic0L"),
+ RS_ENUM("MUX_VX1", "DMic0R"),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_voice_dmic1_mix[] = {
+ RS_INT("Voice Capture Mixer Capture", 1),
+ RS_ENUM("MUX_VX0", "DMic1L"),
+ RS_ENUM("MUX_VX1", "DMic1R"),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_voice_dmic2_mix[] = {
+ RS_INT("Voice Capture Mixer Capture", 1),
+ RS_ENUM("MUX_VX0", "DMic2L"),
+ RS_ENUM("MUX_VX1", "DMic2R"),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_voice_bluetooth_mix[] = {
+ RS_INT("Voice Capture Mixer Capture", 1),
+ RS_ENUM("MUX_VX0", "BT Left"),
+ RS_ENUM("MUX_VX1", "BT Right"),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_voice_echo_mix[] = {
+ RS_INT("Voice Capture Mixer Capture", 1),
+ RS_ENUM("MUX_VX0", "Echo Left"),
+ RS_ENUM("MUX_VX1", "Echo Right"),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_voice_vxrec_mix[] = {
+ RS_INT("Voice Capture Mixer Capture", 1),
+ RS_ENUM("MUX_VX0", "VX Left"),
+ RS_ENUM("MUX_VX1", "VX Right"),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_be_headsetmic_mix[] = {
+ RS_INT("AMIC UL Volume", 120),
+ RS_ENUM("Analog Left Capture Route", "Headset Mic"),
+ RS_ENUM("Analog Right Capture Route", "Headset Mic"),
+ RS_INT("Capture Preamplifier Volume", 1),
+ RS_INT("Capture Volume", 4),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_be_onboardmic_mix[] = {
+ RS_INT("AMIC UL Volume", 120),
+ RS_ENUM("Analog Left Capture Route", "Main Mic"),
+ RS_ENUM("Analog Right Capture Route", "Sub Mic"),
+ RS_INT("Capture Preamplifier Volume", 1),
+ RS_INT("Capture Volume", 4),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_be_auxfm_mix[] = {
+ RS_INT("AMIC UL Volume", 120),
+ RS_ENUM("Analog Left Capture Route", "Aux/FM Left"),
+ RS_ENUM("Analog Right Capture Route", "Aux/FM Right"),
+ RS_INT("Capture Preamplifier Volume", 1),
+ RS_INT("Capture Volume", 4),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_be_dmic0_mix[] = {
+ RS_INT("DMIC1 UL Volume", 140),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_be_dmic1_mix[] = {
+ RS_INT("DMIC2 UL Volume", 140),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_be_dmic2_mix[] = {
+ RS_INT("DMIC3 UL Volume", 140),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_be_bluetooth_mix[] = {
+ RS_INT("BT UL Volume", 120),
+ RS_INT("DL1 BT_VX Switch", 1),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_be_echo_mix[] = {
+ RS_INT("Echo Mixer DL1", 1),
+ RS_INT("Echo Mixer DL2", 1),
+ RS_INT("Echo DL1 Volume", 120),
+ RS_INT("Echo DL2 Volume", 120),
+ RS_NULL,
+};
+
+static struct route_setting g_capture_be_vxrec_mix[] = {
+ RS_INT("Capture Mixer Tones", 1),
+ RS_INT("Capture Mixer Voice Playback", 1),
+ RS_INT("Capture Mixer Voice Capture", 1),
+ RS_INT("Capture Mixer Media Playback", 1),
+ RS_INT("VXREC Media Volume", 120),
+ RS_INT("VXREC Tones Volume", 120),
+ RS_INT("VXREC Voice DL Volume", 120),
+ RS_INT("VXREC Voice UL Volume", 120),
+ RS_NULL,
+};
+
+/* side effects: initializes g_abe_api and g_card_mix_defaults */
+static int detect_abe_api(struct audio_tool_mixer_cache *cache)
+{
+ int abe_api = ABE_API_0951;
+ int has_aux_switch = 0;
+ int count = 0;
+ int n;
+
+ /* Detect if using 09.56 API */
+ for (n = 0 ; n < cache->count ; ++n) {
+ if (0 == strcmp("DL1 PDM_DL2 Switch", cache->ctrls[n].name)) {
+ abe_api = ABE_API_0956;
+ break;
+ }
+ }
+
+ /* Detect if have aux switch */
+ for (n = 0 ; n < cache->count ; ++n) {
+ if (0 == strcmp("Aux Left Playback Switch", cache->ctrls[n].name)) {
+ has_aux_switch = 1;
+ break;
+ }
+ }
+
+ count = sizeof(g_defaults_common) / sizeof(g_defaults_common[0]);
+ if (has_aux_switch) {
+ count += sizeof(g_defaults_aux_switch) / sizeof(g_defaults_aux_switch[0]);
+ }
+ switch (abe_api) {
+ case ABE_API_0951:
+ count += sizeof(g_defaults_0951) / sizeof(g_defaults_0951[0]);
+ break;
+ case ABE_API_0956:
+ count += sizeof(g_defaults_0956) / sizeof(g_defaults_0956[0]);
+ break;
+ default:
+ assert(0);
+ }
+
+ g_card_mix_defaults.ctrls = calloc(count, sizeof(struct audio_tool_mixer_control_info));
+ memcpy(g_card_mix_defaults.ctrls, &g_defaults_common, sizeof(g_defaults_common));
+ n = sizeof(g_defaults_common) / sizeof(g_defaults_common[0]);
+ if (has_aux_switch) {
+ memcpy(g_card_mix_defaults.ctrls + n, &g_defaults_aux_switch,
+ sizeof(g_defaults_aux_switch));
+ n += sizeof(g_defaults_aux_switch) / sizeof(g_defaults_aux_switch[0]);
+ }
+ switch (abe_api) {
+ case ABE_API_0951:
+ memcpy(g_card_mix_defaults.ctrls + n, &g_defaults_0951, sizeof(g_defaults_0951));
+ break;
+ case ABE_API_0956:
+ memcpy(g_card_mix_defaults.ctrls + n, &g_defaults_0956, sizeof(g_defaults_0956));
+ break;
+ default:
+ assert(0);
+ }
+
+ g_card_mix_defaults.count = count;
+ g_abe_api = abe_api;
+
+
+ /* initialize static id's */
+ for (n = 0 ; n < g_card_mix_defaults.count ; ++n) {
+ g_card_mix_defaults.ctrls[n].id = n;
+ }
+
+ /* Set up the routes */
+ switch (abe_api) {
+ case ABE_API_0951:
+ g_playback_multimedia_handsfree_mix = g_playback_multimedia_handsfree_mix_0951;
+ g_playback_voice_handsfree_mix = g_playback_voice_handsfree_mix_0951;
+ g_playback_tones_handsfree_mix = g_playback_tones_handsfree_mix_0951;
+ g_playback_be_earpiece_mix = g_playback_be_earpiece_mix_0951;
+ g_playback_be_handsfree_mix = g_playback_be_handsfree_mix_0951;
+ break;
+ case ABE_API_0956:
+ g_playback_multimedia_handsfree_mix = g_playback_multimedia_handsfree_mix_0956;
+ g_playback_voice_handsfree_mix = g_playback_voice_handsfree_mix_0956;
+ g_playback_tones_handsfree_mix = g_playback_tones_handsfree_mix_0956;
+ g_playback_be_earpiece_mix = g_playback_be_earpiece_mix_0956;
+ g_playback_be_handsfree_mix = g_playback_be_handsfree_mix_0956;
+ break;
+ default:
+ assert(0);
+ }
+
+ return 0;
+}
+
+static int get_mixer_defaults(struct audio_tool_mixer_cache *cache)
+{
+ struct audio_tool_mixer_cache *defs = &g_card_mix_defaults;
+ int m, n;
+ int ret = 0;
+
+ if (g_abe_api == ABE_API_NULL)
+ detect_abe_api(cache);
+
+ mixer_cache_reset_touch(defs);
+ mixer_cache_reset_touch(cache);
+
+ for (m = 0 ; m < cache->count ; ++m) {
+ n = mixer_cache_get_id_by_name(defs,
+ cache->ctrls[m].name);
+ if (n < 0) {
+ fprintf(stderr, "Warning: No default defined for %s\n", cache->ctrls[m].name);
+ ret = 1;
+ continue;
+ }
+ if (cache->ctrls[m].type != defs->ctrls[n].type) {
+ fprintf(stderr, "Warning: type mismatch on %s\n", cache->ctrls[m].name);
+ ret = 1;
+ continue;
+ }
+ memcpy(&cache->ctrls[m].value, &defs->ctrls[n].value,
+ sizeof(g_defaults_common[0].value));
+
+ mixer_cache_touch(cache, m);
+ mixer_cache_touch(defs, n);
+ }
+
+ ret = mixer_cache_audit_touch(cache, 1) ? 1 : ret;
+ ret = mixer_cache_audit_touch(defs, 1) ? 1 : ret;
+
+ return ret;
+}
+
+static int set_route_by_array(struct mixer *mixer, struct route_setting *route,
+ int enable)
+{
+ struct mixer_ctl *ctl;
+ unsigned int i, j;
+
+ /* Go through the route array and set each value */
+ i = 0;
+ while (route[i].ctl_name) {
+ ctl = mixer_get_ctl_by_name(mixer, route[i].ctl_name);
+ if (!ctl)
+ return -EINVAL;
+
+ if (route[i].strval) {
+ if (enable)
+ mixer_ctl_set_enum_by_string(ctl, route[i].strval);
+ else
+ mixer_ctl_set_enum_by_string(ctl, "Off");
+ } else {
+ /* This ensures multiple (i.e. stereo) values are set jointly */
+ for (j = 0; j < mixer_ctl_get_num_values(ctl); j++) {
+ if (enable)
+ mixer_ctl_set_value(ctl, j, route[i].intval);
+ else
+ mixer_ctl_set_value(ctl, j, 0);
+ }
+ }
+ i++;
+ }
+
+ return 0;
+}
+
+static int get_fe_be_names(int direction, char ***fes, char ***bes)
+{
+ if (direction == AUDIO_DIRECTION_PLAYBACK) {
+ *fes = g_playback_frontends;
+ *bes = g_playback_backends;
+ } else {
+ assert(direction == AUDIO_DIRECTION_CAPTURE);
+ *fes = g_capture_frontends;
+ *bes = g_capture_backends;
+ }
+ return 0;
+}
+
+#define FE_P_MM 0
+#define FE_P_VX 1
+#define FE_P_TONES 2
+#define FE_P_MMLP 3
+
+#define FE_C_MM 0
+#define FE_C_MM2 1
+#define FE_C_VX 2
+
+#define BE_P_HS 0
+#define BE_P_HF 1
+#define BE_P_EP 3
+#define BE_P_BT 4
+
+#define BE_C_HSMIC 0
+#define BE_C_OBMIC 1
+#define BE_C_AUXFM 2
+#define BE_C_DMIC0 3
+#define BE_C_DMIC1 4
+#define BE_C_DMIC2 5
+#define BE_C_BT 6
+#define BE_C_ECHO 7
+#define BE_C_VXREC 8
+
+static int config_playback(struct mixer *mixer, const char* fe,
+ const char* be, int enable, int *optional_port)
+{
+ int f, b;
+ int ret = 0;
+ int port;
+
+ if (0 == strcmp(fe, "Multimedia")) {
+ f = FE_P_MM;
+ port = 0;
+ } else if (0 == strcmp(fe, "Voice")) {
+ f = FE_P_VX;
+ port = 2;
+ } else if (0 == strcmp(fe, "Tones")) {
+ f = FE_P_TONES;
+ port = 3;
+ } else if (0 == strcmp(fe, "MultimediaLP")) {
+ f = FE_P_MMLP;
+ port = 6;
+ } else {
+ return EINVAL;
+ }
+
+ if (optional_port)
+ *optional_port = port;
+
+ if (0 == strcmp(be, "Headset")) {
+ b = BE_P_HS;
+ } else if (0 == strcmp(be, "Handsfree")) {
+ b = BE_P_HF;
+ } else if (0 == strcmp(be, "Earpiece")) {
+ b = BE_P_EP;
+ } else if (0 == strcmp(be, "Bluetooth")) {
+ b = BE_P_BT;
+ } else {
+ return EINVAL;
+ }
+
+ switch (f) {
+ case FE_P_MM:
+ case FE_P_MMLP:
+ if (b == BE_P_HF) {
+ ret = set_route_by_array(mixer,
+ g_playback_multimedia_handsfree_mix, enable);
+ } else {
+ ret = set_route_by_array(mixer,
+ g_playback_multimedia_accessory_mix, enable);
+ }
+ break;
+ case FE_P_VX:
+ if (b == BE_P_HF) {
+ ret = set_route_by_array(mixer,
+ g_playback_voice_handsfree_mix, enable);
+ } else {
+ ret = set_route_by_array(mixer,
+ g_playback_voice_accessory_mix, enable);
+ }
+ break;
+ case FE_P_TONES:
+ if (b == BE_P_HF) {
+ ret = set_route_by_array(mixer,
+ g_playback_tones_handsfree_mix, enable);
+ } else {
+ ret = set_route_by_array(mixer,
+ g_playback_tones_accessory_mix, enable);
+ }
+ break;
+ default:
+ assert(0);
+ }
+
+ if (ret)
+ return ret;
+
+ switch (b) {
+ case BE_P_HS:
+ ret = set_route_by_array(mixer,
+ g_playback_be_headset_mix, enable);
+ break;
+ case BE_P_HF:
+ ret = set_route_by_array(mixer,
+ g_playback_be_handsfree_mix, enable);
+ break;
+ case BE_P_EP:
+ ret = set_route_by_array(mixer,
+ g_playback_be_earpiece_mix, enable);
+ break;
+ case BE_P_BT:
+ ret = set_route_by_array(mixer,
+ g_playback_be_bluetooth_mix, enable);
+ break;
+ default:
+ assert(0);
+ }
+
+ return ret;
+}
+
+static int config_capture(struct mixer *mixer, const char* fe,
+ const char* be, int enable, int *optional_port)
+{
+ int f, b;
+ int ret = 0;
+ int port;
+
+ if (0 == strcmp(fe, "Multimedia")) {
+ f = FE_C_MM;
+ port = 0;
+ } else if (0 == strcmp(fe, "Multimedia2")) {
+ f = FE_C_MM2;
+ port = 1;
+ } else if (0 == strcmp(fe, "Voice")) {
+ f = FE_C_VX;
+ port = 2;
+ } else {
+ return EINVAL;
+ }
+
+ if (optional_port)
+ *optional_port = port;
+
+ if (0 == strcmp(be, "HeadsetMic")) {
+ b = BE_C_HSMIC;
+ } else if (0 == strcmp(be, "OnboardMic")) {
+ b = BE_C_OBMIC;
+ } else if (0 == strcmp(be, "Aux/FM")) {
+ b = BE_C_AUXFM;
+ } else if (0 == strcmp(be, "DMic0")) {
+ b = BE_C_DMIC0;
+ } else if (0 == strcmp(be, "DMic1")) {
+ b = BE_C_DMIC1;
+ } else if (0 == strcmp(be, "DMic2")) {
+ b = BE_C_DMIC2;
+ } else if (0 == strcmp(be, "Bluetooth")) {
+ b = BE_C_BT;
+ } else if (0 == strcmp(be, "Echo")) {
+ b = BE_C_ECHO;
+ } else if (0 == strcmp(be, "VoiceRec")) {
+ b = BE_C_VXREC;
+ } else {
+ return EINVAL;
+ }
+
+ switch (f) {
+ case FE_C_MM:
+ switch (b) {
+ case BE_C_HSMIC:
+ case BE_C_OBMIC:
+ case BE_C_AUXFM:
+ ret = set_route_by_array(mixer,
+ g_capture_multimedia_amic_mix, enable);
+ break;
+ case BE_C_DMIC0:
+ ret = set_route_by_array(mixer,
+ g_capture_multimedia_dmic0_mix, enable);
+ break;
+ case BE_C_DMIC1:
+ ret = set_route_by_array(mixer,
+ g_capture_multimedia_dmic1_mix, enable);
+ break;
+ case BE_C_DMIC2:
+ ret = set_route_by_array(mixer,
+ g_capture_multimedia_dmic2_mix, enable);
+ break;
+ case BE_C_BT:
+ ret = set_route_by_array(mixer,
+ g_capture_multimedia_bluetooth_mix, enable);
+ break;
+ case BE_C_ECHO:
+ ret = set_route_by_array(mixer,
+ g_capture_multimedia_echo_mix, enable);
+ break;
+ case BE_C_VXREC:
+ ret = set_route_by_array(mixer,
+ g_capture_multimedia_vxrec_mix, enable);
+ break;
+ }
+ break;
+ case FE_C_MM2:
+ switch (b) {
+ case BE_C_HSMIC:
+ case BE_C_OBMIC:
+ case BE_C_AUXFM:
+ ret = set_route_by_array(mixer,
+ g_capture_multimedia2_amic_mix, enable);
+ break;
+ case BE_C_DMIC0:
+ ret = set_route_by_array(mixer,
+ g_capture_multimedia2_dmic0_mix, enable);
+ break;
+ case BE_C_DMIC1:
+ ret = set_route_by_array(mixer,
+ g_capture_multimedia2_dmic1_mix, enable);
+ break;
+ case BE_C_DMIC2:
+ ret = set_route_by_array(mixer,
+ g_capture_multimedia2_dmic2_mix, enable);
+ break;
+ case BE_C_BT:
+ ret = set_route_by_array(mixer,
+ g_capture_multimedia2_bluetooth_mix, enable);
+ break;
+ case BE_C_ECHO:
+ ret = set_route_by_array(mixer,
+ g_capture_multimedia2_echo_mix, enable);
+ break;
+ case BE_C_VXREC:
+ ret = set_route_by_array(mixer,
+ g_capture_multimedia2_vxrec_mix, enable);
+ break;
+ }
+ break;
+ case FE_C_VX:
+ switch (b) {
+ case BE_C_HSMIC:
+ case BE_C_OBMIC:
+ case BE_C_AUXFM:
+ ret = set_route_by_array(mixer,
+ g_capture_voice_amic_mix, enable);
+ break;
+ case BE_C_DMIC0:
+ ret = set_route_by_array(mixer,
+ g_capture_voice_dmic0_mix, enable);
+ break;
+ case BE_C_DMIC1:
+ ret = set_route_by_array(mixer,
+ g_capture_voice_dmic1_mix, enable);
+ break;
+ case BE_C_DMIC2:
+ ret = set_route_by_array(mixer,
+ g_capture_voice_dmic2_mix, enable);
+ break;
+ case BE_C_BT:
+ ret = set_route_by_array(mixer,
+ g_capture_voice_bluetooth_mix, enable);
+ break;
+ case BE_C_ECHO:
+ ret = set_route_by_array(mixer,
+ g_capture_voice_echo_mix, enable);
+ break;
+ case BE_C_VXREC:
+ ret = set_route_by_array(mixer,
+ g_capture_voice_vxrec_mix, enable);
+ break;
+ }
+ break;
+ default:
+ assert(0);
+ }
+
+ if (ret)
+ return ret;
+
+ switch (b) {
+ case BE_C_HSMIC:
+ ret = set_route_by_array(mixer,
+ g_capture_be_headsetmic_mix, enable);
+ break;
+ case BE_C_OBMIC:
+ ret = set_route_by_array(mixer,
+ g_capture_be_onboardmic_mix, enable);
+ break;
+ case BE_C_AUXFM:
+ ret = set_route_by_array(mixer,
+ g_capture_be_auxfm_mix, enable);
+ break;
+ case BE_C_DMIC0:
+ ret = set_route_by_array(mixer,
+ g_capture_be_dmic0_mix, enable);
+ break;
+ case BE_C_DMIC1:
+ ret = set_route_by_array(mixer,
+ g_capture_be_dmic1_mix, enable);
+ break;
+ case BE_C_DMIC2:
+ ret = set_route_by_array(mixer,
+ g_capture_be_dmic2_mix, enable);
+ break;
+ case BE_C_BT:
+ ret = set_route_by_array(mixer,
+ g_capture_be_bluetooth_mix, enable);
+ break;
+ case BE_C_ECHO:
+ ret = set_route_by_array(mixer,
+ g_capture_be_echo_mix, enable);
+ break;
+ case BE_C_VXREC:
+ ret = set_route_by_array(mixer,
+ g_capture_be_vxrec_mix, enable);
+ break;
+ default:
+ assert(0);
+ }
+
+ return ret;
+}
+
+static int config(struct mixer *mixer, int direction, const char* fe,
+ const char* be, int enable, int *optional_port)
+{
+ if (g_abe_api == ABE_API_NULL) {
+ struct audio_tool_mixer_cache cache;
+ mixer_cache_init(&cache);
+ if(mixer_cache_populate(&cache, mixer)) {
+ fprintf(stderr, "Error: could not populate mixer cache for card\n");
+ return ENODEV;
+ }
+ if(detect_abe_api(&cache)) {
+ fprintf(stderr, "Error: could not detect ABE version\n");
+ return ENODEV;
+ }
+ mixer_cache_deinit(&cache);
+ }
+
+ if (direction == AUDIO_DIRECTION_PLAYBACK) {
+ return config_playback(mixer, fe, be, enable, optional_port);
+ } else {
+ return config_capture(mixer, fe, be, enable, optional_port);
+ }
+}
+
+/* NULL terminated list: */
+static const char* supported_cards[] = {
+ "OMAP45",
+ "SDP4430",
+ "OMAP5",
+ "OMAP5EVM",
+ "Tablet44xx",
+ "Panda",
+ NULL
+};
+
+static int probe(void)
+{
+ int card = -ENODEV;
+ const char** cardname;
+
+ for (cardname = supported_cards ; *cardname ; ++cardname) {
+ card = ah_card_find_by_name(*cardname);
+
+ if (card >= 0)
+ return 0;
+ }
+
+ return ENODEV;
+
+}
+
+static struct audio_tool_card_module g_omap_abe_mod_template = {
+ .type = AUDIO_TOOL_MOD_TYPE_CARD,
+ .name = "",
+ .probe = probe,
+ .get_mixer_defaults = get_mixer_defaults,
+ .get_fe_be_names = get_fe_be_names,
+ .config = config,
+};
+
+static void __init init(void)
+{
+ const char** cardname;
+ int card;
+ int ret;
+
+ for (cardname = supported_cards ; *cardname ; ++cardname) {
+ card = ah_card_find_by_name(*cardname);
+
+ if (card < 0)
+ continue;
+
+ struct audio_tool_card_module *mod;
+ mod = malloc(sizeof(struct audio_tool_card_module));
+ if (!mod) {
+ fprintf(stderr, "Error: could not allocate memory for module %s\n",
+ *cardname);
+ }
+ *mod = g_omap_abe_mod_template;
+ mod->name = *cardname;
+
+ ret = audio_tool_module_register((struct audio_tool_module*)mod);
+ if (ret) {
+ fprintf(stderr, "Error: could not register module %s (%s)\n",
+ *cardname, strerror(ret));
+ }
+ }
+
+}
diff --git a/audio-tool/cmdline.c b/audio-tool/cmdline.c
new file mode 100644
index 0000000..5bb6aeb
--- /dev/null
+++ b/audio-tool/cmdline.c
@@ -0,0 +1,708 @@
+/*
+ File autogenerated by gengetopt version 2.22.6
+ generated with the following command:
+ gengetopt --input=/src/external/audio-tool/cmdline.ggo --file-name=cmdline --unamed-opts
+
+ The developers of gengetopt consider the fixed text that goes in all
+ gengetopt output files to be in the public domain:
+ we make no copyright claims on it.
+*/
+
+/* If we use autoconf. */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef FIX_UNUSED
+#define FIX_UNUSED(X) (void) (X) /* avoid warnings for unused params */
+#endif
+
+#include <getopt.h>
+
+#include "cmdline.h"
+
+const char *gengetopt_args_info_purpose = "";
+
+const char *gengetopt_args_info_usage = "Usage: audio-tool [options] command [command args]";
+
+const char *gengetopt_args_info_versiontext = "";
+
+const char *gengetopt_args_info_description = "Audio utility knife for playback, capture, and tests\n\nCommands:\n play <filename> - playback of an RIFF WAV file\n cap <filename> - capture to an RIFF WAV file\n mix <ctrl#> <value> - manipulate the ALSA mixer\n tone <wave> <freq> <vol dB> - generate a tone (sine wave, square wave, etc)\n pulse - generate impulses on the period boundaries\n save - save current mixer state to a file\n restore - restore mixer state from a file\n defaults - put card in audio-tool's 'default' state\n";
+
+const char *gengetopt_args_info_help[] = {
+ " -h, --help Print help and exit",
+ " -V, --version Print version and exit",
+ "\nOptions:",
+ " -D, --card=INT audio card number (default=`0')",
+ " -d, --device=INT audio port (default=`0')",
+ " -p, --periods=INT frames per period (default=`1032')",
+ " -n, --num-periods=INT number of periods per buffer (default=`4')",
+ " -t, --time=INT maximum duration of command (default=`0')",
+ " -c, --channels=INT number of channels (default=`2')",
+ " -m, --channel-mask=INT Bit mask with enabled channels\n (default=`0xFFFFFFFF')",
+ " -b, --bits=INT Number of bits in output format (signed)\n (default=`16')",
+ " -r, --rate=INT Sample rate (default=`48000')",
+ "\nCopyright 2012 Texas Instruments Incorporated\nCopyright 2011 The Android Open Source Project\nCopyright 2011 Gabriel M. Beddingfield\n",
+ 0
+};
+
+typedef enum {ARG_NO
+ , ARG_INT
+} cmdline_parser_arg_type;
+
+static
+void clear_given (struct gengetopt_args_info *args_info);
+static
+void clear_args (struct gengetopt_args_info *args_info);
+
+static int
+cmdline_parser_internal (int argc, char **argv, struct gengetopt_args_info *args_info,
+ struct cmdline_parser_params *params, const char *additional_error);
+
+
+static char *
+gengetopt_strdup (const char *s);
+
+static
+void clear_given (struct gengetopt_args_info *args_info)
+{
+ args_info->help_given = 0 ;
+ args_info->version_given = 0 ;
+ args_info->card_given = 0 ;
+ args_info->device_given = 0 ;
+ args_info->periods_given = 0 ;
+ args_info->num_periods_given = 0 ;
+ args_info->time_given = 0 ;
+ args_info->channels_given = 0 ;
+ args_info->channel_mask_given = 0 ;
+ args_info->bits_given = 0 ;
+ args_info->rate_given = 0 ;
+}
+
+static
+void clear_args (struct gengetopt_args_info *args_info)
+{
+ FIX_UNUSED (args_info);
+ args_info->card_arg = 0;
+ args_info->card_orig = NULL;
+ args_info->device_arg = 0;
+ args_info->device_orig = NULL;
+ args_info->periods_arg = 1032;
+ args_info->periods_orig = NULL;
+ args_info->num_periods_arg = 4;
+ args_info->num_periods_orig = NULL;
+ args_info->time_arg = 0;
+ args_info->time_orig = NULL;
+ args_info->channels_arg = 2;
+ args_info->channels_orig = NULL;
+ args_info->channel_mask_arg = 0xFFFFFFFF;
+ args_info->channel_mask_orig = NULL;
+ args_info->bits_arg = 16;
+ args_info->bits_orig = NULL;
+ args_info->rate_arg = 48000;
+ args_info->rate_orig = NULL;
+
+}
+
+static
+void init_args_info(struct gengetopt_args_info *args_info)
+{
+
+
+ args_info->help_help = gengetopt_args_info_help[0] ;
+ args_info->version_help = gengetopt_args_info_help[1] ;
+ args_info->card_help = gengetopt_args_info_help[3] ;
+ args_info->device_help = gengetopt_args_info_help[4] ;
+ args_info->periods_help = gengetopt_args_info_help[5] ;
+ args_info->num_periods_help = gengetopt_args_info_help[6] ;
+ args_info->time_help = gengetopt_args_info_help[7] ;
+ args_info->channels_help = gengetopt_args_info_help[8] ;
+ args_info->channel_mask_help = gengetopt_args_info_help[9] ;
+ args_info->bits_help = gengetopt_args_info_help[10] ;
+ args_info->rate_help = gengetopt_args_info_help[11] ;
+
+}
+
+void
+cmdline_parser_print_version (void)
+{
+ printf ("%s %s\n",
+ (strlen(CMDLINE_PARSER_PACKAGE_NAME) ? CMDLINE_PARSER_PACKAGE_NAME : CMDLINE_PARSER_PACKAGE),
+ CMDLINE_PARSER_VERSION);
+
+ if (strlen(gengetopt_args_info_versiontext) > 0)
+ printf("\n%s\n", gengetopt_args_info_versiontext);
+}
+
+static void print_help_common(void) {
+ cmdline_parser_print_version ();
+
+ if (strlen(gengetopt_args_info_purpose) > 0)
+ printf("\n%s\n", gengetopt_args_info_purpose);
+
+ if (strlen(gengetopt_args_info_usage) > 0)
+ printf("\n%s\n", gengetopt_args_info_usage);
+
+ printf("\n");
+
+ if (strlen(gengetopt_args_info_description) > 0)
+ printf("%s\n\n", gengetopt_args_info_description);
+}
+
+void
+cmdline_parser_print_help (void)
+{
+ int i = 0;
+ print_help_common();
+ while (gengetopt_args_info_help[i])
+ printf("%s\n", gengetopt_args_info_help[i++]);
+}
+
+void
+cmdline_parser_init (struct gengetopt_args_info *args_info)
+{
+ clear_given (args_info);
+ clear_args (args_info);
+ init_args_info (args_info);
+
+ args_info->inputs = 0;
+ args_info->inputs_num = 0;
+}
+
+void
+cmdline_parser_params_init(struct cmdline_parser_params *params)
+{
+ if (params)
+ {
+ params->override = 0;
+ params->initialize = 1;
+ params->check_required = 1;
+ params->check_ambiguity = 0;
+ params->print_errors = 1;
+ }
+}
+
+struct cmdline_parser_params *
+cmdline_parser_params_create(void)
+{
+ struct cmdline_parser_params *params =
+ (struct cmdline_parser_params *)malloc(sizeof(struct cmdline_parser_params));
+ cmdline_parser_params_init(params);
+ return params;
+}
+
+static void
+free_string_field (char **s)
+{
+ if (*s)
+ {
+ free (*s);
+ *s = 0;
+ }
+}
+
+
+static void
+cmdline_parser_release (struct gengetopt_args_info *args_info)
+{
+ unsigned int i;
+ free_string_field (&(args_info->card_orig));
+ free_string_field (&(args_info->device_orig));
+ free_string_field (&(args_info->periods_orig));
+ free_string_field (&(args_info->num_periods_orig));
+ free_string_field (&(args_info->time_orig));
+ free_string_field (&(args_info->channels_orig));
+ free_string_field (&(args_info->channel_mask_orig));
+ free_string_field (&(args_info->bits_orig));
+ free_string_field (&(args_info->rate_orig));
+
+
+ for (i = 0; i < args_info->inputs_num; ++i)
+ free (args_info->inputs [i]);
+
+ if (args_info->inputs_num)
+ free (args_info->inputs);
+
+ clear_given (args_info);
+}
+
+
+static void
+write_into_file(FILE *outfile, const char *opt, const char *arg, const char *values[])
+{
+ FIX_UNUSED (values);
+ if (arg) {
+ fprintf(outfile, "%s=\"%s\"\n", opt, arg);
+ } else {
+ fprintf(outfile, "%s\n", opt);
+ }
+}
+
+
+int
+cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info)
+{
+ int i = 0;
+
+ if (!outfile)
+ {
+ fprintf (stderr, "%s: cannot dump options to stream\n", CMDLINE_PARSER_PACKAGE);
+ return EXIT_FAILURE;
+ }
+
+ if (args_info->help_given)
+ write_into_file(outfile, "help", 0, 0 );
+ if (args_info->version_given)
+ write_into_file(outfile, "version", 0, 0 );
+ if (args_info->card_given)
+ write_into_file(outfile, "card", args_info->card_orig, 0);
+ if (args_info->device_given)
+ write_into_file(outfile, "device", args_info->device_orig, 0);
+ if (args_info->periods_given)
+ write_into_file(outfile, "periods", args_info->periods_orig, 0);
+ if (args_info->num_periods_given)
+ write_into_file(outfile, "num-periods", args_info->num_periods_orig, 0);
+ if (args_info->time_given)
+ write_into_file(outfile, "time", args_info->time_orig, 0);
+ if (args_info->channels_given)
+ write_into_file(outfile, "channels", args_info->channels_orig, 0);
+ if (args_info->channel_mask_given)
+ write_into_file(outfile, "channel-mask", args_info->channel_mask_orig, 0);
+ if (args_info->bits_given)
+ write_into_file(outfile, "bits", args_info->bits_orig, 0);
+ if (args_info->rate_given)
+ write_into_file(outfile, "rate", args_info->rate_orig, 0);
+
+
+ i = EXIT_SUCCESS;
+ return i;
+}
+
+int
+cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info)
+{
+ FILE *outfile;
+ int i = 0;
+
+ outfile = fopen(filename, "w");
+
+ if (!outfile)
+ {
+ fprintf (stderr, "%s: cannot open file for writing: %s\n", CMDLINE_PARSER_PACKAGE, filename);
+ return EXIT_FAILURE;
+ }
+
+ i = cmdline_parser_dump(outfile, args_info);
+ fclose (outfile);
+
+ return i;
+}
+
+void
+cmdline_parser_free (struct gengetopt_args_info *args_info)
+{
+ cmdline_parser_release (args_info);
+}
+
+/** @brief replacement of strdup, which is not standard */
+char *
+gengetopt_strdup (const char *s)
+{
+ char *result = 0;
+ if (!s)
+ return result;
+
+ result = (char*)malloc(strlen(s) + 1);
+ if (result == (char*)0)
+ return (char*)0;
+ strcpy(result, s);
+ return result;
+}
+
+int
+cmdline_parser (int argc, char **argv, struct gengetopt_args_info *args_info)
+{
+ return cmdline_parser2 (argc, argv, args_info, 0, 1, 1);
+}
+
+int
+cmdline_parser_ext (int argc, char **argv, struct gengetopt_args_info *args_info,
+ struct cmdline_parser_params *params)
+{
+ int result;
+ result = cmdline_parser_internal (argc, argv, args_info, params, 0);
+
+ if (result == EXIT_FAILURE)
+ {
+ cmdline_parser_free (args_info);
+ exit (EXIT_FAILURE);
+ }
+
+ return result;
+}
+
+int
+cmdline_parser2 (int argc, char **argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required)
+{
+ int result;
+ struct cmdline_parser_params params;
+
+ params.override = override;
+ params.initialize = initialize;
+ params.check_required = check_required;
+ params.check_ambiguity = 0;
+ params.print_errors = 1;
+
+ result = cmdline_parser_internal (argc, argv, args_info, ¶ms, 0);
+
+ if (result == EXIT_FAILURE)
+ {
+ cmdline_parser_free (args_info);
+ exit (EXIT_FAILURE);
+ }
+
+ return result;
+}
+
+int
+cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog_name)
+{
+ FIX_UNUSED (args_info);
+ FIX_UNUSED (prog_name);
+ return EXIT_SUCCESS;
+}
+
+
+static char *package_name = 0;
+
+/**
+ * @brief updates an option
+ * @param field the generic pointer to the field to update
+ * @param orig_field the pointer to the orig field
+ * @param field_given the pointer to the number of occurrence of this option
+ * @param prev_given the pointer to the number of occurrence already seen
+ * @param value the argument for this option (if null no arg was specified)
+ * @param possible_values the possible values for this option (if specified)
+ * @param default_value the default value (in case the option only accepts fixed values)
+ * @param arg_type the type of this option
+ * @param check_ambiguity @see cmdline_parser_params.check_ambiguity
+ * @param override @see cmdline_parser_params.override
+ * @param no_free whether to free a possible previous value
+ * @param multiple_option whether this is a multiple option
+ * @param long_opt the corresponding long option
+ * @param short_opt the corresponding short option (or '-' if none)
+ * @param additional_error possible further error specification
+ */
+static
+int update_arg(void *field, char **orig_field,
+ unsigned int *field_given, unsigned int *prev_given,
+ char *value, const char *possible_values[],
+ const char *default_value,
+ cmdline_parser_arg_type arg_type,
+ int check_ambiguity, int override,
+ int no_free, int multiple_option,
+ const char *long_opt, char short_opt,
+ const char *additional_error)
+{
+ char *stop_char = 0;
+ const char *val = value;
+ int found;
+ FIX_UNUSED (field);
+
+ stop_char = 0;
+ found = 0;
+
+ if (!multiple_option && prev_given && (*prev_given || (check_ambiguity && *field_given)))
+ {
+ if (short_opt != '-')
+ fprintf (stderr, "%s: `--%s' (`-%c') option given more than once%s\n",
+ package_name, long_opt, short_opt,
+ (additional_error ? additional_error : ""));
+ else
+ fprintf (stderr, "%s: `--%s' option given more than once%s\n",
+ package_name, long_opt,
+ (additional_error ? additional_error : ""));
+ return 1; /* failure */
+ }
+
+ FIX_UNUSED (default_value);
+
+ if (field_given && *field_given && ! override)
+ return 0;
+ if (prev_given)
+ (*prev_given)++;
+ if (field_given)
+ (*field_given)++;
+ if (possible_values)
+ val = possible_values[found];
+
+ switch(arg_type) {
+ case ARG_INT:
+ if (val) *((int *)field) = strtol (val, &stop_char, 0);
+ break;
+ default:
+ break;
+ };
+
+ /* check numeric conversion */
+ switch(arg_type) {
+ case ARG_INT:
+ if (val && !(stop_char && *stop_char == '\0')) {
+ fprintf(stderr, "%s: invalid numeric value: %s\n", package_name, val);
+ return 1; /* failure */
+ }
+ break;
+ default:
+ ;
+ };
+
+ /* store the original value */
+ switch(arg_type) {
+ case ARG_NO:
+ break;
+ default:
+ if (value && orig_field) {
+ if (no_free) {
+ *orig_field = value;
+ } else {
+ if (*orig_field)
+ free (*orig_field); /* free previous string */
+ *orig_field = gengetopt_strdup (value);
+ }
+ }
+ };
+
+ return 0; /* OK */
+}
+
+
+int
+cmdline_parser_internal (
+ int argc, char **argv, struct gengetopt_args_info *args_info,
+ struct cmdline_parser_params *params, const char *additional_error)
+{
+ int c; /* Character of the parsed option. */
+
+ int error_occurred = 0;
+ struct gengetopt_args_info local_args_info;
+
+ int override;
+ int initialize;
+ int check_required;
+ int check_ambiguity;
+
+ package_name = argv[0];
+
+ override = params->override;
+ initialize = params->initialize;
+ check_required = params->check_required;
+ check_ambiguity = params->check_ambiguity;
+
+ if (initialize)
+ cmdline_parser_init (args_info);
+
+ cmdline_parser_init (&local_args_info);
+
+ optarg = 0;
+ optind = 0;
+ opterr = params->print_errors;
+ optopt = '?';
+
+ while (1)
+ {
+ int option_index = 0;
+
+ static struct option long_options[] = {
+ { "help", 0, NULL, 'h' },
+ { "version", 0, NULL, 'V' },
+ { "card", 1, NULL, 'D' },
+ { "device", 1, NULL, 'd' },
+ { "periods", 1, NULL, 'p' },
+ { "num-periods", 1, NULL, 'n' },
+ { "time", 1, NULL, 't' },
+ { "channels", 1, NULL, 'c' },
+ { "channel-mask", 1, NULL, 'm' },
+ { "bits", 1, NULL, 'b' },
+ { "rate", 1, NULL, 'r' },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long (argc, argv, "hVD:d:p:n:t:c:m:b:r:", long_options, &option_index);
+
+ if (c == -1) break; /* Exit from `while (1)' loop. */
+
+ switch (c)
+ {
+ case 'h': /* Print help and exit. */
+ cmdline_parser_print_help ();
+ cmdline_parser_free (&local_args_info);
+ exit (EXIT_SUCCESS);
+
+ case 'V': /* Print version and exit. */
+ cmdline_parser_print_version ();
+ cmdline_parser_free (&local_args_info);
+ exit (EXIT_SUCCESS);
+
+ case 'D': /* audio card number. */
+
+
+ if (update_arg( (void *)&(args_info->card_arg),
+ &(args_info->card_orig), &(args_info->card_given),
+ &(local_args_info.card_given), optarg, 0, "0", ARG_INT,
+ check_ambiguity, override, 0, 0,
+ "card", 'D',
+ additional_error))
+ goto failure;
+
+ break;
+ case 'd': /* audio port. */
+
+
+ if (update_arg( (void *)&(args_info->device_arg),
+ &(args_info->device_orig), &(args_info->device_given),
+ &(local_args_info.device_given), optarg, 0, "0", ARG_INT,
+ check_ambiguity, override, 0, 0,
+ "device", 'd',
+ additional_error))
+ goto failure;
+
+ break;
+ case 'p': /* frames per period. */
+
+
+ if (update_arg( (void *)&(args_info->periods_arg),
+ &(args_info->periods_orig), &(args_info->periods_given),
+ &(local_args_info.periods_given), optarg, 0, "1032", ARG_INT,
+ check_ambiguity, override, 0, 0,
+ "periods", 'p',
+ additional_error))
+ goto failure;
+
+ break;
+ case 'n': /* number of periods per buffer. */
+
+
+ if (update_arg( (void *)&(args_info->num_periods_arg),
+ &(args_info->num_periods_orig), &(args_info->num_periods_given),
+ &(local_args_info.num_periods_given), optarg, 0, "4", ARG_INT,
+ check_ambiguity, override, 0, 0,
+ "num-periods", 'n',
+ additional_error))
+ goto failure;
+
+ break;
+ case 't': /* maximum duration of command. */
+
+
+ if (update_arg( (void *)&(args_info->time_arg),
+ &(args_info->time_orig), &(args_info->time_given),
+ &(local_args_info.time_given), optarg, 0, "0", ARG_INT,
+ check_ambiguity, override, 0, 0,
+ "time", 't',
+ additional_error))
+ goto failure;
+
+ break;
+ case 'c': /* number of channels. */
+
+
+ if (update_arg( (void *)&(args_info->channels_arg),
+ &(args_info->channels_orig), &(args_info->channels_given),
+ &(local_args_info.channels_given), optarg, 0, "2", ARG_INT,
+ check_ambiguity, override, 0, 0,
+ "channels", 'c',
+ additional_error))
+ goto failure;
+
+ break;
+ case 'm': /* Bit mask with enabled channels. */
+
+
+ if (update_arg( (void *)&(args_info->channel_mask_arg),
+ &(args_info->channel_mask_orig), &(args_info->channel_mask_given),
+ &(local_args_info.channel_mask_given), optarg, 0, "0xFFFFFFFF", ARG_INT,
+ check_ambiguity, override, 0, 0,
+ "channel-mask", 'm',
+ additional_error))
+ goto failure;
+
+ break;
+ case 'b': /* Number of bits in output format (signed). */
+
+
+ if (update_arg( (void *)&(args_info->bits_arg),
+ &(args_info->bits_orig), &(args_info->bits_given),
+ &(local_args_info.bits_given), optarg, 0, "16", ARG_INT,
+ check_ambiguity, override, 0, 0,
+ "bits", 'b',
+ additional_error))
+ goto failure;
+
+ break;
+ case 'r': /* Sample rate. */
+
+
+ if (update_arg( (void *)&(args_info->rate_arg),
+ &(args_info->rate_orig), &(args_info->rate_given),
+ &(local_args_info.rate_given), optarg, 0, "48000", ARG_INT,
+ check_ambiguity, override, 0, 0,
+ "rate", 'r',
+ additional_error))
+ goto failure;
+
+ break;
+
+ case 0: /* Long option with no short option */
+ case '?': /* Invalid option. */
+ /* `getopt_long' already printed an error message. */
+ goto failure;
+
+ default: /* bug: option not considered. */
+ fprintf (stderr, "%s: option unknown: %c%s\n", CMDLINE_PARSER_PACKAGE, c, (additional_error ? additional_error : ""));
+ abort ();
+ } /* switch */
+ } /* while */
+
+
+
+
+ cmdline_parser_release (&local_args_info);
+
+ if ( error_occurred )
+ return (EXIT_FAILURE);
+
+ if (optind < argc)
+ {
+ int i = 0 ;
+ int found_prog_name = 0;
+ /* whether program name, i.e., argv[0], is in the remaining args
+ (this may happen with some implementations of getopt,
+ but surely not with the one included by gengetopt) */
+
+ i = optind;
+ while (i < argc)
+ if (argv[i++] == argv[0]) {
+ found_prog_name = 1;
+ break;
+ }
+ i = 0;
+
+ args_info->inputs_num = argc - optind - found_prog_name;
+ args_info->inputs =
+ (char **)(malloc ((args_info->inputs_num)*sizeof(char *))) ;
+ while (optind < argc)
+ if (argv[optind++] != argv[0])
+ args_info->inputs[ i++ ] = gengetopt_strdup (argv[optind-1]) ;
+ }
+
+ return 0;
+
+failure:
+
+ cmdline_parser_release (&local_args_info);
+ return (EXIT_FAILURE);
+}
diff --git a/audio-tool/cmdline.ggo b/audio-tool/cmdline.ggo
new file mode 100644
index 0000000..fb0f282
--- /dev/null
+++ b/audio-tool/cmdline.ggo
@@ -0,0 +1,78 @@
+package "audio-tool"
+usage "audio-tool [options] command [command args]"
+description "Audio utility knife for playback, capture, and tests
+
+Commands:
+ play <filename> - playback of an RIFF WAV file
+ cap <filename> - capture to an RIFF WAV file
+ mix <ctrl#> <value> - manipulate the ALSA mixer
+ tone <wave> <freq> <vol dB> - generate a tone (sine wave, square wave, etc)
+ pulse - generate impulses on the period boundaries
+ save - save current mixer state to a file
+ restore - restore mixer state from a file
+ defaults - put card in audio-tool's 'default' state
+"
+
+section "Options"
+
+option "card" D
+ "audio card number"
+ int
+ default="0"
+ optional
+
+option "device" d
+ "audio port"
+ int
+ default="0"
+ optional
+
+option "periods" p
+ "frames per period"
+ int
+ default="1032"
+ optional
+
+option "num-periods" n
+ "number of periods per buffer"
+ int
+ default="4"
+ optional
+
+option "time" t
+ "maximum duration of command"
+ int
+ default="0"
+ optional
+
+option "channels" c
+ "number of channels"
+ int
+ default="2"
+ optional
+
+option "channel-mask" m
+ "Bit mask with enabled channels"
+ int
+ default="0xFFFFFFFF"
+ optional
+
+option "bits" b
+ "Number of bits in output format (signed)"
+ int
+ default="16"
+ optional
+
+option "rate" r
+ "Sample rate"
+ int
+ default="48000"
+ optional
+
+section "Copyrights"
+
+text "
+Copyright 2012 Texas Instruments Incorporated
+Copyright 2011 The Android Open Source Project
+Copyright 2011 Gabriel M. Beddingfield
+"
\ No newline at end of file
diff --git a/audio-tool/cmdline.h b/audio-tool/cmdline.h
new file mode 100644
index 0000000..4bfd7d6
--- /dev/null
+++ b/audio-tool/cmdline.h
@@ -0,0 +1,211 @@
+/** @file cmdline.h
+ * @brief The header file for the command line option parser
+ * generated by GNU Gengetopt version 2.22.6
+ * http://www.gnu.org/software/gengetopt.
+ * DO NOT modify this file, since it can be overwritten
+ * @author GNU Gengetopt by Lorenzo Bettini */
+
+#ifndef CMDLINE_H
+#define CMDLINE_H
+
+/* If we use autoconf. */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h> /* for FILE */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifndef CMDLINE_PARSER_PACKAGE
+/** @brief the program name (used for printing errors) */
+#define CMDLINE_PARSER_PACKAGE "audio-tool"
+#endif
+
+#ifndef CMDLINE_PARSER_PACKAGE_NAME
+/** @brief the complete program name (used for help and version) */
+#define CMDLINE_PARSER_PACKAGE_NAME "audio-tool"
+#endif
+
+#ifndef CMDLINE_PARSER_VERSION
+/** @brief the program version */
+#define CMDLINE_PARSER_VERSION VERSION
+#endif
+
+/** @brief Where the command line options are stored */
+struct gengetopt_args_info
+{
+ const char *help_help; /**< @brief Print help and exit help description. */
+ const char *version_help; /**< @brief Print version and exit help description. */
+ int card_arg; /**< @brief audio card number (default='0'). */
+ char * card_orig; /**< @brief audio card number original value given at command line. */
+ const char *card_help; /**< @brief audio card number help description. */
+ int device_arg; /**< @brief audio port (default='0'). */
+ char * device_orig; /**< @brief audio port original value given at command line. */
+ const char *device_help; /**< @brief audio port help description. */
+ int periods_arg; /**< @brief frames per period (default='1032'). */
+ char * periods_orig; /**< @brief frames per period original value given at command line. */
+ const char *periods_help; /**< @brief frames per period help description. */
+ int num_periods_arg; /**< @brief number of periods per buffer (default='4'). */
+ char * num_periods_orig; /**< @brief number of periods per buffer original value given at command line. */
+ const char *num_periods_help; /**< @brief number of periods per buffer help description. */
+ int time_arg; /**< @brief maximum duration of command (default='0'). */
+ char * time_orig; /**< @brief maximum duration of command original value given at command line. */
+ const char *time_help; /**< @brief maximum duration of command help description. */
+ int channels_arg; /**< @brief number of channels (default='2'). */
+ char * channels_orig; /**< @brief number of channels original value given at command line. */
+ const char *channels_help; /**< @brief number of channels help description. */
+ int channel_mask_arg; /**< @brief Bit mask with enabled channels (default='0xFFFFFFFF'). */
+ char * channel_mask_orig; /**< @brief Bit mask with enabled channels original value given at command line. */
+ const char *channel_mask_help; /**< @brief Bit mask with enabled channels help description. */
+ int bits_arg; /**< @brief Number of bits in output format (signed) (default='16'). */
+ char * bits_orig; /**< @brief Number of bits in output format (signed) original value given at command line. */
+ const char *bits_help; /**< @brief Number of bits in output format (signed) help description. */
+ int rate_arg; /**< @brief Sample rate (default='48000'). */
+ char * rate_orig; /**< @brief Sample rate original value given at command line. */
+ const char *rate_help; /**< @brief Sample rate help description. */
+
+ unsigned int help_given ; /**< @brief Whether help was given. */
+ unsigned int version_given ; /**< @brief Whether version was given. */
+ unsigned int card_given ; /**< @brief Whether card was given. */
+ unsigned int device_given ; /**< @brief Whether device was given. */
+ unsigned int periods_given ; /**< @brief Whether periods was given. */
+ unsigned int num_periods_given ; /**< @brief Whether num-periods was given. */
+ unsigned int time_given ; /**< @brief Whether time was given. */
+ unsigned int channels_given ; /**< @brief Whether channels was given. */
+ unsigned int channel_mask_given ; /**< @brief Whether channel-mask was given. */
+ unsigned int bits_given ; /**< @brief Whether bits was given. */
+ unsigned int rate_given ; /**< @brief Whether rate was given. */
+
+ char **inputs ; /**< @brief unamed options (options without names) */
+ unsigned inputs_num ; /**< @brief unamed options number */
+} ;
+
+/** @brief The additional parameters to pass to parser functions */
+struct cmdline_parser_params
+{
+ int override; /**< @brief whether to override possibly already present options (default 0) */
+ int initialize; /**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */
+ int check_required; /**< @brief whether to check that all required options were provided (default 1) */
+ int check_ambiguity; /**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */
+ int print_errors; /**< @brief whether getopt_long should print an error message for a bad option (default 1) */
+} ;
+
+/** @brief the purpose string of the program */
+extern const char *gengetopt_args_info_purpose;
+/** @brief the usage string of the program */
+extern const char *gengetopt_args_info_usage;
+/** @brief the description string of the program */
+extern const char *gengetopt_args_info_description;
+/** @brief all the lines making the help output */
+extern const char *gengetopt_args_info_help[];
+
+/**
+ * The command line parser
+ * @param argc the number of command line options
+ * @param argv the command line options
+ * @param args_info the structure where option information will be stored
+ * @return 0 if everything went fine, NON 0 if an error took place
+ */
+int cmdline_parser (int argc, char **argv,
+ struct gengetopt_args_info *args_info);
+
+/**
+ * The command line parser (version with additional parameters - deprecated)
+ * @param argc the number of command line options
+ * @param argv the command line options
+ * @param args_info the structure where option information will be stored
+ * @param override whether to override possibly already present options
+ * @param initialize whether to initialize the option structure my_args_info
+ * @param check_required whether to check that all required options were provided
+ * @return 0 if everything went fine, NON 0 if an error took place
+ * @deprecated use cmdline_parser_ext() instead
+ */
+int cmdline_parser2 (int argc, char **argv,
+ struct gengetopt_args_info *args_info,
+ int override, int initialize, int check_required);
+
+/**
+ * The command line parser (version with additional parameters)
+ * @param argc the number of command line options
+ * @param argv the command line options
+ * @param args_info the structure where option information will be stored
+ * @param params additional parameters for the parser
+ * @return 0 if everything went fine, NON 0 if an error took place
+ */
+int cmdline_parser_ext (int argc, char **argv,
+ struct gengetopt_args_info *args_info,
+ struct cmdline_parser_params *params);
+
+/**
+ * Save the contents of the option struct into an already open FILE stream.
+ * @param outfile the stream where to dump options
+ * @param args_info the option struct to dump
+ * @return 0 if everything went fine, NON 0 if an error took place
+ */
+int cmdline_parser_dump(FILE *outfile,
+ struct gengetopt_args_info *args_info);
+
+/**
+ * Save the contents of the option struct into a (text) file.
+ * This file can be read by the config file parser (if generated by gengetopt)
+ * @param filename the file where to save
+ * @param args_info the option struct to save
+ * @return 0 if everything went fine, NON 0 if an error took place
+ */
+int cmdline_parser_file_save(const char *filename,
+ struct gengetopt_args_info *args_info);
+
+/**
+ * Print the help
+ */
+void cmdline_parser_print_help(void);
+/**
+ * Print the version
+ */
+void cmdline_parser_print_version(void);
+
+/**
+ * Initializes all the fields a cmdline_parser_params structure
+ * to their default values
+ * @param params the structure to initialize
+ */
+void cmdline_parser_params_init(struct cmdline_parser_params *params);
+
+/**
+ * Allocates dynamically a cmdline_parser_params structure and initializes
+ * all its fields to their default values
+ * @return the created and initialized cmdline_parser_params structure
+ */
+struct cmdline_parser_params *cmdline_parser_params_create(void);
+
+/**
+ * Initializes the passed gengetopt_args_info structure's fields
+ * (also set default values for options that have a default)
+ * @param args_info the structure to initialize
+ */
+void cmdline_parser_init (struct gengetopt_args_info *args_info);
+/**
+ * Deallocates the string fields of the gengetopt_args_info structure
+ * (but does not deallocate the structure itself)
+ * @param args_info the structure to deallocate
+ */
+void cmdline_parser_free (struct gengetopt_args_info *args_info);
+
+/**
+ * Checks that all the required options were specified
+ * @param args_info the structure to check
+ * @param prog_name the name of the program that will be used to print
+ * possible errors
+ * @return
+ */
+int cmdline_parser_required (struct gengetopt_args_info *args_info,
+ const char *prog_name);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* CMDLINE_H */
diff --git a/audio-tool/config.c b/audio-tool/config.c
new file mode 100644
index 0000000..b398eb1
--- /dev/null
+++ b/audio-tool/config.c
@@ -0,0 +1,73 @@
+/*
+ * config.c
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ * 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 Texas Instruments Incorporated 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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 "config.h"
+#include "cmdline.h"
+
+#include <assert.h>
+
+int parse_args(struct audio_tool_config *conf, int *argc, char*** argv)
+{
+ int ret;
+ struct gengetopt_args_info args_info;
+
+ assert(conf);
+
+ ret = cmdline_parser(*argc, *argv, &args_info);
+
+ if (!ret) {
+ conf->card = args_info.card_arg;
+ conf->device = args_info.device_arg;
+ conf->period_size = args_info.periods_arg;
+ conf->num_periods = args_info.num_periods_arg;
+ conf->duration = args_info.time_arg;
+ conf->channels = args_info.channels_arg;
+ conf->channel_mask = (uint32_t) args_info.channel_mask_arg;
+ conf->bits = args_info.bits_arg;
+ conf->rate = args_info.rate_arg;
+
+ *argc = args_info.inputs_num;
+ *argv = args_info.inputs;
+ }
+
+ return ret;
+}
+
+void usage(void)
+{
+ cmdline_parser_print_help();
+}
+
diff --git a/audio-tool/config.h b/audio-tool/config.h
new file mode 100644
index 0000000..c83e305
--- /dev/null
+++ b/audio-tool/config.h
@@ -0,0 +1,55 @@
+/*
+ * config.h
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ * 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 Texas Instruments Incorporated 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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.
+ *
+ */
+
+#ifndef __AUDIO_TOOL_CONFIG_H__
+#define __AUDIO_TOOL_CONFIG_H__
+
+#include <stdint.h>
+
+struct audio_tool_config {
+ int card;
+ int device;
+ int period_size;
+ int num_periods;
+ int duration;
+ int channels;
+ uint32_t channel_mask;
+ int bits;
+ int rate;
+};
+
+#endif /* __OMAP_AUDIO_TOOL_CONFIG_H__ */
+
diff --git a/audio-tool/config_cmd.c b/audio-tool/config_cmd.c
new file mode 100644
index 0000000..f4c983c
--- /dev/null
+++ b/audio-tool/config_cmd.c
@@ -0,0 +1,228 @@
+/*
+ * config_cmd.c
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ * 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 Texas Instruments Incorporated 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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 <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <tinyalsa/asoundlib.h>
+
+#include "config.h"
+#include "config_cmd.h"
+#include "module.h"
+#include "alsa-control.h"
+
+#define BUFSIZE 512
+
+static void usage(int card)
+{
+ char **p_fes, **p_bes, **c_fes, **c_bes;
+ char cardname[BUFSIZE];
+ struct audio_tool_card_module *mod;
+ int ret;
+
+ ret = ah_card_get_name(card, cardname, BUFSIZE);
+ if (ret) {
+ fprintf(stderr, "Error: couldn't get name for card %d (%s)\n",
+ card, strerror(-ret));
+ return;
+ }
+
+ mod = (struct audio_tool_card_module*)audio_tool_get_module(
+ AUDIO_TOOL_MOD_TYPE_CARD, cardname);
+ if (!mod) {
+ fprintf(stderr, "Error: couldn't get module for card %d\n",
+ card);
+ return;
+ }
+
+ ret = mod->get_fe_be_names(AUDIO_DIRECTION_PLAYBACK, &p_fes, &p_bes);
+ if (ret) {
+ fprintf(stderr, "Error: couldn't get listing of FE's/BE's for card\n");
+ return;
+ }
+ ret = mod->get_fe_be_names(AUDIO_DIRECTION_CAPTURE, &c_fes, &c_bes);
+ if (ret) {
+ fprintf(stderr, "Error: couldn't get listing of FE's/BE's for card\n");
+ return;
+ }
+
+ printf("Usage: audio-tool config <play|cap> <frontend> <backend> <1|0|disable|enable>\n");
+ printf("\n");
+ printf("For card %d, the options are:\n", card);
+ printf("PLAYBACK (play):\n");
+ printf(" Frontends:");
+ while (*p_fes) {
+ printf(" %s", *p_fes);
+ ++p_fes;
+ }
+ printf("\n");
+ printf(" Backends:");
+ while (*p_bes) {
+ printf(" %s", *p_bes);
+ ++p_bes;
+ }
+ printf("\n");
+ printf("CAPTURE (cap):\n");
+ mod->get_fe_be_names(AUDIO_DIRECTION_CAPTURE, &c_fes, &c_bes);
+ printf(" Frontends:");
+ while (*c_fes) {
+ printf(" %s", *c_fes);
+ ++c_fes;
+ }
+ printf("\n");
+ printf(" Backends:");
+ while (*c_bes) {
+ printf(" %s", *c_bes);
+ ++c_bes;
+ }
+ printf("\n");
+ printf("\n");
+ printf("To see options for a different card, use the -D option\n");
+}
+
+int config_cmd_main(const struct audio_tool_config *config, int argc, char **argv)
+{
+ struct mixer *mixer;
+ struct audio_tool_card_module *mod;
+ char *dir_arg, *fe_arg, *be_arg, *enable_arg;
+ char **fes, **bes;
+ char cardname[BUFSIZE];
+ int card = config->card;
+ int ret = 0;
+ int direction;
+ int port = 0;
+ int enable;
+
+ if (argc < 5) {
+ usage(card);
+ return 0;
+ }
+
+ dir_arg = argv[1];
+ fe_arg = argv[2];
+ be_arg = argv[3];
+ enable_arg = argv[4];
+
+ if (0 == strcmp(dir_arg, "play")) {
+ direction = AUDIO_DIRECTION_PLAYBACK;
+ } else if (0 == strcmp(dir_arg, "cap")) {
+ direction = AUDIO_DIRECTION_CAPTURE;
+ } else {
+ fprintf(stderr, "Error: %s is not 'play' or 'cap'\n", dir_arg);
+ usage(card);
+ return 1;
+ }
+
+ if ((0 == strcmp(enable_arg, "1")) || (0 == strcmp(enable_arg, "enable"))) {
+ enable = 1;
+ } else if ((0 == strcmp(enable_arg, "0")) || (0 == strcmp(enable_arg, "disable"))) {
+ enable = 0;
+ } else {
+ fprintf(stderr, "Error: '%s' is not '0', '1', 'disable', or 'enable'\n",
+ enable_arg);
+ return 1;
+ }
+
+ ret = ah_card_get_name(card, cardname, BUFSIZE);
+ if (ret) {
+ fprintf(stderr, "Error: couldn't get name for card %d (%s)\n",
+ card, strerror(-ret));
+ return ret;
+ }
+
+ mod = (struct audio_tool_card_module*)audio_tool_get_module(
+ AUDIO_TOOL_MOD_TYPE_CARD, cardname);
+ if (!mod) {
+ fprintf(stderr, "Error: couldn't get module for card %d\n",
+ card);
+ return ret;
+ }
+
+ ret = EINVAL;
+ if (direction == AUDIO_DIRECTION_PLAYBACK) {
+ ret = mod->get_fe_be_names(AUDIO_DIRECTION_PLAYBACK, &fes, &bes);
+ } else {
+ ret = mod->get_fe_be_names(AUDIO_DIRECTION_CAPTURE, &fes, &bes);
+ }
+ if (ret) {
+ fprintf(stderr, "Error: could not get frontends and backends for this device\n");
+ return ret;
+ }
+
+ /* check that user args are in fe/be lists */
+ while (*fes) {
+ if (0 == strcmp(*fes, fe_arg))
+ break;
+ ++fes;
+ }
+ if (!*fes) {
+ fprintf(stderr, "Error: '%s' is not a supported frontend\n", fe_arg);
+ return 1;
+ }
+ while (*bes) {
+ if (0 == strcmp(*bes, be_arg))
+ break;
+ ++bes;
+ }
+ if (!*bes) {
+ fprintf(stderr, "Error: '%s' is not a supported backend\n", be_arg);
+ return 1;
+ }
+
+ mixer = mixer_open(card);
+ if (!mixer) {
+ fprintf(stderr, "Error: could not open mixer for card %d\n", card);
+ return 1;
+ }
+
+ ret = mod->config(mixer, direction, fe_arg, be_arg, enable, &port);
+
+ if (ret) {
+ fprintf(stderr, "Error: could not configure path (%s)\n", strerror(-ret));
+ } else {
+ printf("Path configured. Use card %d port %d\n", card, port);
+ }
+
+ return ret;
+}
+
diff --git a/audio-tool/config_cmd.h b/audio-tool/config_cmd.h
new file mode 100644
index 0000000..e8f7526
--- /dev/null
+++ b/audio-tool/config_cmd.h
@@ -0,0 +1,43 @@
+/*
+ * config_cmd.h
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ * 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 Texas Instruments Incorporated 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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.
+ *
+ */
+
+#ifndef __AUDIO_TOOL_CONFIG_CMD_H__
+#define __AUDIO_TOOL_CONFIG_CMD_H__
+
+int config_cmd_main(const struct audio_tool_config *config, int argc, char **argv);
+
+#endif /* __OMAP_AUDIO_TOOL_CONFIG_CMD_H__ */
+
diff --git a/audio-tool/defaults.c b/audio-tool/defaults.c
new file mode 100644
index 0000000..b8d6874
--- /dev/null
+++ b/audio-tool/defaults.c
@@ -0,0 +1,123 @@
+/*
+ * defaults.c
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ * 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 Texas Instruments Incorporated 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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 <errno.h>
+#include <stdio.h>
+#include <tinyalsa/asoundlib.h>
+
+#include "defaults.h"
+#include "config.h"
+#include "alsa-control.h"
+#include "mixer_cache.h"
+#include "module.h"
+
+#define BUFSIZE 512
+
+int defaults_main(const struct audio_tool_config *config, int argc, char **argv)
+{
+ struct audio_tool_card_module *card_mod;
+ struct mixer *mixer;
+ struct audio_tool_mixer_cache cache;
+ char buf[BUFSIZE] = "";
+ int card = config->card;
+ int cards = ah_card_count();
+ int ret = 0;
+
+ if (card < 0 || card >= cards) {
+ fprintf(stderr, "Error: card %d does not exist\n", card);
+ ret = 1;
+ goto end;
+ }
+
+ ret = ah_card_get_name(card, buf, BUFSIZE);
+ if (ret) {
+ fprintf(stderr, "Error: can't get name of card %d (%s)",
+ card, strerror(-ret));
+ ret = 1;
+ goto end;
+ }
+
+ card_mod = (struct audio_tool_card_module*)audio_tool_get_module(
+ AUDIO_TOOL_MOD_TYPE_CARD, buf);
+ if (!card_mod) {
+ fprintf(stderr, "Error: could not find the defaults for this card (%s)\n",
+ buf);
+ ret = 1;
+ goto end;
+ }
+
+ mixer = mixer_open(card);
+ if (!mixer) {
+ fprintf(stderr, "Error: could not open mixer device (%s)\n",
+ strerror(errno));
+ ret = 1;
+ goto end;
+ }
+
+ ret = mixer_cache_init(&cache);
+ if (ret) {
+ fprintf(stderr, "Error: could not initialize the mixer cache (%s)\n",
+ strerror(ret));
+ ret = 1;
+ goto mixer_cache_err;
+ }
+
+ ret = mixer_cache_populate(&cache, mixer);
+ if (ret) {
+ fprintf(stderr, "Error: could not populate the mixer cache (%s)\n",
+ strerror(-ret));
+ ret = 1;
+ goto mixer_cache_pop_err;
+ }
+
+ if (card_mod->get_mixer_defaults(&cache)) {
+ fprintf(stderr, "Warning: mixer defaults mismatched\n");
+ }
+
+ if (mixer_cache_apply(&cache, mixer)) {
+ fprintf(stderr, "Error: could not apply mixer setting\n");
+ ret = 1;
+ goto mixer_cache_pop_err;
+ }
+
+mixer_cache_pop_err:
+ mixer_cache_deinit(&cache);
+mixer_cache_err:
+ mixer_close(mixer);
+end:
+
+ return ret;
+}
diff --git a/audio-tool/defaults.h b/audio-tool/defaults.h
new file mode 100644
index 0000000..61fdf1c
--- /dev/null
+++ b/audio-tool/defaults.h
@@ -0,0 +1,45 @@
+/*
+ * defaults.h
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ * 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 Texas Instruments Incorporated 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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.
+ *
+ */
+
+#ifndef __AUDIO_TOOL_DEFAULTS_H__
+#define __AUDIO_TOOL_DEFAULTS_H__
+
+struct audio_tool_config;
+
+int defaults_main(const struct audio_tool_config *config, int argc, char **argv);
+
+#endif /* __OMAP_AUDIO_TOOL_DEFAULTS_H__ */
+
diff --git a/audio-tool/generate-wave-table.c b/audio-tool/generate-wave-table.c
new file mode 100644
index 0000000..aac91a0
--- /dev/null
+++ b/audio-tool/generate-wave-table.c
@@ -0,0 +1,370 @@
+/* Copyright (c) 2011, Gabriel M. Beddingfield <gabrbedd@gmail.com>
+ * All rights reserved.
+ *
+ * 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 <organization> 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 <COPYRIGHT HOLDER> 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.
+ */
+
+/*
+ * generate-wave-table.c
+ *
+ * Creates a static wave-table that can be used later as a look-up table
+ * for synthesis.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <assert.h>
+
+typedef enum {
+ DATA_TYPE_NONE = 0,
+ DATA_TYPE_S16,
+ DATA_TYPE_S32,
+} data_t;
+
+typedef enum {
+ WAVE_TYPE_SINE = 1,
+ WAVE_TYPE_COSINE,
+ WAVE_TYPE_SQUARE,
+ WAVE_TYPE_TRIANGLE,
+ WAVE_TYPE_SAW,
+} wave_t;
+
+struct signal_spec {
+ data_t type;
+ wave_t wave;
+ unsigned length;
+};
+
+typedef int (*table_generator_func)(struct signal_spec *spec,
+ unsigned offset, unsigned count, void *buf);
+typedef int (*table_output_func)(void *buf, data_t type, unsigned count);
+
+#define FS_S16 0x7FFF
+#define FS_S32 0x7FFFFFFF
+
+int square_wave_generator(struct signal_spec *spec, unsigned offset,
+ unsigned count, void *buf)
+{
+ int16_t s16, *s16ptr;
+ int32_t s32, *s32ptr;
+ unsigned midpoint, k, end;
+
+ assert( spec );
+
+ if ((offset + count) > spec->length) {
+ fprintf(stderr, "Warning: generating square wave beyond "
+ "the predetermined length.\n");
+ }
+
+ midpoint = spec->length / 2;
+ if (midpoint > offset)
+ midpoint -= offset;
+ else
+ midpoint = 0;
+ end = spec->length;
+ if (end > offset)
+ end -= offset;
+ else
+ end = 0;
+
+ if (spec->type == DATA_TYPE_S16) {
+ s16ptr = (int16_t*)buf;
+ for (k = 0 ; k < count ; ++k) {
+ if (k < midpoint) {
+ s16 = FS_S16;
+ } else {
+ s16 = -FS_S16;
+ }
+ s16ptr[k] = s16;
+ }
+ } else if (spec->type == DATA_TYPE_S32) {
+ s32ptr = (int32_t*)buf;
+ for (k = 0 ; k < count ; ++k) {
+ if (k < midpoint) {
+ s32 = FS_S32;
+ } else {
+ s32 = -FS_S32;
+ }
+ s32ptr[k] = s32;
+ }
+ } else {
+ assert( 0 );
+ }
+
+ return 0;
+}
+
+int sine_wave_generator(struct signal_spec *spec, unsigned offset,
+ unsigned count, void *buf)
+{
+ int16_t s16, *s16ptr;
+ int32_t s32, *s32ptr;
+ double value, angle;
+ unsigned k;
+
+ assert( spec );
+
+ if (spec->type == DATA_TYPE_S16) {
+ s16ptr = (int16_t*)buf;
+ for (k = 0 ; k < count ; ++k) {
+ angle = 2.0 * M_PI * ((double)(offset + k)) / ((double)spec->length);
+ value = sin(angle) * FS_S16;
+ s16 = round(value);
+ s16ptr[k] = s16;
+ }
+ } else if (spec->type == DATA_TYPE_S32) {
+ s32ptr = (int32_t*)buf;
+ for (k = 0 ; k < count ; ++k) {
+ angle = 2.0 * M_PI * ((double)(offset + k)) / ((double)spec->length);
+ value = sin(angle) * FS_S32;
+ s32 = round(value);
+ s32ptr[k] = s32;
+ }
+ } else {
+ assert( 0 );
+ }
+
+ return 0;
+}
+
+int triangle_wave_generator(struct signal_spec *spec, unsigned offset,
+ unsigned count, void *buf)
+{
+ int16_t s16, *s16ptr;
+ int32_t s32, *s32ptr;
+ double value;
+ unsigned k;
+
+ assert( spec );
+
+ if (spec->type == DATA_TYPE_S16) {
+ s16ptr = (int16_t*)buf;
+ for (k = 0 ; k < count ; ++k) {
+ value = 4.0 * ((double)(offset + k)) / ((double)spec->length);
+ if (value <= 1.0) {
+ /* NO-OP */
+ } else if (value <= 3.0) {
+ value = 2.0 - value;
+ } else {
+ value = value - 4.0;
+ }
+ s16 = round(value * FS_S16);
+ s16ptr[k] = s16;
+ }
+ } else if (spec->type == DATA_TYPE_S32) {
+ s32ptr = (int32_t*)buf;
+ for (k = 0 ; k < count ; ++k) {
+ value = 4.0 * ((double)(offset + k)) / ((double)spec->length);
+ if (value <= 1.0) {
+ /* NO-OP */
+ } else if (value <= 3.0) {
+ value = 2.0 - value;
+ } else {
+ value = value - 4.0;
+ }
+ s32 = round(value * FS_S32);
+ s32ptr[k] = s32;
+ }
+ } else {
+ assert( 0 );
+ }
+
+ return 0;
+}
+
+int sawtooth_wave_generator(struct signal_spec *spec, unsigned offset,
+ unsigned count, void *buf)
+{
+ int16_t s16, *s16ptr;
+ int32_t s32, *s32ptr;
+ double value;
+ unsigned k;
+
+ assert( spec );
+
+ if (spec->type == DATA_TYPE_S16) {
+ s16ptr = (int16_t*)buf;
+ for (k = 0 ; k < count ; ++k) {
+ value = 2.0 * ((double)(offset + k)) / ((double)spec->length) - 1.0;
+ s16 = round(value * FS_S16);
+ s16ptr[k] = s16;
+ }
+ } else if (spec->type == DATA_TYPE_S32) {
+ s32ptr = (int32_t*)buf;
+ for (k = 0 ; k < count ; ++k) {
+ value = 2.0 * ((double)(offset + k)) / ((double)spec->length) - 1.0;
+ s32 = round(value * FS_S32);
+ s32ptr[k] = s32;
+ }
+ } else {
+ assert( 0 );
+ }
+
+ return 0;
+}
+
+int stdout_c_header_table(void *buf, data_t type, unsigned count)
+{
+ int16_t *s16ptr = (int16_t*)buf;
+ int32_t *s32ptr = (int32_t*)buf;
+ unsigned k;
+
+ assert( buf );
+
+ if (type == DATA_TYPE_S16) {
+ for (k=0 ; k<count ; ++k) {
+ fprintf(stdout, "0x%04hx,\n", s16ptr[k]);
+ }
+ } else if (type == DATA_TYPE_S32) {
+ for (k=0 ; k<count ; ++k) {
+ fprintf(stdout, "0x%08x,\n", s32ptr[k]);
+ }
+ } else {
+ assert( 0 );
+ }
+
+ return 0;
+}
+
+struct wave_type {
+ const char* name;
+ table_generator_func generator;
+ wave_t wave_id;
+};
+
+static struct wave_type g_types[] = {
+ {
+ .name = "square",
+ .generator = square_wave_generator,
+ },
+ {
+ .name = "sine",
+ .generator = sine_wave_generator,
+ },
+ {
+ .name = "triangle",
+ .generator = triangle_wave_generator,
+ },
+ {
+ .name = "sawtooth",
+ .generator = sawtooth_wave_generator,
+ },
+ { 0 }
+};
+
+#define BUFSIZE 4096
+#define UNSIGNED_MAX (~0U)
+
+int main(int argc, char *argv[])
+{
+ table_generator_func gen = square_wave_generator;
+ table_output_func out = stdout_c_header_table;
+ struct signal_spec spec = {
+ .type = DATA_TYPE_S16,
+ .wave = WAVE_TYPE_SQUARE,
+ .length = 512,
+ };
+ char buf[BUFSIZE];
+ unsigned k, count;
+ long tmp;
+ struct wave_type *ptr;
+ char *arg_wave, *arg_length, *arg_format;
+
+ if (argc < 4) {
+ printf("Usage: generate-wave-table <wave_type> <length> <format>\n");
+ printf("wave_types:\n");
+ for (ptr = g_types ; ptr->name != 0 ; ++ptr) {
+ printf(" %s\n", ptr->name);
+ }
+ printf("length: a positive integer\n");
+ printf("format: S16 or S32\n");
+ printf("All output is to stdout in a format suitable for C files.\n");
+ printf("Data generated is in the native byte order.\n");
+ return 0;
+ }
+
+ arg_wave = argv[1];
+ arg_length = argv[2];
+ arg_format = argv[3];
+
+ for (ptr = g_types ; ptr->name != NULL ; ++ptr) {
+ if (strcmp(arg_wave, ptr->name) == 0) {
+ spec.wave = ptr->wave_id;
+ gen = ptr->generator;
+ break;
+ }
+ }
+ if (ptr->name == NULL) {
+ fprintf(stderr, "Error: invalid wave type %s\n", arg_wave);
+ return 1;
+ }
+
+ tmp = atol(arg_length);
+ if (tmp < 8) {
+ fprintf(stderr, "Error: wave length must be at least 8 "
+ "samples\n");
+ return 1;
+ }
+ if (tmp > UINT_MAX) {
+ fprintf(stderr, "Error: wave length must fit into an "
+ "unsigned integer\n");
+ return 1;
+ }
+ spec.length = tmp;
+
+ if (0 == strcmp(arg_format, "S16")) {
+ spec.type = DATA_TYPE_S16;
+ } else if (0 == strcmp(arg_format, "S32")) {
+ spec.type = DATA_TYPE_S32;
+ } else {
+ fprintf(stderr, "Error: unsupported format %s "
+ "(must be 'S16' or 'S32')\n", arg_format);
+ return 1;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ count = 0;
+ for (k=0 ; k<spec.length ; k += count) {
+ count = BUFSIZE;
+ switch (spec.type) {
+ case DATA_TYPE_S16:
+ count /= 2;
+ break;
+ case DATA_TYPE_S32:
+ count /= 4;
+ break;
+ default:
+ assert(0);
+ }
+ if (k + count > spec.length)
+ count = spec.length - k;
+ gen(&spec, k, count, buf);
+ out(buf, spec.type, count);
+ }
+
+ return 0;
+}
diff --git a/audio-tool/include/tinyalsa/asoundlib.h b/audio-tool/include/tinyalsa/asoundlib.h
new file mode 100644
index 0000000..1546220
--- /dev/null
+++ b/audio-tool/include/tinyalsa/asoundlib.h
@@ -0,0 +1,208 @@
+/* asoundlib.h
+**
+** Copyright 2011, 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.
+*/
+
+#ifndef ASOUNDLIB_H
+#define ASOUNDLIB_H
+
+#include <sys/time.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*
+ * PCM API
+ */
+
+struct pcm;
+
+#define PCM_OUT 0x00000000
+#define PCM_IN 0x10000000
+#define PCM_MMAP 0x00000001
+#define PCM_NOIRQ 0x00000002
+#define PCM_NORESTART 0x00000004 /* PCM_NORESTART - when set, calls to
+ * pcm_write for a playback stream will not
+ * attempt to restart the stream in the case
+ * of an underflow, but will return -EPIPE
+ * instead. After the first -EPIPE error, the
+ * stream is considered to be stopped, and a
+ * second call to pcm_write will attempt to
+ * restart the stream.
+ */
+
+/* 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_XRUN 4
+#define PCM_STATE_DRAINING 5
+#define PCM_STATE_PAUSED 6
+#define PCM_STATE_SUSPENDED 7
+#define PCM_STATE_DISCONNECTED 8
+
+/* Bit formats */
+enum pcm_format {
+ PCM_FORMAT_S16_LE = 0,
+ PCM_FORMAT_S32_LE,
+ PCM_FORMAT_S8,
+ PCM_FORMAT_S24_LE,
+ PCM_FORMAT_S24_3LE,
+
+ PCM_FORMAT_MAX,
+};
+
+/* Configuration for a stream */
+struct pcm_config {
+ unsigned int channels;
+ unsigned int rate;
+ unsigned int period_size;
+ unsigned int period_count;
+ enum pcm_format format;
+
+ /* Values to use for the ALSA start, stop and silence thresholds. Setting
+ * any one of these values to 0 will cause the default tinyalsa values to be
+ * used instead. Tinyalsa defaults are as follows.
+ *
+ * start_threshold : period_count * period_size
+ * stop_threshold : period_count * period_size
+ * silence_threshold : 0
+ */
+ unsigned int start_threshold;
+ unsigned int stop_threshold;
+ unsigned int silence_threshold;
+};
+
+/* Mixer control types */
+enum mixer_ctl_type {
+ MIXER_CTL_TYPE_BOOL,
+ MIXER_CTL_TYPE_INT,
+ MIXER_CTL_TYPE_ENUM,
+ MIXER_CTL_TYPE_BYTE,
+ MIXER_CTL_TYPE_IEC958,
+ MIXER_CTL_TYPE_INT64,
+ MIXER_CTL_TYPE_UNKNOWN,
+
+ MIXER_CTL_TYPE_MAX,
+};
+
+/* Open and close a stream */
+struct pcm *pcm_open(unsigned int card, unsigned int device,
+ unsigned int flags, struct pcm_config *config);
+int pcm_close(struct pcm *pcm);
+int pcm_is_ready(struct pcm *pcm);
+
+/* 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 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.
+ * 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
+ * for the application to write.
+ */
+int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail,
+ struct timespec *tstamp);
+
+/* Write data to the fifo.
+ * Will start playback on the first write or on a write that
+ * occurs after a fifo underrun.
+ */
+int pcm_write(struct pcm *pcm, const void *data, unsigned int count);
+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_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);
+
+/* Start and stop a PCM channel that doesn't transfer data */
+int pcm_start(struct pcm *pcm);
+int pcm_stop(struct pcm *pcm);
+
+
+/*
+ * MIXER API
+ */
+
+struct mixer;
+struct mixer_ctl;
+
+/* Open and close a mixer */
+struct mixer *mixer_open(unsigned int card);
+void mixer_close(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);
+struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name);
+
+/* Get info about mixer controls */
+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);
+unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl);
+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);
+
+/* 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_set_value(struct mixer_ctl *ctl, unsigned int id, int value);
+int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string);
+
+/* Determe range of integer mixer controls */
+int mixer_ctl_get_range_min(struct mixer_ctl *ctl);
+int mixer_ctl_get_range_max(struct mixer_ctl *ctl);
+
+/* Interrupt driven API */
+int pcm_wait(struct pcm *pcm, int timeout);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/audio-tool/main.c b/audio-tool/main.c
new file mode 100644
index 0000000..275c48c
--- /dev/null
+++ b/audio-tool/main.c
@@ -0,0 +1,117 @@
+/*
+ * audio-tool.c
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ * 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 Texas Instruments Incorporated 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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 "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <libgen.h>
+
+#include "tinyplay.h"
+#include "tinycap.h"
+#include "tinymix.h"
+#include "pulse-generator.h"
+#include "tone-generator.h"
+#include "save.h"
+#include "restore.h"
+#include "defaults.h"
+#include "config_cmd.h"
+#include "wav_chan_splitter.h"
+#include "rms.h"
+
+/* defined in config.c */
+int parse_args(struct audio_tool_config *config, int *argc, char ***argv);
+void usage(void);
+
+int main(int argc, char* argv[])
+{
+ struct audio_tool_config config;
+ int ret;
+ int tinyplay = 0, tinycap = 0, tinymix = 0;
+
+ /* support backwards-compatible mode with the tinyutils */
+ if (strcmp(basename(argv[0]), "tinyplay") == 0) {
+ tinyplay = 1;
+ } else if (strcmp(basename(argv[0]), "tinycap") == 0) {
+ tinycap = 1;
+ } else if (strcmp(basename(argv[0]), "tinymix") == 0) {
+ tinymix = 1;
+ }
+
+ /* redefines argc and argv */
+ ret = parse_args(&config, &argc, &argv);
+
+ if (!ret && (argc | tinyplay | tinycap | tinymix)) {
+ /* Check for special modes first since argv[0] may be NULL */
+ if (tinyplay) {
+ ret = tinyplay_main(&config, argc, argv, tinyplay);
+ } else if (tinycap) {
+ ret = tinycap_main(&config, argc, argv, tinycap);
+ } else if (tinymix) {
+ ret = tinymix_main(&config, argc, argv, tinymix);
+
+ /* normal modes */
+ } else if (strcmp(argv[0], "play") == 0) {
+ ret = tinyplay_main(&config, argc, argv, tinyplay);
+ } else if (strcmp(argv[0], "cap") == 0
+ || strcmp(argv[0], "capture") == 0) {
+ ret = tinycap_main(&config, argc, argv, tinycap);
+ } else if (strcmp(argv[0], "mix") == 0) {
+ ret = tinymix_main(&config, argc, argv, tinymix);
+ } else if (strcmp(argv[0], "pulse") == 0) {
+ ret = pulse_generator_main(&config, argc, argv);
+ } else if (strcmp(argv[0], "tone") == 0) {
+ ret = tone_generator_main(&config, argc, argv);
+ } else if (strcmp(argv[0], "save") == 0) {
+ ret = save_main(&config, argc, argv);
+ } else if (strcmp(argv[0], "restore") == 0) {
+ ret = restore_main(&config, argc, argv);
+ } else if (strcmp(argv[0], "defaults") == 0) {
+ ret = defaults_main(&config, argc, argv);
+ } else if (strcmp(argv[0], "config") == 0) {
+ ret = config_cmd_main(&config, argc, argv);
+ } else if (strcmp(argv[0], "split") == 0) {
+ ret = split_main(argc, argv);
+ } else if (strcmp(argv[0], "rms") == 0) {
+ ret = rms_main(argc, argv);
+ } else {
+ usage();
+ ret = 1;
+ }
+ }
+
+ return ret;
+}
diff --git a/audio-tool/mixer.c b/audio-tool/mixer.c
new file mode 100644
index 0000000..9475e03
--- /dev/null
+++ b/audio-tool/mixer.c
@@ -0,0 +1,413 @@
+/* mixer.c
+**
+** Copyright 2011, 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 <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <sys/ioctl.h>
+
+#include <linux/ioctl.h>
+#include <sys/ioctl.h>
+#define __force
+#define __bitwise
+#define __user
+#include <sound/asound.h>
+
+#include <tinyalsa/asoundlib.h>
+
+struct mixer_ctl {
+ struct mixer *mixer;
+ struct snd_ctl_elem_info *info;
+ char **ename;
+};
+
+struct mixer {
+ int fd;
+ struct snd_ctl_elem_info *info;
+ struct mixer_ctl *ctl;
+ unsigned int count;
+};
+
+void mixer_close(struct mixer *mixer)
+{
+ unsigned int n,m;
+
+ if (!mixer)
+ return;
+
+ if (mixer->fd >= 0)
+ close(mixer->fd);
+
+ 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;
+ for (m = 0; m < max; m++)
+ free(mixer->ctl[n].ename[m]);
+ free(mixer->ctl[n].ename);
+ }
+ }
+ free(mixer->ctl);
+ }
+
+ if (mixer->info)
+ free(mixer->info);
+
+ free(mixer);
+
+ /* TODO: verify frees */
+}
+
+struct mixer *mixer_open(unsigned int card)
+{
+ struct snd_ctl_elem_list elist;
+ struct snd_ctl_elem_info tmp;
+ struct snd_ctl_elem_id *eid = NULL;
+ struct mixer *mixer = NULL;
+ unsigned int n, m;
+ int fd;
+ char fn[256];
+
+ snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
+ fd = open(fn, O_RDWR);
+ if (fd < 0)
+ return 0;
+
+ memset(&elist, 0, sizeof(elist));
+ if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
+ goto fail;
+
+ mixer = calloc(1, sizeof(*mixer));
+ if (!mixer)
+ 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)
+ goto fail;
+
+ eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));
+ if (!eid)
+ goto fail;
+
+ mixer->count = elist.count;
+ mixer->fd = fd;
+ elist.space = mixer->count;
+ elist.pids = eid;
+ if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
+ goto fail;
+
+ for (n = 0; n < mixer->count; n++) {
+ struct snd_ctl_elem_info *ei = mixer->info + n;
+ 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*));
+ if (!enames)
+ goto fail;
+ mixer->ctl[n].ename = enames;
+ for (m = 0; m < ei->value.enumerated.items; m++) {
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.id.numid = ei->id.numid;
+ tmp.value.enumerated.item = m;
+ if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
+ goto fail;
+ enames[m] = strdup(tmp.value.enumerated.name);
+ if (!enames[m])
+ goto fail;
+ }
+ }
+ }
+
+ free(eid);
+ return mixer;
+
+fail:
+ /* TODO: verify frees in failure case */
+ if (eid)
+ free(eid);
+ if (mixer)
+ mixer_close(mixer);
+ else if (fd >= 0)
+ close(fd);
+ return 0;
+}
+
+unsigned int mixer_get_num_ctls(struct mixer *mixer)
+{
+ if (!mixer)
+ return 0;
+
+ return mixer->count;
+}
+
+struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)
+{
+ if (mixer && (id < mixer->count))
+ return mixer->ctl + id;
+
+ return NULL;
+}
+
+struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name)
+{
+ unsigned int n;
+
+ if (!mixer)
+ return NULL;
+
+ for (n = 0; n < mixer->count; n++)
+ if (!strcmp(name, (char*) mixer->info[n].id.name))
+ return mixer->ctl + n;
+
+ return NULL;
+}
+
+const char *mixer_ctl_get_name(struct mixer_ctl *ctl)
+{
+ if (!ctl)
+ return NULL;
+
+ return (const char *)ctl->info->id.name;
+}
+
+enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl)
+{
+ if (!ctl)
+ return MIXER_CTL_TYPE_UNKNOWN;
+
+ 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;
+ case SNDRV_CTL_ELEM_TYPE_BYTES: return MIXER_CTL_TYPE_BYTE;
+ case SNDRV_CTL_ELEM_TYPE_IEC958: return MIXER_CTL_TYPE_IEC958;
+ case SNDRV_CTL_ELEM_TYPE_INTEGER64: return MIXER_CTL_TYPE_INT64;
+ default: return MIXER_CTL_TYPE_UNKNOWN;
+ };
+}
+
+const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl)
+{
+ if (!ctl)
+ return "";
+
+ 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";
+ case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTE";
+ case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958";
+ case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64";
+ default: return "Unknown";
+ };
+}
+
+unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl)
+{
+ if (!ctl)
+ return 0;
+
+ 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;
+
+ range = (ei->value.integer.max - ei->value.integer.min);
+
+ return ei->value.integer.min + (range * percent) / 100;
+}
+
+static int int_to_percent(struct snd_ctl_elem_info *ei, int value)
+{
+ int range = (ei->value.integer.max - ei->value.integer.min);
+
+ if (range == 0)
+ return 0;
+
+ return ((value - ei->value.integer.min) / range) * 100;
+}
+
+int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id)
+{
+ if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
+ return -EINVAL;
+
+ 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))
+ return -EINVAL;
+
+ 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)
+{
+ struct snd_ctl_elem_value ev;
+ int ret;
+
+ if (!ctl || (id >= ctl->info->count))
+ return -EINVAL;
+
+ 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:
+ return !!ev.value.integer.value[id];
+
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ return ev.value.integer.value[id];
+
+ case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+ return ev.value.enumerated.item[id];
+
+ case SNDRV_CTL_ELEM_TYPE_BYTES:
+ return ev.value.bytes.data[id];
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+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))
+ return -EINVAL;
+
+ 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:
+ ev.value.integer.value[id] = !!value;
+ break;
+
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ ev.value.integer.value[id] = value;
+ break;
+
+ case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+ ev.value.enumerated.item[id] = value;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
+}
+
+int mixer_ctl_get_range_min(struct mixer_ctl *ctl)
+{
+ if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
+ return -EINVAL;
+
+ 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))
+ return -EINVAL;
+
+ return ctl->info->value.integer.max;
+}
+
+unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl)
+{
+ if (!ctl)
+ return 0;
+
+ 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))
+ return NULL;
+
+ return (const char *)ctl->ename[enum_id];
+}
+
+int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
+{
+ unsigned int i, num_enums;
+ struct snd_ctl_elem_value ev;
+ int ret;
+
+ if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED))
+ return -EINVAL;
+
+ 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;
+ ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
+ if (ret < 0)
+ return ret;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
diff --git a/audio-tool/mixer_cache.c b/audio-tool/mixer_cache.c
new file mode 100644
index 0000000..a0ec65f
--- /dev/null
+++ b/audio-tool/mixer_cache.c
@@ -0,0 +1,224 @@
+/*
+ * mixer_cache.c
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ * 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 Texas Instruments Incorporated 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <tinyalsa/asoundlib.h>
+
+#include "mixer_cache.h"
+
+int mixer_cache_init(struct audio_tool_mixer_cache *cache)
+{
+ if (!cache)
+ return EINVAL;
+
+ memset(cache, 0, sizeof(struct audio_tool_mixer_cache));
+
+ return 0;
+}
+
+void mixer_cache_deinit(struct audio_tool_mixer_cache *cache)
+{
+ if (!cache)
+ return;
+ if (!cache->ctrls)
+ return;
+ free(cache->ctrls);
+}
+
+int mixer_cache_populate(struct audio_tool_mixer_cache *cache, struct mixer *mixer)
+{
+ struct mixer_ctl *ctl;
+ struct audio_tool_mixer_control_info *cur;
+ const char* name;
+ size_t count, n, v;
+ int tmp;
+
+ if (!cache)
+ return EINVAL;
+
+ if (!mixer)
+ return EINVAL;
+
+ if (cache->ctrls)
+ free(cache->ctrls);
+
+ count = mixer_get_num_ctls(mixer);
+ cache->count = count;
+
+ if (count) {
+ cache->ctrls = calloc(count, sizeof(struct audio_tool_mixer_control_info));
+ if (! cache->ctrls)
+ return -ENOMEM;
+ }
+
+ for (n = 0, cur = cache->ctrls ; n < count ; ++n, ++cur) {
+ ctl = mixer_get_ctl(mixer, n);
+ if (!ctl)
+ return ENODEV;
+ name = mixer_ctl_get_name(ctl);
+ if (!name)
+ return ENODEV;
+
+ cur->id = n;
+ cur->type = mixer_ctl_get_type(ctl);
+ strcpy(cur->name, name);
+ cur->num_values = mixer_ctl_get_num_values(ctl);
+ if (cur->num_values > MAX_NUM_VALUES)
+ cur->num_values = MAX_NUM_VALUES;
+ for (v = 0 ; v < cur->num_values ; ++v) {
+ switch (cur->type) {
+ case MIXER_CTL_TYPE_BOOL:
+ case MIXER_CTL_TYPE_INT:
+ cur->value.integer[v] = mixer_ctl_get_value(ctl, v);
+ break;
+ case MIXER_CTL_TYPE_ENUM:
+ tmp = mixer_ctl_get_value(ctl, v);
+ name = mixer_ctl_get_enum_string(ctl, tmp);
+ strcpy(cur->value.enumerated[v], name);
+ break;
+ case MIXER_CTL_TYPE_BYTE:
+ cur->value.byte[v] = mixer_ctl_get_value(ctl, v);
+ break;
+ case MIXER_CTL_TYPE_INT64:
+ cur->value.integer64[v] = mixer_ctl_get_value(ctl, v);
+ break;
+ default:
+ (void)0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int mixer_cache_apply(struct audio_tool_mixer_cache *cache, struct mixer *mixer)
+{
+ struct audio_tool_mixer_control_info *cur;
+ struct mixer_ctl *ctl;
+ int n, k, ret=0;
+
+ for (n = 0 ; n < cache->count ; ++n) {
+ cur = &cache->ctrls[n];
+ ctl = mixer_get_ctl(mixer, n);
+ for (k = 0 ; k < cur->num_values ; ++k) {
+ switch (cur->type) {
+ case MIXER_CTL_TYPE_BOOL:
+ case MIXER_CTL_TYPE_INT:
+ mixer_ctl_set_value(ctl, k, cur->value.integer[k]);
+ break;
+ case MIXER_CTL_TYPE_ENUM:
+ mixer_ctl_set_enum_by_string(ctl, cur->value.enumerated[k]);
+ break;
+ case MIXER_CTL_TYPE_BYTE:
+ mixer_ctl_set_value(ctl, k, cur->value.byte[k]);
+ break;
+ case MIXER_CTL_TYPE_INT64:
+ mixer_ctl_set_value(ctl, k, cur->value.integer64[k]);
+ break;
+ default:
+ ret = 1;
+ }
+ }
+ }
+
+ return ret;
+}
+
+int mixer_cache_get_id_by_name(struct audio_tool_mixer_cache *cache, const char* name)
+{
+ struct audio_tool_mixer_control_info *cur;
+
+ if (!cache)
+ return -EINVAL;
+ if (!name)
+ return -EINVAL;
+
+ for (cur = cache->ctrls ; cur < (cache->ctrls + cache->count) ; ++cur) {
+ if (strcmp(cur->name, name) == 0)
+ return cur->id;
+ }
+
+ return -ENODEV;
+}
+
+void mixer_cache_reset_touch(struct audio_tool_mixer_cache *cache)
+{
+ struct audio_tool_mixer_control_info *cur;
+
+ if (!cache)
+ return;
+
+ for (cur = cache->ctrls ; cur < (cache->ctrls + cache->count) ; ++cur) {
+ cur->touch = 0;
+ }
+}
+
+void mixer_cache_touch(struct audio_tool_mixer_cache *cache, int id)
+{
+ if (!cache)
+ return;
+ if (id < 0)
+ return;
+ if (id > cache->count)
+ return;
+
+ cache->ctrls[id].touch = 1;
+}
+
+int mixer_cache_audit_touch(struct audio_tool_mixer_cache *cache, int verbose)
+{
+ struct audio_tool_mixer_control_info *cur;
+ int ret = 0;
+
+ if (!cache)
+ return EINVAL;
+
+ for (cur = cache->ctrls ; cur < (cache->ctrls + cache->count) ; ++cur) {
+ if (!cur->touch) {
+ ret = 1;
+ if (verbose) {
+ fprintf(stderr, "Warning: Control #%d (%s) was not touched\n",
+ cur->id, cur->name);
+ }
+ }
+ }
+
+ return ret;
+}
+
diff --git a/audio-tool/mixer_cache.h b/audio-tool/mixer_cache.h
new file mode 100644
index 0000000..6c2642d
--- /dev/null
+++ b/audio-tool/mixer_cache.h
@@ -0,0 +1,84 @@
+/*
+ * mixer_cache.h
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ * 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 Texas Instruments Incorporated 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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.
+ *
+ */
+
+#ifndef __AUDIO_TOOL_MIXER_CACHE_H__
+#define __AUDIO_TOOL_MIXER_CACHE_H__
+
+#include <stdint.h>
+#include <tinyalsa/asoundlib.h>
+
+/* In kernel this is typically 44 */
+#define AUDIO_TOOL_MIX_CTL_NAME_MAX 128
+
+struct audio_tool_mixer_control_info;
+struct audio_tool_mixer_cache;
+
+/* Returns 0 on success, errno on failure*/
+int mixer_cache_init(struct audio_tool_mixer_cache *cache);
+void mixer_cache_deinit(struct audio_tool_mixer_cache *cache);
+/* Returns 0 on success, errno on failure */
+int mixer_cache_populate(struct audio_tool_mixer_cache *cache, struct mixer *mixer);
+/* Returns control id (>=0), or negative errno on failure */
+int mixer_cache_get_id_by_name(struct audio_tool_mixer_cache *cache, const char* name);
+void mixer_cache_reset_touch(struct audio_tool_mixer_cache *cache);
+void mixer_cache_touch(struct audio_tool_mixer_cache *cache, int id);
+int mixer_cache_audit_touch(struct audio_tool_mixer_cache *cache, int verbose);
+int mixer_cache_apply(struct audio_tool_mixer_cache *cache, struct mixer *mixer);
+
+#define MAX_NUM_VALUES 8
+
+struct audio_tool_mixer_control_info {
+ unsigned id;
+ char name[AUDIO_TOOL_MIX_CTL_NAME_MAX];
+ enum mixer_ctl_type type;
+ unsigned num_values;
+ union {
+ long integer[128];
+ int64_t integer64[64];
+ char enumerated[MAX_NUM_VALUES][64];
+ unsigned char byte[512];
+ } value;
+
+ int touch; /* For use with full-list processing */
+};
+
+struct audio_tool_mixer_cache {
+ size_t count;
+ struct audio_tool_mixer_control_info *ctrls;
+};
+
+#endif /* __OMAP_AUDIO_TOOL_MIXER_CACHE_H__ */
+
diff --git a/audio-tool/module.c b/audio-tool/module.c
new file mode 100644
index 0000000..6439743
--- /dev/null
+++ b/audio-tool/module.c
@@ -0,0 +1,109 @@
+/*
+ * module.c
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ * 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 Texas Instruments Incorporated 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "module.h"
+
+struct module_node {
+ struct module_node *next;
+ struct audio_tool_module *module;
+};
+
+struct module_node *g_module_list = 0;
+
+static void module_list_push_back(struct module_node *list, struct module_node *node)
+{
+ if (!list)
+ return;
+
+ while (list->next)
+ list = list->next;
+
+ list->next = node;
+}
+
+int audio_tool_module_register(struct audio_tool_module *mod)
+{
+ struct module_node *node;
+
+ if (!mod)
+ return EINVAL;
+
+ node = calloc(1, sizeof(struct module_node));
+
+ if (!node)
+ return ENOMEM;
+
+ node->module = mod;
+
+ if (!g_module_list) {
+ g_module_list = node;
+ } else {
+ module_list_push_back(g_module_list, node);
+ }
+
+ return 0;
+}
+
+struct audio_tool_module* audio_tool_get_module(int module_type,
+ const char* optional_name)
+{
+ struct module_node *node = g_module_list;
+
+ if (!module_type || module_type > AUDIO_TOOL_MOD_TYPE_MAX)
+ return NULL;
+
+ for ( ; node ; node = node->next ) {
+ if (node->module->type != module_type)
+ continue;
+
+ if (optional_name) {
+ if(!strcmp(node->module->name, optional_name))
+ break;
+ } else {
+ if (!node->module->probe())
+ break;
+ }
+ }
+
+ if (node)
+ return node->module;
+
+ return 0;
+}
diff --git a/audio-tool/module.h b/audio-tool/module.h
new file mode 100644
index 0000000..bd10f03
--- /dev/null
+++ b/audio-tool/module.h
@@ -0,0 +1,121 @@
+/*
+ * module.h
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ * 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 Texas Instruments Incorporated 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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.
+ *
+ */
+
+#ifndef __AUDIO_TOOL_MODULE_H__
+#define __AUDIO_TOOL_MODULE_H__
+
+struct audio_tool_module;
+struct audio_tool_card_module;
+struct audio_tool_command_module;
+struct audio_tool_mixer_cache;
+struct mixer;
+
+#ifndef __GNUC__
+#error The module interface requires GCCs constructor/destructor attributes.
+#endif
+
+#define __init __attribute__((constructor))
+#define __exit __attribute__((destructor))
+
+int audio_tool_module_register(struct audio_tool_module *mod);
+
+/* Board module access */
+struct audio_tool_module* audio_tool_get_module(int module_type,
+ const char* optional_name);
+
+/* Module types */
+#define AUDIO_TOOL_MOD_TYPE_ANY 0
+#define AUDIO_TOOL_MOD_TYPE_CARD 1
+#define AUDIO_TOOL_MOD_TYPE_COMMAND 2
+
+#define AUDIO_TOOL_MOD_TYPE_MAX AUDIO_TOOL_MOD_TYPE_COMMAND
+
+/* The type and the name (together) make the module unique.
+ * However, uniqueness is not enforced.
+ */
+struct audio_tool_module {
+ int type;
+ const char* name;
+
+ /* Required: return non-zero if the module supports the current device */
+ int (*probe)(void);
+};
+
+#define AUDIO_DIRECTION_PLAYBACK 0
+#define AUDIO_DIRECTION_CAPTURE 1
+
+struct audio_tool_card_module {
+ int type;
+ const char* name;
+
+ /* Required: return zero if the module supports the current device */
+ int (*probe)(void);
+
+ /* Required: Takes an existing mixer_cache and populates
+ * it with default values. Returns 0 on success.
+ */
+ int (*get_mixer_defaults)(struct audio_tool_mixer_cache *cache);
+
+ /* Required: Supply front-ends and back-ends this card supports.
+ * direction: 0 for playback, 1 for capture
+ * Returns: fes and bes point to static, null-terminated array of strings
+ */
+ int (*get_fe_be_names)(int direction, char ***fes, char ***bes);
+
+ /* Required */
+ int (*config)(struct mixer *mixer, int direction, const char* fe,
+ const char* be, int enable, int *optional_port);
+};
+
+/* Module for implementing sub-commands
+ *
+ * The name is the same as the sub-command name.
+ */
+struct audio_tool_command_module {
+ int type;
+ const char* name;
+
+ /* Required: return zero if the module supports the current device */
+ int (*probe)(void);
+
+ /* Required: The main entry point to the command's function
+ * argc and argv are passed directly from the command line.
+ */
+ int (*main)(int argc, char* argv[]);
+};
+
+#endif /* __OMAP_AUDIO_TOOL_MODULE_H__ */
+
diff --git a/audio-tool/oscillator-table.c b/audio-tool/oscillator-table.c
new file mode 100644
index 0000000..b4dd044
--- /dev/null
+++ b/audio-tool/oscillator-table.c
@@ -0,0 +1,214 @@
+/* Copyright (c) 2011, Gabriel M. Beddingfield <gabrbedd@gmail.com>
+ * All rights reserved.
+ *
+ * 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 <organization> 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 <COPYRIGHT HOLDER> 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.
+ */
+
+/*
+ * oscillator-table.c
+ *
+ * Utilities generating an accurate waveform to an audio output.
+ */
+
+#include <assert.h>
+#include <math.h>
+#include <stdint.h>
+#include <limits.h>
+
+#include "oscillator-table.h"
+
+/*#define CHECK_MATH*/
+
+typedef void (*write_to_buffer_func_t)(void *out, uint16_t frame, uint8_t channels,
+ uint8_t channel, int16_t value);
+
+void write_s16_to_s8(void *out, uint16_t frame, uint8_t channels,
+ uint8_t channel, int16_t value)
+{
+ int8_t *dest = out;
+ dest[frame * channels + channel] = value >> 8;
+}
+
+void write_s16_to_s16(void *out, uint16_t frame, uint8_t channels,
+ uint8_t channel, int16_t value)
+{
+ int16_t *dest = out;
+ dest[frame * channels + channel] = value;
+}
+
+void write_s16_to_s24(void *out, uint16_t frame, uint8_t channels,
+ uint8_t channel, int16_t value)
+{
+ union s24_t {
+ int32_t v;
+ unsigned char c[4];
+ } val;
+ int64_t tmp = value;
+ unsigned char *d;
+
+ /* scale 0x7FFF to 0x7FFFFF */
+ tmp = (tmp * 0x7FFFFF) / 0x7FFF;
+ assert( tmp <= (int64_t)0x7FFFFF );
+ assert( (tmp >= 0) || (-tmp <= (int64_t)0x7FFFFF ) );
+ val.v = tmp;
+ d = out;
+ d += 3 * (frame * channels + channel);
+ d[0] = val.c[0];
+ d[1] = val.c[1];
+ d[2] = val.c[2];
+}
+
+void write_s16_to_s32(void *out, uint16_t frame, uint8_t channels,
+ uint8_t channel, int16_t value)
+{
+ int32_t *dest = out;
+ int64_t tmp = value;
+ int32_t v;
+
+ /* scale 0x7FFF to 0x7FFFFFFF */
+ tmp = (tmp * 0x7FFFFFFF) / 0x7FFF;
+ assert( tmp <= (int64_t)0x7FFFFFFF );
+ assert( (tmp >= 0) || (-tmp <= (int64_t)0x7FFFFFFF ) );
+ v = tmp;
+ dest[frame * channels + channel] = v;
+}
+
+/**
+ * \brief render an oscillator table to an output buffer, using the freq specified
+ *
+ * \param out The output buffer (PCM16)
+ * \param tbl The source oscillator table
+ * \param offset the frame offset of the wave as rendered on the output
+ * \param count the number of frames to render this cycle
+ * \param wave_scale specification of the desired output wavelength
+ * \param channels number of output channels in the buffer (range: [1,32])
+ * \param clannel_mask mask indicating which channels to write to
+ * \param vol_frac An integer fraction for attenuating the signal
+ * \param format output bits (should be 8, 16, 24, or 32)
+ *
+ * \returns non-zero on error.
+ */
+int oscillator_table_render(void *out, struct wave_table *tbl, uint32_t offset,
+ uint16_t count, const struct wave_scale wave_scale, uint8_t channels,
+ uint32_t channel_mask, uint16_t vol_frac, int bits)
+{
+ int32_t num;
+ const int32_t denom = USHRT_MAX;
+ uint32_t tbl_len, wave_len;
+ uint32_t k, frame, f_reg, r_reg;
+ uint32_t p;
+ int16_t val;
+ int32_t a, b;
+ uint8_t ch;
+ int32_t chbit;
+ char interpolate = 1;
+ int16_t shift;
+ uint32_t f;
+#ifdef CHECK_MATH
+ uint32_t ck_p, ck_tbl_len, ck_wave_len;
+#endif
+ write_to_buffer_func_t write_to_buffer;
+
+ assert( out );
+ assert( tbl );
+ assert( channels > 0 );
+ assert( 0 == (bits % 8) );
+ assert( bits > 0 );
+ assert( bits <= 32 );
+
+ switch (bits) {
+ case 8: write_to_buffer = write_s16_to_s8; break;
+ case 16: write_to_buffer = write_s16_to_s16; break;
+ case 24: write_to_buffer = write_s16_to_s24; break;
+ case 32: write_to_buffer = write_s16_to_s32; break;
+ }
+
+ num = vol_frac;
+ tbl_len = tbl->length << wave_scale.sub_shift;
+ wave_len = (wave_scale.length << wave_scale.sub_shift) | wave_scale.sub;
+#ifdef CHECK_MATH
+ ck_tbl_len = (uint64_t)tbl->length << wave_scale.sub_shift;
+ ck_wave_len = ((uint64_t)wave_scale.length << wave_scale.sub_shift) | wave_scale.sub;
+#endif
+ if ( (tbl_len / wave_len) > 4 )
+ interpolate = 0;
+
+ for (k=0 ; k < count ; ++k) {
+ /* This bit of code is over-optimized for 32-bit processors.
+ * See the "CHECK_MATH" section for grokkable code.
+ * Why? Because it was a fun challenge.
+ *
+ * Strategy: Break "frame" into 8-bit chunks, and build
+ * up "p", being careful to preserve remainders.
+ */
+ assert( (1<<16) > tbl->mask ); /* uncomment code if this fails */
+ assert( wave_len == (wave_len & 0xFFFFFF) );
+ assert( tbl_len == (tbl_len & 0xFFFFFF) );
+ frame = offset + k;
+ p = 0;
+ f_reg = 0;
+ r_reg = 0;
+ for (shift=24 ; shift >= 0 ; shift -= 8) {
+ f = (frame >> shift) & 0xFF;
+ f_reg = f * tbl_len;
+ r_reg = (r_reg << 8) + f_reg % wave_len;
+ p = (p << 8) + f_reg / wave_len + r_reg / wave_len;
+ r_reg %= wave_len;
+ }
+ p &= tbl->mask;
+#ifdef CHECK_MATH
+ ck_p = ((uint64_t)(offset + k) * ck_tbl_len) / ck_wave_len;
+ ck_p &= tbl->mask;
+ assert( p == ck_p );
+#endif
+ if (interpolate) {
+ /* interpolate */
+ int32_t water;
+ a = tbl->data[p];
+ b = tbl->data[p + 1];
+ assert( (int32_t)r_reg >= 0 );
+ assert( (int32_t)wave_len >= 0 );
+ /* water down r_reg and wave_len if they will overflow */
+ water = 4;
+ while ((r_reg >> water) > 0x7FFF)
+ ++water;
+ val = a + ((b-a) * ((int32_t)r_reg >> water)) / ((int32_t)wave_len >> water);
+ if (a < b)
+ assert( a <= val && val <= b );
+ else
+ assert( a >= val && val >= b );
+ } else {
+ val = tbl->data[p];
+ }
+ val = (val * num) / denom;
+ for (ch = 0, chbit=1 ; ch < channels ; ++ch, chbit <<= 1) {
+ if ( channel_mask & chbit )
+ write_to_buffer(out, k, channels, ch, val);
+ else
+ write_to_buffer(out, k, channels, ch, 0);
+ }
+ }
+
+ return 0;
+}
+
diff --git a/audio-tool/oscillator-table.h b/audio-tool/oscillator-table.h
new file mode 100644
index 0000000..6bb1a71
--- /dev/null
+++ b/audio-tool/oscillator-table.h
@@ -0,0 +1,61 @@
+/* Copyright (c) 2012, Gabriel M. Beddingfield <gabrbedd@gmail.com>
+ * All rights reserved.
+ *
+ * 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 <organization> 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 <COPYRIGHT HOLDER> 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.
+ */
+#ifndef __LIBGABE_OSCILLATOR_TABLE_H__
+#define __LIBGABE_OSCILLATOR_TABLE_H__
+
+#define IS_POWER_OF_TWO(x) ((0) == ((x) & ((x)-1)))
+
+struct wave_table {
+ const char* name;
+ uint16_t length; /* Must be a power of two */
+ uint16_t mask; /* = length - 1 */
+ int16_t *data;
+};
+
+struct wave_scale {
+ uint16_t length;
+ uint16_t sub; /* sub-frames, binary fraction with
+ denominator = ((1 << sub_shift) - 1)*/
+ uint16_t sub_den; /* must be (power of 2)-1 */
+ uint16_t sub_shift; /* denominator shift, sub_den = (1 << sub_shift) - 1 */
+};
+
+#define STATIC_ARRAY_SIZE(ra) (sizeof(ra)/sizeof(ra[0]))
+
+#define DECLARE_TABLE(t_name, t_data) \
+ { \
+ .name = (t_name), \
+ .length = STATIC_ARRAY_SIZE(t_data), \
+ .mask = STATIC_ARRAY_SIZE(t_data) - 1, \
+ .data = (t_data), \
+ }
+
+int oscillator_table_render(void *out, struct wave_table *tbl, uint32_t offset,
+ uint16_t count, const struct wave_scale wave_scale, uint8_t channels,
+ uint32_t channel_mask, uint16_t vol_frac, int bits);
+
+#endif /* __LIBGABE_OSCILLATOR_TABLE_H__ */
+
diff --git a/audio-tool/pcm.c b/audio-tool/pcm.c
new file mode 100644
index 0000000..758e4b1
--- /dev/null
+++ b/audio-tool/pcm.c
@@ -0,0 +1,837 @@
+/* pcm.c
+**
+** Copyright 2011, 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 <fcntl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <poll.h>
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <limits.h>
+
+#include <linux/ioctl.h>
+#define __force
+#define __bitwise
+#define __user
+#include <sound/asound.h>
+
+#include <tinyalsa/asoundlib.h>
+
+#define PARAM_MAX SNDRV_PCM_HW_PARAM_LAST_INTERVAL
+#define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP (1<<2)
+
+static inline int param_is_mask(int p)
+{
+ return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
+ (p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
+}
+
+static inline int param_is_interval(int p)
+{
+ return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) &&
+ (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL);
+}
+
+static inline struct snd_interval *param_to_interval(struct snd_pcm_hw_params *p, int n)
+{
+ return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]);
+}
+
+static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n)
+{
+ return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
+}
+
+static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit)
+{
+ if (bit >= SNDRV_MASK_MAX)
+ return;
+ if (param_is_mask(n)) {
+ struct snd_mask *m = param_to_mask(p, n);
+ m->bits[0] = 0;
+ m->bits[1] = 0;
+ m->bits[bit >> 5] |= (1 << (bit & 31));
+ }
+}
+
+static void param_set_min(struct snd_pcm_hw_params *p, int n, unsigned int val)
+{
+ if (param_is_interval(n)) {
+ struct snd_interval *i = param_to_interval(p, n);
+ i->min = val;
+ }
+}
+
+static void param_set_max(struct snd_pcm_hw_params *p, int n, unsigned int val)
+{
+ if (param_is_interval(n)) {
+ struct snd_interval *i = param_to_interval(p, n);
+ i->max = val;
+ }
+}
+
+static void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned int val)
+{
+ if (param_is_interval(n)) {
+ struct snd_interval *i = param_to_interval(p, n);
+ i->min = val;
+ i->max = val;
+ i->integer = 1;
+ }
+}
+
+static unsigned int param_get_int(struct snd_pcm_hw_params *p, int n)
+{
+ if (param_is_interval(n)) {
+ struct snd_interval *i = param_to_interval(p, n);
+ if (i->integer)
+ return i->max;
+ }
+ return 0;
+}
+
+static void param_init(struct snd_pcm_hw_params *p)
+{
+ int n;
+
+ memset(p, 0, sizeof(*p));
+ for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK;
+ n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) {
+ struct snd_mask *m = param_to_mask(p, n);
+ m->bits[0] = ~0;
+ m->bits[1] = ~0;
+ }
+ for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
+ n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) {
+ struct snd_interval *i = param_to_interval(p, n);
+ i->min = 0;
+ i->max = ~0;
+ }
+}
+
+#define PCM_ERROR_MAX 128
+
+struct pcm {
+ int fd;
+ unsigned int flags;
+ int running:1;
+ int underruns;
+ unsigned int buffer_size;
+ unsigned int boundary;
+ char error[PCM_ERROR_MAX];
+ struct pcm_config config;
+ struct snd_pcm_mmap_status *mmap_status;
+ struct snd_pcm_mmap_control *mmap_control;
+ struct snd_pcm_sync_ptr *sync_ptr;
+ void *mmap_buffer;
+ unsigned int noirq_frames_per_msec;
+};
+
+unsigned int pcm_get_buffer_size(struct pcm *pcm)
+{
+ return pcm->buffer_size;
+}
+
+const char* pcm_get_error(struct pcm *pcm)
+{
+ return pcm->error;
+}
+
+static int oops(struct pcm *pcm, int e, const char *fmt, ...)
+{
+ va_list ap;
+ int sz;
+
+ va_start(ap, fmt);
+ vsnprintf(pcm->error, PCM_ERROR_MAX, fmt, ap);
+ va_end(ap);
+ sz = strlen(pcm->error);
+
+ if (errno)
+ snprintf(pcm->error + sz, PCM_ERROR_MAX - sz,
+ ": %s", strerror(e));
+ return -1;
+}
+
+static unsigned int pcm_format_to_alsa(enum pcm_format format)
+{
+ switch (format) {
+ case PCM_FORMAT_S32_LE:
+ return SNDRV_PCM_FORMAT_S32_LE;
+ case PCM_FORMAT_S8:
+ return SNDRV_PCM_FORMAT_S8;
+ case PCM_FORMAT_S24_LE:
+ return SNDRV_PCM_FORMAT_S24_LE;
+ case PCM_FORMAT_S24_3LE:
+ return SNDRV_PCM_FORMAT_S24_3LE;
+ default:
+ case PCM_FORMAT_S16_LE:
+ return SNDRV_PCM_FORMAT_S16_LE;
+ };
+}
+
+static unsigned int pcm_format_to_bits(enum pcm_format format)
+{
+ switch (format) {
+ case PCM_FORMAT_S32_LE:
+ case PCM_FORMAT_S24_LE:
+ return 32;
+ case PCM_FORMAT_S24_3LE:
+ return 24;
+ default:
+ case PCM_FORMAT_S16_LE:
+ return 16;
+ };
+}
+
+unsigned int pcm_bytes_to_frames(struct pcm *pcm, unsigned int bytes)
+{
+ return bytes / (pcm->config.channels *
+ (pcm_format_to_bits(pcm->config.format) >> 3));
+}
+
+unsigned int pcm_frames_to_bytes(struct pcm *pcm, unsigned int frames)
+{
+ return frames * pcm->config.channels *
+ (pcm_format_to_bits(pcm->config.format) >> 3);
+}
+
+static int pcm_sync_ptr(struct pcm *pcm, int flags) {
+ if (pcm->sync_ptr) {
+ pcm->sync_ptr->flags = flags;
+ if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+static int pcm_hw_mmap_status(struct pcm *pcm) {
+
+ if (pcm->sync_ptr)
+ return 0;
+
+ int page_size = sysconf(_SC_PAGE_SIZE);
+ pcm->mmap_status = mmap(NULL, page_size, PROT_READ, MAP_FILE | MAP_SHARED,
+ pcm->fd, SNDRV_PCM_MMAP_OFFSET_STATUS);
+ if (pcm->mmap_status == MAP_FAILED)
+ pcm->mmap_status = NULL;
+ if (!pcm->mmap_status)
+ goto mmap_error;
+
+ pcm->mmap_control = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
+ MAP_FILE | MAP_SHARED, pcm->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL);
+ if (pcm->mmap_control == MAP_FAILED)
+ pcm->mmap_control = NULL;
+ if (!pcm->mmap_control) {
+ munmap(pcm->mmap_status, page_size);
+ pcm->mmap_status = NULL;
+ goto mmap_error;
+ }
+ pcm->mmap_control->avail_min = 1;
+
+ return 0;
+
+mmap_error:
+
+ pcm->sync_ptr = calloc(1, sizeof(*pcm->sync_ptr));
+ if (!pcm->sync_ptr)
+ return -ENOMEM;
+ pcm->mmap_status = &pcm->sync_ptr->s.status;
+ pcm->mmap_control = &pcm->sync_ptr->c.control;
+ pcm->mmap_control->avail_min = 1;
+ pcm_sync_ptr(pcm, 0);
+
+ return 0;
+}
+
+static void pcm_hw_munmap_status(struct pcm *pcm) {
+ if (pcm->sync_ptr) {
+ free(pcm->sync_ptr);
+ pcm->sync_ptr = NULL;
+ } else {
+ int page_size = sysconf(_SC_PAGE_SIZE);
+ if (pcm->mmap_status)
+ munmap(pcm->mmap_status, page_size);
+ if (pcm->mmap_control)
+ munmap(pcm->mmap_control, page_size);
+ }
+ pcm->mmap_status = NULL;
+ pcm->mmap_control = NULL;
+}
+
+static int pcm_areas_copy(struct pcm *pcm, unsigned int pcm_offset,
+ const char *src, unsigned int src_offset,
+ unsigned int frames)
+{
+ int size_bytes = pcm_frames_to_bytes(pcm, frames);
+ int pcm_offset_bytes = pcm_frames_to_bytes(pcm, 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);
+ return 0;
+}
+
+static int pcm_mmap_write_areas(struct pcm *pcm, const char *src,
+ unsigned int offset, unsigned int size)
+{
+ void *pcm_areas;
+ int commit;
+ unsigned int pcm_offset, frames, count = 0;
+
+ while (size > 0) {
+ frames = size;
+ pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
+ pcm_areas_copy(pcm, pcm_offset, src, offset, frames);
+ commit = pcm_mmap_commit(pcm, pcm_offset, frames);
+ if (commit < 0) {
+ oops(pcm, commit, "failed to commit %d frames\n", frames);
+ return commit;
+ }
+
+ offset += commit;
+ count += commit;
+ size -= commit;
+ }
+ return count;
+}
+
+int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail,
+ struct timespec *tstamp)
+{
+ int frames;
+ int rc;
+ snd_pcm_uframes_t hw_ptr;
+
+ if (!pcm_is_ready(pcm))
+ return -1;
+
+ rc = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_APPL|SNDRV_PCM_SYNC_PTR_HWSYNC);
+ if (rc < 0)
+ return -1;
+
+ if ((pcm->mmap_status->state != PCM_STATE_RUNNING) &&
+ (pcm->mmap_status->state != PCM_STATE_DRAINING))
+ return -1;
+
+ *tstamp = pcm->mmap_status->tstamp;
+ if (tstamp->tv_sec == 0 && tstamp->tv_nsec == 0)
+ return -1;
+
+ hw_ptr = pcm->mmap_status->hw_ptr;
+ if (pcm->flags & PCM_IN)
+ frames = hw_ptr - pcm->mmap_control->appl_ptr;
+ else
+ frames = hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr;
+
+ if (frames < 0)
+ return -1;
+
+ *avail = (unsigned int)frames;
+
+ return 0;
+}
+
+int pcm_write(struct pcm *pcm, const void *data, unsigned int count)
+{
+ struct snd_xferi x;
+
+ if (pcm->flags & PCM_IN)
+ return -EINVAL;
+
+ x.buf = (void*)data;
+ x.frames = count / (pcm->config.channels *
+ pcm_format_to_bits(pcm->config.format) / 8);
+
+ for (;;) {
+ if (!pcm->running) {
+ if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE))
+ return oops(pcm, errno, "cannot prepare channel");
+ 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->running = 0;
+ if (errno == EPIPE) {
+ /* we failed to make our window -- try to restart if we are
+ * allowed to do so. Otherwise, simply allow the EPIPE error to
+ * propagate up to the app level */
+ pcm->underruns++;
+ if (pcm->flags & PCM_NORESTART)
+ return -EPIPE;
+ continue;
+ }
+ return oops(pcm, errno, "cannot write stream data");
+ }
+ return 0;
+ }
+}
+
+int pcm_read(struct pcm *pcm, void *data, unsigned int count)
+{
+ struct snd_xferi x;
+
+ if (!(pcm->flags & PCM_IN))
+ return -EINVAL;
+
+ x.buf = data;
+ x.frames = count / (pcm->config.channels *
+ pcm_format_to_bits(pcm->config.format) / 8);
+
+ for (;;) {
+ if (!pcm->running) {
+ if (pcm_start(pcm) < 0) {
+ fprintf(stderr, "start error");
+ return -errno;
+ }
+ }
+ if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) {
+ pcm->running = 0;
+ if (errno == EPIPE) {
+ /* we failed to make our window -- try to restart */
+ pcm->underruns++;
+ continue;
+ }
+ return oops(pcm, errno, "cannot read stream data");
+ }
+ return 0;
+ }
+}
+
+static struct pcm bad_pcm = {
+ .fd = -1,
+};
+
+int pcm_close(struct pcm *pcm)
+{
+ if (pcm == &bad_pcm)
+ return 0;
+
+ pcm_hw_munmap_status(pcm);
+
+ if (pcm->flags & PCM_MMAP) {
+ pcm_stop(pcm);
+ munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
+ }
+
+ if (pcm->fd >= 0)
+ close(pcm->fd);
+ pcm->running = 0;
+ pcm->buffer_size = 0;
+ pcm->fd = -1;
+ free(pcm);
+ return 0;
+}
+
+struct pcm *pcm_open(unsigned int card, unsigned int device,
+ unsigned int flags, struct pcm_config *config)
+{
+ struct pcm *pcm;
+ struct snd_pcm_info info;
+ struct snd_pcm_hw_params params;
+ struct snd_pcm_sw_params sparams;
+ char fn[256];
+ int rc;
+
+ pcm = calloc(1, sizeof(struct pcm));
+ if (!pcm || !config)
+ return &bad_pcm; /* TODO: could support default config here */
+
+ pcm->config = *config;
+
+ snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
+ flags & PCM_IN ? 'c' : 'p');
+
+ pcm->flags = flags;
+ pcm->fd = open(fn, O_RDWR);
+ if (pcm->fd < 0) {
+ oops(pcm, errno, "cannot open device '%s'", fn);
+ return pcm;
+ }
+
+ if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) {
+ oops(pcm, errno, "cannot get info");
+ goto fail_close;
+ }
+
+ param_init(¶ms);
+ param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT,
+ pcm_format_to_alsa(config->format));
+ param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_SUBFORMAT,
+ SNDRV_PCM_SUBFORMAT_STD);
+ param_set_min(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, config->period_size);
+ param_set_int(¶ms, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ pcm_format_to_bits(config->format));
+ param_set_int(¶ms, SNDRV_PCM_HW_PARAM_FRAME_BITS,
+ pcm_format_to_bits(config->format) * config->channels);
+ param_set_int(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS,
+ config->channels);
+ param_set_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS, config->period_count);
+ param_set_int(¶ms, SNDRV_PCM_HW_PARAM_RATE, config->rate);
+
+ if (flags & PCM_NOIRQ) {
+
+ if (!(flags & PCM_MMAP)) {
+ oops(pcm, -EINVAL, "noirq only currently supported with mmap().");
+ goto fail;
+ }
+
+ params.flags |= SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP;
+ pcm->noirq_frames_per_msec = config->rate / 1000;
+ }
+
+ if (flags & PCM_MMAP)
+ param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS,
+ SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
+ else
+ param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS,
+ SNDRV_PCM_ACCESS_RW_INTERLEAVED);
+
+ if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, ¶ms)) {
+ oops(pcm, errno, "cannot set hw params");
+ goto fail_close;
+ }
+
+ /* get our refined hw_params */
+ config->period_size = param_get_int(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+ config->period_count = param_get_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS);
+ pcm->buffer_size = config->period_count * config->period_size;
+
+ if (flags & PCM_MMAP) {
+ pcm->mmap_buffer = mmap(NULL, pcm_frames_to_bytes(pcm, pcm->buffer_size),
+ PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, pcm->fd, 0);
+ if (pcm->mmap_buffer == MAP_FAILED) {
+ oops(pcm, -errno, "failed to mmap buffer %d bytes\n",
+ pcm_frames_to_bytes(pcm, pcm->buffer_size));
+ goto fail_close;
+ }
+ }
+
+
+ memset(&sparams, 0, sizeof(sparams));
+ sparams.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE;
+ sparams.period_step = 1;
+ sparams.avail_min = 1;
+
+ if (!config->start_threshold)
+ pcm->config.start_threshold = sparams.start_threshold =
+ config->period_count * config->period_size / 2;
+ else
+ sparams.start_threshold = config->start_threshold;
+
+ /* pick a high stop threshold - todo: does this need further tuning */
+ if (!config->stop_threshold) {
+ if (pcm->flags & PCM_IN)
+ pcm->config.stop_threshold = sparams.stop_threshold =
+ config->period_count * config->period_size * 10;
+ else
+ pcm->config.stop_threshold = sparams.stop_threshold =
+ config->period_count * config->period_size;
+ }
+ else
+ sparams.stop_threshold = config->stop_threshold;
+
+ sparams.xfer_align = config->period_size / 2; /* needed for old kernels */
+ sparams.silence_size = 0;
+ sparams.silence_threshold = config->silence_threshold;
+ pcm->boundary = sparams.boundary = pcm->buffer_size;
+
+ while (pcm->boundary * 2 <= INT_MAX - pcm->buffer_size)
+ pcm->boundary *= 2;
+
+ if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) {
+ oops(pcm, errno, "cannot set sw params");
+ goto fail;
+ }
+
+ rc = pcm_hw_mmap_status(pcm);
+ if (rc < 0) {
+ oops(pcm, rc, "mmap status failed");
+ goto fail;
+ }
+
+ pcm->underruns = 0;
+ return pcm;
+
+fail:
+ if (flags & PCM_MMAP)
+ munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
+fail_close:
+ close(pcm->fd);
+ pcm->fd = -1;
+ return pcm;
+}
+
+int pcm_is_ready(struct pcm *pcm)
+{
+ return pcm->fd >= 0;
+}
+
+int pcm_start(struct pcm *pcm)
+{
+ if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE) < 0)
+ return oops(pcm, errno, "cannot prepare channel");
+
+ if (pcm->flags & PCM_MMAP)
+ pcm_sync_ptr(pcm, 0);
+
+ if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START) < 0)
+ return oops(pcm, errno, "cannot start channel");
+
+ pcm->running = 1;
+ return 0;
+}
+
+int pcm_stop(struct pcm *pcm)
+{
+ if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP) < 0)
+ return oops(pcm, errno, "cannot stop channel");
+
+ pcm->running = 0;
+ return 0;
+}
+
+static inline int pcm_mmap_playback_avail(struct pcm *pcm)
+{
+ int avail;
+
+ avail = pcm->mmap_status->hw_ptr + pcm->buffer_size - pcm->mmap_control->appl_ptr;
+
+ if (avail < 0)
+ avail += pcm->boundary;
+ else if (avail > (int)pcm->boundary)
+ avail -= pcm->boundary;
+
+ return avail;
+}
+
+static inline int pcm_mmap_capture_avail(struct pcm *pcm)
+{
+ int avail = pcm->mmap_status->hw_ptr - pcm->mmap_control->appl_ptr;
+ if (avail < 0)
+ avail += pcm->boundary;
+ return avail;
+}
+
+static inline int pcm_mmap_avail(struct pcm *pcm)
+{
+ pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC);
+ if (pcm->flags & PCM_IN)
+ return pcm_mmap_capture_avail(pcm);
+ else
+ return pcm_mmap_playback_avail(pcm);
+}
+
+static void pcm_mmap_appl_forward(struct pcm *pcm, int frames)
+{
+ unsigned int appl_ptr = pcm->mmap_control->appl_ptr;
+ appl_ptr += frames;
+
+ /* check for boundary wrap */
+ if (appl_ptr > pcm->boundary)
+ appl_ptr -= pcm->boundary;
+ pcm->mmap_control->appl_ptr = appl_ptr;
+}
+
+int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset,
+ unsigned int *frames)
+{
+ unsigned int continuous, copy_frames, avail;
+
+ /* return the mmap buffer */
+ *areas = pcm->mmap_buffer;
+
+ /* and the application offset in frames */
+ *offset = pcm->mmap_control->appl_ptr % pcm->buffer_size;
+
+ avail = pcm_mmap_avail(pcm);
+ if (avail > pcm->buffer_size)
+ avail = pcm->buffer_size;
+ continuous = pcm->buffer_size - *offset;
+
+ /* we can only copy frames if the are availabale and continuos */
+ copy_frames = *frames;
+ if (copy_frames > avail)
+ copy_frames = avail;
+ if (copy_frames > continuous)
+ copy_frames = continuous;
+ *frames = copy_frames;
+
+ return 0;
+}
+
+int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames)
+{
+ /* update the application pointer in userspace and kernel */
+ pcm_mmap_appl_forward(pcm, frames);
+ pcm_sync_ptr(pcm, 0);
+
+ return frames;
+}
+
+int pcm_avail_update(struct pcm *pcm)
+{
+ pcm_sync_ptr(pcm, 0);
+ return pcm_mmap_avail(pcm);
+}
+
+int pcm_state(struct pcm *pcm)
+{
+ int err = pcm_sync_ptr(pcm, 0);
+ if (err < 0)
+ return err;
+
+ return pcm->mmap_status->state;
+}
+
+int pcm_wait(struct pcm *pcm, int timeout)
+{
+ struct pollfd pfd;
+ int err;
+
+ pfd.fd = pcm->fd;
+ pfd.events = POLLOUT | POLLERR | POLLNVAL;
+
+ do {
+ /* let's wait for avail or timeout */
+ err = poll(&pfd, 1, timeout);
+ if (err < 0)
+ return -errno;
+
+ /* timeout ? */
+ if (err == 0)
+ return 0;
+
+ /* have we been interrupted ? */
+ if (errno == -EINTR)
+ continue;
+
+ /* check for any errors */
+ if (pfd.revents & (POLLERR | POLLNVAL)) {
+ switch (pcm_state(pcm)) {
+ case PCM_STATE_XRUN:
+ return -EPIPE;
+ case PCM_STATE_SUSPENDED:
+ return -ESTRPIPE;
+ case PCM_STATE_DISCONNECTED:
+ return -ENODEV;
+ default:
+ return -EIO;
+ }
+ }
+ /* poll again if fd not ready for IO */
+ } while (!(pfd.revents & (POLLIN | POLLOUT)));
+
+ return 1;
+}
+
+int pcm_mmap_write(struct pcm *pcm, const void *buffer, unsigned int bytes)
+{
+ int err = 0, frames, avail;
+ unsigned int offset = 0, count;
+
+ if (bytes == 0)
+ return 0;
+
+ count = pcm_bytes_to_frames(pcm, bytes);
+
+ while (count > 0) {
+
+ /* get the available space for writing new frames */
+ avail = pcm_avail_update(pcm);
+ if (avail < 0) {
+ fprintf(stderr, "cannot determine available mmap frames");
+ return err;
+ }
+
+ /* start the audio if we reach the threshold */
+ if (!pcm->running &&
+ (pcm->buffer_size - avail) >= pcm->config.start_threshold) {
+ if (pcm_start(pcm) < 0) {
+ fprintf(stderr, "start error: hw 0x%x app 0x%x avail 0x%x\n",
+ (unsigned int)pcm->mmap_status->hw_ptr,
+ (unsigned int)pcm->mmap_control->appl_ptr,
+ avail);
+ return -errno;
+ }
+ }
+
+ /* sleep until we have space to write new frames */
+ if (pcm->running &&
+ (unsigned int)avail < pcm->mmap_control->avail_min) {
+ int time = -1;
+
+ if (pcm->flags & PCM_NOIRQ)
+ time = (pcm->buffer_size - avail - pcm->mmap_control->avail_min)
+ / pcm->noirq_frames_per_msec;
+
+ err = pcm_wait(pcm, time);
+ if (err < 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,
+ (unsigned int)pcm->mmap_control->appl_ptr,
+ avail);
+ pcm->mmap_control->appl_ptr = 0;
+ return err;
+ }
+ continue;
+ }
+
+ frames = count;
+ if (frames > avail)
+ frames = avail;
+
+ if (!frames)
+ break;
+
+ /* copy frames from buffer */
+ frames = pcm_mmap_write_areas(pcm, 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,
+ (unsigned int)pcm->mmap_control->appl_ptr,
+ avail);
+ return frames;
+ }
+
+ offset += frames;
+ count -= frames;
+ }
+
+ return 0;
+}
diff --git a/audio-tool/pulse-generator.c b/audio-tool/pulse-generator.c
new file mode 100644
index 0000000..bd7deff
--- /dev/null
+++ b/audio-tool/pulse-generator.c
@@ -0,0 +1,273 @@
+/* pulse-generator.c
+**
+** Copyright 2011, 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 <tinyalsa/asoundlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <time.h>
+#include <signal.h>
+#include <string.h>
+
+#include "config.h"
+#include "pulse-generator.h"
+
+static volatile int running = 1;
+
+static void sigint_handler(int sig)
+{
+ running = 0;
+}
+
+struct interval_tracker {
+ struct timespec last;
+ struct timespec now;
+ size_t count;
+ double sum_ms;
+ double sum2_ms;
+ double max_ms;
+ double min_ms;
+ int initd;
+};
+
+static void ivt_init(struct interval_tracker *it)
+{
+ memset(it, 0, sizeof(struct interval_tracker));
+}
+
+static void ivt_lap(struct interval_tracker *it)
+{
+ double elapsed;
+ clock_gettime(CLOCK_MONOTONIC, &it->now);
+ if (it->initd) {
+ it->count++;
+ elapsed = (it->now.tv_sec - it->last.tv_sec) * 1000000.0
+ + (it->now.tv_nsec - it->last.tv_nsec) / 1000.0;
+ it->sum_ms += elapsed;
+ it->sum2_ms += elapsed*elapsed;
+ if (it->count == 1) {
+ it->max_ms = it->min_ms = elapsed;
+ } else {
+ if (elapsed > it->max_ms)
+ it->max_ms = elapsed;
+ if (elapsed < it->min_ms)
+ it->min_ms = elapsed;
+ }
+#if 0
+ printf("now=%ld dt=%08u max=%08u min=%08u\n",
+ 1000000 * (it->now.tv_sec % 100) + it->now.tv_nsec / 1000,
+ (unsigned)(elapsed+.5), (unsigned)(it->max_ms+.5), (unsigned)(it->min_ms+.5));
+#endif
+ }
+ it->last = it->now;
+ it->initd = 1;
+}
+
+static double ivt_average(struct interval_tracker *it)
+{
+ if (it->count <= 0)
+ return 0.0;
+ return it->sum_ms / (double)(it->count);
+}
+
+#define PULSE_AT_FRONT (1<<0)
+#define PULSE_AT_END (1<<1)
+#define PULSE_AT_MIDDLE (1<<2)
+
+struct play_pulses_params {
+ unsigned int card;
+ unsigned int device;
+ unsigned int channels;
+ unsigned int rate;
+ unsigned int bits;
+ unsigned int period_size;
+ unsigned int period_count;
+ unsigned int pulse_position;
+};
+
+static void play_pulses(struct play_pulses_params *params);
+
+int pulse_generator_main(const struct audio_tool_config *config, int argc, char **argv)
+{
+ struct play_pulses_params params = {
+ .card = 0,
+ .device = 0,
+ .channels = 2,
+ .rate = 48000,
+ .bits = 16,
+ .period_size = 1024,
+ .period_count = 4,
+ .pulse_position = PULSE_AT_FRONT,
+ };
+
+ if (argc > 4) {
+ printf("Usage: audio-tool [options] pulse [front] [middle] [end]\n");
+ return 1;
+ } else if (argc > 1) {
+ int n;
+ params.pulse_position = 0;
+ for (n = 1 ; n < argc ; ++n) {
+ if (strcmp("front", argv[n]) == 0) {
+ params.pulse_position |= PULSE_AT_FRONT;
+ } else if (strcmp("middle", argv[n]) == 0) {
+ params.pulse_position |= PULSE_AT_MIDDLE;
+ } else if (strcmp("end", argv[n]) == 0) {
+ params.pulse_position |= PULSE_AT_END;
+ } else {
+ printf("Usage: audio-tool [options] pulse [front] [middle] [end]\n");
+ return 1;
+ }
+ }
+ }
+
+ params.device = config->device;
+ params.period_size = config->period_size;
+ params.period_count = config->num_periods;
+ params.card = config->card;
+ params.channels = config->channels;
+ params.rate = config->rate;
+ params.bits = config->bits;
+
+ signal(SIGINT, sigint_handler);
+ play_pulses(¶ms);
+
+ return 0;
+}
+
+static void play_pulses(struct play_pulses_params *params)
+{
+ struct pcm_config config;
+ struct pcm *pcm;
+ struct interval_tracker it;
+ char *buffer;
+ int size;
+ int num_read;
+
+ ivt_init(&it);
+
+ config.channels = params->channels;
+ config.rate = params->rate;
+ config.period_size = params->period_size;
+ config.period_count = params->period_count;
+ if (params->bits == 32)
+ config.format = PCM_FORMAT_S32_LE;
+ else if (params->bits == 16)
+ config.format = PCM_FORMAT_S16_LE;
+ config.start_threshold = params->period_size;
+ config.stop_threshold = params->period_size * params->period_count;
+ config.silence_threshold = 0;
+
+ pcm = pcm_open(params->card, params->device, PCM_OUT, &config);
+ if (!pcm || !pcm_is_ready(pcm)) {
+ fprintf(stderr, "Unable to open PCM device %u (%s)\n",
+ params->device, pcm_get_error(pcm));
+ return;
+ }
+
+ size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
+ printf("size = %d\n", size);
+ buffer = malloc(size);
+ if (!buffer) {
+ fprintf(stderr, "Unable to allocate %d bytes\n", size);
+ free(buffer);
+ pcm_close(pcm);
+ return;
+ }
+
+ printf("Playing sample: %u ch, %u hz, %u bit\n",
+ params->channels, params->rate, params->bits);
+
+ int k, stat;
+ int interrupt_tol = 20 + 1000000 * params->period_size / params->rate;
+ int pulse_position = params->pulse_position;
+ int period_bytes = pcm_frames_to_bytes(pcm, params->period_size);
+ char pulse_byte = 0x1f;
+ pcm_start(pcm);
+ do {
+ memset(buffer, 0, size);
+ num_read = period_bytes;
+ if (pulse_position & PULSE_AT_FRONT) {
+ for( k = 0 ; (k < size) && (k < 4) ; ++k ) {
+ buffer[k] = pulse_byte;
+ }
+ }
+ if (pulse_position & PULSE_AT_MIDDLE) {
+ for( k = (period_bytes/2) - 4 ; (k < size) && (k < (period_bytes/2)) ; ++k ) {
+ buffer[k] = pulse_byte;
+ }
+ }
+ if (pulse_position & PULSE_AT_END) {
+ for( k = period_bytes-1 ; (k > 0) && (k >= (period_bytes-4)) ; --k ) {
+ buffer[k] = pulse_byte;
+ }
+ }
+
+ stat = pcm_wait(pcm, interrupt_tol /* ms timeout */);
+ switch (stat) {
+ case 0: /* timeout */
+ printf("timeout");
+ break;
+ case 1:
+ ivt_lap(&it);
+ if (pcm_write(pcm, buffer, num_read)) {
+ fprintf(stderr, "Error playing sample\n");
+ num_read = 0;
+ }
+ break;
+ case -EINTR:
+ printf("wait was interrupted\n");
+ break;
+ case -EPIPE:
+ printf("xrun\n");
+ break;
+ case -ESTRPIPE:
+ printf("state suspended\n");
+ break;
+ case -ENODEV:
+ printf("disconnected\n");
+ num_read = 0;
+ break;
+ case -EIO:
+ printf("EIEIO\n");
+ num_read = 0;
+ break;
+ default:
+ printf("unknown error\n");
+ }
+
+ } while (running && (num_read > 0));
+
+ pcm_stop(pcm);
+
+ printf("avg=%g max=%g min=%g\n",
+ ivt_average(&it), it.max_ms, it.min_ms);
+
+ free(buffer);
+ pcm_close(pcm);
+}
diff --git a/audio-tool/pulse-generator.h b/audio-tool/pulse-generator.h
new file mode 100644
index 0000000..3487a5e
--- /dev/null
+++ b/audio-tool/pulse-generator.h
@@ -0,0 +1,33 @@
+/* pulse-generator.h
+**
+** Copyright 2011, 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.
+*/
+#ifndef __AUDIO_TOOL_PULSE_GENERATOR_H__
+#define __AUDIO_TOOL_PULSE_GENERATOR_H__
+
+int pulse_generator_main(const struct audio_tool_config *config, int argc, char **argv);
+
+#endif /* __AUDIO_TOOL_PULSE_GENERATOR_H__ */
diff --git a/audio-tool/restore.c b/audio-tool/restore.c
new file mode 100644
index 0000000..6de1927
--- /dev/null
+++ b/audio-tool/restore.c
@@ -0,0 +1,217 @@
+/*
+ * restore.c
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ * 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 Texas Instruments Incorporated 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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 <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <tinyalsa/asoundlib.h>
+
+#include "config.h"
+#include "restore.h"
+#include "mixer_cache.h"
+
+#define BUFSIZE (1L << 15)
+
+static int read_line(char *dest, size_t max, const char *src, size_t size, size_t *offset)
+{
+ size_t n;
+
+ for (n = 0 ; (n < max) && (src[*offset] != '\n') && (*offset < size) ; ++n, *offset += 1) {
+ dest[n] = src[*offset];
+ }
+
+ if (n < max)
+ dest[n] = '\0';
+ else
+ dest[n-1] = '\0';
+
+ if ((*offset < size) && (src[*offset] == '\n'))
+ *offset += 1;
+
+ return n;
+}
+
+/* Notes:
+ * - param 'line' is modified by this function
+ * - param 'db' has mixer_cache_touch() called on it.
+ */
+static void process_line(char* line, struct audio_tool_mixer_cache *db, struct mixer *mixer)
+{
+ size_t length = strlen(line);
+ size_t pos = 0, sep;
+ const char* sep_ptr;
+ enum field_t { FNAME=0, FTYPE, FCOUNT, FVALS } field = FNAME;
+ unsigned control_id, val, val_no;
+ const char *ctl_type;
+ unsigned file_count, ctl_count;
+ struct mixer_ctl *ctl;
+
+ if (!length)
+ return;
+
+ if (line[0] == '#')
+ return;
+
+ control_id = -1;
+ val_no = 0;
+ while (pos < length) {
+ sep_ptr = strstr(&line[pos], "\t");
+ if (sep_ptr)
+ sep = (size_t) (sep_ptr - line);
+ else
+ sep = length;
+ if (sep < length)
+ line[sep] = '\0';
+ switch(field) {
+ case FNAME:
+ control_id = mixer_cache_get_id_by_name(db, &line[pos]);
+ if (control_id == -1) {
+ printf("Error: could not find control %s\n", &line[pos]);
+ pos = length;
+ }
+ ctl = mixer_get_ctl(mixer, control_id);
+ break;
+ case FTYPE:
+ ctl_type = mixer_ctl_get_type_string(ctl);
+ if (0 != strcmp(ctl_type, &line[pos])) {
+ printf("Error: type mismatch for control #%d: file=%s card=%s\n",
+ control_id, &line[pos], ctl_type);
+ pos = length;
+ }
+ break;
+ case FCOUNT:
+ ctl_count = mixer_ctl_get_num_values(ctl);
+ file_count = atoi(&line[pos]);
+ if (ctl_count != file_count) {
+ printf("Error: mismatch in the count of control #%d's values: "
+ "file=%d card=%d\n", control_id, file_count, ctl_count);
+ pos = length;
+ }
+ break;
+ case FVALS:
+ if (0 == strcmp("#N/A", &line[pos])) {
+ pos = length;
+ } else if (0 == strcmp("ENUM", ctl_type)) {
+ mixer_ctl_set_enum_by_string(ctl, &line[pos]);
+ } else {
+ val = atoi(&line[pos]);
+ mixer_ctl_set_value(ctl, val_no, val);
+ }
+ ++val_no;
+ break;
+ }
+
+ if (field != FVALS)
+ ++field;
+
+ pos = sep + 1;
+ }
+ mixer_cache_touch(db, control_id);
+}
+
+int restore_main(const struct audio_tool_config *config, int argc, char **argv)
+{
+ struct mixer *mixer;
+ int card = config->card;
+ int ret = 0;
+ char *filename = 0;
+ char buf[BUFSIZE];
+ int fd;
+ char *src;
+ size_t cursor;
+ struct stat fd_stat;
+ struct audio_tool_mixer_cache db;
+
+ if (argc != 2) {
+ printf("Usage: audio-tool restore <filename>\n");
+ return 1;
+ }
+
+ filename = argv[1];
+
+ mixer = mixer_open(card);
+ if (!mixer) {
+ printf("Could not open mixer for card %d\n", card);
+ return 1;
+ }
+
+ fd = open(filename, O_RDONLY);
+ if (fd == -1) {
+ printf("Could not open file %s for reading (error=%s)\n", filename, strerror(errno));
+ return 1;
+ }
+
+ if(-1 == fstat(fd, &fd_stat)) {
+ printf("Could not stat file %s\n", filename);
+ return 1;
+ }
+
+ src = mmap(NULL, fd_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (src == MAP_FAILED) {
+ printf("Could not mmap the file %s (error=%s)\n", filename, strerror(errno));
+ return 1;
+ }
+
+ if (mixer_cache_init(&db)) {
+ printf("Could not initialize mixer cache\n");
+ return 1;
+ }
+ if (mixer_cache_populate(&db, mixer)) {
+ printf("Could not populate mixer cache\n");
+ return 1;
+ }
+
+ mixer_cache_reset_touch(&db);
+ cursor = 0;
+ while(read_line(buf, BUFSIZE, src, fd_stat.st_size, &cursor)) {
+ process_line(buf, &db, mixer);
+ }
+ ret = mixer_cache_audit_touch(&db, 1);
+
+ munmap(src, fd_stat.st_size);
+ close(fd);
+ mixer_close(mixer);
+
+ return ret;
+}
+
diff --git a/audio-tool/restore.h b/audio-tool/restore.h
new file mode 100644
index 0000000..8d9f366
--- /dev/null
+++ b/audio-tool/restore.h
@@ -0,0 +1,43 @@
+/*
+ * restore.h
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ * 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 Texas Instruments Incorporated 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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.
+ *
+ */
+
+#ifndef __AUDIO_TOOL_RESTORE_H__
+#define __AUDIO_TOOL_RESTORE_H__
+
+int restore_main(const struct audio_tool_config *config, int argc, char **argv);
+
+#endif /* __OMAP_AUDIO_TOOL_RESTORE_H__ */
+
diff --git a/audio-tool/rms.c b/audio-tool/rms.c
new file mode 100644
index 0000000..52212cb
--- /dev/null
+++ b/audio-tool/rms.c
@@ -0,0 +1,121 @@
+/*
+ * rms.c
+ *
+ * calculate RMS of a wav file per channel
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <math.h>
+#include <inttypes.h>
+
+#include "rms.h"
+
+#define MAX_FILENAME_LEN 256
+#define MAX_CHANNELS 8
+
+#define ID_RIFF 0x46464952
+#define ID_WAVE 0x45564157
+#define ID_FMT 0x20746d66
+#define ID_DATA 0x61746164
+
+struct wav_header {
+ uint32_t riff_id;
+ uint32_t riff_sz;
+ uint32_t riff_fmt;
+ uint32_t fmt_id;
+ uint32_t fmt_sz;
+ 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;
+ uint32_t data_id;
+ uint32_t data_sz;
+};
+
+/* Reuse tinyplay/tinycap code for header parsing */
+static int parse_header (char * filename, struct wav_header * wav_header) {
+ FILE* file;
+ file = fopen(filename, "rb");
+ if (!file) {
+ fprintf(stderr, "Unable to open file '%s'\n", filename);
+ return 1;
+ }
+ fread(wav_header, sizeof(struct wav_header), 1, file);
+ if ((wav_header->riff_id != ID_RIFF) ||
+ (wav_header->riff_fmt != ID_WAVE) ||
+ (wav_header->data_id != ID_DATA)) {
+ fprintf(stderr, "Error: '%s' is not a PCM Wav file\n", filename);
+ fclose(file);
+ return 1;
+ }
+ fclose(file);
+ return 0;
+}
+
+static int rms (char *filename, const struct wav_header * wav_header) {
+ char outname[MAX_FILENAME_LEN];
+ uint8_t i = 0;
+ char *extension_pos;
+ FILE *fp; /* input file pointer */
+ uint16_t data[MAX_CHANNELS];
+ uint64_t rms[MAX_CHANNELS];
+ uint64_t num_samples = 0;
+
+ fp = fopen(filename, "rb");
+ /* Jump to data block */
+ fseek(fp, sizeof(struct wav_header), SEEK_SET);
+
+ strncpy(outname, filename, sizeof(outname));
+ /* Get extension index of filename */
+ extension_pos = strrchr(outname, '.');
+ /* Point to '\0' instead if no extension is found */
+ if (!extension_pos)
+ extension_pos = strrchr(outname, '\0');
+ if (!extension_pos) {
+ fprintf(stderr, "Filename is too long!\n");
+ return 1;
+ }
+
+ /* Split data block */
+ while(fread(data, wav_header->block_align, 1, fp) == 1) {
+ num_samples++;
+ for (i = 0; i < wav_header->num_channels; i++)
+ rms[i] += data[i]*data[i];
+ }
+
+ for (i = 0; i < wav_header->num_channels; i++) {
+ printf("channel %d rms is %f\n",i, sqrt((float)rms[i])/num_samples);
+ }
+ fclose(fp);
+ return 0;
+}
+
+int rms_main (int argc, char* argv[]) {
+ char *filename;
+ struct wav_header wav_header, wav_header_out;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: audio-tool rms file.wav\n");
+ return 1;
+ }
+ filename = argv[1];
+
+ if (parse_header(filename, &wav_header))
+ return 1;
+
+ if (wav_header.num_channels > MAX_CHANNELS) {
+ fprintf(stderr, "Error: only support %d or less channels\n", MAX_CHANNELS);
+ return 1;
+ }
+
+ if(rms(filename, &wav_header))
+ return 1;
+
+ return 0;
+}
+
diff --git a/audio-tool/rms.h b/audio-tool/rms.h
new file mode 100644
index 0000000..f053064
--- /dev/null
+++ b/audio-tool/rms.h
@@ -0,0 +1,7 @@
+#ifndef __AUDIO_TOOL_RMS_H__
+#define __AUDIO_TOOL_RMS_H__
+
+int rms_main (int argc, char* argv[]);
+
+#endif /* __AUDIO_TOOL_RMS_H__ */
+
diff --git a/audio-tool/save.c b/audio-tool/save.c
new file mode 100644
index 0000000..fc345b8
--- /dev/null
+++ b/audio-tool/save.c
@@ -0,0 +1,109 @@
+/*
+ * save.c
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ * 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 Texas Instruments Incorporated 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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 <tinyalsa/asoundlib.h>
+
+#include "config.h"
+#include "save.h"
+
+int save_main(const struct audio_tool_config *config, int argc, char **argv)
+{
+ struct mixer *mixer;
+ int card = config->card;
+ char *filename = 0;
+ FILE *dest;
+ unsigned count, n;
+
+ if (argc != 2) {
+ printf("Usage: audio-tool save <filename>\n");
+ return 1;
+ }
+
+ filename = argv[1];
+
+ mixer = mixer_open(card);
+ if (!mixer) {
+ printf("Could not open mixer for card %d\n", card);
+ return 1;
+ }
+
+ dest = fopen(filename, "wt");
+ if (!dest) {
+ printf("Could not open file %s for writing\n", filename);
+ return 1;
+ }
+
+ fprintf(dest, "# Format: CTL_NAME<tab>CTL_TYPE<tab>NUM_VALS<tab>VAL1"
+ "<tab>VAL2...\n");
+ count = mixer_get_num_ctls(mixer);
+ for (n = 0 ; n < count ; ++n) {
+ struct mixer_ctl *ctl;
+ const char *ctl_name, *ctl_type;
+ enum mixer_ctl_type type;
+ unsigned val, num_vals, k;
+
+ ctl = mixer_get_ctl(mixer, n);
+ ctl_name = mixer_ctl_get_name(ctl);
+ ctl_type = mixer_ctl_get_type_string(ctl);
+ num_vals = mixer_ctl_get_num_values(ctl);
+ type = mixer_ctl_get_type(ctl);
+ fprintf(dest, "%s\t%s\t%u", ctl_name, ctl_type, num_vals);
+
+ for (k = 0 ; k < num_vals ; ++k) {
+ switch(type) {
+ case MIXER_CTL_TYPE_INT:
+ case MIXER_CTL_TYPE_BOOL:
+ case MIXER_CTL_TYPE_BYTE:
+ fprintf(dest, "\t%u", mixer_ctl_get_value(ctl, k));
+ break;
+ case MIXER_CTL_TYPE_ENUM:
+ val = mixer_ctl_get_value(ctl, k);
+ fprintf(dest, "\t%s", mixer_ctl_get_enum_string(ctl, val));
+ break;
+ default:
+ fprintf(dest, "\t#N/A");
+ }
+ }
+ fprintf(dest, "\n");
+ }
+
+ fclose(dest);
+ mixer_close(mixer);
+
+ return 0;
+}
+
diff --git a/audio-tool/save.h b/audio-tool/save.h
new file mode 100644
index 0000000..3d30d91
--- /dev/null
+++ b/audio-tool/save.h
@@ -0,0 +1,43 @@
+/*
+ * save.h
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ *
+ * 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 Texas Instruments Incorporated 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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.
+ *
+ */
+
+#ifndef __AUDIO_TOOL_SAVE_H__
+#define __AUDIO_TOOL_SAVE_H__
+
+int save_main(const struct audio_tool_config *config, int argc, char **argv);
+
+#endif /* __OMAP_AUDIO_TOOL_SAVE_H__ */
+
diff --git a/audio-tool/tinycap.c b/audio-tool/tinycap.c
new file mode 100644
index 0000000..6b87dc4
--- /dev/null
+++ b/audio-tool/tinycap.c
@@ -0,0 +1,216 @@
+/* tinycap.c
+**
+** Copyright 2011, 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 <tinyalsa/asoundlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <signal.h>
+#include <string.h>
+
+#include "config.h"
+#include "tinycap.h"
+
+#define ID_RIFF 0x46464952
+#define ID_WAVE 0x45564157
+#define ID_FMT 0x20746d66
+#define ID_DATA 0x61746164
+
+#define FORMAT_PCM 1
+
+struct wav_header {
+ uint32_t riff_id;
+ uint32_t riff_sz;
+ uint32_t riff_fmt;
+ uint32_t fmt_id;
+ uint32_t fmt_sz;
+ 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;
+ uint32_t data_id;
+ uint32_t data_sz;
+};
+
+int capturing = 1;
+
+static
+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,
+ unsigned int period_count, unsigned int duration);
+
+void sigint_handler(int sig)
+{
+ capturing = 0;
+}
+
+int tinycap_main(const struct audio_tool_config *config, int argc, char **argv,
+ int legacy_mode)
+{
+ FILE *file;
+ struct wav_header header;
+ unsigned int card = 0;
+ unsigned int device = 0;
+ unsigned int channels = 2;
+ unsigned int rate = 44100;
+ unsigned int bits = 16;
+ unsigned int frames;
+ unsigned int period_size = 1024;
+ unsigned int period_count = 4;
+ unsigned int duration = 0;
+ int arg;
+
+ if ((!legacy_mode && argc != 2) || (legacy_mode && argc != 1)) {
+ fprintf(stderr, "Usage: audio-tool [options] capture file.wav\n");
+ return 1;
+ }
+
+ if (legacy_mode)
+ arg = 0;
+ else
+ arg = 1;
+
+ file = fopen(argv[arg], "wb");
+ if (!file) {
+ fprintf(stderr, "Unable to create file '%s'\n", argv[arg]);
+ return 1;
+ }
+
+ device = config->device;
+ channels = config->channels;
+ rate = config->rate;
+ bits = config->bits;
+ card = config->card;
+ period_size = config->period_size;
+ period_count = config->num_periods;
+ duration = config->duration;
+
+ header.riff_id = ID_RIFF;
+ header.riff_sz = 0;
+ header.riff_fmt = ID_WAVE;
+ header.fmt_id = ID_FMT;
+ header.fmt_sz = 16;
+ header.audio_format = FORMAT_PCM;
+ header.num_channels = channels;
+ header.sample_rate = rate;
+ header.bits_per_sample = bits;
+ 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);
+
+ /* 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,
+ period_size, period_count, duration);
+ printf("Captured %d frames\n", frames);
+
+ /* write header now all information is known */
+ header.data_sz = frames * header.block_align;
+ fseek(file, 0, SEEK_SET);
+ fwrite(&header, sizeof(struct wav_header), 1, file);
+
+ fclose(file);
+
+ return 0;
+}
+
+static
+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,
+ unsigned int period_count, unsigned int duration)
+{
+ struct pcm_config config;
+ struct pcm *pcm;
+ char *buffer;
+ unsigned int size;
+ unsigned int bytes_read = 0;
+ unsigned long requested;
+
+ config.channels = channels;
+ 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 == 24) {
+ config.format = PCM_FORMAT_S24_3LE;
+ } else if (bits == 16) {
+ config.format = PCM_FORMAT_S16_LE;
+ } else {
+ fprintf(stderr, "Bits per sample %d not supported\n", bits);
+ return 0;
+ }
+ config.start_threshold = 0;
+ config.stop_threshold = 0;
+ config.silence_threshold = 0;
+
+ pcm = pcm_open(card, device, PCM_IN, &config);
+ if (!pcm || !pcm_is_ready(pcm)) {
+ fprintf(stderr, "Unable to open PCM device (%s)\n",
+ pcm_get_error(pcm));
+ return 0;
+ }
+
+ 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);
+ free(buffer);
+ pcm_close(pcm);
+ return 0;
+ }
+
+ if (duration)
+ requested = pcm_frames_to_bytes(pcm, rate * duration);
+ else
+ requested = 0;
+
+ printf("Capturing sample: %u ch, %u hz, %u bit\n", channels, rate, bits);
+
+ while (capturing && (!requested || (bytes_read < requested)) &&
+ !pcm_read(pcm, buffer, size)) {
+ if (fwrite(buffer, 1, size, file) != size) {
+ fprintf(stderr,"Error capturing sample\n");
+ break;
+ }
+ bytes_read += size;
+ }
+
+ free(buffer);
+ pcm_close(pcm);
+
+ return bytes_read / ((bits / 8) * channels);
+}
diff --git a/audio-tool/tinycap.h b/audio-tool/tinycap.h
new file mode 100644
index 0000000..b36f14a
--- /dev/null
+++ b/audio-tool/tinycap.h
@@ -0,0 +1,34 @@
+/* tinycap.h
+**
+** Copyright 2011, 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.
+*/
+#ifndef __TINYALSA_TINYCAP_H__
+#define __TINYALSA_TINYCAP_H__
+
+int tinycap_main(const struct audio_tool_config *config, int argc, char **argv,
+ int legacy_mode);
+
+#endif /* __TINYALSA_TINYCAP_H__ */
diff --git a/audio-tool/tinymix.c b/audio-tool/tinymix.c
new file mode 100644
index 0000000..babce63
--- /dev/null
+++ b/audio-tool/tinymix.c
@@ -0,0 +1,255 @@
+/* tinymix.c
+**
+** Copyright 2011, 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 <assert.h>
+#include <tinyalsa/asoundlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "config.h"
+#include "tinymix.h"
+
+static void tinymix_list_controls(struct mixer *mixer);
+static void tinymix_detail_control(struct mixer *mixer, unsigned int id,
+ int print_all);
+static void tinymix_set_value(struct mixer *mixer, unsigned int id,
+ char *value);
+static void tinymix_print_enum(struct mixer_ctl *ctl, int print_all);
+static int tinymix_string_is_integer(const char *str);
+static unsigned int tinymix_get_control_id_for_name(struct mixer *mixer, const char *str);
+
+int tinymix_main(const struct audio_tool_config *config, int argc, char **argv,
+ int legacy_mode)
+{
+ struct mixer *mixer;
+ int card = 0;
+ unsigned int ctrl_id;
+
+ card = config->card;
+
+ mixer = mixer_open(card);
+ if (!mixer) {
+ fprintf(stderr, "Failed to open mixer\n");
+ return EXIT_FAILURE;
+ }
+
+ if (!legacy_mode) {
+ --argc;
+ ++argv;
+ }
+
+ if (argc > 0)
+ ctrl_id = tinymix_get_control_id_for_name(mixer, argv[0]);
+
+ if (argc == 0)
+ tinymix_list_controls(mixer);
+ else if (argc == 1)
+ tinymix_detail_control(mixer, ctrl_id, 1);
+ else if (argc == 2)
+ tinymix_set_value(mixer, ctrl_id, argv[1]);
+ else
+ printf("Usage: tinymix [-D card] [control id] [value to set]\n");
+
+ mixer_close(mixer);
+
+ return 0;
+}
+
+static int tinymix_string_is_integer(const char *str)
+{
+ const char *p;
+
+ assert(str);
+
+ for (p = str ; p != '\0' ; ++p) {
+ if (!isdigit(*p)) {
+ return 0;
+ }
+ }
+
+ if (p == str) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static unsigned int tinymix_get_control_id_for_name(struct mixer *mixer, const char *str)
+{
+ if (tinymix_string_is_integer(str)) {
+ int v = atoi(str);
+ if (v < 0) {
+ v = 0;
+ }
+ return (unsigned int)v;
+ } else {
+ unsigned int num_ctls;
+ unsigned int n;
+ struct mixer_ctl *ctl;
+ const char *name;
+
+ num_ctls = mixer_get_num_ctls(mixer);
+ for (n = 0 ; n < num_ctls ; ++n) {
+ ctl = mixer_get_ctl(mixer, n);
+ name = mixer_ctl_get_name(ctl);
+ if (0 == strcmp(name, str)) {
+ return n;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void tinymix_list_controls(struct mixer *mixer)
+{
+ struct mixer_ctl *ctl;
+ const char *name, *type;
+ unsigned int num_ctls, num_values;
+ unsigned int i;
+
+ num_ctls = mixer_get_num_ctls(mixer);
+
+ printf("Number of controls: %d\n", num_ctls);
+
+ printf("ctl\ttype\tnum\t%-40s value\n", "name");
+ for (i = 0; i < num_ctls; i++) {
+ ctl = mixer_get_ctl(mixer, i);
+
+ 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);
+ tinymix_detail_control(mixer, i, 0);
+ }
+}
+
+static void tinymix_print_enum(struct mixer_ctl *ctl, int print_all)
+{
+ unsigned int num_enums;
+ unsigned int i;
+ const char *string;
+
+ num_enums = mixer_ctl_get_num_enums(ctl);
+
+ for (i = 0; i < num_enums; i++) {
+ string = mixer_ctl_get_enum_string(ctl, i);
+ if (print_all)
+ printf("\t%s%s", mixer_ctl_get_value(ctl, 0) == (int)i ? ">" : "",
+ string);
+ else if (mixer_ctl_get_value(ctl, 0) == (int)i)
+ printf(" %-s", string);
+ }
+}
+
+static void tinymix_detail_control(struct mixer *mixer, unsigned int id,
+ int print_all)
+{
+ struct mixer_ctl *ctl;
+ enum mixer_ctl_type type;
+ unsigned int num_values;
+ unsigned int i;
+ int min, max;
+
+ if (id >= mixer_get_num_ctls(mixer)) {
+ fprintf(stderr, "Invalid mixer control\n");
+ return;
+ }
+
+ ctl = mixer_get_ctl(mixer, id);
+
+ type = mixer_ctl_get_type(ctl);
+ num_values = mixer_ctl_get_num_values(ctl);
+
+ if (print_all)
+ printf("%s:", mixer_ctl_get_name(ctl));
+
+ for (i = 0; i < num_values; i++) {
+ switch (type)
+ {
+ case MIXER_CTL_TYPE_INT:
+ printf(" %d", mixer_ctl_get_value(ctl, i));
+ break;
+ case MIXER_CTL_TYPE_BOOL:
+ printf(" %s", mixer_ctl_get_value(ctl, i) ? "On" : "Off");
+ break;
+ 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));
+ break;
+ default:
+ printf(" unknown");
+ break;
+ };
+ }
+
+ if (print_all) {
+ if (type == MIXER_CTL_TYPE_INT) {
+ min = mixer_ctl_get_range_min(ctl);
+ max = mixer_ctl_get_range_max(ctl);
+ printf(" (range %d->%d)", min, max);
+ }
+ }
+ printf("\n");
+}
+
+static void tinymix_set_value(struct mixer *mixer, unsigned int id,
+ char *string)
+{
+ struct mixer_ctl *ctl;
+ enum mixer_ctl_type type;
+ unsigned int num_values;
+ unsigned int i;
+
+ ctl = mixer_get_ctl(mixer, id);
+ type = mixer_ctl_get_type(ctl);
+ num_values = mixer_ctl_get_num_values(ctl);
+
+ if (isdigit(string[0])) {
+ int value = atoi(string);
+
+ for (i = 0; i < num_values; i++) {
+ if (mixer_ctl_set_value(ctl, i, value)) {
+ fprintf(stderr, "Error: invalid value\n");
+ return;
+ }
+ }
+ } else {
+ if (type == MIXER_CTL_TYPE_ENUM) {
+ if (mixer_ctl_set_enum_by_string(ctl, string))
+ fprintf(stderr, "Error: invalid enum value\n");
+ } else {
+ fprintf(stderr, "Error: only enum types can be set with strings\n");
+ }
+ }
+}
+
diff --git a/audio-tool/tinymix.h b/audio-tool/tinymix.h
new file mode 100644
index 0000000..be27666
--- /dev/null
+++ b/audio-tool/tinymix.h
@@ -0,0 +1,34 @@
+/* tinycap.h
+**
+** Copyright 2011, 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.
+*/
+#ifndef __TINYALSA_TINYMIX_H__
+#define __TINYALSA_TINYMIX_H__
+
+int tinymix_main(const struct audio_tool_config *config, int argc, char **argv,
+ int legacy_mode);
+
+#endif /* __TINYALSA_TINYMIX_H__ */
diff --git a/audio-tool/tinyplay.c b/audio-tool/tinyplay.c
new file mode 100644
index 0000000..507dac4
--- /dev/null
+++ b/audio-tool/tinyplay.c
@@ -0,0 +1,205 @@
+/* tinyplay.c
+**
+** Copyright 2011, 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 <tinyalsa/asoundlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "config.h"
+#include "tinyplay.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
+void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels,
+ unsigned int rate, unsigned int bits, unsigned int period_size,
+ unsigned int period_count, unsigned int duration);
+
+int tinyplay_main(const struct audio_tool_config *config, int argc, char **argv,
+ int legacy_mode)
+{
+ FILE *file;
+ struct riff_wave_header riff_wave_header;
+ struct chunk_header chunk_header;
+ struct chunk_fmt chunk_fmt;
+ unsigned int device = 0;
+ unsigned int card = 0;
+ unsigned int period_size = 1024;
+ unsigned int period_count = 4;
+ unsigned int duration = -1;
+ int arg;
+ char *filename;
+ int more_chunks = 1;
+
+ if ((!legacy_mode && argc != 2) || (legacy_mode && argc != 1)) {
+ fprintf(stderr, "Usage: audio-tool [options] play file.wav\n");
+ return 1;
+ }
+
+ if (legacy_mode)
+ arg = 0;
+ else
+ arg = 1;
+
+ filename = argv[1];
+ file = fopen(filename, "rb");
+ if (!file) {
+ fprintf(stderr, "Unable to open file '%s'\n", filename);
+ return 1;
+ }
+
+ device = config->device;
+ period_size = config->period_size;
+ period_count = config->num_periods;
+ card = config->card;
+ duration = config->duration;
+
+ 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);
+
+ play_sample(file, card, device, chunk_fmt.num_channels, chunk_fmt.sample_rate,
+ chunk_fmt.bits_per_sample, period_size, period_count, duration);
+
+ fclose(file);
+
+ return 0;
+}
+
+static
+void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels,
+ unsigned int rate, unsigned int bits, unsigned int period_size,
+ unsigned int period_count, unsigned int duration)
+{
+ struct pcm_config config;
+ struct pcm *pcm;
+ char *buffer;
+ int size;
+ int num_read;
+ unsigned long requested;
+ unsigned long written = 0;
+
+ config.channels = channels;
+ 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.start_threshold = 0;
+ config.stop_threshold = 0;
+ config.silence_threshold = 0;
+
+ pcm = pcm_open(card, device, PCM_OUT, &config);
+ if (!pcm || !pcm_is_ready(pcm)) {
+ fprintf(stderr, "Unable to open PCM device %u (%s)\n",
+ device, pcm_get_error(pcm));
+ return;
+ }
+
+ 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);
+ free(buffer);
+ pcm_close(pcm);
+ return;
+ }
+
+ if (duration)
+ requested = pcm_frames_to_bytes(pcm, rate * duration);
+ else
+ requested = 0;
+
+ printf("Playing sample: %u ch, %u hz, %u bit\n", channels, rate, bits);
+
+ do {
+ num_read = fread(buffer, 1, size, file);
+ if (num_read > 0) {
+ if (pcm_write(pcm, buffer, num_read)) {
+ fprintf(stderr, "Error playing sample\n");
+ break;
+ }
+ }
+ written += num_read;
+ } while ((num_read > 0) && (!requested || (written < requested)));
+
+ free(buffer);
+ pcm_close(pcm);
+}
+
diff --git a/audio-tool/tinyplay.h b/audio-tool/tinyplay.h
new file mode 100644
index 0000000..89b4920
--- /dev/null
+++ b/audio-tool/tinyplay.h
@@ -0,0 +1,34 @@
+/* tinyplay.h
+**
+** Copyright 2011, 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.
+*/
+#ifndef __TINYALSA_TINYPLAY_H__
+#define __TINYALSA_TINYPLAY_H__
+
+int tinyplay_main(const struct audio_tool_config *config, int argc, char **argv,
+ int legacy_mode);
+
+#endif /* __TINYALSA_TINYPLAY_H__ */
diff --git a/audio-tool/tone-generator.c b/audio-tool/tone-generator.c
new file mode 100644
index 0000000..713e2c5
--- /dev/null
+++ b/audio-tool/tone-generator.c
@@ -0,0 +1,274 @@
+/* Copyright (c) 2011, Gabriel M. Beddingfield <gabrbedd@gmail.com>
+ * All rights reserved.
+ *
+ * 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 <organization> 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 <COPYRIGHT HOLDER> 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.
+ */
+
+/*
+ * tone-generator.c
+ *
+ * Utility for generating an accurate waveform to an audio output.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <stdint.h>
+#include <limits.h>
+#include <tinyalsa/asoundlib.h>
+#include "oscillator-table.h"
+
+#include "config.h"
+#include "tone-generator.h"
+
+/* LOAD ALL THE WAVE TABLES
+ *
+ * WAVE TABLE ASSUMPTIONS:
+ *
+ * - All lengths are a power of 2 (8, 16, 32, 64, ...)
+ *
+ * - The wave tables are sufficiently large such that
+ * interpolation is not necessary. For example, at a
+ * sample rate of 48000 Hz, a wave table length 4096
+ * should be sufficient for all frequencies above
+ * 24 Hz (48000/4096 = 11.72 Hz).
+ */
+static int16_t g_table_square_wave_data[] = {
+#include "table_square.c"
+};
+
+static int16_t g_table_sine_wave_data[] = {
+#include "table_sine.c"
+};
+
+static int16_t g_table_triangle_wave_data[] = {
+#include "table_triangle.c"
+};
+
+static int16_t g_table_sawtooth_wave_data[] = {
+#include "table_sawtooth.c"
+};
+
+static struct wave_table g_wave_tables[] = {
+ DECLARE_TABLE("square", g_table_square_wave_data),
+ DECLARE_TABLE("sine", g_table_sine_wave_data),
+ DECLARE_TABLE("triangle", g_table_triangle_wave_data),
+ DECLARE_TABLE("sawtooth", g_table_sawtooth_wave_data),
+ { 0 }
+};
+
+static int check_wave_tables()
+{
+ assert( STATIC_ARRAY_SIZE(g_table_square_wave_data) <= 0xFFFF );
+ assert( STATIC_ARRAY_SIZE(g_table_sine_wave_data) <= 0xFFFF );
+ assert( STATIC_ARRAY_SIZE(g_table_triangle_wave_data) <= 0xFFFF );
+ assert( STATIC_ARRAY_SIZE(g_table_sawtooth_wave_data) <= 0xFFFF );
+
+ return 0;
+}
+
+struct tone_generator_config {
+ int card;
+ int device;
+ struct wave_table *wave_table;
+ struct wave_scale wave_scale;
+ struct pcm_config pcm_config;
+ uint32_t duration;
+ int16_t volume; /* binary fraction / USHRT_MAX */
+ uint32_t chan_mask;
+ int bits;
+};
+
+static int inner_main(struct tone_generator_config config)
+{
+ struct pcm_config *pcm_config = &config.pcm_config;
+ struct pcm *pcm;
+ unsigned pos;
+ void *buf;
+
+ pcm = pcm_open(config.card, config.device, PCM_OUT, pcm_config);
+ if (!pcm) {
+ fprintf(stderr, "Could not open sound card\n");
+ fprintf(stderr, "%s\n", pcm_get_error(pcm));
+ return 1;
+ }
+ if (!pcm_is_ready(pcm)) {
+ fprintf(stderr, "Sound card not ready\n");
+ fprintf(stderr, "%s\n", pcm_get_error(pcm));
+ return 1;
+ }
+
+ buf = calloc(config.bits / 8,
+ pcm_config->period_size * pcm_config->channels);
+ if (!buf) {
+ fprintf(stderr, "Could not allocate memory for buffer\n");
+ return 1;
+ }
+
+ for (pos=0 ; (!config.duration || (pos < config.duration)) ; pos += pcm_config->period_size) {
+ oscillator_table_render(buf,
+ config.wave_table,
+ pos,
+ pcm_config->period_size,
+ config.wave_scale,
+ pcm_config->channels,
+ config.chan_mask, /* write to all channels */
+ config.volume,
+ config.bits);
+ if (pcm_write(pcm,
+ buf,
+ pcm_config->channels * pcm_config->period_size * (config.bits/8))) {
+ fprintf(stderr, "Error writing to sound card\n");
+ fprintf(stderr, "%s\n", pcm_get_error(pcm));
+ break;
+ }
+ }
+
+ pcm_close(pcm);
+
+ return 0;
+}
+
+static void usage()
+{
+ struct wave_table *ptr;
+
+ printf("Usage: audio-tool [options] tone <wave_type> <frequency> [<vol_db>]\n");
+ printf("\n");
+ printf("wave_type:\n");
+ for (ptr=g_wave_tables ; ptr->name ; ++ptr) {
+ printf(" %s\n", ptr->name);
+ }
+ printf("frequency: non-negative real number\n");
+ printf("vol_db: (optional) Volume attenuation in dB FS (implied negative, must be >= 0, default=0)\n");
+}
+
+int tone_generator_main(const struct audio_tool_config *at_config, int argc, char* argv[])
+{
+ struct tone_generator_config config = {
+ .card = 0,
+ .device = 0,
+ .chan_mask = ~0,
+ };
+ struct pcm_config pcm_config;
+ struct wave_table *ptr, *table;
+ struct wave_scale wave_scale;
+ double freq;
+ char *arg_wave_type, *arg_freq, *arg_voldb;
+ double tmp;
+
+ if ((argc < 3) || (argc > 4)) {
+ usage();
+ return 1;
+ }
+
+ if (check_wave_tables())
+ return 1;
+
+ arg_wave_type = argv[1];
+ arg_freq = argv[2];
+ if (argc > 3)
+ arg_voldb = argv[3];
+ else
+ arg_voldb = "0";
+
+ /* Set sane defaults */
+ memset(&pcm_config, 0, sizeof(struct pcm_config));
+ switch (at_config->bits) {
+ case 8: pcm_config.format = PCM_FORMAT_S8; break;
+ case 16: pcm_config.format = PCM_FORMAT_S16_LE; break;
+ case 24: pcm_config.format = PCM_FORMAT_S24_LE; break;
+ case 32: pcm_config.format = PCM_FORMAT_S32_LE; break;
+ default:
+ assert(0);
+ }
+
+ config.device = at_config->device;
+ config.card = at_config->card;
+ pcm_config.period_size = at_config->period_size;
+ pcm_config.period_count = at_config->num_periods;
+ pcm_config.rate = at_config->rate;
+ pcm_config.channels = at_config->channels;
+ config.chan_mask = at_config->channel_mask;
+ config.duration = at_config->duration * pcm_config.rate;
+ config.bits = at_config->bits;
+
+ for (ptr = g_wave_tables ; ptr->name ; ++ptr) {
+ if (strcmp(arg_wave_type, ptr->name) == 0) {
+ table = ptr;
+ assert( IS_POWER_OF_TWO(table->length) );
+ assert( table->mask == table->length - 1 );
+ break;
+ }
+ }
+ if (ptr->name == 0) {
+ fprintf(stderr, "Invalied wave_type parameter\n");
+ return 1;
+ }
+
+ tmp = atof(arg_freq);
+ if (tmp < 10.0) {
+ fprintf(stderr, "Error: frequency must be > 10Hz\n");
+ return 1;
+ }
+ freq = tmp;
+
+ tmp = atof(arg_voldb);
+ if (tmp < 0 ) {
+ fprintf(stderr, "Volume attenuation must be greater than 0 dB FS\n");
+ return 1;
+ }
+ /* Convert db to fraction */
+ tmp = -tmp;
+ tmp = pow(10.0, tmp/10.0);
+ config.volume = (unsigned short) (tmp * ((double)USHRT_MAX));
+
+ tmp = ((double)pcm_config.rate) / freq;
+ wave_scale.length = tmp;
+ tmp = (tmp - wave_scale.length) * 0xFFF;
+ wave_scale.sub = tmp;
+ wave_scale.sub_den = 0xFFF;
+ wave_scale.sub_shift = 12;
+
+ /* This restriction prevents overflows in render()
+ */
+ {
+ uint16_t bits = 0;
+ while ((1<<bits) < table->length) ++bits;
+ if (wave_scale.sub_shift + bits > 24) {
+ fprintf(stderr, "bits(wave_scale) + bits(table.length) "
+ " must be less than or equal to 24\n");
+ return 1;
+ }
+ }
+
+ memcpy(&config.pcm_config, &pcm_config, sizeof(pcm_config));
+ memcpy(&config.wave_scale, &wave_scale, sizeof(wave_scale));
+ config.wave_table = table;
+
+ return inner_main(config);
+
+ return 0;
+}
diff --git a/audio-tool/tone-generator.h b/audio-tool/tone-generator.h
new file mode 100644
index 0000000..e4c6aee
--- /dev/null
+++ b/audio-tool/tone-generator.h
@@ -0,0 +1,31 @@
+/* Copyright (c) 2011, Gabriel M. Beddingfield <gabrbedd@gmail.com>
+ * All rights reserved.
+ *
+ * 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 <organization> 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 <COPYRIGHT HOLDER> 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.
+ */
+#ifndef __AUDIO_TOOL_TONE_GENERATOR_H__
+#define __AUDIO_TOOL_TONE_GENERATOR_H__
+
+int tone_generator_main(const struct audio_tool_config *config, int argc, char **argv);
+
+#endif /* __AUDIO_TOOL_TONE_GENERATOR_H__ */
diff --git a/audio-tool/wav_chan_splitter.c b/audio-tool/wav_chan_splitter.c
new file mode 100644
index 0000000..90ee406
--- /dev/null
+++ b/audio-tool/wav_chan_splitter.c
@@ -0,0 +1,138 @@
+/*
+ * wav_chan_splitter.c
+ *
+ * Utility for splitting a multichannel wav file to multiple mono wav files.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "wav_chan_splitter.h"
+
+#define MAX_FILENAME_LEN 256
+#define MAX_CHANNELS 8
+#define MAX_BYTES 4 // Up to 32-bit samples
+
+/* Reuse tinyplay/tinycap code for header parsing */
+static int parse_header (char * filename, wav_header_t * wav_header) {
+ FILE* file;
+ file = fopen(filename, "rb");
+ if (!file) {
+ fprintf(stderr, "Unable to open file '%s'\n", filename);
+ return 1;
+ }
+ fread(wav_header, sizeof(wav_header_t), 1, file);
+ if ((wav_header->riff_id != ID_RIFF) ||
+ (wav_header->riff_fmt != ID_WAVE) ||
+ (wav_header->data_id != ID_DATA)) {
+ fprintf(stderr, "Error: '%s' is not a PCM Wav file\n", filename);
+ fclose(file);
+ return 1;
+ }
+ fclose(file);
+ return 0;
+}
+
+/* Create mono header based on multichannel header */
+static int create_new_header (wav_header_t * wav_header_out, wav_header_t * wav_header) {
+ if (!wav_header_out || !wav_header) {
+ fprintf(stderr, "Invalid argument for %s\n", __func__);
+ return 1;
+ }
+ memcpy(wav_header_out, wav_header, sizeof(wav_header_t));
+ wav_header_out->num_channels = 1;
+ wav_header_out->byte_rate = (wav_header->byte_rate)/(wav_header->num_channels);
+ wav_header_out->block_align = (wav_header->block_align)/(wav_header->num_channels);
+ wav_header_out->data_sz = (wav_header->data_sz)/(wav_header->num_channels);
+ /* Tinycap doesn't set riff_sz field, fill it up for the split files */
+ /* Definition: size of the rest of header (36 bytes) + datachunk size */
+ wav_header_out->riff_sz = wav_header_out->data_sz + 36;
+ return 0;
+}
+
+static int split (char *filename, const wav_header_t * wav_header, const wav_header_t * wav_header_out) {
+ char outname[MAX_FILENAME_LEN];
+ uint8_t i = 0;
+ char *extension_pos;
+ FILE *fp; /* input file pointer */
+ FILE *fplist[MAX_CHANNELS]; /* output file pointers */
+ uint8_t data[MAX_CHANNELS*MAX_BYTES];
+ size_t num_bytes_per_sample;
+
+ fp = fopen(filename, "rb");
+ /* Jump to data block */
+ fseek(fp, sizeof(wav_header_t), SEEK_SET);
+
+ strncpy(outname, filename, sizeof(outname));
+ /* Get extension index of filename */
+ extension_pos = strrchr(outname, '.');
+ /* Point to '\0' instead if no extension is found */
+ if (!extension_pos)
+ extension_pos = strrchr(outname, '\0');
+ if (!extension_pos) {
+ fprintf(stderr, "Filename is too long!\n");
+ return 1;
+ }
+
+ printf("splitting...\n");
+
+ /* Create files <filename_[01 - N].wav> and copy headers */
+ for (i = 0; i < wav_header->num_channels; i++) {
+ snprintf(extension_pos, sizeof(outname) - (extension_pos - outname), "_%02d.wav", i + 1);
+ fplist[i] = fopen(outname, "wb");
+ fwrite(wav_header_out, sizeof(wav_header_t), 1, fplist[i]);
+ }
+
+ num_bytes_per_sample = wav_header_out->bits_per_sample >> 3;
+
+ /* Split data block */
+ while(fread(data, wav_header->block_align, 1, fp) == 1) {
+ for (i = 0; i < wav_header->num_channels; i++)
+ fwrite(&data[i*num_bytes_per_sample], num_bytes_per_sample, 1, fplist[i]);
+ }
+
+ for (i = 0; i < wav_header->num_channels; i++) {
+ fclose(fplist[i]);
+ }
+ fclose(fp);
+ return 0;
+}
+
+int split_main (int argc, char* argv[]) {
+ char *filename;
+ wav_header_t wav_header, wav_header_out;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: audio-tool split file.wav\n");
+ return 1;
+ }
+ filename = argv[1];
+
+ if (parse_header(filename, &wav_header))
+ return 1;
+
+ printf("filename: %s\nformat: %d\nnum_channels: %d\nsample_rate: %d\nbyte_rate: %d\nblock_align: %d\nbits_per_sample: %d\n\n",
+ filename, wav_header.audio_format, wav_header.num_channels, wav_header.sample_rate,
+ wav_header.byte_rate, wav_header.block_align, wav_header.bits_per_sample);
+
+ if (wav_header.num_channels > MAX_CHANNELS) {
+ fprintf(stderr, "Error: only support %d or less channels\n", MAX_CHANNELS);
+ return 1;
+ }
+
+ if (wav_header.num_channels <= 1) {
+ fprintf(stderr, "Error: audio channel is already mono or invalid\n");
+ return 1;
+ }
+
+ if (create_new_header(&wav_header_out, &wav_header))
+ return 1;
+
+ if(split(filename, &wav_header, &wav_header_out))
+ return 1;
+
+ printf("Done!\n");
+ return 0;
+}
diff --git a/audio-tool/wav_chan_splitter.h b/audio-tool/wav_chan_splitter.h
new file mode 100644
index 0000000..fa63f9f
--- /dev/null
+++ b/audio-tool/wav_chan_splitter.h
@@ -0,0 +1,28 @@
+#ifndef __AUDIO_TOOL_WAV_CHAN_SPLITTER_H__
+#define __AUDIO_TOOL_WAV_CHAN_SPLITTER_H__
+
+
+#define ID_RIFF 0x46464952
+#define ID_WAVE 0x45564157
+#define ID_FMT 0x20746d66
+#define ID_DATA 0x61746164
+
+typedef struct wav_header {
+ uint32_t riff_id;
+ uint32_t riff_sz;
+ uint32_t riff_fmt;
+ uint32_t fmt_id;
+ uint32_t fmt_sz;
+ 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;
+ uint32_t data_id;
+ uint32_t data_sz;
+} wav_header_t;
+
+int split_main (int argc, char* argv[]);
+
+#endif /* __AUDIO_TOOL_WAV_CHAN_SPLITTER_H__ */