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__ */