summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMisael Lopez Cruz <misael.lopez@ti.com>2013-10-23 01:32:27 -0500
committerAngela Stegmaier <a0866189@ti.com>2013-11-01 11:42:55 -0500
commit5aadbddedac5f56c83535c45c6c940ef67c96b5d (patch)
tree8440df0f50fec7fe4758cc8e365096ad32c4f255
parentc81ce74a427ed2112cf82a9d012b932d929a5afa (diff)
downloadcommon-open-5aadbddedac5f56c83535c45c6c940ef67c96b5d.tar.gz
audio: tiaudioutils: Add initial implementation
The tiaudioutils library is meant to provide building blocks to facilitate the implementation of AudioHALs that use multizone audio. Change-Id: I90c7af12aa69ea3bba12efc9cb517abef4ab0dcd Signed-off-by: Angela Stegmaier <a0866189@ti.com> Signed-off-by: Misael Lopez Cruz <misael.lopez@ti.com>
-rw-r--r--audio/utils/Android.mk54
-rw-r--r--audio/utils/doc/Doxyfile1781
-rw-r--r--audio/utils/include/tiaudioutils/ALSAMixer.h329
-rw-r--r--audio/utils/include/tiaudioutils/ALSAPcm.h235
-rw-r--r--audio/utils/include/tiaudioutils/Base.h251
-rw-r--r--audio/utils/include/tiaudioutils/Log.h30
-rw-r--r--audio/utils/include/tiaudioutils/MonoPipe.h284
-rw-r--r--audio/utils/include/tiaudioutils/MumStream.h257
-rw-r--r--audio/utils/include/tiaudioutils/Pcm.h377
-rw-r--r--audio/utils/include/tiaudioutils/Resampler.h219
-rw-r--r--audio/utils/include/tiaudioutils/SimpleStream.h509
-rw-r--r--audio/utils/include/tiaudioutils/Stream.h1072
-rw-r--r--audio/utils/src/ALSAMixer.cpp331
-rw-r--r--audio/utils/src/ALSAPcm.cpp210
-rw-r--r--audio/utils/src/Base.cpp185
-rw-r--r--audio/utils/src/MonoPipe.cpp226
-rw-r--r--audio/utils/src/MumStream.cpp449
-rw-r--r--audio/utils/src/Pcm.cpp122
-rw-r--r--audio/utils/src/Resampler.cpp292
-rw-r--r--audio/utils/src/SimpleStream.cpp605
-rw-r--r--audio/utils/src/Stream.cpp1088
21 files changed, 8906 insertions, 0 deletions
diff --git a/audio/utils/Android.mk b/audio/utils/Android.mk
new file mode 100644
index 0000000..c038c56
--- /dev/null
+++ b/audio/utils/Android.mk
@@ -0,0 +1,54 @@
+# Copyright (C) 2013 Texas Instruments
+#
+# 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)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libtiaudioutils
+
+LOCAL_SRC_FILES := \
+ src/Base.cpp \
+ src/Pcm.cpp \
+ src/ALSAPcm.cpp \
+ src/ALSAMixer.cpp \
+ src/SimpleStream.cpp \
+ src/MumStream.cpp \
+ src/Stream.cpp \
+ src/Resampler.cpp \
+ src/MonoPipe.cpp
+
+LOCAL_C_INCLUDES += \
+ $(LOCAL_PATH)/include \
+ external/tinyalsa/include \
+ external/speex/include \
+ system/media/audio_utils/include \
+ system/media/audio_route/include \
+ frameworks/av/include
+
+LOCAL_SHARED_LIBRARIES := \
+ liblog \
+ libtinyalsa \
+ libspeexresampler \
+ libaudioutils \
+ libaudioroute \
+ libnbaio \
+ libutils
+
+LOCAL_SHARED_LIBRARIES += libstlport
+include external/stlport/libstlport.mk
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/audio/utils/doc/Doxyfile b/audio/utils/doc/Doxyfile
new file mode 100644
index 0000000..78568d6
--- /dev/null
+++ b/audio/utils/doc/Doxyfile
@@ -0,0 +1,1781 @@
+# Doxyfile 1.7.6.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or sequence of words) that should
+# identify the project. Note that if you do not use Doxywizard you need
+# to put quotes around the project name if it contains spaces.
+
+PROJECT_NAME = "libtiaudioutils"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY =
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding
+# "class=itcl::class" will allow you to use the command class in the
+# itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
+# unions with only public data fields will be shown inline in the documentation
+# of the scope in which they are defined (i.e. file, namespace, or group
+# documentation), provided this scope is documented. If set to NO (the default),
+# structs, classes, and unions are shown on a separate page (for HTML and Man
+# pages) or section (for LaTeX and RTF).
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+SYMBOL_CACHE_SIZE = 0
+
+# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
+# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
+# their name and scope. Since this can be an expensive process and often the
+# same symbol appear multiple times in the code, doxygen keeps a cache of
+# pre-resolved symbols. If the cache is too small doxygen will become slower.
+# If the cache is too large, memory is wasted. The cache size is given by this
+# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files
+# containing the references data. This must be a list of .bib files. The
+# .bib extension is automatically appended if omitted. Using this command
+# requires the bibtex tool to be installed. See also
+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
+# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
+# feature you need bibtex and perl available in the search path.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT =
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS = */test/*
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when
+# changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# style sheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the style sheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
+# at top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it. Since the tabs have the same information as the
+# navigation tree you can set this option to NO if you already set
+# GENERATE_TREEVIEW to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+# Since the tree basically has the same information as the tab index you
+# could consider to set DISABLE_INDEX to NO when enabling this option.
+
+GENERATE_TREEVIEW = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE = 4
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the
+# mathjax.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH = http://www.mathjax.org/mathjax
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
+# names that should be enabled during MathJax rendering.
+
+MATHJAX_EXTENSIONS =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
+# http://en.wikipedia.org/wiki/BibTeX for more info.
+
+LATEX_BIB_STYLE = plain
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load style sheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will use the Helvetica font for all dot files that
+# doxygen generates. When you want a differently looking font you can specify
+# the font name using DOT_FONTNAME. You need to make sure dot is able to find
+# the font, which can be done by putting it in a standard location or by setting
+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# directory containing the font.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the Helvetica font.
+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
+# set the path where dot can find it.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used. If you choose svg you need to set
+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible in IE 9+ (other browsers do not have this requirement).
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+# Note that this requires a modern browser other than Internet Explorer.
+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible. Older versions of IE do not have SVG support.
+
+INTERACTIVE_SVG = NO
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = YES
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/audio/utils/include/tiaudioutils/ALSAMixer.h b/audio/utils/include/tiaudioutils/ALSAMixer.h
new file mode 100644
index 0000000..fb6de31
--- /dev/null
+++ b/audio/utils/include/tiaudioutils/ALSAMixer.h
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * 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.
+ */
+
+/**
+ * \file ALSAMixer.h
+ * \brief Classes for controlling the mixer interface of an ALSA card
+ *
+ * Contains classes involved in the control of the mixer interface of an ALSA
+ * sound card.
+ */
+
+#ifndef _TIAUDIOUTILS_ALSAMIXER_H_
+#define _TIAUDIOUTILS_ALSAMIXER_H_
+
+#include <string>
+#include <list>
+#include <map>
+
+#include <tiaudioutils/Base.h>
+#include <tiaudioutils/Pcm.h>
+
+/* forward declaration */
+struct audio_route;
+
+namespace tiaudioutils {
+
+using std::string;
+using std::list;
+using std::map;
+
+/**
+ * \class ALSAControl
+ * \brief ALSA kcontrol
+ *
+ * An ALSA control that can be string or integer. Other control types like
+ * boolean are treatead as integer type. This class is only meant to carry
+ * control information like its name and value. The control takes effect
+ * when is passed to an ALSAMixer.
+ */
+class ALSAControl {
+ public:
+ /**
+ * \brief Integer ALSA control constructor
+ *
+ * Constructs an integer ALSA control that can be used for boolean,
+ * integer, etc. kcontrols. It's caller responsibility to pass a
+ * valid name (e.g. non-empty string).
+ *
+ * \param name Control name
+ * \param val Control value
+ */
+ ALSAControl(string &name, int val);
+
+ /**
+ * \brief String ALSA control constructor
+ *
+ * Constructs a string ALSA control that can be used for enumerated
+ * type kcontrols. It's caller responsibility to pass a valid name
+ * and value (e.g. non-empty strings).
+ *
+ * \param name Control name
+ * \param val Control value
+ */
+ ALSAControl(string &name, string &val);
+
+ /**
+ * \brief Integer ALSA control constructor
+ *
+ * Constructs an integer ALSA control that can be used for boolean,
+ * integer, etc. kcontrols. It's caller responsibility to pass a
+ * valid name (e.g. non-empty string).
+ *
+ * \param name Control name as a C-style char pointer
+ * \param val Control value
+ */
+ ALSAControl(const char *name, int val);
+
+ /**
+ * \brief String ALSA control constructor
+ *
+ * Constructs a string ALSA control that can be used for enumerated
+ * type kcontrols. It's caller responsibility to pass a valid name
+ * and value (e.g. non-empty strings).
+ *
+ * \param name Control name as a C-style char pointer
+ * \param val Control value as a C-style char pointer
+ */
+ ALSAControl(const char *name, const char *val);
+
+ /**
+ * \brief ALSA control destructor
+ *
+ * Destroy an ALSA control object.
+ */
+ virtual ~ALSAControl() {}
+
+ /**
+ * \brief Get the control name
+ *
+ * Gets the ALSA control name.
+ *
+ * \return Reference to the control name
+ */
+ const string& name() const { return mName; }
+
+ /**
+ * \brief Get the control string value
+ *
+ * Gets the string value of an ALSA control.
+ *
+ * \return Reference to the control value.
+ * Empty string if the control is not of string type
+ */
+ const string& strVal() const { return mStrVal; }
+
+ /**
+ * \brief Get the control integer value
+ *
+ * Gets the integer value of an ALSA control.
+ *
+ * \return Value of the control
+ * \retval -1 if the control is not of integer type
+ */
+ int intVal() const { return mIntVal; }
+
+ /**
+ * \brief Test if the control type is integer
+ *
+ * Test if the ALSA control type is integer.
+ *
+ * \return true if the control is integer type, false otherwise
+ */
+ bool isIntegerType() const;
+
+ /**
+ * \brief Test if the control type is string
+ *
+ * Test if the ALSA control type is string.
+ *
+ * \return true if the control is string type, false otherwise
+ */
+ bool isStringType() const;
+
+ protected:
+ string mName; /**< Control name */
+ string mStrVal; /**< String value of the control */
+ int mIntVal; /**< Integer value of the control */
+ enum {
+ TYPE_INT, /**< Integer type: bool, int, byte, etc */
+ TYPE_STR /**< String type: enum */
+ } mType; /**< Control type category */
+};
+
+/**
+ * \typedef ALSAControlList
+ * \brief List of ALSA Controls
+ *
+ * STL-based list of ALSA controls. It's used to pass controls to be set or
+ * cleared to the ALSAMixer.
+ */
+typedef list<ALSAControl> ALSAControlList;
+
+/**
+ * \class ALSAMixer
+ * \brief ALSA Mixer
+ *
+ * The ALSA mixer is used to set or clear kcontrols in a sound card.
+ */
+class ALSAMixer {
+ public:
+ /**
+ * \brief ALSA mixer constructor
+ *
+ * Constructs an ALSA mixer of a sound card.
+ *
+ * \param card ALSA card id
+ */
+ ALSAMixer(uint32_t card);
+
+ /**
+ * \brief ALSA mixer destructor
+ *
+ * Destroys an ALSA mixer object.
+ */
+ virtual ~ALSAMixer();
+
+ /**
+ * \brief Check the result of constructing an ALSA mixer
+ *
+ * Result of constructing a ALSA mixer. It must be checked before
+ * using any methods. Result is undefined if ALSAMixer is used when
+ * in an unitialized state.
+ *
+ * \return true is mixer construction is correct, false otherwise
+ */
+ bool initCheck() const;
+
+ /**
+ * \brief Set or clear the value of an ALSAControl
+ *
+ * Sets or clears the value of an individual ALSAControl in the card's
+ * mixer.
+ *
+ * \param control The ALSAControl to be set or cleared
+ * \param on true to set the control, false to clear it
+ * \return 0 on success, otherwise negative error code
+ */
+ int set(const ALSAControl &control, bool on);
+
+ /**
+ * \brief Set or clear the value of a list of controls
+ *
+ * Sets or clears the value of an ALSAControlList to the card's mixer.
+ * There is no unwind mechanism to set the controls to their initial value
+ * if this method fails while setting an intermediate control of the list.
+ * Controls with multiple values will all be set to the same ALSAControl
+ * value.
+ *
+ * \param controls The list of ALSA controls to be set or cleared
+ * \param on true to set the control list, false to clear it
+ * \return 0 on success, otherwise negative error code
+ */
+ int set(const ALSAControlList &controls, bool on);
+
+ /**
+ * \brief Initialize routes to the default value
+ *
+ * Initializes card controls based on the default values defined in the
+ * card's XML file, if the file is available.
+ *
+ * \return 0 on success, otherwise negative error code
+ */
+ int initRoutes();
+
+ /**
+ * \brief Apply or reset the controls of an audio path
+ *
+ * Applies or resets the controls of a named path as defined in the
+ * card's XML path, if the file is available. The name of the path
+ * must match any of the declared "path" elements in the XML file.
+ * All the controls in the corresponding XML "path" element will be
+ * applied, but only those controls that have a reset value in the
+ * card defaults section will be reset.
+ *
+ * \param path The path name
+ * \param on true to apply the path, false to reset it
+ * \return 0 on success, otherwise negative error code
+ */
+ int setPath(const char *path, bool on);
+
+ /**
+ * \brief Apply or reset the controls for an audio device
+ *
+ * Applies or resets the controls needed for audio devices (one or more)
+ * based on XML "path" elements named after the Android audio device
+ * definitions (e.g. AUDIO_DEVICE_OUT_SPEAKER, AUDIO_DEVICE_IN_BUILTIN_MIC).
+ * All the controls in the corresponding XML "path" element for a given
+ * device will be applied, but only those controls that have a reset value
+ * in the card defaults section will be reset.
+ *
+ * \param devices The audio devices to apply or reset the path
+ * \param on true to apply the path, false to reset it
+ * \return 0 on success, otherwise negative error code
+ */
+ int setPath(audio_devices_t devices, bool on);
+
+ /**
+ * \brief Update the controls for an audio device transition
+ *
+ * Update the audio routes for a device transition in two steps:
+ * - Reset the paths for inactive devices (devices that after the
+ * transition are no longer used)
+ * - Apply the paths for new active devices (devices that after the
+ * transition are newly used)
+ *
+ * \param oldDevices The devices before the transition
+ * \param newDevices The devices required after the transition
+ * \return 0 on success, otherwise negative error code
+ */
+ int updatePath(audio_devices_t oldDevices, audio_devices_t newDevices);
+
+ private:
+ ALSAMixer(const ALSAMixer &mixer);
+ ALSAMixer& operator=(const ALSAMixer &mixer);
+
+ protected:
+ /** The prefix of the path where to search for the card's route XML file */
+ static const string kAudioRoutePrefix;
+ /** The suffix of the card's route XML file */
+ static const string kAudioRouteSuffix;
+
+ /**
+ * \brief Initialize map of audio device to definition name
+ *
+ * Initialize the internal map of audio devices (audio_device_t) to device
+ * name (string). It allows to search paths in the card's XML file by their
+ * Android device name, e.g. AUDIO_DEVICE_OUT_SPEAKER, AUDIO_DEVICE_IN_BUILTIN_MIC.
+ */
+ void initDeviceMap();
+
+ /**
+ * Map between Android audio devices and their names.
+ * Used to find paths in the XML routes file for a corresponding audio device.
+ */
+ typedef map<audio_devices_t, string> DeviceMap;
+
+ uint32_t mCard; /**< Card id */
+ struct mixer *mMixer; /**< tinyalsa mixer */
+ struct audio_route *mRouter; /**< XML-based audio router */
+ DeviceMap mDevMap; /**< Android device to device name */
+ Mutex mLock; /**< Lock the access to tinyalsa mixer calls */
+};
+
+} /* namespace tiaudioutils */
+
+#endif /* _TIAUDIOUTILS_ALSAMIXER_H_ */
diff --git a/audio/utils/include/tiaudioutils/ALSAPcm.h b/audio/utils/include/tiaudioutils/ALSAPcm.h
new file mode 100644
index 0000000..830e304
--- /dev/null
+++ b/audio/utils/include/tiaudioutils/ALSAPcm.h
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * 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.
+ */
+
+/**
+ * \file ALSAPcm.h
+ * \brief Classes for accessing the PCM interface of an ALSA card
+ *
+ * Contains classes required to play and capture data from an ALSA sound card,
+ * incluiding a class for the ALSA card itself.
+ */
+
+#ifndef _TIAUDIOUTILS_ALSAPCM_H_
+#define _TIAUDIOUTILS_ALSAPCM_H_
+
+#include <tiaudioutils/Base.h>
+#include <tiaudioutils/Pcm.h>
+
+namespace tiaudioutils {
+
+class ALSAInPort;
+class ALSAOutPort;
+
+/**
+ * \class ALSAInPort
+ * \brief ALSA capture port
+ *
+ * The ALSA PCM port used for capture.
+ */
+class ALSAInPort : public PcmInPort {
+ public:
+ /**
+ * \brief ALSA input port constructor
+ *
+ * Constructs an ALSA input/capture port with period count.
+ *
+ * \param card The card id
+ * \param port The port id
+ * \param period_count The period count
+ */
+ ALSAInPort(uint32_t card,
+ uint32_t port,
+ uint32_t period_count = kDefaultNumPeriods);
+
+ /**
+ * \brief ALSA input port destructor
+ *
+ * Destroys an ALSA input/capture port object.
+ */
+ virtual ~ALSAInPort();
+
+ /**
+ * \brief Get the id of the ALSA sound card
+ *
+ * Gets the id of the ALSA sound card that this port belongs to.
+ *
+ * \return The sound card id
+ */
+ uint32_t getCardId() const { return mCardId; }
+
+ /**
+ * \brief Get the port id
+ *
+ * Gets the ALSA input/capture port id which is the PCM device number.
+ *
+ * \return The PCM port id
+ */
+ uint32_t getPortId() const { return mPortId; }
+
+ /**
+ * \brief Open the PCM port for capture
+ *
+ * Opens the ALSA PCM port for capture with the given parameters.
+ *
+ * \param params PcmParams used to open the port
+ * \return 0 on success, otherwise negative error code
+ */
+ int open(const PcmParams &params);
+
+ /**
+ * \brief Close the PCM port
+ *
+ * Closes the ALSA PCM port opened by calling open().
+ */
+ void close();
+
+ /**
+ * \brief Test if the PCM port is open and ready
+ *
+ * Tests if the PCM port is open and ready for capture.
+ *
+ * \return true if the port is open, false otherwise
+ */
+ bool isOpen() const;
+
+ /**
+ * \brief Read audio data from the PCM input port
+ *
+ * Reads audio data from the PCM capture port to the passed buffer.
+ *
+ * \param buffer Pointer to the destination buffer
+ * \param frames Number of frames to be read
+ * \return 0 on success, otherwise negative error code
+ */
+ int read(void *buffer, size_t frames);
+
+ /** Default number of periods for the tinyalsa config of the capture port */
+ static const uint32_t kDefaultNumPeriods = 3;
+
+ private:
+ ALSAInPort(const ALSAInPort &port);
+ ALSAInPort& operator=(const ALSAInPort &port);
+
+ protected:
+ uint32_t mCardId; /**< Id of the ALSA card */
+ uint32_t mPortId; /**< Id of the ALSA PCM device */
+ uint32_t mPeriodCount; /**< Period count of this port */
+ struct pcm *mPcm; /**< tinyalsa pcm handle */
+ mutable Mutex mLock; /**< Synchronize PCM port use */
+};
+
+/**
+ * \class ALSAOutPort
+ * \brief ALSA playback port
+ *
+ * The ALSA PCM port used for playback.
+ */
+class ALSAOutPort : public PcmOutPort {
+ public:
+ /**
+ * \brief ALSA output port constructor
+ *
+ * Constructs an ALSA output/playback port with period count.
+ *
+ * \param card The card id
+ * \param port The port id
+ * \param period_count The period count
+ */
+ ALSAOutPort(uint32_t card,
+ uint32_t port,
+ uint32_t period_count = kDefaultNumPeriods);
+
+ /**
+ * \brief ALSA output port destructor
+ *
+ * Destroys an ALSA output/playback port object.
+ */
+ virtual ~ALSAOutPort();
+
+ /**
+ * \brief Get the id of the ALSA sound card
+ *
+ * Gets the id of the ALSA sound card that this port belongs to.
+ *
+ * \return The sound card id
+ */
+ uint32_t getCardId() const { return mCardId; }
+
+ /**
+ * \brief Get the port id
+ *
+ * Gets the ALSA output/playback port id which is the PCM device number.
+ *
+ * \return The PCM port id
+ */
+ uint32_t getPortId() const { return mPortId; }
+
+ /**
+ * \brief Open the PCM port for playback
+ *
+ * Opens the ALSA PCM port for playback with the given parameters.
+ * The port is open in non-mmap mode.
+ *
+ * \param params PcmParams used to open the port
+ * \return 0 on success, otherwise negative error code
+ */
+ int open(const PcmParams &params);
+
+ /**
+ * \brief Close the PCM port
+ *
+ * Closes the ALSA PCM port opened by calling open().
+ */
+ virtual void close();
+
+ /**
+ * \brief Test if the PCM port is open and ready
+ *
+ * Tests if the PCM port is open and ready for playback.
+ *
+ * \return true if the port is open, false otherwise
+ */
+ bool isOpen() const;
+
+ /**
+ * \brief Write audio data to the PCM output port
+ *
+ * Writes audio data to the PCM playback port from the passed buffer.
+ *
+ * \param buffer Pointer to the source buffer
+ * \param frames Number of frames to be written
+ * \return Number of frames written, otherwise negative error code
+ */
+ int write(const void *buffer, size_t frames);
+
+ /** Default number of periods for the tinyalsa config of the playback port */
+ static const uint32_t kDefaultNumPeriods = 4;
+
+ private:
+ ALSAOutPort(const ALSAOutPort &port);
+ ALSAOutPort& operator=(const ALSAOutPort &port);
+
+ protected:
+ uint32_t mCardId; /**< Id of the ALSA card */
+ uint32_t mPortId; /**< Id of the ALSA PCM device */
+ uint32_t mPeriodCount; /**< Period count of this port */
+ struct pcm *mPcm; /**< tinyalsa pcm handle */
+ mutable Mutex mLock; /**< Synchronize PCM port use */
+};
+
+} /* namespace tiaudioutils */
+
+#endif /* _TIAUDIOUTILS_ALSAPCM_H_ */
diff --git a/audio/utils/include/tiaudioutils/Base.h b/audio/utils/include/tiaudioutils/Base.h
new file mode 100644
index 0000000..dc2a6f1
--- /dev/null
+++ b/audio/utils/include/tiaudioutils/Base.h
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * 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.
+ */
+
+/**
+ * \file Base.h
+ * \brief Miscellaneous base clases
+ *
+ * Miscellaneous classes used across tiaudioutils.
+ */
+
+#ifndef _TIAUDIOUTILS_BASE_H_
+#define _TIAUDIOUTILS_BASE_H_
+
+#include <sys/types.h>
+#include <utils/Mutex.h>
+#include <pthread.h>
+#include <string>
+#include <map>
+
+namespace tiaudioutils {
+
+using std::string;
+using std::map;
+using android::Mutex;
+using android::AutoMutex;
+
+/**
+ * \class SlotMap
+ * \brief Slot map
+ *
+ * The slot map between source and destination streams. Each channel in the
+ * source can be mapped to any channel in the destination. That gives enough
+ * flexibility for doing buffer remix. Destination channel is stored first in
+ * the map and source channel is stored second. This enables a map to be specified
+ * which can duplicate a single source channel to multiple destination channels.
+ */
+class SlotMap : public map<uint32_t, uint32_t> {
+ public:
+ /**
+ * \brief Default constructor
+ *
+ * Constructs a default (but invalid) slot map. Entries to the map can be added
+ * later to make the map valid.
+ */
+ SlotMap() {}
+
+ /**
+ * \brief Symmetric slot map constructor
+ *
+ * Constructs a slot map where the source slots map to the same slots in
+ * the destination.
+ *
+ * \param mask Mask of the slots to be mapped
+ */
+ SlotMap(uint32_t mask);
+
+ /**
+ * \brief Consecutive slots map constructor
+ *
+ * Constructs a slot map where the slots in the source are processed in
+ * ascending order and mapped to their corresponding slot in the destination,
+ * also processed in ascending order.
+ *
+ * \param srcMask Mask of the source slots to be mapped
+ * \param dstMask Mask of the destination slots to be mapped
+ */
+ SlotMap(uint32_t srcMask, uint32_t dstMask);
+
+ /**
+ * \brief Check if the slot map is valid
+ *
+ * Checks if the current state of the slot map is valid, defined as:
+ * - at least one slot is mapped
+ * - same number of source and destination channels
+ *
+ * \return true if slot map is valid, false otherwise
+ */
+ bool isValid() const;
+
+ /**
+ * \brief Get the number of channels in the map
+ *
+ * Gets the number of slots/channels in the map. Calling this function when
+ * the map is in invalid state is a logical error.
+ *
+ * \return Number of slots/channels being mapped
+ */
+ uint32_t getChannelCount() const;
+
+ /**
+ * \brief Get the mask of the source slots
+ *
+ * Gets the mask of the source slots. No assumptions about the map order must
+ * be made from the mask value.
+ *
+ * \return The mask with the slot positions in the source being mapped
+ */
+ uint32_t getSrcMask() const;
+
+ /**
+ * \brief Get the mask of the destination slots
+ *
+ * Gets the mask of the destination slots. No assumptions about the map order
+ * must be made from the mask value.
+ *
+ * \return The mask with the slot positions in the destination being mapped
+ */
+ uint32_t getDstMask() const;
+};
+
+/**
+ * The pthread's start routine passed during thread creation. It runs the
+ * _threadLoop() of ThreadBase.
+ *
+ * \param me Pointer to the ThreadBase object.
+ */
+extern "C" void* threadWrapper(void *me);
+
+/**
+ * \class ThreadBase
+ * \brief Thread base
+ *
+ * Base class of a thread. Derived classes must implement threadFunc() to give
+ * implementation specific functionality to the thread. The thread will continue
+ * executing threadFunc() as long as: threadFunc() returns 0 or stop() is not
+ * called.
+ */
+class ThreadBase {
+ public:
+ /**
+ * \brief Default constructor
+ *
+ * Constructs an unnamed thread.
+ */
+ ThreadBase();
+
+ /**
+ * \brief Named thread constructor
+ *
+ * Constructs a named thread.
+ *
+ * \param name Thread name
+ */
+ ThreadBase(const string &name);
+
+ /**
+ * \brief Named thread constructor
+ *
+ * Constructs a named thread.
+ *
+ * \param name Thread name
+ */
+ ThreadBase(const char *name);
+
+ /**
+ * \brief Destructor
+ *
+ * Destroys a thread similar to the way stop() would.
+ */
+ virtual ~ThreadBase();
+
+ /**
+ * \brief Start thread execution
+ *
+ * Starts the execution of the thread whose main function is threadFunc()
+ * implemented by derived classes. Thread execution continues as long as
+ * the threadFunc() returns 0 and it hasn't been stopped by stop().
+ * Execution loop continues otherwise.
+ *
+ * \return 0 on success, otherwise negative error code
+ */
+ int run();
+
+ /**
+ * \brief Stop thread execution
+ *
+ * Stops the execution of the thread on completion of next thread loop.
+ * Status of threadFunc() upon stopping is returned by this method.
+ *
+ * \return The return value of threadFunc()
+ */
+ int stop();
+
+ /**
+ * \brief Test if thread is running
+ *
+ * Test if the thread is running.
+ *
+ * \return true if thread is running, false otherwise
+ */
+ bool isRunning() const;
+
+ /**
+ * \brief Get the thread name
+ *
+ * Get the name of the thread.
+ *
+ * \return Thread name as a C-style char pointer
+ */
+ const char *name() const { return mName.c_str(); }
+
+ friend void* threadWrapper(void *me);
+
+ private:
+ ThreadBase(const ThreadBase &thread);
+ ThreadBase& operator=(const ThreadBase &thread);
+
+ /**
+ * \brief Thread's internal loop
+ *
+ * The thread's internal loop calls threadFunc() until is returns a value
+ * different than 0 or the thread is stopped through stop().
+ *
+ * \return The return value of threadFunc()
+ */
+ int _threadLoop();
+
+ pthread_t mThread; /**< pthread handle of this thread */
+ string mName; /**< Name of the thread */
+ volatile bool mRunning; /**< Running status of the thread */
+ mutable Mutex mMutex;
+
+ protected:
+ /**
+ * \brief Implementation-specific thread function
+ *
+ * Derived classes must implement it. It should return 0 if the function has
+ * to be executed again on the next loop. Returning an error code breaks the
+ * thread loop.
+ *
+ * \return 0 if function has to be executed again, otherwise an error code
+ */
+ virtual int threadFunc() = 0;
+};
+
+} /* namespace tiaudioutils */
+
+#endif /* _TIAUDIOUTILS_BASE_H_ */
diff --git a/audio/utils/include/tiaudioutils/Log.h b/audio/utils/include/tiaudioutils/Log.h
new file mode 100644
index 0000000..dfc5d79
--- /dev/null
+++ b/audio/utils/include/tiaudioutils/Log.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * 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.
+ */
+
+#ifndef _TIAUDIOUTILS_LOG_H_
+#define _TIAUDIOUTILS_LOG_H_
+
+/* As usual, LOG_NDEBUG and VERY_VERBOSE_LOGGING must be defined before */
+#define LOG_TAG "tiaudioutils"
+#ifdef VERY_VERBOSE_LOGGING
+#define ALOGVV ALOGV
+#else
+#define ALOGVV(...) do { } while(0)
+#endif
+
+#include <cutils/log.h>
+
+#endif /* _TIAUDIOUTILS_LOG_H_ */
diff --git a/audio/utils/include/tiaudioutils/MonoPipe.h b/audio/utils/include/tiaudioutils/MonoPipe.h
new file mode 100644
index 0000000..c0c352b
--- /dev/null
+++ b/audio/utils/include/tiaudioutils/MonoPipe.h
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * 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.
+ */
+
+/**
+ * \file MonoPipe.h
+ * \brief Classes for writing and reading to/from a mono pipe
+ *
+ * Contains the MonoPipe and its helper buffer provider MonoReader and
+ * MonoWriter that can be used to seamlessly connect the pipe with
+ * stream writers/readers like PcmWriter and PcmReader.
+ */
+
+#ifndef _TIAUDIOUTILS_MONOPIPE_H_
+#define _TIAUDIOUTILS_MONOPIPE_H_
+
+#include <tiaudioutils/Base.h>
+#include <tiaudioutils/Pcm.h>
+
+namespace android {
+ class MonoPipe;
+ class MonoPipeReader;
+}
+
+namespace tiaudioutils {
+
+/**
+ * \class MonoPipe
+ * \brief Mono pipe
+ *
+ * Blocking mono pipe, only one reader and one writer are possible, although
+ * that constraint is not enforced by the class.
+ * The pipe can be accessed directly through read() and write() methods, but
+ * it's also possible through the buffer providers PipeWriter and PipeReader.
+ * The latter approach allows to connect the providers with PCM streams (e.g.
+ * InStream, OutStream) to read and write to PCM ports.
+ */
+class MonoPipe {
+ public:
+ /**
+ * \brief Mono pipe constructor
+ *
+ * Constructs a mono pipe for the specified PCM parameters: sample rate,
+ * sample size and channels. This mono pipe is implemented on top of
+ * NBAIO's mono pipe, so the same limitations apply:
+ * - Sample rate: 44100 and 48000
+ * - Sample size: 16-bits
+ * - Channels: mono and stereo
+ *
+ * \param params PCM params for the pipe as described above
+ * \param frames The size of pipe in frames, forced to be at least to 3x
+ * the frameCount of passed PCM params
+ */
+ MonoPipe(const PcmParams &params, uint32_t frames = 0);
+
+ /**
+ * \brief Mono pipe destructor
+ *
+ * Destroys the mono pipe and its underlying NBAIO mono pipe.
+ */
+ virtual ~MonoPipe();
+
+ /**
+ * \brief Check the result of constructing the mono pipe
+ *
+ * Result of constructing the mono pipe. It must be checked before using
+ * any methods. Result is undefined if the pipe is used when in an
+ * unitialized state.
+ *
+ * \return true if pipe construction is correct, false otherwise
+ */
+ virtual bool initCheck() const;
+
+ /**
+ * \brief Get the parameters of the pipe
+ *
+ * Get the native PcmParams used in the mono pipe.
+ *
+ * \return Reference to the PCM params of the pipe
+ */
+ const PcmParams& getParams() const { return mParams; }
+
+ /**
+ * \brief Read data from the pipe
+ *
+ * Reads data from the mono pipe. The call blocks if the pipe doesn't
+ * have the requested frames, but only for equivalent time to the
+ * missing frames.
+ *
+ * \param buffer Pointer to the destination buffer
+ * \param frames Number of frames to be read
+ * \return Number of frames read, otherwise negative error code
+ */
+ virtual int read(void *buffer, uint32_t frames);
+
+ /**
+ * \brief Check the result of constructing the mono pipe
+ *
+ * Writes data to the mono pipe. The call blocks if the pipe is full.
+ *
+ * \param buffer Pointer to the source buffer
+ * \param frames Number of frames to be written
+ * \return Number of frames written, otherwise negative error code
+ */
+ virtual int write(const void *buffer, uint32_t frames);
+
+ /**
+ * \brief Query the number of frames in the pipe
+ *
+ * Queries the number of frames available in the pipe. A subsequent
+ * read() call for that many frames should succeed.
+ *
+ * \return The number of available frames to be read, negative error
+ * code if the pipe is in bad state
+ */
+ virtual int availableToRead() const;
+
+ /**
+ * \brief Query the empty frame locations in the pipe
+ *
+ * Queries the empty frame locations in the pipe. A subsequent write()
+ * call for that many frames should succeed.
+ *
+ * \return The number of available frames to be write, negative error
+ * code if the pipe is in bad state
+ */
+ virtual int availableToWrite() const;
+
+ protected:
+ /** Ratio between pipe frame count and params frame count */
+ static const uint32_t kPipeSizeFactor = 3;
+
+ android::MonoPipe *mSink; /**< Mono pipe */
+ android::MonoPipeReader *mSource; /**< Reader for the mono pipe */
+ PcmParams mParams; /**< PCM parameters of the pipe */
+};
+
+/**
+ * \class PipeReader
+ * \brief Mono pipe reader
+ *
+ * Buffer provider that can be used to read data from the MonoPipe. This
+ * provider can be used along with OutStream and PcmWriter to read data
+ * from the pipe and write it to a PCM port.
+ */
+class PipeReader : public BufferProvider {
+ public:
+ /**
+ * \brief Pipe reader constructor
+ *
+ * Constructs a buffer provider that is capable of reading data from
+ * the specified mono pipe.
+ *
+ * \param pipe The mono pipe to read data from
+ */
+ PipeReader(MonoPipe *pipe);
+
+ /**
+ * \brief Pipe reader destructor
+ *
+ * Destroys a pipe reader object.
+ */
+ virtual ~PipeReader();
+
+ /**
+ * \brief Check the result of constructing the mono pipe reader
+ *
+ * Result of constructing the mono pipe reader. It must be checked before
+ * using any methods. Result is undefined if the writer is used when in an
+ * unitialized state.
+ *
+ * \return true if pipe construction is correct, false otherwise
+ */
+ bool initCheck() const { return mPipe != NULL; }
+
+ /**
+ * \brief Get the next buffer filled with data from the pipe
+ *
+ * Gets the next audio buffer filled with PCM data read from the pipe.
+ * The buffer can be filled with zeroes if there are no frames
+ * available to read in the pipe, see MonoPipe read() for more
+ * details.
+ *
+ * \param buffer Pointer to the buffer to fill with next buffer's info
+ * \return 0 on success, otherwise negative error code
+ */
+ int getNextBuffer(BufferProvider::Buffer *buffer);
+
+ /**
+ * \brief Release the buffer from the pipe
+ *
+ * Release the audio buffer previously obtained through getNextBuffer().
+ * It's a logical error to release a buffer not previously obtained,
+ * result is undefined.
+ *
+ * \param buffer Pointer to the buffer to be released
+ */
+ void releaseBuffer(BufferProvider::Buffer *buffer);
+
+ protected:
+ MonoPipe *mPipe; /**< The mono pipe used by this writer */
+ BufferProvider::Buffer mBuffer; /**< Buffer with data to be written to the pipe */
+};
+
+/**
+ * \class PipeWriter
+ * \brief Mono pipe writer
+ *
+ * Buffer provider that can be used to write data to the MonoPipe. This
+ * provider can be used along with InStream and PcmReader to read
+ * data from a PCM port and write it to the pipe.
+ */
+class PipeWriter : public BufferProvider {
+ public:
+ /**
+ * \brief Pipe writer constructor
+ *
+ * Constructs a buffer provider that is capable of writing data to
+ * the specified mono pipe.
+ *
+ * \param pipe The mono pipe to write data to
+ */
+ PipeWriter(MonoPipe *pipe);
+
+ /**
+ * \brief Pipe writer destructor
+ *
+ * Destroys a pipe writer object.
+ */
+ virtual ~PipeWriter();
+
+ /**
+ * \brief Check the result of constructing the mono pipe writer
+ *
+ * Result of constructing the mono pipe writer. It must be checked before
+ * using any methods. Result is undefined if the writer is used when in an
+ * unitialized state.
+ *
+ * \return true if pipe construction is correct, false otherwise
+ */
+ bool initCheck() const { return mPipe != NULL; }
+
+ /**
+ * \brief Get the next buffer that will be written to the pipe
+ *
+ * Gets the next audio buffer that can be filled with PCM data for the
+ * mono pipe.
+ *
+ * \param buffer Pointer to the buffer to fill with next buffer's info
+ * \return 0 on success, otherwise negative error code
+ */
+ int getNextBuffer(BufferProvider::Buffer *buffer);
+
+ /**
+ * \brief Release the buffer from the pipe
+ *
+ * Release the audio buffer previously obtained through getNextBuffer().
+ * It's a logical error to release a buffer not previously obtained,
+ * result is undefined.
+ *
+ * \param buffer Pointer to the buffer to be released
+ */
+ void releaseBuffer(BufferProvider::Buffer *buffer);
+
+ protected:
+ MonoPipe *mPipe; /**< The mono pipe used by this writer */
+ BufferProvider::Buffer mBuffer; /**< Buffer with data to be written to the pipe */
+};
+
+} /* namespace tiaudioutils */
+
+#endif /* _TIAUDIOUTILS_MONOPIPE_H_ */
diff --git a/audio/utils/include/tiaudioutils/MumStream.h b/audio/utils/include/tiaudioutils/MumStream.h
new file mode 100644
index 0000000..73507eb
--- /dev/null
+++ b/audio/utils/include/tiaudioutils/MumStream.h
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * 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.
+ */
+
+/**
+ * \file MumStream.h
+ * \brief Classes for merge and unmerge audio streams
+ *
+ * Merge class allows multiple incoming inputs to be assembled in a single
+ * output with flexible slot mapping. Unmerge class produces multiple outputs
+ * from a single input stream.
+ */
+
+#ifndef _TIAUDIOUTILS_MUMSTREAM_H_
+#define _TIAUDIOUTILS_MUMSTREAM_H_
+
+#include <sys/types.h>
+#include <system/audio.h>
+#include <errno.h>
+#include <set>
+
+#include <tiaudioutils/Pcm.h>
+#include <tiaudioutils/Base.h>
+
+#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
+
+namespace tiaudioutils {
+
+using std::set;
+using android::sp;
+using android::wp;
+
+class InStream;
+class OutStream;
+
+/**
+ * \class Merge
+ * \brief %Merge
+ *
+ * Merge multiple audio streams into a single output stream.
+ */
+class Merge {
+ public:
+ /**
+ * \brief Constructor of a merge
+ *
+ * Constructs a merge object with the given stream parameters. Initially,
+ * the merge object contains no streams to be merged.
+ *
+ * \param params Output stream parameters (see PcmParams)
+ */
+ Merge(const PcmParams &params);
+
+ /**
+ * \brief Destructor of a merge
+ *
+ * Destroys a merge object.
+ */
+ virtual ~Merge();
+
+ /**
+ * \brief Check the result of constructing a merge object
+ *
+ * Result of constructing a Merge. It must be checked before using any Merge
+ * methods.
+ * Result is undefined otherwise.
+ *
+ * \return true if merge construction is correct, false otherwise
+ */
+ virtual bool initCheck() const;
+
+ /**
+ * \brief Get the PcmParams of the merge instance
+ *
+ * Get the PcmParams used in this merge instance.
+ *
+ * \return Reference to the PCM params of the merge
+ */
+ virtual const PcmParams& getParams() const { return mParams; }
+
+ /**
+ * \brief Register a stream for merge
+ *
+ * Register a output stream to be merged.
+ *
+ * \param stream Pointer to a Stream instance to be registered
+ * \return 0 on success, otherwise negative error code
+ */
+ virtual int registerStream(const sp<OutStream>& stream);
+
+ /**
+ * \brief Un-register a stream
+ *
+ * Un-register an output stream from a merge instance.
+ *
+ * \param stream Pointer to a Stream instance to be un-registered
+ */
+ virtual void unregisterStream(sp<OutStream>& stream);
+
+ /**
+ * \brief Process the merge stream
+ *
+ * Process the merge stream operation. It's caller responsibility to pass a
+ * valid buffer previously allocated.
+ *
+ * \param outBuffer Buffer where merged data will be copied to.
+ * \return 0 on success, otherwise negative error code
+ */
+ virtual int process(BufferProvider::Buffer &outBuffer);
+
+ private:
+ Merge(const Merge &merge);
+ Merge& operator=(const Merge &merge);
+
+ protected:
+ /**
+ * \brief Merge an individual stream
+ *
+ * Merge a given stream buffer into the output.
+ *
+ * \param stream The stream to be merged
+ * \param inBuffer The source buffer to be merged
+ * \param outBuffer The destination buffer
+ */
+ virtual void merge(sp<OutStream> stream,
+ BufferProvider::Buffer &inBuffer,
+ BufferProvider::Buffer &outBuffer);
+
+ /** Set of weak pointers to the registered output streams */
+ typedef set< wp<OutStream> > StreamSet;
+
+ PcmParams mParams; /**< PCM parameters used during merge operation */
+ uint32_t mDstMask; /**< The mask of the channels currently being merged */
+ uint32_t mContainerBytes; /**< The number of bytes in the container per sample */
+ StreamSet mStreams; /**< Set of registered OutStream objects */
+ Mutex mLock; /**< Lock to synchronize registration and processing */
+};
+
+/**
+ * \class UnMerge
+ * \brief %UnMerge
+ *
+ * UnMerge a single audio stream into multiple streams.
+ */
+class UnMerge {
+ public:
+ /**
+ * \brief Constructor of an unmerge
+ *
+ * Constructs an unmerge object with the given stream parameters. Initially,
+ * the unmerge object contains no streams to be un-merged.
+ *
+ * \param params Input stream parameters (see PcmParams)
+ */
+ UnMerge(const PcmParams &params);
+
+ /**
+ * \brief Destructor of an unmerge
+ *
+ * Destroys an unmerge object.
+ */
+ virtual ~UnMerge();
+
+ /**
+ * \brief Check the result of constructing an unmerge object
+ *
+ * Result of constructing an UnMerge. It must be checked before using any
+ * UnMerge methods.
+ * Result is undefined otherwise.
+ *
+ * \return true if unmerge construction is correct, false otherwise
+ */
+ virtual bool initCheck() const;
+
+ /**
+ * \brief Get the PcmParams of the unmerge instance
+ *
+ * Get the PcmParams used in this unmerge instance.
+ *
+ * \return Reference to the PCM params of the unmerge
+ */
+ virtual const PcmParams& getParams() const { return mParams; }
+
+ /**
+ * \brief Register an stream for unmerge
+ *
+ * Register an input stream to be filled with un-merged content.
+ *
+ * \param stream Pointer to an InputStream instance to be registered
+ * \return 0 on success, otherwise negative error code
+ */
+ virtual int registerStream(const sp<InStream>& stream);
+
+ /**
+ * \brief Un-register an stream
+ *
+ * Un-register an input stream from an UnMerge instance.
+ *
+ * \param stream Pointer to an InStream instance to be un-registered
+ */
+ virtual void unregisterStream(sp<InStream>& stream);
+
+ /**
+ * \brief Process the unmerge stream
+ *
+ * Process the unmerge stream operation. It's caller responsibility to pass
+ * a valid buffer previously allocated.
+ *
+ * \param inBuffer Buffer from where unmerged data will be copied.
+ * \return 0 on success, otherwise negative error code
+ */
+ virtual int process(BufferProvider::Buffer &inBuffer);
+
+ private:
+ UnMerge(const UnMerge &unmerge);
+ UnMerge& operator=(const UnMerge &unmerge);
+
+ protected:
+ /**
+ * \brief UnMerge an individual stream
+ *
+ * UnMerge data from the input to a given stream buffer.
+ *
+ * \param stream The stream to be filled with unmerged content
+ * \param inBuffer The source buffer to be unmerged
+ * \param outBuffer The destination buffer
+ */
+ virtual void unmerge(sp<InStream> stream,
+ BufferProvider::Buffer &inBuffer,
+ BufferProvider::Buffer &outBuffer);
+
+ /** Set of weak pointers to the registered input streams */
+ typedef set< wp<InStream> > StreamSet;
+
+ PcmParams mParams; /**< PCM parameters used during unmerge operation */
+ uint32_t mSrcMask; /**< The mask of the channels currently being unmerged */
+ uint32_t mContainerBytes; /**< The number of bytes in the container per sample */
+ StreamSet mStreams; /**< Set of registered InStream objects */
+ Mutex mLock; /**< Lock to synchronize registration and processing */
+};
+
+} /* namespace tiaudioutils */
+
+#endif /* _TIAUDIOUTILS_MUMSTREAM_H_ */
diff --git a/audio/utils/include/tiaudioutils/Pcm.h b/audio/utils/include/tiaudioutils/Pcm.h
new file mode 100644
index 0000000..a086d36
--- /dev/null
+++ b/audio/utils/include/tiaudioutils/Pcm.h
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * 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.
+ */
+
+/**
+ * \file Pcm.h
+ * \brief Abstract classes of PCM ports, input and output
+ *
+ * Defines the PCM ports involved in audio playback and capture.
+ */
+
+#ifndef _TIAUDIOUTILS_PCM_H_
+#define _TIAUDIOUTILS_PCM_H_
+
+#include <hardware/audio.h>
+#include <system/audio.h>
+
+#include <tiaudioutils/Base.h>
+
+/* forward declaration */
+struct pcm_config;
+
+namespace tiaudioutils {
+
+/**
+ * \enum StreamDirection
+ * \brief Audio stream direction
+ *
+ * Enum that represents the direction of the audio stream: playback or capture
+ */
+enum StreamDirection {
+ PLAYBACK, /**< Playback direction */
+ CAPTURE /**< Capture direction */
+};
+
+class PcmPort;
+class PcmInPort;
+class PcmOutPort;
+
+/**
+ * \class BufferProvider
+ * \brief %Buffer provider
+ *
+ * It represents components than can provider buffers for capture or playback.
+ */
+class BufferProvider {
+ public:
+ /**
+ * \class Buffer
+ * \brief Audio buffer
+ *
+ * The audio buffer defined as a pointer to the audio data and its size
+ * in frames.
+ */
+ struct Buffer {
+ /**
+ * \brief Default constructor
+ *
+ * Constructs a default (but invalid) audio buffer.
+ */
+ Buffer() : raw(NULL), frameCount(0) { }
+ union {
+ void *raw;
+ int32_t *i32;
+ int16_t *i16;
+ int8_t *i8;
+ }; /**< Pointer to the audio data */
+ size_t frameCount; /**< %Buffer size in frames */
+ };
+
+ protected:
+ BufferProvider() {}
+ virtual ~BufferProvider() {}
+
+ public:
+ /**
+ * \brief Get the next buffer
+ *
+ * Get the next audio buffer with at most the requested number of frames.
+ * The buffer provider can update it with the actual number of frames it
+ * was able to provide.
+ * The buffer provider is in charge of populating the buffer data pointer
+ * with a buffer that is valid until the call to releaseBuffer().
+ *
+ * \param buffer Pointer to the buffer to fill with next buffer's info
+ * \return 0 on success, otherwise negative error code
+ */
+ virtual int getNextBuffer(Buffer* buffer) = 0;
+
+ /**
+ * \brief Release the buffer
+ *
+ * Release the audio buffer previously obtained through getNextBuffer().
+ * It's a logical error to release a buffer not previously obtained, result
+ * is undefined.
+ *
+ * \param buffer Pointer to the buffer to be released
+ */
+ virtual void releaseBuffer(Buffer* buffer) = 0;
+};
+
+/**
+ * \class PcmParams
+ * \brief PCM parameters
+ *
+ * The PCM parameters that define an audio stream.
+ */
+class PcmParams {
+ public:
+ /**
+ * \brief Default constructor
+ *
+ * Constructs default (but invalid) PCM parameters. PCM parameters must be
+ * explicitly set for them to be valid for further use.
+ */
+ PcmParams();
+
+ /**
+ * \brief Constructor from individiual parameter values
+ *g
+ * Constructs the PCM params from explicit values for each field
+ *
+ * \param chan Number of channels/slots in a frame
+ * \param bits Number of bits in a sample
+ * \param rate Sample rate of the stream
+ * \param frames Number of frames in the buffer
+ */
+ PcmParams(uint32_t chan, uint32_t bits, uint32_t rate, uint32_t frames = 0);
+
+ /**
+ * \brief Constructor from a tinyalsa config
+ *
+ * Constructs the PCM params from the config parameters of tinyalsa.
+ * Most parameters are built from their corresponding counterpart, except
+ * for the buffer size which is set from the tinyalsa's period size.
+ *
+ * \param config tinyalsa PCM config
+ */
+ PcmParams(const struct pcm_config &config);
+
+ /**
+ * \brief Constructor from Android's audio config
+ *
+ * Constructs the PCM params from the Android's audio config and an optional
+ * buffer size. Parameters like channels, bit/sample and rate are built from
+ * their Android's audio config counterpart. Buffer size is not defined in
+ * Android's audio config hence it's taken from the passed argument.
+ *
+ * \param config Android audio config structuure
+ * \param frames Number of frames in the audio buffer
+ */
+ PcmParams(const struct audio_config &config, uint32_t frames = 0);
+
+ /**
+ * \brief Test if current state of params is valid
+ *
+ * Test if the current state of the PCM parameters is valid.
+ *
+ * \return true is params are valid, false otherwise
+ */
+ bool isValid() const;
+
+ /**
+ * \brief Get the number of bytes in a sample
+ *
+ * Get the number of bytes in an audio sample.
+ */
+ uint32_t sampleSize() const { return sampleBits / 8; };
+
+ /**
+ * \brief Get the number of bytes in a frame
+ *
+ * Get the number of bytes in an audio frame.
+ */
+ uint32_t frameSize() const { return sampleSize() * channels; };
+
+ /**
+ * \brief Get the number of bytes in the buffer
+ *
+ * Get the number of bytes in the audio buffer.
+ */
+ uint32_t bufferSize() const { return sampleSize() * channels * frameCount; }
+
+ /**
+ * \brief Get the number of samples in the buffer
+ *
+ * Gets the number of audio samples in the buffer. The term sample
+ * refers to the container of a single audio channel.
+ */
+ uint32_t sampleCount() const { return channels * frameCount; }
+
+ /**
+ * \brief Convert frames to bytes
+ *
+ * Convert the number of audio frames to bytes using the PCM parameters in
+ * this object.
+ *
+ * \param frames Number of audio frames to be converted
+ * \return Number of bytes in the passed frames
+ */
+ uint32_t framesToBytes(uint32_t frames) const;
+
+ /**
+ * \brief Convert bytes to frames
+ *
+ * Convert the number of bytes to frames using the PCM parameters in
+ * this object.
+ *
+ * \param bytes Number of bytes to be converted
+ * \return Number of frames in the passed frames
+ */
+ uint32_t bytesToFrames(uint32_t bytes) const;
+
+ /**
+ * \brief Fill pcm_config struct
+ *
+ * Fill a tinyalsa's pcm_config from the parameters of this object. Few fields
+ * of pcm_config are populated: channels, rate, format and period_size. Other
+ * fields must be populated outside this method as PcmParams contains less
+ * information than pcm_config.
+ *
+ * \param config tinyalsa pcm_config to be filled
+ */
+ void toPcmConfig(struct pcm_config &config) const;
+
+ /**
+ * \brief Fill audio_config struct
+ *
+ * Fill an Android's pcm_config from the parameters of this object for the
+ * given stream direction as channel mask is direction dependent in
+ * audio_config.
+ *
+ * \param config audio_config struct to be filled
+ * \param dir Stream direction
+ */
+ void toAudioConfig(struct audio_config &config, StreamDirection dir) const;
+
+ uint32_t channels; /**< The number of channels in a frame */
+ uint32_t sampleBits; /**< The number of bits in a sample */
+ uint32_t sampleRate; /**< The sample rate of the stream */
+ uint32_t frameCount; /**< The number of frames in the buffer */
+};
+
+/**
+ * \class PcmPort
+ * \brief PCM port
+ *
+ * PcmPort abstracts the parts of a PCM port that are common for playback
+ * and capture.
+ */
+class PcmPort {
+ public:
+ /**
+ * \brief Destructor of a PCM port
+ *
+ * Destroys a PCM port object.
+ */
+ virtual ~PcmPort() {}
+
+ /**
+ * \brief Get the card id
+ *
+ * Get the id of the sound card that this PCM port belongs to.
+ *
+ * \return The sound card id
+ */
+ virtual uint32_t getCardId() const = 0;
+
+ /**
+ * \brief Get port id
+ *
+ * Get the id of the PCM port.
+ *
+ * \return The PCM port id
+ */
+ virtual uint32_t getPortId() const = 0;
+
+ /**
+ * \brief Open the PCM port
+ *
+ * Opens a PCM port with the requested PCM parameters.
+ *
+ * \param params Reference to the PCM params
+ * \return 0 on success, otherwise negative error code
+ */
+ virtual int open(const PcmParams &params) = 0;
+
+ /**
+ * \brief Close the PCM port
+ *
+ * Closes the PCM port if open.
+ */
+ virtual void close() = 0;
+
+ /**
+ * \brief Query if PCM port is open
+ *
+ * Query is PCM is open and ready to use.
+ *
+ * \return true if port is open, false otherwise
+ */
+ virtual bool isOpen() const = 0;
+};
+
+/**
+ * \class PcmInPort
+ * \brief PCM input port
+ *
+ * PCM input port represents a port with capture capability.
+ */
+class PcmInPort : public PcmPort {
+ public:
+ /**
+ * \brief Destructor of a PCM input port
+ *
+ * Destroys a PCM input port object.
+ */
+ virtual ~PcmInPort() {}
+
+ /**
+ * \brief Read audio frames
+ *
+ * Read audio frames from the PCM input port. The PCM input port must be
+ * open first by calling open() method. Number of frames read can be less
+ * than requested.
+ *
+ * \param buffer Pointer to the buffer where data will be copied to
+ * \param frames Number of frames to be read
+ * \return Number of frames read, otherwise error code
+ */
+ virtual int read(void *buffer, size_t frames) = 0;
+};
+
+/**
+ * \class PcmOutPort
+ * \brief PCM output port
+ *
+ * PCM output port represents a port with playback capability.
+ */
+class PcmOutPort : public PcmPort {
+ public:
+ /**
+ * \brief Destructor of a PCM output port
+ *
+ * Destroys a PCM output port object.
+ */
+ virtual ~PcmOutPort() {}
+
+ /**
+ * \brief Write audio frames
+ *
+ * Write audio frames from the PCM input port. The PCM output port must be
+ * already open by calling open() method. Number of frames written can be less
+ * than requested.
+ *
+ * \param buffer Pointer to the buffer where playback data resides
+ * \param frames Number of frames to be written
+ * \return Number of frames written, otherwise error code
+ */
+ virtual int write(const void *buffer, size_t frames) = 0;
+};
+
+} /* namespace tiaudioutils */
+
+#endif /* _TIAUDIOUTILS_PCM_H_ */
diff --git a/audio/utils/include/tiaudioutils/Resampler.h b/audio/utils/include/tiaudioutils/Resampler.h
new file mode 100644
index 0000000..21023eb
--- /dev/null
+++ b/audio/utils/include/tiaudioutils/Resampler.h
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * 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.
+ */
+
+/**
+ * \file Resampler.h
+ * \brief Speex-based resampler
+ *
+ * A Speex-based resampler that supports multiple in and out rates, but
+ * only 16-bits/sample
+ */
+
+#ifndef _TIAUDIOUTILS_RESAMPLER_H_
+#define _TIAUDIOUTILS_RESAMPLER_H_
+
+#include <tiaudioutils/Pcm.h>
+
+/* forward declaration */
+struct SpeexResamplerState_;
+/** Type definition as in speex_resampler.h header */
+typedef struct SpeexResamplerState_ SpeexResamplerState;
+
+namespace tiaudioutils {
+
+
+/**
+ * \class Resampler
+ * \brief Speex-based resampler
+ *
+ * %Resampler based on Speex, it provides flexible input and output sample rates but
+ * requires 16-bits/sample.
+ */
+class Resampler {
+ public:
+ /**
+ * \brief %Resampler constructor from single parameters
+ *
+ * Constructs a resampler with same parameters for input and output sides.
+ * The input or output sample rates can be set later through setInSampleRate()
+ * or setOutSampleRate().
+ *
+ * \param params Input and output side params
+ * \param quality Speex resampler quality
+ */
+ Resampler(const PcmParams &params,
+ uint32_t quality = QualityDefault);
+
+ /**
+ * \brief %Resampler constructor from input and output parameters
+ *
+ * Constructs a resampler with the specified input and output parameters.
+ * Both parameters must be valid, have 16-bits/sample and same number of
+ * channels.
+ *
+ * \param inParams Input side parameters
+ * \param outParams Output side parameters
+ * \param quality Speex resampler quality
+ */
+ Resampler(const PcmParams &inParams,
+ const PcmParams &outParams,
+ uint32_t quality = QualityDefault);
+
+ /**
+ * \brief %Resampler destructor
+ *
+ * Destroy a resampler object.
+ */
+ virtual ~Resampler();
+
+ /**
+ * \brief Check the result of constructing the resampler
+ *
+ * Tests whether the resampler construction is correct or not. It must be
+ * checked before using any methods. Result is undefined if the resampler
+ * is used when in an unitialized state.
+ *
+ * \return true if resampler construction is correct, false otherwise
+ */
+ virtual bool initCheck() const;
+
+ /**
+ * \brief Set a new input sample rate
+ *
+ * Sets a new sample rate for the input side.
+ *
+ * \param rate New input rate
+ * \return 0 on success, otherwise negative error code
+ */
+ virtual int setInSampleRate(uint32_t rate);
+
+ /**
+ * \brief Set a new output sample rate
+ *
+ * Sets a new sample rate for the output side.
+ *
+ * \param rate New output rate
+ * \return 0 on success, otherwise negative error code
+ */
+ virtual int setOutSampleRate(uint32_t rate);
+
+ /**
+ * \brief Get resampling ratio
+ *
+ * Gets the numerator and denominator of the resampling ratio.
+ * The ratio is expressed as input_rate / output_rate, reduced
+ * to the least common denominator.
+ *
+ * \param num Reference for the numerator
+ * \param den Reference for the denominator
+ */
+ void getRatio(uint32_t &num, uint32_t &den) const;
+
+ /**
+ * \brief Test if this is an upsampler
+ *
+ * Tests whether or not this resampler is configured for upsampling.
+ *
+ * \return true if this is an upsampler, false otherwise
+ */
+ bool isUpsampler() const { return mRatioNum > mRatioDen; }
+
+ /**
+ * \brief Test if this is a downsampler
+ *
+ * Tests whether or not this resampler is configured for downsampling.
+ *
+ * \return true if this is an downsampler, false otherwise
+ */
+ bool isDownsampler() const { return mRatioNum < mRatioDen; }
+
+ /**
+ * \brief Resample from buffer provider to supplied buffer
+ *
+ * Resamples from the specified provider and outputs at most specified frame
+ * count into the passed buffer.
+ *
+ * \param provider Buffer provider to get the audio frames for resampling
+ * \param outBuffer Pointer to the buffer where resampled data will be stored
+ * \param outFrames Requested number of output frames
+ * \return 0 on success, otherwise negative error code
+ */
+ virtual int resample(BufferProvider &provider,
+ void *outBuffer,
+ uint32_t outFrames);
+
+ /**
+ * \brief Resample from passed buffers
+ *
+ * Resamples audio from the specified buffers. Requested input and output
+ * frames will be updated with the actual resampled frames.
+ *
+ * \param inBuffer Pointer to the buffer with data to be resampled
+ * \param inFrames As input, the requested number of input frames.
+ * As output, the actual number of resampled input frames
+ * \param outBuffer Pointer to the buffer with resampled data
+ * \param outFrames As input, the requested number of output frames.
+ * As output, the actual number of resampled output frames
+ */
+ virtual int resample(const void *inBuffer, uint32_t &inFrames,
+ void *outBuffer, uint32_t &outFrames);
+
+ static const uint32_t QualityVoIP = 3; /**< VoIP resampler quality */
+ static const uint32_t QualityDefault = 4; /**< Default resampler quality */
+ static const uint32_t QualityDesktop = 5; /**< Desktop resampler quality */
+ static const uint32_t QualityMax = 10; /**< Maximum resampler quality */
+
+ private:
+ Resampler(const Resampler &resampler);
+ Resampler& operator==(const Resampler &resampler);
+
+ protected:
+ /**
+ * \brief Create the Speex-based resampler
+ *
+ * Creates the Speex-based resampler with the sample rate of the input and
+ * output parameters.
+ */
+ void createResampler();
+
+ /**
+ * \brief Reallocate intermediate buffer to new frame count
+ *
+ * Reallocates intermediate buffer to store the specified number of frames.
+ * The intermediate buffer is used to store the audio data obtained from the
+ * buffer provider in the resampler() method.
+ * It's caller's responsibility to ensure the resize occurs only when current
+ * buffer is not large enough to hold new frame count.
+ */
+ void reallocateBuffer(uint32_t frames);
+
+ static const uint32_t kHeadRoomFrames = 10; /**< Extra frames to allocate for */
+
+ PcmParams mInParams; /**< PCM params of the input stream */
+ PcmParams mOutParams; /**< PCM params of the output stream */
+ BufferProvider::Buffer mBuffer; /**< Local buffer to store frames to be converted */
+ SpeexResamplerState *mSpeexRsmp; /**< Speex resampler */
+ uint32_t mQuality; /**< Quality of the Speex resampler */
+ uint32_t mAvail; /**< Number of frames in the buffer */
+ uint32_t mBufferFrames; /**< Buffer size, excluding the overhead room frames */
+ uint32_t mRatioNum; /**< Numerator of the sample rate ratio */
+ uint32_t mRatioDen; /**< Denominator of the sample rate ratio */
+ mutable Mutex mLock; /**< Access to the internal Speex resampler */
+};
+
+} /* namespace tiaudioutils */
+
+#endif /* _TIAUDIOUTILS_RESAMPLER_H */
diff --git a/audio/utils/include/tiaudioutils/SimpleStream.h b/audio/utils/include/tiaudioutils/SimpleStream.h
new file mode 100644
index 0000000..f9b6db7
--- /dev/null
+++ b/audio/utils/include/tiaudioutils/SimpleStream.h
@@ -0,0 +1,509 @@
+/*
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * 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.
+ */
+
+/**
+ * \file SimpleStream.h
+ * \brief Classes for writing and reading data to/from PCM ports
+ *
+ * Contains the SimpleReader, SimpleWriter and their corresponding streams.
+ * They are called "simple" because they are non-threaded and non-merge/unmerge
+ * capable.
+ * The underlying PCM ports are not tied to any specific card or port type,
+ * as long as they implement interfaces defined in Pcm.h.
+ */
+
+#ifndef _TIAUDIOUTILS_SIMPLESTREAM_H_
+#define _TIAUDIOUTILS_SIMPLESTREAM_H_
+
+#include <tiaudioutils/Pcm.h>
+
+namespace tiaudioutils {
+
+class SimpleWriter;
+class SimpleReader;
+class Resampler;
+
+/**
+ * \class SimpleInStream
+ * \brief PCM reader stream
+ *
+ * The stream with PCM data for capture.
+ */
+class SimpleInStream {
+ public:
+ /**
+ * \brief Simple PCM input constructor
+ *
+ * Constructs a PCM stream for the given parameters. Channel reorder
+ * is not possible with this type of stream.
+ *
+ * \param params PCM params for the stream
+ */
+ SimpleInStream(const PcmParams &params);
+
+ /**
+ * \brief Simple PCM input destructor
+ *
+ * Destroys the PCM stream.
+ */
+ virtual ~SimpleInStream();
+
+ /**
+ * \brief Check the result of constructing the simple PCM input
+ *
+ * Result of constructing a simple PCM input. It must be checked before
+ * using any methods. Result is undefined if the PCM input is
+ * used when in an unitialized state.
+ *
+ * \return true if PCM output is correct, false otherwise
+ */
+ virtual bool initCheck() const;
+
+ /**
+ * \brief Get the PcmParams of the stream
+ *
+ * Get the PcmParams used in this stream.
+ *
+ * \return Reference to the PCM params of the stream
+ */
+ const PcmParams& getParams() const { return mParams; }
+
+ /**
+ * \brief Start the stream
+ *
+ * Starts the stream for use. The PCM reader will open the underlying
+ * PCM port.
+ *
+ * \return 0 on success, otherwise negative error code
+ */
+ virtual int start();
+
+ /**
+ * \brief Stop the stream
+ *
+ * Stops the stream. The PCM reader will close the underlying PCM port.
+ */
+ virtual void stop();
+
+ /**
+ * \brief Test if the stream is started
+ *
+ * Tests whether the stream has been started or not.
+ *
+ * \return true if the stream is started, false if it's stopped
+ */
+ virtual bool isStarted() const;
+
+ /**
+ * \brief Read data from the stream
+ *
+ * Reads data from the stream which directly translates into reading from
+ * the PCM port. The stream must be started through start() before
+ * the stream is able to read.
+ *
+ * \param buffer Pointer to the destination buffer
+ * \param frames Number of frames to be read
+ * \return Number of frames read, otherwise negative error code
+ */
+ virtual int read(void *buffer, size_t frames);
+
+ friend class SimpleReader;
+
+ private:
+ SimpleInStream(const SimpleInStream &stream);
+ SimpleInStream& operator=(const SimpleInStream &stream);
+
+ protected:
+ PcmParams mParams; /**< PCM params of the stream */
+ SimpleReader *mReader; /**< PCM reader that the stream is registered to */
+ bool mStarted; /**< Whether the stream is started or not */
+ mutable Mutex mLock; /**< Synchronize stream start/stop */
+};
+
+/**
+ * \class SimpleOutStream
+ * \brief PCM writer stream
+ *
+ * The stream with PCM data for playback that can be used with SimpleWriter.
+ */
+class SimpleOutStream {
+ public:
+ /**
+ * \brief Simple PCM output stream constructor
+ *
+ * Constructs a PCM stream for the given parameters. Channel reorder
+ * is not possible with this type of stream.
+ *
+ * \param params PCM params for the stream
+ */
+ SimpleOutStream(const PcmParams &params);
+
+ /**
+ * \brief Simple PCM output stream destructor
+ *
+ * Destroys the PCM stream.
+ */
+ virtual ~SimpleOutStream();
+
+ /**
+ * \brief Check the result of constructing the simple PCM output
+ *
+ * Result of constructing a simple PCM output. It must be checked before
+ * using any methods. Result is undefined if the PCM output is
+ * used when in an unitialized state.
+ *
+ * \return true if PCM output is correct, false otherwise
+ */
+ virtual bool initCheck() const;
+
+ /**
+ * \brief Get the PcmParams of the stream
+ *
+ * Get the PcmParams used in this stream.
+ *
+ * \return Reference to the PCM params of the stream
+ */
+ const PcmParams& getParams() const { return mParams; }
+
+ /**
+ * \brief Start the stream
+ *
+ * Starts the stream for use. The PCM writer will open the underlying
+ * PCM port.
+ *
+ * \return 0 on success, otherwise negative error code
+ */
+ virtual int start();
+
+ /**
+ * \brief Stop the stream
+ *
+ * Stops the stream. The PCM writer will close the underlying PCM port.
+ */
+ virtual void stop();
+
+ /**
+ * \brief Test if the stream is started
+ *
+ * Tests whether the stream has been started or not.
+ *
+ * \return true if the stream is started, false if it's stopped
+ */
+ virtual bool isStarted() const;
+
+ /**
+ * \brief Write data to the stream
+ *
+ * Writes data to the stream which directly translates into writing to
+ * the PCM port. The stream must be started through start() before
+ * the stream is able to write.
+ *
+ * \param buffer Pointer to the source buffer
+ * \param frames Number of frames to be written
+ * \return Number of frames written, otherwise negative error code
+ */
+ virtual int write(const void *buffer, size_t frames);
+
+ friend class SimpleWriter;
+
+ private:
+ SimpleOutStream(const SimpleOutStream &stream);
+ SimpleOutStream& operator=(const SimpleOutStream &stream);
+
+ protected:
+ PcmParams mParams; /**< PCM params of the stream */
+ SimpleWriter *mWriter; /**< PCM writer that the stream is registered to */
+ bool mStarted; /**< Whether the stream is started or not */
+ mutable Mutex mLock; /**< Synchronize stream start/stop */
+};
+
+/**
+ * \class SimpleReader
+ * \brief Simple PCM reader
+ *
+ * Basic PCM reader where a single stream is recorded from a capture port.
+ * There is no merge capability or buffer adaptation.
+ */
+class SimpleReader {
+ public:
+ /**
+ * \brief Simple PCM reader constructor
+ *
+ * Constructs a simple PCM reader for a given capture port
+ *
+ * \param port Pointer to the PCM port to read from
+ * \param params PCM parameters to be used on the port
+ */
+ SimpleReader(PcmInPort *port, const PcmParams &params);
+
+ /**
+ * \brief Simple PCM reader destructor
+ *
+ * Destroys a simple PCM reader object.
+ */
+ virtual ~SimpleReader();
+
+ /**
+ * \brief Check the result of constructing the simple PCM reader
+ *
+ * Result of constructing a simple PCM reader. It must be checked before
+ * using any methods. Result is undefined if the PCM simple reader is
+ * used when in an unitialized state.
+ *
+ * \return true if PCM reader construction is correct, false otherwise
+ */
+ virtual bool initCheck() const;
+
+ /**
+ * \brief Get the PcmParams of the PCM reader
+ *
+ * Get the PcmParams used in this PCM reader, that is, the parameters
+ * used to open the underlying PCM capture port.
+ *
+ * \return Reference to the PCM params of the reader
+ */
+ const PcmParams& getParams() const { return mParams; }
+
+ /**
+ * \brief Register a stream
+ *
+ * Registers a stream to the PCM reader. A single stream can be registered
+ * to this type of PCM reader.
+ *
+ * \param stream PCM stream to be registered
+ * \return 0 on success, otherwise negative error code
+ */
+ virtual int registerStream(SimpleInStream *stream);
+
+ /**
+ * \brief Unregister a PCM stream
+ *
+ * Unregisters a stream from the PCM reader. The stream must have
+ * been previously registered using registerStream().
+ *
+ * \param stream PCM stream to be unregistered
+ */
+ virtual void unregisterStream(SimpleInStream *stream);
+
+ friend class SimpleInStream;
+
+ private:
+ SimpleReader(const SimpleReader &reader);
+ SimpleReader& operator=(const SimpleReader &reader);
+
+ protected:
+ /**
+ * \brief Open the PCM reader
+ *
+ * Opens the underlying PCM capture port. This method is expected
+ * to be called by the registered stream in its start() call.
+ *
+ * \return 0 on success, otherwise negative error code
+ */
+ virtual int open();
+
+ /**
+ * \brief Close the PCM reader
+ *
+ * Closes the underlying PCM capture port. This method is expected
+ * to be called by the registered stream in its stop() call.
+ */
+ virtual void close();
+
+ /**
+ * \brief Read data from the PCM capture port
+ *
+ * Read audio data from the PCM capture port.
+ *
+ * \param buffer Pointer to the destination buffer
+ * \param frames Number of frames to be read
+ * \return Number of frames read, otherwise negative error code
+ */
+ virtual int read(void *buffer, size_t frames);
+
+ /**
+ * \class ReadProvider
+ * \brief PCM read provider
+ *
+ * Buffer provider that delivers buffers filled with data read from
+ * the PCM input port. It's used as the buffer provider for the
+ * resampler to ensure that it delivers the requested number of frames.
+ */
+ class ReadProvider : public BufferProvider {
+ public:
+ /**
+ * Constructs a buffer provider that captures data from the PCM
+ * port set in the specified reader, uses its resampler and
+ * intermediate buffer to return the resampled data.
+ *
+ * \param reader Reader that has the resampler and intermediate buffer
+ */
+ ReadProvider(SimpleReader &reader) : mReader(reader) {}
+
+ /**
+ * Destroys the PCM capture buffer provider
+ */
+ virtual ~ReadProvider() {}
+
+ /**
+ * \brief Get the next buffer with new PCM input data
+ *
+ * Gets the next audio buffer with new PCM data read from the
+ * port used by the reader.
+ *
+ * \param buffer Pointer to the buffer to fill with next buffer's info
+ * \return 0 on success, otherwise negative error code
+ */
+ int getNextBuffer(BufferProvider::Buffer *buffer);
+
+ /**
+ * \brief Release the buffer from the stream
+ *
+ * Release the audio buffer previously obtained through getNextBuffer().
+ * It's a logical error to release a buffer not previously obtained,
+ * result is undefined.
+ *
+ * \param buffer Pointer to the buffer to be released
+ */
+ void releaseBuffer(BufferProvider::Buffer *buffer);
+ protected:
+ SimpleReader &mReader; /**< Reader that has the resampler and buffer */
+ };
+
+ PcmInPort *mPort; /**< PCM in port that produces stream data */
+ PcmParams mParams; /**< PCM params of the port */
+ SimpleInStream *mStream; /**< The registered stream */
+ Resampler *mResampler; /**< Resampler if needed */
+ BufferProvider::Buffer mBuffer; /**< Intermediate buffer for data to be resampled */
+ ReadProvider mBufferProvider; /**< Buffer provider used for resampling */
+ Mutex mLock; /**< Used to synchronize methods of the reader */
+};
+
+/**
+ * \class SimpleWriter
+ * \brief Simple PCM writer
+ *
+ * Basic PCM writer where a single stream is played to a playback port.
+ * There is no merge capability or buffer adaptation.
+ */
+class SimpleWriter {
+ public:
+ /**
+ * \brief Simple PCM writer constructor
+ *
+ * Constructs a simple PCM writer for a given playback port
+ *
+ * \param port Pointer to the PCM port to write to
+ * \param params PCM parameters to be used on the port
+ */
+ SimpleWriter(PcmOutPort *port, const PcmParams &params);
+
+ /**
+ * \brief Simple PCM writer destructor
+ *
+ * Destroys a simple PCM writer object.
+ */
+ virtual ~SimpleWriter();
+
+ /**
+ * \brief Check the result of constructing the simple PCM writer
+ *
+ * Result of constructing a simple PCM writer. It must be checked before
+ * using any methods. Result is undefined if the PCM simple writer is
+ * used when in an unitialized state.
+ *
+ * \return true if PCM writer construction is correct, false otherwise
+ */
+ virtual bool initCheck() const;
+
+ /**
+ * \brief Get the PcmParams of the PCM writer
+ *
+ * Get the PcmParams used in this PCM writer, that is, the parameters
+ * used to open the underlying PCM playback port.
+ *
+ * \return Reference to the PCM params of the reader
+ */
+ const PcmParams& getParams() const { return mParams; }
+
+ /**
+ * \brief Register a stream
+ *
+ * Registers a stream to the PCM writer. A single stream can be registered
+ * to this type of PCM writer.
+ *
+ * \param stream PCM stream to be registered
+ * \return 0 on success, otherwise negative error code
+ */
+ virtual int registerStream(SimpleOutStream *stream);
+
+ /**
+ * \brief Unregister a PCM stream
+ *
+ * Unregisters a stream from the PCM writer. The stream must have
+ * been previously registered using registerStream().
+ *
+ * \param stream PCM stream to be unregistered
+ */
+ virtual void unregisterStream(SimpleOutStream *stream);
+
+ friend class SimpleOutStream;
+
+ private:
+ SimpleWriter(const SimpleWriter &writer);
+ SimpleWriter& operator=(const SimpleWriter &writer);
+
+ protected:
+ /**
+ * \brief Open the PCM writer
+ *
+ * Opens the underlying PCM playback port. This method is expected
+ * to be called by the registered stream in its start() call.
+ *
+ * \return 0 on success, otherwise negative error code
+ */
+ virtual int open();
+
+ /**
+ * \brief Close the PCM writer
+ *
+ * Closes the underlying PCM playback port. This method is expected
+ * to be called by the registered stream in its stop() call.
+ */
+ virtual void close();
+
+ /**
+ * \brief Write data to the PCM playback port
+ *
+ * Write audio data to the PCM playback port.
+ *
+ * \param buffer Pointer to the source buffer
+ * \param frames Number of frames to be written
+ * \return Number of frames written, otherwise negative error code
+ */
+ virtual int write(const void *buffer, size_t frames);
+
+ PcmOutPort *mPort; /**< PCM out port that consumes stream data */
+ PcmParams mParams; /**< PCM params of the port */
+ SimpleOutStream *mStream; /**< The registered stream */
+ Resampler *mResampler; /**< Resampler if needed */
+ BufferProvider::Buffer mBuffer; /**< Intermediate buffer for resampled data */
+ Mutex mLock; /**< Used to synchronize methods of the writer */
+};
+
+} /* namespace tiaudioutils */
+
+#endif /* _TIAUDIOUTILS_SIMPLESTREAM_H_ */
diff --git a/audio/utils/include/tiaudioutils/Stream.h b/audio/utils/include/tiaudioutils/Stream.h
new file mode 100644
index 0000000..e81bce9
--- /dev/null
+++ b/audio/utils/include/tiaudioutils/Stream.h
@@ -0,0 +1,1072 @@
+/*
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * 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.
+ */
+
+/**
+ * \file Stream.h
+ * \brief Classes for writing and reading data to/from PCM ports
+ *
+ * Contains the PcmReader, PcmWriter and their corresponding streams.
+ * The reader is capable of unmerging the incoming buffers for the
+ * PCM port into multiple streams. Similarly, the writer is capable of
+ * merging multiple streams into a single one that goes to the PCM port.
+ * The underlying PCM ports are not tied to any
+ * specific card or port type, as long as they implement interfaces defined
+ * in Pcm.h.
+ */
+
+#ifndef _TIAUDIOUTILS_STREAM_H_
+#define _TIAUDIOUTILS_STREAM_H_
+
+#include <semaphore.h>
+#include <set>
+
+#include <tiaudioutils/Base.h>
+#include <tiaudioutils/Pcm.h>
+#include <tiaudioutils/MumStream.h>
+#include <tiaudioutils/MonoPipe.h>
+
+namespace tiaudioutils {
+
+using std::set;
+using android::RefBase;
+
+class PcmWriter;
+class PcmReader;
+class Resampler;
+
+/**
+ * \class BufferAdaptor
+ * \brief Buffer Adaptor
+ *
+ * Single producer, single consumer buffer adaptor that converts push-type
+ * calls (write()) to pull-type ones (getNextBuffer(), releaseBuffer()).
+ * There is no data copied during the conversion.
+ */
+class BufferAdaptor : public BufferProvider {
+ public:
+ /**
+ * \brief Buffer adaptor constructor
+ *
+ * Constructs a single producer, single consumer buffer adaptor.
+ */
+ BufferAdaptor();
+
+ /**
+ * \brief Buffer adaptor destructor
+ *
+ * Destroys a buffer adaptor object.
+ */
+ virtual ~BufferAdaptor();
+
+ /**
+ * \brief Check the result of constructing a buffer adaptor.
+ *
+ * Result of constructing a buffer adaptor. It must be checked before
+ * using any buffer adaptor methods. Result is undefined otherwise.
+ *
+ * \return true if buffer adaptor construction is correct, false otherwise
+ */
+ virtual bool initCheck() const { return true; }
+
+ /**
+ * \brief Push side of the read adaptor
+ *
+ * Audio streams using the push model can use this method as they would
+ * normally do to read data from a buffer. The call blocks until the other
+ * side of the adaptor calls getNextBuffer() and releaseBuffer().
+ *
+ * \param buffer Pointer to the audio buffer
+ * \param frames Number of frames to be read
+ * \return Number of frames read, otherwise negative error code
+ */
+ virtual int read(void * const buffer, size_t frames);
+
+ /**
+ * \brief Push side of the write adaptor
+ *
+ * Audio streams using the push model can use this method as they would
+ * normally do to write data to a buffer. The call blocks until the other
+ * side of the adaptor calls getNextBuffer() and releaseBuffer().
+ *
+ * \param buffer Pointer to the audio buffer
+ * \param frames Number of frames to be written
+ * \return Number of frames written, otherwise negative error code
+ */
+ virtual int write(const void *buffer, size_t frames);
+
+ /**
+ * \brief Pull side side of the adaptor (acquire part)
+ *
+ * Audio outputs using the pull model can use this method as they would
+ * normally do to get the next available buffer. The call blocks until
+ * the other side of the adaptor calls write().
+ *
+ * \param buffer Pointer to the Buffer to be filled up
+ * \return 0 on success, otherwise negative error code
+ */
+ virtual int getNextBuffer(BufferProvider::Buffer* buffer);
+
+ /**
+ * \brief Pull side side of the adaptor (release part)
+ *
+ * Audio outputs using the pull model can use this method as they would
+ * normally do to release a buffer previously acquired through
+ * getNextBuffer().
+ * The call unblocks write().
+ *
+ * \param buffer Pointer to the Buffer to be released
+ */
+ virtual void releaseBuffer(BufferProvider::Buffer* buffer);
+
+ /**
+ * \brief Retrieve the number of times that getNextBuffer did not get a
+ * buffer
+ *
+ * The getNextBuffer call performs a sem_trywait() instead of a sem_wait()
+ * in order to avoid blocking forever in case no buffers are given.
+ * When sem_trywait() returns without getting a next buffer, the event is
+ * recorded. The number of times this happens can be retrieved by using
+ * getNumUnderruns().
+ */
+ virtual uint32_t getNumUnderruns() const { return mUnderRuns; }
+
+ protected:
+ const void *mBuffer; /**< Buffer pointer to be passed pull side */
+ size_t mFrames; /**< Number of frames to be passed to pull side */
+ sem_t mFill; /**< Semaphore to indicate a new buffer has been
+ pushed */
+ sem_t mEmpty; /**< Semaphore to indicate readiness for a new buffer
+ to be pushed */
+ sem_t mDone; /**< Semaphore to indicate the pushed buffer has been
+ processed */
+ int mStatus; /**< Return value for read() / write() */
+ Mutex mMutex; /**< Local mutex protection */
+ uint32_t mUnderRuns; /**< Number of times getNextBuffer( did not get a
+ buffer */
+};
+
+/**
+ * \class InStream
+ * \brief PCM reader stream
+ *
+ * The stream with PCM data for capture.
+ */
+class InStream : public RefBase, public BufferProvider {
+ public:
+ /**
+ * \brief PCM input stream constructor from parameters
+ *
+ * Constructs a PCM stream for the given parameters. Slot map is
+ * created based on the number of channels in those parameters,
+ * assuming that the N channels are located in the first N slots.
+ *
+ * \param params PCM params for the stream
+ * \param provider The buffer provider associated with the stream.
+ */
+ InStream(const PcmParams &params,
+ BufferProvider *provider = NULL);
+
+ /**
+ * \brief PCM input stream constructor with specific slot map
+ *
+ * Constructs a PCM stream with requested parameters and using a
+ * specific slot map.
+ *
+ * \param params PCM params for the stream
+ * \param map Slot map to be used when registered to the reader
+ * \param provider The buffer provider associated with the stream.
+ */
+ InStream(const PcmParams &params,
+ const SlotMap &map,
+ BufferProvider *provider = NULL);
+
+ /**
+ * \brief Destructor for a PCM input stream
+ *
+ * Destroys a PCM reader stream object.
+ */
+ virtual ~InStream();
+
+ /**
+ * \brief Check the result of constructing an input stream
+ *
+ * Result of constructing an input stream. It must be checked before
+ * using any stream methods. Result is undefined otherwise.
+ *
+ * \return true if stream construction is correct, false otherwise
+ */
+ virtual bool initCheck() const;
+
+ /**
+ * \brief Get the PcmParams of the stream
+ *
+ * Get the PcmParams used in this stream.
+ *
+ * \return Reference to the PCM params of the stream
+ */
+ const PcmParams& getParams() const { return mParams; }
+
+ /**
+ * \brief Get the slot map of the stream
+ *
+ * Get the SlotMap used in this stream.
+ *
+ * \return Reference to the slot map of the stream
+ */
+ const SlotMap& getSlotMap() const { return mMap; }
+
+ /**
+ * \brief Test if the stream can resample
+ *
+ * Tests if this stream is capable of sample rate conversion.
+ *
+ * \return true if stream can resample, false otherwise
+ */
+ virtual bool canResample() const { return mResampler != NULL; }
+
+ /**
+ * \brief Get the next buffer from the stream
+ *
+ * Gets the next audio buffer from the buffer provider associated
+ * with the stream, it can be a provider set during construction or
+ * a buffer adaptor.
+ *
+ * \param buffer Pointer to the buffer to fill with next buffer's info
+ * \return 0 on success, otherwise negative error code
+ */
+ int getNextBuffer(BufferProvider::Buffer *buffer);
+
+ /**
+ * \brief Release the buffer from the stream
+ *
+ * Release the audio buffer previously obtained through getNextBuffer().
+ * It's a logical error to release a buffer not previously obtained,
+ * result is undefined.
+ *
+ * \param buffer Pointer to the buffer to be released
+ */
+ void releaseBuffer(BufferProvider::Buffer *buffer);
+
+ /**
+ * \brief Start the stream
+ *
+ * Starts the stream for use. If this stream is the first active stream
+ * in the PCM reader, the PCM reader will open the underlying port.
+ *
+ * \return 0 on success, otherwise negative error code
+ */
+ virtual int start();
+
+ /**
+ * \brief Stop the stream
+ *
+ * Stops the stream. If this stream is the last active stream in the
+ * PCM reader, the PCM reader will close the underlying port.
+ */
+ virtual void stop();
+
+ /**
+ * \brief Test if the stream is started
+ *
+ * Tests whether the stream has been started or not.
+ *
+ * \return true if the stream is started, false if it's stopped
+ */
+ virtual bool isStarted() const;
+
+ /**
+ * \brief Read data from the stream
+ *
+ * The base PCM input stream does not support read method.
+ *
+ * \param buffer Pointer to the destination buffer
+ * \param frames Number of frames to be read
+ * \return Returns -ENOTSUP
+ */
+ virtual int read(void *buffer, size_t frames);
+
+ friend class PcmReader;
+
+ private:
+ InStream(const InStream &stream);
+ InStream& operator=(const InStream &stream);
+
+ protected:
+ /**
+ * \brief Get next audio buffer to be resampled
+ *
+ * Gets the next audio buffer to be resampled. This method is used
+ * only when the stream has a resampled attached.
+ *
+ * \param buffer Pointer to the buffer to fill with next buffer's info
+ * \return 0 on success, otherwise negative error code
+ */
+ int getNextBufferForResample(BufferProvider::Buffer *buffer);
+
+ /**
+ * \brief Release the buffer with resampled data
+ *
+ * Release the audio buffer with resampled data. The buffer must be
+ * previously obtained through getNextBufferForResample().
+ * It's a logical error to release a buffer not previously obtained,
+ * result is undefined.
+ *
+ * \param buffer Pointer to the buffer to be released
+ */
+ void releaseResampledBuffer(BufferProvider::Buffer *buffer);
+
+
+ BufferProvider *mBufferProvider; /**< Buffer provider */
+ PcmParams mParams; /**< PCM parameters used by this stream */
+ SlotMap mMap; /**< Slot map for the channels of this stream */
+ PcmReader *mReader; /**< PCM reader that the stream is registered to */
+ Resampler *mResampler; /**< Resampler set by the reader when stream is registered */
+ BufferProvider::Buffer mRsmpBuffer; /**< Intermediate buffer with data to be resampled */
+ bool mStarted; /**< Whether the stream is started or not */
+ mutable Mutex mLock; /**< Synchronize stream start/stop */
+};
+
+/**
+ * \class AdaptedInStream
+ * \brief PCM reader adapted stream
+ *
+ * The stream with PCM data for capture. The stream uses a buffer adaptor
+ * so it can accept read() calls on the consumer side and seamlessly
+ * connect with the PcmReader that expects getNextBuffer() and releaseBuffer().
+ */
+class AdaptedInStream : public InStream {
+ public:
+ /**
+ * \brief Adapted PCM input stream constructor from parameters
+ *
+ * Constructs an adapted PCM stream for the given parameters. Slot map
+ * is created based on the number of channels in those parameters,
+ * assuming that the N channels are located in the first N slots.
+ *
+ * \param params PCM params for the stream
+ */
+ AdaptedInStream(const PcmParams &params);
+
+ /**
+ * \brief Adapted PCM input stream constructor with specific slot map
+ *
+ * Constructs an adapted PCM stream with requested parameters and using
+ * a specific slot map.
+ *
+ * \param params PCM params for the stream
+ * \param map Slot map to be used when registered to the reader
+ */
+ AdaptedInStream(const PcmParams &params,
+ const SlotMap &map);
+
+ /**
+ * \brief Destructor for an adapted PCM input stream
+ *
+ * Destroys a PCM reader adapted stream object.
+ */
+ virtual ~AdaptedInStream() {}
+
+ /**
+ * \brief Read data from the stream using a buffer adaptor
+ *
+ * Reads data from the stream using a buffer adaptor. The owner of
+ * the stream can use this method while the PcmReader or UnMerge can
+ * use the other side of the adaptor that exposes getNextBuffer()
+ * and releaseBuffer() methods.
+ *
+ * \param buffer Pointer to the destination buffer
+ * \param frames Number of frames to be read
+ * \return Number of frames read, otherwise negative error code.
+ */
+ int read(void *buffer, size_t frames);
+
+ protected:
+ BufferAdaptor mAdaptor; /**< Buffer adaptor needed for read() */
+};
+
+/**
+ * \class BufferedInStream
+ * \brief PCM reader buffered stream
+ *
+ * The stream with PCM data for capture. The stream uses a pipe so it
+ * can accept read() calls on the consumer side and seamlessly connect
+ * with the PcmReader that expects getNextBuffer() and releaseBuffer().
+ */
+class BufferedInStream : public InStream {
+ public:
+ /**
+ * \brief Buffered PCM input stream constructor from parameters
+ *
+ * Constructs a buffered PCM stream for the given parameters. Slot map
+ * is created based on the number of channels in those parameters,
+ * assuming that the N channels are located in the first N slots.
+ *
+ * \param params PCM params for the stream
+ * \param frames Size of the buffer in frames. If not specified, uses
+ * the MonoPipe default size which is 3x the frame count
+ * in the PCM params
+ */
+ BufferedInStream(const PcmParams &params,
+ uint32_t frames = 0);
+
+ /**
+ * \brief Buffered PCM input stream constructor with specific slot map
+ *
+ * Constructs a buffered PCM stream with requested parameters and using
+ * a specific slot map.
+ *
+ * \param params PCM params for the stream
+ * \param map Slot map to be used when registered to the reader
+ * \param frames Size of the buffer in frames. If not specified, uses
+ * the MonoPipe default size which is 3x the frame count
+ * in the PCM params
+ */
+ BufferedInStream(const PcmParams &params,
+ const SlotMap &map,
+ uint32_t frames = 0);
+
+ /**
+ * \brief Destructor for an buffered PCM input stream
+ *
+ * Destroys a PCM reader buffered stream object.
+ */
+ virtual ~BufferedInStream() {}
+
+ /**
+ * \brief Check the result of constructing an input stream
+ *
+ * Result of constructing a buffered input stream. It must be checked
+ * before using any stream methods. Result is undefined otherwise.
+ *
+ * \return true if stream construction is correct, false otherwise
+ */
+ bool initCheck() const;
+
+ /**
+ * \brief Read data from the stream using a pipe
+ *
+ * Reads data from the stream using a mono pipe. The owner of the
+ * stream can use this method while the PcmReader or UnMerge can
+ * write to the pipe using a PipeWriter.
+ *
+ * \param buffer Pointer to the destination buffer
+ * \param frames Number of frames to be read
+ * \return Number of frames read, otherwise negative error code.
+ */
+ int read(void *buffer, size_t frames);
+
+ protected:
+ MonoPipe mPipe; /**< Mono pipe between producer and consumer */
+ PipeWriter mPipeWriter; /**< BufferProvider used to write to the pipe */
+};
+
+/**
+ * \class OutStream
+ * \brief PCM writer stream
+ *
+ * The stream with PCM data for playback.
+ */
+class OutStream : public RefBase, public BufferProvider {
+ public:
+ /**
+ * \brief PCM output stream constructor from parameters
+ *
+ * Constructs a PCM stream for the given parameters. Slot map is
+ * created based on the number of channels in those parameters,
+ * assuming that the N channels are located in the first N slots.
+ *
+ * \param params PCM params for the stream
+ * \param provider The buffer provider associated with the stream.
+ * If none is specified, it will use a BufferAdaptor.
+ */
+ OutStream(const PcmParams &params,
+ BufferProvider *provider = NULL);
+
+ /**
+ * \brief PCM output stream constructor with specific slot map
+ *
+ * Constructs a PCM stream with requested parameters and using a
+ * specific slot map.
+ *
+ * \param params PCM params for the stream
+ * \param map Slot map to be used when registered to the writer
+ * \param provider The buffer provider associated with the stream.
+ * If none is specified, it will use a BufferAdaptor.
+ */
+ OutStream(const PcmParams &params,
+ const SlotMap &map,
+ BufferProvider *provider = NULL);
+
+ /**
+ * \brief Destructor for a PCM output stream
+ *
+ * Destroys a PCM writer stream object.
+ */
+ virtual ~OutStream();
+
+ /**
+ * \brief Check the result of constructing the stream
+ *
+ * Result of constructing an OutStream. It must be checked before using
+ * any stream methods. Result is undefined otherwise.
+ *
+ * \return true if stream construction is correct, false otherwise
+ */
+ virtual bool initCheck() const;
+
+ /**
+ * \brief Get the PcmParams of the stream
+ *
+ * Get the PcmParams used in this stream.
+ *
+ * \return Reference to the PCM params of the stream
+ */
+ const PcmParams& getParams() const { return mParams; }
+
+ /**
+ * \brief Get the slot map of the stream
+ *
+ * Get the SlotMap used in this stream.
+ *
+ * \return Reference to the slot map of the stream
+ */
+ const SlotMap& getSlotMap() const { return mMap; }
+
+ /**
+ * \brief Test if the stream can resample
+ *
+ * Tests if this stream is capable of sample rate conversion.
+ *
+ * \return true if stream can resample, false otherwise
+ */
+ virtual bool canResample() const { return mResampler != NULL; }
+
+ /**
+ * \brief Get the next buffer from the stream
+ *
+ * Gets the next audio buffer from the buffer provider associated
+ * with the stream, it can be a provider set during construction or
+ * a buffer adaptor.
+ *
+ * \param buffer Pointer to the buffer to fill with next buffer's info
+ * \return 0 on success, otherwise negative error code
+ */
+ int getNextBuffer(BufferProvider::Buffer *buffer);
+
+ /**
+ * \brief Release the buffer from the stream
+ *
+ * Release the audio buffer previously obtained through getNextBuffer().
+ * It's a logical error to release a buffer not previously obtained,
+ * result is undefined.
+ *
+ * \param buffer Pointer to the buffer to be released
+ */
+ void releaseBuffer(BufferProvider::Buffer *buffer);
+
+ /**
+ * \brief Start the stream
+ *
+ * Starts the stream for use. If this stream is the first active stream
+ * in the PCM writer, the PCM writer will open the underlying port.
+ *
+ * \return 0 on success, otherwise negative error code
+ */
+ virtual int start();
+
+ /**
+ * \brief Stop the stream
+ *
+ * Stops the stream. If this stream is the last active stream in the
+ * PCM writer, the PCM writer will close the underlying port.
+ */
+ virtual void stop();
+
+ /**
+ * \brief Test if the stream is started
+ *
+ * Tests whether the stream has been started or not.
+ *
+ * \return true if the stream is started, false if it's stopped
+ */
+ virtual bool isStarted() const;
+
+ /**
+ * \brief Write data to the stream
+ *
+ * The base PCM output stream does not support write method.
+ *
+ * \param buffer Pointer to the source buffer
+ * \param frames Number of frames to be written
+ * \return Returns -ENOTSUP
+ */
+ virtual int write(const void *buffer, size_t frames);
+
+ friend class PcmWriter;
+
+ private:
+ OutStream(const OutStream &stream);
+ OutStream& operator=(const OutStream &stream);
+
+ protected:
+ /**
+ * \brief Get the next buffer from the stream and resample
+ *
+ * Gets the next audio buffer from the buffer provider associated
+ * with the stream and resamples it. Regular releaseBuffer() call
+ * can be used to release the resampled buffer.
+ *
+ * \param buffer Pointer to the buffer to fill with next buffer's info
+ * \return 0 on success, otherwise negative error code
+ */
+ int getNextResampledBuffer(BufferProvider::Buffer *buffer);
+
+ BufferProvider *mBufferProvider; /**< Buffer provider */
+ PcmParams mParams; /**< PCM parameters used by this stream */
+ SlotMap mMap; /**< Slot map for the channels of this stream */
+ PcmWriter *mWriter; /**< PCM writer that the stream is registered to */
+ Resampler *mResampler; /**< Resampler set by the writer when stream is registered */
+ BufferProvider::Buffer mRsmpBuffer; /**< Intermediate buffer with resampled data */
+ bool mStarted; /**< Whether the stream is started or not */
+ mutable Mutex mLock; /**< Synchronize stream start/stop */
+};
+
+/**
+ * \class AdaptedOutStream
+ * \brief PCM writer adapted stream
+ *
+ * The stream with PCM data for playback. The stream uses a buffer adaptor
+ * so it can accept write() calls on the producer side and seamlessly
+ * connect with the PcmWriter that expects getNextBuffer() and releaseBuffer().
+ */
+class AdaptedOutStream : public OutStream {
+ public:
+ /**
+ * \brief Adapted PCM output stream constructor from parameters
+ *
+ * Constructs an adapted PCM stream for the given parameters. Slot map
+ * is created based on the number of channels in those parameters,
+ * assuming that the N channels are located in the first N slots.
+ *
+ * \param params PCM params for the stream
+ */
+ AdaptedOutStream(const PcmParams &params);
+
+ /**
+ * \brief Adapted PCM output stream constructor with specific slot map
+ *
+ * Constructs an adapted PCM stream with requested parameters and using
+ * a specific slot map.
+ *
+ * \param params PCM params for the stream
+ * \param map Slot map to be used when registered to the writer
+ */
+ AdaptedOutStream(const PcmParams &params,
+ const SlotMap &map);
+
+ /**
+ * \brief Destructor for an adapted PCM output stream
+ *
+ * Destroys a PCM writer adapted stream object.
+ */
+ virtual ~AdaptedOutStream() {}
+
+ /**
+ * \brief Write data to the stream using a buffer adaptor
+ *
+ * Writes data to the stream using a buffer adaptor. The owner of
+ * the stream can use this method while the PcmWriter or Merge can
+ * use the other side of the adaptor that exposes getNextBuffer()
+ * and releaseBuffer() methods.
+ *
+ * \param buffer Pointer to the source buffer
+ * \param frames Number of frames to be written
+ * \return Number of frames written, otherwise negative error code.
+ */
+ int write(const void *buffer, size_t frames);
+
+ protected:
+ BufferAdaptor mAdaptor; /**< Buffer adaptor needed for write() */
+};
+
+/**
+ * \class BufferedOutStream
+ * \brief PCM writer buffered stream
+ *
+ * The stream with PCM data for playback. The stream uses a pipe so it
+ * can accept write() calls on the producer side and seamlessly connect
+ * with the PcmWriter that expects getNextBuffer() and releaseBuffer().
+ */
+class BufferedOutStream : public OutStream {
+ public:
+ /**
+ * \brief Buffered PCM output stream constructor from parameters
+ *
+ * Constructs a buffered PCM stream for the given parameters. Slot map
+ * is created based on the number of channels in those parameters,
+ * assuming that the N channels are located in the first N slots.
+ *
+ * \param params PCM params for the stream
+ * \param frames Size of the buffer in frames. If not specified, uses
+ * the MonoPipe default size which is 3x the frame count
+ * in the PCM params
+ */
+ BufferedOutStream(const PcmParams &params,
+ uint32_t frames = 0);
+
+ /**
+ * \brief Buffered PCM output stream constructor with specific slot map
+ *
+ * Constructs a buffered PCM stream with requested parameters and using
+ * a specific slot map.
+ *
+ * \param params PCM params for the stream
+ * \param frames Size of the buffer in frames. If not specified, uses
+ * the MonoPipe default size which is 3x the frame count
+ * in the PCM params
+ * \param map Slot map to be used when registered to the writer
+ */
+ BufferedOutStream(const PcmParams &params,
+ const SlotMap &map,
+ uint32_t frames = 0);
+
+ /**
+ * \brief Destructor for an buffered PCM output stream
+ *
+ * Destroys a PCM writer buffered stream object.
+ */
+ virtual ~BufferedOutStream() {}
+
+ /**
+ * \brief Check the result of constructing the stream
+ *
+ * Result of constructing a BufferedOutStream. It must be checked before
+ * using any stream methods. Result is undefined otherwise.
+ *
+ * \return true if stream construction is correct, false otherwise
+ */
+ bool initCheck() const;
+
+ /**
+ * \brief Write data to the stream using a pipe
+ *
+ * Writes data to the stream using a mono pipe. The owner of the
+ * stream can use this method while the PcmWriter or Merge can
+ * read from the pipe using a PipeReader.
+ *
+ * \param buffer Pointer to the source buffer
+ * \param frames Number of frames to be written
+ * \return Number of frames written, otherwise negative error code.
+ */
+ int write(const void *buffer, size_t frames);
+
+ protected:
+ MonoPipe mPipe; /**< Mono pipe between producer and consumer */
+ PipeReader mPipeReader; /**< BufferProvider used to read from the pipe */
+};
+
+/**
+ * \class PcmReader
+ * \brief PCM reader
+ *
+ * PCM reader with unmerge capability. This reader captures data from the
+ * PCM in port, unmerges and produces data for the multiple streams that
+ * are registered.
+ */
+class PcmReader : protected ThreadBase {
+ public:
+
+ /**
+ * \brief PCM reader constructor
+ *
+ * Constructs a PCM reader for a given capture port. The reader
+ * captures data from the PCM port, unmerges and produces data for the
+ * registered streams.
+ *
+ * \param port Pointer to the PCM port to read from
+ * \param params PCM parameters to be used on the port
+ */
+ PcmReader(PcmInPort *port, const PcmParams &params);
+
+ /**
+ * \brief PCM reader destructor
+ *
+ * Destroys a PCM reader object.
+ */
+ virtual ~PcmReader();
+
+ /**
+ * \brief Check the result of constructing the PCM reader
+ *
+ * Result of constructing a PCM reader. It must be checked before
+ * using any methods. Result is undefined if the PCM reader is
+ * used when in an unitialized state.
+ *
+ * \return true if PCM construction is correct, false otherwise
+ */
+ virtual bool initCheck() const;
+
+ /**
+ * \brief Get the PcmParams of the PCM reader
+ *
+ * Get the PcmParams used in this PCM reader, that is, the parameters
+ * used to open the underlying PCM capture port.
+ *
+ * \return Reference to the PCM params of the reader
+ */
+ const PcmParams& getParams() const { return mParams; }
+
+ /**
+ * \brief Register a stream
+ *
+ * Registers a stream to the PCM reader. Multiple streams can be
+ * registered to the reader, each one is associated with one or more
+ * slots of the input stream that is read from the PCM in port.
+ *
+ * \param stream PCM stream to be registered
+ * \return 0 on success, otherwise negative error code
+ */
+ virtual int registerStream(sp<InStream>& stream);
+
+ /**
+ * \brief Unregister a PCM stream
+ *
+ * Unregisters a stream from the PCM reader. The stream must have
+ * been previously registered using registerStream().
+ *
+ * \param stream PCM stream to be unregistered
+ */
+ virtual void unregisterStream(sp<InStream>& stream);
+
+ friend class InStream;
+
+ private:
+ PcmReader(const PcmReader &reader);
+ PcmReader& operator=(const PcmReader &reader);
+
+ protected:
+ /**
+ * \brief Open the PCM reader
+ *
+ * Opens the underlying PCM capture port. This method is expected
+ * to be called by registered streams in their start() call.
+ * Calls to this method are counted so that the PCM port is open
+ * only when the first stream is started.
+ *
+ * \return 0 on success, otherwise negative error code
+ */
+ virtual int open();
+
+ /**
+ * \brief Close the PCM reader
+ *
+ * Closes the underlying PCM capture port. This method is expected
+ * to be called by registered streams in their stop() call.
+ * Calls to this method are counted so that the PCM port is closed
+ * automatically when the last stream stops.
+ */
+ virtual void close();
+
+ /**
+ * \brief Reader's thread funcion
+ *
+ * Internal buffer is filled with data read from the PCM device.
+ * The unmerge instance processes this buffer and produces data
+ * for the registered streams.
+ *
+ * \return 0 on success, otherwise negative error code
+ */
+ int threadFunc();
+
+ /**
+ * \brief Create and attach a resampler
+ *
+ * Creates and attaches a resampler to an input stream. An intermediate
+ * buffer to store samples coming from the PCM port is also allocated.
+ *
+ * \param stream The stream that requires the resampler
+ * \return 0 on success, otherwise negative error code
+ */
+ int attachResampler(InStream *stream);
+
+ /**
+ * \brief Detach and remove the resampler
+ *
+ * Detaches and deletes the resampler of an output stream, if any. The
+ * intermediate buffer allocated in attachResampler() is also freed.
+ *
+ * \param stream The stream that requires the resampler
+ */
+ void detachResampler(InStream *stream);
+
+ /** Set of weak pointers to the registered input streams */
+ typedef set< wp<InStream> > StreamSet;
+
+ PcmInPort *mPort; /**< PCM capture port */
+ PcmParams mParams; /**< PCM parameters to open the port */
+ UnMerge mUnMerge; /**< Unmerge instance if needed */
+ StreamSet mStreams; /**< Registered stream */
+ BufferProvider::Buffer mBuffer; /**< Buffer populated with own allocated memory */
+ int mUsers; /**< Reference count of active users/streams */
+ Mutex mLock; /**< Synchronize stream registration/unregistration */
+};
+
+/**
+ * \class PcmWriter
+ * \brief PCM writer
+ *
+ * PCM writer with merge capability. This writer can take multiple output
+ * streams, merge them into a single multi-channel stream and write it
+ * to a PCM output port.
+ */
+class PcmWriter : protected ThreadBase {
+ public:
+ /**
+ * \brief PCM writer constructor
+ *
+ * Constructs a PCM writer for a given playback port. The writer
+ * will accept multiple streams, merge them and write to the PCM port.
+ *
+ * \param port Pointer to the PCM port to write to
+ * \param params PCM parameters to be used on the port
+ */
+ PcmWriter(PcmOutPort *port, const PcmParams &params);
+
+ /**
+ * \brief PCM merge destructor
+ *
+ * Destroys a PCM writer object.
+ */
+ virtual ~PcmWriter();
+
+ /**
+ * \brief Check the result of constructing the PCM writer
+ *
+ * Result of constructing a PCM writer. It must be checked before
+ * using any methods. Result is undefined if the PCM writer is
+ * used when in an unitialized state.
+ *
+ * \return true if PCM construction is correct, false otherwise
+ */
+ virtual bool initCheck() const;
+
+ /**
+ * \brief Get the PcmParams of the PCM writer
+ *
+ * Get the PcmParams used in this PCM writer, that is, the parameters
+ * used to open the underlying PCM playback port.
+ *
+ * \return Reference to the PCM params of the reader
+ */
+ const PcmParams& getParams() const { return mParams; }
+
+ /**
+ * \brief Register a stream
+ *
+ * Registers a stream to the PCM writer. Multiple streams can be
+ * registered to the writer, each one is associated with one or more
+ * slots of the output stream that is written to the PCM out port.
+ *
+ * \param stream PCM stream to be registered
+ * \return 0 on success, otherwise negative error code
+ */
+ virtual int registerStream(sp<OutStream>& stream);
+
+ /**
+ * \brief Unregister a PCM stream
+ *
+ * Unregisters a stream from the PCM writer. The stream must have
+ * been previously registered using registerStream().
+ *
+ * \param stream PCM stream to be unregistered
+ */
+ virtual void unregisterStream(sp<OutStream>& stream);
+
+ friend class OutStream;
+
+ private:
+ PcmWriter(const PcmWriter &writer);
+ PcmWriter& operator=(const PcmWriter &writer);
+
+ protected:
+ /**
+ * \brief Open the PCM writer
+ *
+ * Opens the underlying PCM playback port. This method is expected
+ * to be called by registered streams in their start() call.
+ * Calls to this method are counted so that the PCM port is open
+ * only when the first stream is started.
+ *
+ * \return 0 on success, otherwise negative error code
+ */
+ virtual int open();
+
+ /**
+ * \brief Close the PCM writer
+ *
+ * Closes the underlying PCM playback port. This method is expected
+ * to be called by registered streams in their stop() call.
+ * Calls to this method are counted so that the PCM port is closed
+ * automatically when the last stream stops.
+ */
+ virtual void close();
+
+ /**
+ * \brief Writer's thread funcion
+ *
+ * The merge instance processes its inputs and the produced buffer is
+ * written to the PCM device.
+ *
+ * \return 0 on success, otherwise negative error code
+ */
+ int threadFunc();
+
+ /**
+ * \brief Create and attach a resampler
+ *
+ * Creates and attaches a resampler to an output stream. An intermediate
+ * buffer to store resampled frames is also allocated.
+ *
+ * \param stream The stream that requires the resampler
+ * \return 0 on success, otherwise negative error code
+ */
+ int attachResampler(OutStream *stream);
+
+ /**
+ * \brief Detach and remove the resampler
+ *
+ * Detaches and deletes the resampler of an output stream, if any. The
+ * intermediate buffer allocated in attachResampler() is also freed.
+ *
+ * \param stream The stream that requires the resampler
+ */
+ void detachResampler(OutStream *stream);
+
+ /** Set of weak pointers to the registered output streams */
+ typedef set< wp<OutStream> > StreamSet;
+
+ PcmOutPort *mPort; /**< PCM playback port */
+ PcmParams mParams; /**< PCM parameters to open the port */
+ Merge mMerge; /**< Merge instance if needed */
+ StreamSet mStreams; /**< Registered streams */
+ BufferProvider::Buffer mBuffer; /**< Buffer populated with own allocated memory */
+ int mUsers; /**< Reference count of active users/streams */
+ Mutex mLock; /**< Synchronize stream registration/unregistration */
+};
+
+} /* namespace tiaudioutils */
+
+#endif /* _TIAUDIOUTILS_STREAM_H_ */
diff --git a/audio/utils/src/ALSAMixer.cpp b/audio/utils/src/ALSAMixer.cpp
new file mode 100644
index 0000000..53b3d9a
--- /dev/null
+++ b/audio/utils/src/ALSAMixer.cpp
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+// #define VERY_VERBOSE_LOGGING
+
+#include <errno.h>
+#include <system/audio.h>
+#include <audio_route/audio_route.h>
+#include <tinyalsa/asoundlib.h>
+
+#include <tiaudioutils/Log.h>
+#include <tiaudioutils/ALSAMixer.h>
+
+namespace tiaudioutils {
+
+ALSAControl::ALSAControl(string &name, int val)
+ : mName(name), mStrVal(""), mIntVal(val), mType(TYPE_INT)
+{
+}
+
+ALSAControl::ALSAControl(string &name, string &val)
+ : mName(name), mStrVal(val), mIntVal(-1), mType(TYPE_STR)
+{
+}
+
+ALSAControl::ALSAControl(const char *name, int val)
+ : mName(name), mStrVal(""), mIntVal(val), mType(TYPE_INT)
+{
+}
+
+ALSAControl::ALSAControl(const char *name, const char *val)
+ : mName(name), mStrVal(val), mIntVal(-1), mType(TYPE_STR)
+{
+}
+
+bool ALSAControl::isIntegerType() const
+{
+ return (mType == TYPE_INT);
+}
+
+bool ALSAControl::isStringType() const
+{
+ return (mType == TYPE_STR);
+}
+
+/* ---------------------------------------------------------------------------------------- */
+
+const string ALSAMixer::kAudioRoutePrefix = "/system/etc/";
+const string ALSAMixer::kAudioRouteSuffix = "_paths.xml";
+
+ALSAMixer::ALSAMixer(uint32_t card)
+ : mCard(card), mRouter(NULL)
+{
+ ALOGI("ALSAMixer: open mixer for hw:%u", card);
+ mMixer = mixer_open(card);
+
+ initDeviceMap();
+
+ char name[256] = "default";
+ /* FIXME read card name from procfs since tinyalsa doesn't support it natively */
+#ifdef OMAP_ENHANCEMENT
+ int ret = mixer_get_card_name(card, name, sizeof(name));
+#else
+ int ret = 0;
+#endif
+ if (!ret) {
+ string xmlRouteFile = kAudioRoutePrefix + name + kAudioRouteSuffix;
+
+ mRouter = audio_route_init(card, xmlRouteFile.c_str());
+ if (mRouter)
+ ALOGI("ALSAMixer: using audio paths file: %s", xmlRouteFile.c_str());
+ else
+ ALOGI("ALSAMixer: no audio paths file found for card hw:%u", mCard);
+ }
+}
+
+ALSAMixer::~ALSAMixer()
+{
+ if (mMixer) {
+ ALOGI("ALSAMixer: close mixer for hw:%u", mCard);
+ mixer_close(mMixer);
+ }
+
+ if (mRouter)
+ audio_route_free(mRouter);
+}
+
+bool ALSAMixer::initCheck() const
+{
+ return (mMixer != NULL);
+}
+
+int ALSAMixer::set(const ALSAControl &control, bool on)
+{
+ struct mixer_ctl *ctl;
+
+ AutoMutex lock(mLock);
+
+ ctl = mixer_get_ctl_by_name(mMixer, control.name().c_str());
+ if (!ctl) {
+ ALOGE("ALSAMixer: failed to get control '%s'", control.name().c_str());
+ return -EINVAL;
+ }
+
+ if (control.isIntegerType()) {
+ int val = on ? control.intVal() : 0;
+
+ ALOGV("ALSAMixer: %s name='%s' val=%d",
+ on ? "set" : "clear", control.name().c_str(), val);
+
+ for (uint32_t i = 0; i < mixer_ctl_get_num_values(ctl); i++)
+ mixer_ctl_set_value(ctl, i, val);
+ } else {
+ const char *val = on ? control.strVal().c_str() : "Off";
+
+ ALOGV("ALSAMixer: set name='%s' val='%s'",
+ control.name().c_str(), control.strVal().c_str());
+
+ mixer_ctl_set_enum_by_string(ctl, val);
+ }
+
+ return 0;
+}
+
+int ALSAMixer::set(const ALSAControlList &controls, bool on)
+{
+ for (ALSAControlList::const_iterator i = controls.begin(); i != controls.end(); ++i) {
+ ALOGV("ALSAMixer: %s control '%s'",
+ on ? "set" : "clear", i->name().c_str());
+
+ int ret = set(*i, on);
+ if (ret) {
+ ALOGE("ALSAMixer: failed to %s control '%s'",
+ on ? "set" : "clear", i->name().c_str());
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int ALSAMixer::initRoutes()
+{
+ if (!mRouter) {
+ ALOGW("ALSAMixer: no audio router available for card hw:%u", mCard);
+ return 0;
+ }
+
+ ALOGV("ALSAMixer: set card default routes");
+
+ audio_route_reset(mRouter);
+ int ret = audio_route_update_mixer(mRouter);
+ if (ret)
+ ALOGE("ALSAMixer: failed to set card default routes %d", ret);
+
+ return ret;
+}
+
+int ALSAMixer::setPath(const char *path, bool on)
+{
+ if (!mRouter) {
+ ALOGW("ALSAMixer: no audio router available for card hw:%u", mCard);
+ return 0;
+ }
+
+ if (path == NULL)
+ return -EINVAL;
+
+ ALOGI("ALSAMixer: %s path for '%s'", on ? "apply" : "reset", path);
+
+ int ret;
+ if (on)
+ ret = audio_route_apply_path(mRouter, path);
+ else
+ ret = audio_route_reset_path(mRouter, path);
+
+ if (ret) {
+ ALOGE("ALSAMixer: path '%s' not found", path);
+ return -EINVAL;
+ }
+
+ ret = audio_route_update_mixer(mRouter);
+ if (ret)
+ ALOGE("ALSAMixer: failed to update mixer for '%s'", path);
+
+ return ret;
+}
+
+int ALSAMixer::setPath(audio_devices_t devices, bool on)
+{
+ if (!mRouter) {
+ ALOGW("ALSAMixer: no audio router available for card hw:%u", mCard);
+ return 0;
+ }
+
+ ALOGV("ALSAMixer: %s path for devices 0x%08x",
+ on ? "apply" : "reset", devices);
+
+ int ret;
+ for (audio_devices_t mask = 1; mask != AUDIO_DEVICE_BIT_DEFAULT; mask <<= 1) {
+ audio_devices_t dev = devices;
+
+ bool found;
+ if (audio_is_input_device(dev)) {
+ dev &= (mask | AUDIO_DEVICE_BIT_IN);
+ found = (dev != AUDIO_DEVICE_BIT_IN);
+ } else {
+ dev &= mask;
+ found = (dev != 0);
+ }
+
+ if (!found)
+ continue;
+
+ DeviceMap::iterator i = mDevMap.find(dev);
+ if (i == mDevMap.end()) {
+ ALOGE("ALSAMixer: no map entry for device 0x%08x", dev);
+ return -EINVAL;
+ }
+
+ const char *path = mDevMap[dev].c_str();
+ ALOGV("ALSAMixer: %s path '%s' for device 0x%08x",
+ on ? "apply" : "reset", path, dev);
+ if (on)
+ ret = audio_route_apply_path(mRouter, path);
+ else
+ ret = audio_route_reset_path(mRouter, path);
+
+ if (ret) {
+ ALOGW("ALSAMixer: path '%s' not found", path);
+ continue;
+ }
+ }
+
+ ret = audio_route_update_mixer(mRouter);
+ if (ret)
+ ALOGE("ALSAMixer: failed to update mixer with new paths %d", ret);
+
+ return ret;
+}
+
+int ALSAMixer::updatePath(audio_devices_t oldDevices, audio_devices_t newDevices)
+{
+ if (!mRouter) {
+ ALOGW("ALSAMixer: no audio router available for card hw:%u", mCard);
+ return 0;
+ }
+
+ audio_devices_t inactive = oldDevices & ~newDevices;
+ audio_devices_t active = newDevices & ~oldDevices;
+
+ ALOGV("ALSAMixer: update paths for devices from 0x%08x to 0x%08x",
+ oldDevices, newDevices);
+
+ /* First reset the paths for inactive devices */
+ int ret = setPath(inactive, false);
+ if (ret) {
+ ALOGE("ALSAMixer: failed to reset path for inactive devices 0x%08x",
+ inactive);
+ return ret;
+ }
+
+ /* Now apply the path for new active devices */
+ ret = setPath(active, true);
+ if (ret)
+ ALOGE("ALSAMixer: failed to apply path for new active devices 0x%08x",
+ active);
+
+ return ret;
+}
+
+#define addToDevMap(device) mDevMap[device] = #device
+
+void ALSAMixer::initDeviceMap()
+{
+ /* input devices */
+ addToDevMap(AUDIO_DEVICE_IN_COMMUNICATION);
+ addToDevMap(AUDIO_DEVICE_IN_AMBIENT);
+ addToDevMap(AUDIO_DEVICE_IN_BUILTIN_MIC);
+ addToDevMap(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET);
+ addToDevMap(AUDIO_DEVICE_IN_WIRED_HEADSET);
+ addToDevMap(AUDIO_DEVICE_IN_AUX_DIGITAL);
+ addToDevMap(AUDIO_DEVICE_IN_VOICE_CALL);
+ addToDevMap(AUDIO_DEVICE_IN_BACK_MIC);
+ addToDevMap(AUDIO_DEVICE_IN_REMOTE_SUBMIX);
+ addToDevMap(AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET);
+ addToDevMap(AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET);
+ addToDevMap(AUDIO_DEVICE_IN_USB_ACCESSORY);
+ addToDevMap(AUDIO_DEVICE_IN_USB_DEVICE);
+ addToDevMap(AUDIO_DEVICE_IN_DEFAULT);
+
+ /* output devices */
+ addToDevMap(AUDIO_DEVICE_OUT_EARPIECE);
+ addToDevMap(AUDIO_DEVICE_OUT_SPEAKER);
+ addToDevMap(AUDIO_DEVICE_OUT_WIRED_HEADSET);
+ addToDevMap(AUDIO_DEVICE_OUT_WIRED_HEADPHONE);
+ addToDevMap(AUDIO_DEVICE_OUT_BLUETOOTH_SCO);
+ addToDevMap(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET);
+ addToDevMap(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT);
+ addToDevMap(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP);
+ addToDevMap(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES);
+ addToDevMap(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER);
+ addToDevMap(AUDIO_DEVICE_OUT_AUX_DIGITAL);
+ addToDevMap(AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET);
+ addToDevMap(AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET);
+ addToDevMap(AUDIO_DEVICE_OUT_USB_ACCESSORY);
+ addToDevMap(AUDIO_DEVICE_OUT_USB_DEVICE);
+ addToDevMap(AUDIO_DEVICE_OUT_REMOTE_SUBMIX);
+ addToDevMap(AUDIO_DEVICE_OUT_DEFAULT);
+
+#ifdef OMAP_ENHANCEMENT
+ addToDevMap(AUDIO_DEVICE_IN_USB_HEADSET);
+ addToDevMap(AUDIO_DEVICE_OUT_WIRED_HEADPHONE2);
+#endif
+}
+
+} /* namespace tiaudioutils */
diff --git a/audio/utils/src/ALSAPcm.cpp b/audio/utils/src/ALSAPcm.cpp
new file mode 100644
index 0000000..37d6833
--- /dev/null
+++ b/audio/utils/src/ALSAPcm.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+// #define VERY_VERBOSE_LOGGING
+
+#include <errno.h>
+#include <tinyalsa/asoundlib.h>
+
+#include <tiaudioutils/Log.h>
+#include <tiaudioutils/ALSAPcm.h>
+
+namespace tiaudioutils {
+
+ALSAInPort::ALSAInPort(uint32_t card, uint32_t port, uint32_t period_count)
+ : mCardId(card), mPortId(port), mPeriodCount(period_count), mPcm(NULL)
+{
+}
+
+ALSAInPort::~ALSAInPort()
+{
+ if (!mPcm || !pcm_is_ready(mPcm))
+ close();
+}
+
+int ALSAInPort::open(const PcmParams &params)
+{
+ struct pcm_config config;
+
+ AutoMutex lock(mLock);
+
+ /* fills rate, channels, format and period size */
+ params.toPcmConfig(config);
+
+ config.period_count = mPeriodCount;
+ config.start_threshold = 1;
+ config.stop_threshold = config.period_size * config.period_count;
+ config.silence_threshold = 0;
+ config.avail_min = 0;
+
+ /* Save the period size and count to check later if were refined */
+ uint32_t periodSize = config.period_size;
+ uint32_t periodCount = config.period_count;
+
+ ALOGI("ALSAInPort: open capture port hw:%u,%u", mCardId, mPortId);
+
+ mPcm = pcm_open(mCardId, mPortId, PCM_IN, &config);
+ if (!pcm_is_ready(mPcm)) {
+ ALOGE("ALSAInPort: failed to open capture port hw:%u,%u: %s",
+ mCardId, mPortId, pcm_get_error(mPcm));
+ return -ENODEV;
+ }
+
+ if ((periodSize != config.period_size) || (periodCount != config.period_count)) {
+ ALOGW("ALSAInPort: params were updated period_size=%u->%u periods=%u->%u",
+ periodSize, config.period_size, periodCount, config.period_count);
+ }
+
+ return 0;
+}
+
+void ALSAInPort::close()
+{
+ ALOGI("ALSAInPort: close capture port hw:%u,%u", mCardId, mPortId);
+
+ AutoMutex lock(mLock);
+ if (mPcm && pcm_is_ready(mPcm)) {
+ pcm_close(mPcm);
+ mPcm = NULL;
+ }
+}
+
+bool ALSAInPort::isOpen() const
+{
+ AutoMutex lock(mLock);
+
+ if (mPcm && pcm_is_ready(mPcm))
+ return true;
+ else
+ return false;
+}
+
+int ALSAInPort::read(void *buffer, size_t frames)
+{
+ AutoMutex lock(mLock);
+
+ if (!mPcm || !pcm_is_ready(mPcm)) {
+ ALOGE("ALSAInPort: port hw:%u,%u is closed, cannot read", mCardId, mPortId);
+ return -EAGAIN;
+ }
+
+ uint32_t bytes = pcm_frames_to_bytes(mPcm, frames);
+
+ ALOGVV("ALSAInPort: read %u frames (%u bytes) buffer %p", frames, bytes, buffer);
+
+ int ret = pcm_read(mPcm, buffer, bytes);
+ if (ret) {
+ ALOGE("ALSAInPort: failed to read %d", ret);
+ return ret;
+ }
+
+ return frames;
+}
+
+/* ---------------------------------------------------------------------------------------- */
+
+ALSAOutPort::ALSAOutPort(uint32_t card, uint32_t port, uint32_t period_count)
+ : mCardId(card), mPortId(port), mPeriodCount(period_count), mPcm(NULL)
+{
+}
+
+ALSAOutPort::~ALSAOutPort()
+{
+ if (!mPcm || !pcm_is_ready(mPcm))
+ close();
+}
+
+int ALSAOutPort::open(const PcmParams &params)
+{
+ struct pcm_config config;
+
+ AutoMutex lock(mLock);
+
+ /* fills rate, channels, format and period size */
+ params.toPcmConfig(config);
+
+ config.period_count = mPeriodCount;
+ config.start_threshold = config.period_size;
+ config.stop_threshold = config.period_size * config.period_count;
+ config.silence_threshold = 0;
+ config.avail_min = 0;
+
+ /* Save the period size and count to check later if were refined */
+ uint32_t periodSize = config.period_size;
+ uint32_t periodCount = config.period_count;
+
+ ALOGI("ALSAOutPort: open playback port hw:%u,%u", mCardId, mPortId);
+
+ mPcm = pcm_open(mCardId, mPortId, PCM_OUT, &config);
+ if (!pcm_is_ready(mPcm)) {
+ ALOGE("ALSAOutPort: failed to open playback port hw:%u,%u: %s",
+ mCardId, mPortId, pcm_get_error(mPcm));
+ return -ENODEV;
+ }
+
+ if ((periodSize != config.period_size) || (periodCount != config.period_count)) {
+ ALOGW("ALSAOutPort: params were updated period_size=%u->%u periods=%u->%u",
+ periodSize, config.period_size, periodCount, config.period_count);
+ }
+
+ return 0;
+}
+
+void ALSAOutPort::close()
+{
+ ALOGI("ALSAOutPort: close playback port hw:%u,%u", mCardId, mPortId);
+
+ AutoMutex lock(mLock);
+ if (mPcm && pcm_is_ready(mPcm)) {
+ pcm_close(mPcm);
+ mPcm = NULL;
+ }
+}
+
+bool ALSAOutPort::isOpen() const
+{
+ AutoMutex lock(mLock);
+
+ if (mPcm && pcm_is_ready(mPcm))
+ return true;
+ else
+ return false;
+}
+
+int ALSAOutPort::write(const void *buffer, size_t frames)
+{
+ AutoMutex lock(mLock);
+
+ if (!mPcm || !pcm_is_ready(mPcm)) {
+ ALOGE("ALSAOutPort: port hw:%u,%u is closed, cannot write", mCardId, mPortId);
+ return -EAGAIN;
+ }
+
+ uint32_t bytes = pcm_frames_to_bytes(mPcm, frames);
+
+ ALOGVV("ALSAOutPort: write %u frames (%u bytes) buffer %p", frames, bytes, buffer);
+
+ int ret = pcm_write(mPcm, buffer, bytes);
+ if (ret) {
+ ALOGE("ALSAOutPort: failed to write %d", ret);
+ return ret;
+ }
+
+ return frames;
+}
+
+} /* namespace tiaudioutils */
diff --git a/audio/utils/src/Base.cpp b/audio/utils/src/Base.cpp
new file mode 100644
index 0000000..97263f8
--- /dev/null
+++ b/audio/utils/src/Base.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+// #define VERY_VERBOSE_LOGGING
+
+#include <system/audio.h>
+
+#include <tiaudioutils/Log.h>
+#include <tiaudioutils/Base.h>
+
+namespace tiaudioutils {
+
+using std::make_pair;
+
+SlotMap::SlotMap(uint32_t mask)
+{
+ uint32_t pos = 0;
+
+ while (mask) {
+ if (mask & 1) {
+ insert(make_pair<uint32_t, uint32_t>(pos, pos));
+ }
+ pos++;
+ mask >>= 1;
+ }
+}
+
+SlotMap::SlotMap(uint32_t srcMask, uint32_t dstMask)
+{
+ uint32_t numChanSrc = popcount(srcMask);
+ uint32_t numChanDst = popcount(dstMask);
+
+ if (numChanSrc != numChanDst) {
+ ALOGW("SlotMap: inconsistent channel count in source (%d) and dest (%d)",
+ numChanSrc, numChanDst);
+ return;
+ }
+
+ uint32_t srcPos;
+ uint32_t dstPos;
+
+ while (srcMask && dstMask) {
+ srcPos = ffs(srcMask);
+ dstPos = ffs(dstMask);
+
+ if (srcPos && dstPos) {
+ /* ffs returns one plus the index */
+ srcPos--;
+ dstPos--;
+ insert(make_pair<uint32_t, uint32_t>(dstPos, srcPos));
+ srcMask &= ~(1U << srcPos);
+ dstMask &= ~(1U << dstPos);
+ }
+ }
+}
+
+bool SlotMap::isValid() const
+{
+ return (size() && ((int)size() == popcount(getDstMask())));
+}
+
+uint32_t SlotMap::getChannelCount() const
+{
+ return size();
+}
+
+uint32_t SlotMap::getSrcMask() const
+{
+ uint32_t mask = 0;
+
+ for (const_iterator i = begin(); i != end(); ++i) {
+ mask |= (1U << i->second);
+ }
+
+ return mask;
+}
+
+uint32_t SlotMap::getDstMask() const
+{
+ uint32_t mask = 0;
+
+ for (const_iterator i = begin(); i != end(); ++i) {
+ mask |= (1U << i->first);
+ }
+
+ return mask;
+}
+
+/* ---------------------------------------------------------------------------------------- */
+
+ThreadBase::ThreadBase()
+ : mName("Thread"), mRunning(false)
+{
+}
+
+ThreadBase::ThreadBase(const string &name)
+ : mName("Thread " + name), mRunning(false)
+{
+}
+
+ThreadBase::ThreadBase(const char *name)
+ : mName("Thread "), mRunning(false)
+{
+ if (name)
+ mName += name;
+}
+
+ThreadBase::~ThreadBase()
+{
+ void *res;
+
+ if (mRunning) {
+ ALOGW("%s is forcefully exiting", name());
+ mRunning = false;
+ pthread_join(mThread, &res);
+ }
+}
+
+int ThreadBase::run()
+{
+ AutoMutex lock(mMutex);
+
+ ALOGI("%s is starting", name());
+ int ret = pthread_create(&mThread, NULL, threadWrapper, this);
+ if (ret) {
+ ALOGE("%s: failed to create pthread %d", name(), ret);
+ return ret;
+ }
+
+ mRunning = true;
+
+ return 0;
+}
+
+int ThreadBase::stop()
+{
+ AutoMutex lock(mMutex);
+ void *res;
+
+ ALOGI("%s is exiting", name());
+ mRunning = false;
+ pthread_join(mThread, &res);
+
+ return (int)res;
+}
+
+bool ThreadBase::isRunning() const
+{
+ AutoMutex lock(mMutex);
+ return mRunning;
+}
+
+int ThreadBase::_threadLoop()
+{
+ int ret = 0;
+
+ while (mRunning) {
+ ret = threadFunc();
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+extern "C" void* threadWrapper(void *me)
+{
+ return (void *)static_cast<ThreadBase *>(me)->_threadLoop();
+}
+
+} /* namespace tiaudioutils */
diff --git a/audio/utils/src/MonoPipe.cpp b/audio/utils/src/MonoPipe.cpp
new file mode 100644
index 0000000..7cf4d94
--- /dev/null
+++ b/audio/utils/src/MonoPipe.cpp
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+// #define VERY_VERBOSE_LOGGING
+
+#include <media/nbaio/NBAIO.h>
+#include <media/nbaio/MonoPipe.h>
+#include <media/nbaio/MonoPipeReader.h>
+#include <media/AudioBufferProvider.h>
+
+#include <tiaudioutils/Log.h>
+#include <tiaudioutils/MonoPipe.h>
+
+namespace tiaudioutils {
+
+using android::NBAIO_Format;
+using android::Format_from_SR_C;
+
+MonoPipe::MonoPipe(const PcmParams &params, uint32_t frames)
+ : mParams(params)
+{
+ if (frames < (kPipeSizeFactor * mParams.frameCount))
+ frames = kPipeSizeFactor * mParams.frameCount;
+
+ ALOGV("MonoPipe: Create pipe for %u frames (%u bytes)",
+ frames, params.framesToBytes(frames));
+
+ NBAIO_Format format = Format_from_SR_C(mParams.sampleRate, mParams.channels);
+
+ NBAIO_Format offers[1] = {format};
+ size_t numCounterOffers = 0;
+
+ mSink = new android::MonoPipe(frames, format, true);
+ if (mSink) {
+ ssize_t index = mSink->negotiate(offers, 1, NULL, numCounterOffers);
+ ALOG_ASSERT(index == 0);
+
+ mSource = new android::MonoPipeReader(mSink);
+ if (mSource) {
+ numCounterOffers = 0;
+ index = mSource->negotiate(offers, 1, NULL, numCounterOffers);
+ ALOG_ASSERT(index == 0);
+ }
+ }
+}
+
+MonoPipe::~MonoPipe()
+{
+ if (mSink) {
+ mSink->shutdown(true);
+ delete mSink;
+ }
+
+ if (mSource)
+ delete mSource;
+}
+
+bool MonoPipe::initCheck() const
+{
+ return ((mSink != NULL) && (mSource != NULL));
+}
+
+int MonoPipe::read(void *buffer, uint32_t frames)
+{
+ ssize_t read = availableToRead();
+
+ /* Wait the time equivalent to the pending frames */
+ if ((read >= 0) && (read < (ssize_t)frames))
+ usleep(((frames - read) * 1000 * 1000) / mParams.sampleRate);
+
+ /* NBAIO pipe reader doesn't block on read() */
+ read = mSource->read(buffer, frames,
+ android::AudioBufferProvider::kInvalidPTS);
+ if (read < 0)
+ ALOGE("MonoPipe: failed to read from pipe %d", read);
+
+ return read;
+}
+
+int MonoPipe::write(const void *buffer, uint32_t frames)
+{
+ ssize_t written = mSink->write(buffer, frames);
+ if (written < 0)
+ ALOGE("MonoPipe: failed to write to pipe %d", written);
+
+ return written;
+}
+
+int MonoPipe::availableToRead() const
+{
+ return mSource->availableToRead();
+}
+
+int MonoPipe::availableToWrite() const
+{
+ return mSink->availableToWrite();
+}
+
+/* ---------------------------------------------------------------------------------------- */
+
+PipeReader::PipeReader(MonoPipe *pipe)
+ : mPipe(pipe)
+{
+}
+
+PipeReader::~PipeReader()
+{
+ if (mBuffer.i8)
+ delete [] mBuffer.i8;
+}
+
+int PipeReader::getNextBuffer(BufferProvider::Buffer *buffer)
+{
+ uint32_t frameSize = mPipe->getParams().frameSize();
+
+ /* resize our internal buffer if needed */
+ if (mBuffer.frameCount < buffer->frameCount) {
+ if (mBuffer.i8)
+ delete [] mBuffer.i8;
+ mBuffer.i8 = new int8_t[buffer->frameCount * frameSize];
+ if (mBuffer.i8 == NULL) {
+ ALOGE("PipeReader: failed to resize internal buffer");
+ return -ENOMEM;
+ }
+ mBuffer.frameCount = buffer->frameCount;
+ }
+
+ int8_t *buf = mBuffer.i8;
+ int pending = buffer->frameCount;
+
+ while (pending > 0) {
+ int read = mPipe->read(buf, pending);
+ if (read < 0) {
+ ALOGE("PipeReader: failed to read from pipe %d", read);
+ return read;
+ } else if (read == 0) {
+ ALOGW("PipeReader: underrun!");
+ break;
+ } else {
+ pending -= read;
+ buf += read * frameSize;
+ }
+ }
+
+ buffer->frameCount -= pending;
+ buffer->raw = mBuffer.raw;
+
+ ALOGW_IF(pending, "PipeReader: unexpected %u pending frames", pending);
+
+ return 0;
+}
+
+void PipeReader::releaseBuffer(BufferProvider::Buffer *buffer)
+{
+ /* Nothing to do to release the buffer, but must be implemented */
+}
+
+/* ---------------------------------------------------------------------------------------- */
+
+PipeWriter::PipeWriter(MonoPipe *pipe)
+ : mPipe(pipe)
+{
+}
+
+PipeWriter::~PipeWriter()
+{
+ if (mBuffer.i8)
+ delete [] mBuffer.i8;
+}
+
+int PipeWriter::getNextBuffer(BufferProvider::Buffer *buffer)
+{
+ uint32_t frameSize = mPipe->getParams().frameSize();
+
+ /* resize our internal buffer if needed */
+ if (mBuffer.frameCount < buffer->frameCount) {
+ if (mBuffer.i8)
+ delete [] mBuffer.i8;
+ mBuffer.i8 = new int8_t[buffer->frameCount * frameSize];
+ if (mBuffer.i8 == NULL) {
+ ALOGE("PipeWriter: failed to resize internal buffer");
+ return -ENOMEM;
+ }
+ mBuffer.frameCount = buffer->frameCount;
+ }
+
+ buffer->raw = mBuffer.raw;
+ buffer->frameCount = mBuffer.frameCount;
+
+ return 0;
+}
+
+void PipeWriter::releaseBuffer(BufferProvider::Buffer *buffer)
+{
+ int8_t *buf = buffer->i8;
+ int pending = buffer->frameCount;
+ uint32_t frameSize = mPipe->getParams().frameSize();
+
+ while (pending > 0) {
+ int written = mPipe->write(buf, pending);
+ if (written < 0) {
+ ALOGE("PipeWriter: failed to write to pipe %d", written);
+ return;
+ }
+ pending -= written;
+ buf += written * frameSize;
+ }
+
+ ALOGW_IF(pending, "PipeWriter: unexpected %u pending frames", pending);
+}
+
+} /* namespace tiaudioutils */
diff --git a/audio/utils/src/MumStream.cpp b/audio/utils/src/MumStream.cpp
new file mode 100644
index 0000000..b49e188
--- /dev/null
+++ b/audio/utils/src/MumStream.cpp
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+// #define VERY_VERBOSE_LOGGING
+
+#include <tiaudioutils/Log.h>
+#include <tiaudioutils/MumStream.h>
+#include <tiaudioutils/Stream.h>
+
+namespace tiaudioutils {
+
+Merge::Merge(const PcmParams &params)
+ : mParams(params), mDstMask(0)
+{
+ mContainerBytes = mParams.sampleBits / 8;
+
+ /* 24-bit samples (3 bytes) are actually in 32-bit containers (4 bytes) */
+ mContainerBytes = (mContainerBytes == 3) ? 4 : mContainerBytes;
+}
+
+Merge::~Merge()
+{
+ for (StreamSet::iterator i = mStreams.begin(); i != mStreams.end(); ++i) {
+ sp<OutStream> stream = (*i).promote();
+ if (stream == 0)
+ continue;
+ ALOGW("Merge: automatically un-registering stream %p during destroy",
+ stream.get());
+ unregisterStream(stream);
+ }
+}
+
+bool Merge::initCheck() const
+{
+ if (!mParams.isValid()) {
+ ALOGE("Merge: params are invalid");
+ return false;
+ }
+
+ if (!mParams.frameCount) {
+ ALOGE("Merge: params.frameCount is invalid");
+ return false;
+ }
+
+ /* Only support 16, 24, and 32-bit samples */
+ if (mParams.sampleBits != 16 &&
+ mParams.sampleBits != 24 &&
+ mParams.sampleBits != 32) {
+ ALOGE("Merge: unsupported bits/sample");
+ return false;
+ }
+
+ return true;
+}
+
+int Merge::registerStream(const sp<OutStream>& stream)
+{
+ if (stream == NULL) {
+ ALOGE("Merge: stream is invalid, cannot register");
+ return -EINVAL;
+ }
+
+ const PcmParams &params = stream->getParams();
+ const SlotMap &map = stream->getSlotMap();
+
+ ALOGV("Merge: register stream %p src 0x%04x dst 0x%04x",
+ stream.get(), map.getSrcMask(), map.getDstMask());
+
+ if (params.sampleBits != mParams.sampleBits) {
+ ALOGE("Merge: stream has incompatible sample size");
+ return -EINVAL;
+ }
+
+ if (!stream->canResample() && (params.sampleRate != mParams.sampleRate)) {
+ ALOGE("Merge: stream has incompatible sample rate");
+ return -EINVAL;
+ }
+
+ if (!stream->canResample() && (params.frameCount != mParams.frameCount)) {
+ ALOGE("Merge: stream has incompatible frame count");
+ return -EINVAL;
+ }
+
+ /*
+ * sanity check that defined dest channels fall within the defined number
+ * of channels for the output
+ */
+ if (map.getDstMask() >= (1U << mParams.channels)) {
+ ALOGE("Merge: stream's dest mask 0x%x requests channels not present in"
+ " the output (%u channel output)",
+ map.getDstMask(), mParams.channels);
+ return -EINVAL;
+ }
+
+ AutoMutex lock(mLock);
+
+ /* check if dst channels overlap with already registered dst channels */
+ if (map.getDstMask() & mDstMask) {
+ ALOGE("Merge: stream's dst mask overlaps already registered streams");
+ return -EINVAL;
+ }
+
+ if (mStreams.find(stream) != mStreams.end()) {
+ ALOGE("Merge: stream is already registered");
+ return -EINVAL;
+ }
+
+ if (mStreams.size() == mParams.channels) {
+ ALOGE("Merge: max number of streams registered");
+ return -ENOMEM;
+ }
+
+ mStreams.insert(stream);
+
+ mDstMask |= map.getDstMask();
+
+ return 0;
+}
+
+void Merge::unregisterStream(sp<OutStream>& stream)
+{
+ if (stream == NULL) {
+ ALOGE("Merge: stream is invalid, cannot unregister");
+ return;
+ }
+
+ const SlotMap &map = stream->getSlotMap();
+
+ ALOGV("Merge: unregister stream %p src 0x%04x dst 0x%04x",
+ stream.get(), map.getSrcMask(), map.getDstMask());
+
+ AutoMutex lock(mLock);
+
+ if (mStreams.find(stream) != mStreams.end()) {
+ mDstMask &= ~map.getDstMask();
+ mStreams.erase(stream);
+ } else {
+ ALOGE("Merge: stream is already un-registered");
+ }
+}
+
+void Merge::merge(sp<OutStream> stream,
+ BufferProvider::Buffer &inBuffer,
+ BufferProvider::Buffer &outBuffer)
+{
+ int16_t *in16 = inBuffer.i16;
+ int16_t *out16 = outBuffer.i16;
+ int32_t *in32 = inBuffer.i32;
+ int32_t *out32 = outBuffer.i32;
+ uint32_t inOffset = stream->getParams().channels;
+ uint32_t outOffset = mParams.channels;
+
+ for (size_t i = 0; i < outBuffer.frameCount && i < inBuffer.frameCount; i++) {
+ SlotMap::const_iterator j = stream->getSlotMap().begin();
+
+ if (mContainerBytes == 2) {
+ for ( ; j != stream->getSlotMap().end(); ++j)
+ out16[j->first] = in16[j->second];
+
+ in16 += inOffset;
+ out16 += outOffset;
+ } else if (mContainerBytes == 4) {
+ for ( ; j != stream->getSlotMap().end(); ++j)
+ out32[j->first] = in32[j->second];
+
+ in32 += inOffset;
+ out32 += outOffset;
+ }
+ }
+}
+
+int Merge::process(BufferProvider::Buffer &outBuffer)
+{
+ int ret = 0;
+
+ if (!outBuffer.raw) {
+ ALOGE("Merge: cannot process invalid audio buffer");
+ return -EINVAL;
+ }
+
+ if (outBuffer.frameCount > mParams.frameCount)
+ outBuffer.frameCount = mParams.frameCount;
+
+ memset(outBuffer.raw, 0x0,
+ outBuffer.frameCount * mParams.channels * mContainerBytes);
+
+ AutoMutex lock(mLock);
+
+ for (StreamSet::iterator i = mStreams.begin(); i != mStreams.end(); ++i) {
+ sp<OutStream> stream = (*i).promote();
+ BufferProvider::Buffer inBuffer;
+
+ inBuffer.frameCount = outBuffer.frameCount;
+
+ if (stream == 0) {
+ ALOGW("Merge: registered stream is no longer valid, skipping");
+ continue;
+ }
+
+ ret = stream->getNextBuffer(&inBuffer);
+ if (ret) {
+ ALOGW("Merge: failed to get buffer from stream %d", ret);
+ continue;
+ }
+
+ if (!outBuffer.frameCount || !inBuffer.frameCount) {
+ ALOGE("Merge: cannot merge 0 frame stream");
+ /* Set error here, so can be propogated to the user */
+ inBuffer.i32 = (int32_t*)(-EINVAL);
+ }
+ else if (outBuffer.frameCount != inBuffer.frameCount) {
+ ALOGE("Merge: unable to process whole stream, not enough frames."
+ "Expected %d, Received %d. Performing partial process. ",
+ outBuffer.frameCount, inBuffer.frameCount);
+ if (outBuffer.frameCount < inBuffer.frameCount) {
+ inBuffer.frameCount = outBuffer.frameCount;
+ }
+ }
+
+ merge(stream, inBuffer, outBuffer);
+
+ stream->releaseBuffer(&inBuffer);
+ }
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------------------------- */
+
+UnMerge::UnMerge(const PcmParams &params)
+ : mParams(params), mSrcMask(0)
+{
+ mContainerBytes = mParams.sampleBits / 8;
+
+ /* 24-bit samples (3 bytes) are actually in 32-bit containers (4 bytes) */
+ mContainerBytes = (mContainerBytes == 3) ? 4 : mContainerBytes;
+}
+
+UnMerge::~UnMerge()
+{
+ for (StreamSet::iterator i = mStreams.begin(); i != mStreams.end(); ++i) {
+ sp<InStream> stream = (*i).promote();
+ ALOGW("UnMerge: automatically un-registering stream %p during destroy",
+ stream.get());
+ unregisterStream(stream);
+ }
+}
+
+bool UnMerge::initCheck() const
+{
+ if (!mParams.isValid()) {
+ ALOGE("UnMerge: params are invalid");
+ return false;
+ }
+
+ if (!mParams.frameCount) {
+ ALOGE("UnMerge: params.frameCount is invalid");
+ return false;
+ }
+
+ /* Only support 16, 24, and 32-bit samples */
+ if (mParams.sampleBits != 16 &&
+ mParams.sampleBits != 24 &&
+ mParams.sampleBits != 32) {
+ ALOGE("UnMerge: unsupported bits/sample");
+ return false;
+ }
+
+ return true;
+}
+
+int UnMerge::registerStream(const sp<InStream>& stream)
+{
+ if (stream == NULL) {
+ ALOGE("UnMerge: stream is invalid, cannot register");
+ return -EINVAL;
+ }
+
+ const PcmParams &params = stream->getParams();
+ const SlotMap &map = stream->getSlotMap();
+
+ ALOGV("UnMerge: register stream %p src 0x%04x dst 0x%04x",
+ stream.get(), map.getSrcMask(), map.getDstMask());
+
+ if (params.sampleBits != mParams.sampleBits) {
+ ALOGE("UnMerge: stream has incompatible sample size");
+ return -EINVAL;
+ }
+
+ if (!stream->canResample() && (params.sampleRate != mParams.sampleRate)) {
+ ALOGE("UnMerge: stream has incompatible sample rate");
+ return -EINVAL;
+ }
+
+ if (!stream->canResample() && (params.frameCount != mParams.frameCount)) {
+ ALOGE("UnMerge: stream has incompatible frame count");
+ return -EINVAL;
+ }
+
+ /*
+ * sanity check that defined src channels fall within the defined number
+ * of channels for the stream
+ */
+ if (map.getSrcMask() >= (1U << mParams.channels)) {
+ ALOGE("UnMerge: stream's src mask 0x%x requests channels not present in"
+ " the input (%u channel input)",
+ map.getSrcMask(), mParams.channels);
+ return -EINVAL;
+ }
+
+ AutoMutex lock(mLock);
+
+ if (mStreams.find(stream) != mStreams.end()) {
+ ALOGE("UnMerge: stream is already registered");
+ return -EINVAL;
+ }
+
+ if (mStreams.size() == mParams.channels) {
+ ALOGE("UnMerge: max number of streams registered");
+ return -ENOMEM;
+ }
+
+ mStreams.insert(stream);
+
+ mSrcMask |= map.getSrcMask();
+
+ return 0;
+}
+
+void UnMerge::unregisterStream(sp<InStream>& stream)
+{
+ if (stream == NULL) {
+ ALOGE("UnMerge: stream is invalid, cannot unregister");
+ return;
+ }
+
+ const SlotMap &map = stream->getSlotMap();
+
+ ALOGV("UnMerge: unregister stream %p src 0x%04x dst 0x%04x",
+ stream.get(), map.getSrcMask(), map.getDstMask());
+
+ AutoMutex lock(mLock);
+
+ if (mStreams.find(stream) != mStreams.end()) {
+ mSrcMask &= ~map.getSrcMask();
+ mStreams.erase(stream);
+ } else {
+ ALOGE("UnMerge: stream is already un-registered");
+ }
+}
+
+void UnMerge::unmerge(sp<InStream> stream,
+ BufferProvider::Buffer &inBuffer,
+ BufferProvider::Buffer &outBuffer)
+{
+ int16_t *in16 = inBuffer.i16;
+ int16_t *out16 = outBuffer.i16;
+ int32_t *in32 = inBuffer.i32;
+ int32_t *out32 = outBuffer.i32;
+ uint32_t inOffset = mParams.channels;
+ uint32_t outOffset = stream->getParams().channels;
+
+ for (size_t i = 0; i < outBuffer.frameCount && i < inBuffer.frameCount; i++) {
+ SlotMap::const_iterator j = stream->getSlotMap().begin();
+
+ if (mContainerBytes == 2) {
+ for ( ; j != stream->getSlotMap().end(); ++j)
+ out16[j->first] = in16[j->second];
+
+ in16 += inOffset;
+ out16 += outOffset;
+ } else if (mContainerBytes == 4) {
+ for ( ; j != stream->getSlotMap().end(); ++j)
+ out32[j->first] = in32[j->second];
+
+ in32 += inOffset;
+ out32 += outOffset;
+ }
+ }
+}
+
+int UnMerge::process(BufferProvider::Buffer &inBuffer)
+{
+ int ret = 0;
+
+ if (!inBuffer.raw) {
+ ALOGE("UnMerge: cannot process invalid audio buffer");
+ return -EINVAL;
+ }
+
+ if (inBuffer.frameCount > mParams.frameCount)
+ inBuffer.frameCount = mParams.frameCount;
+
+ AutoMutex lock(mLock);
+
+ for (StreamSet::iterator i = mStreams.begin(); i != mStreams.end(); ++i) {
+ sp<InStream> stream = (*i).promote();
+ BufferProvider::Buffer outBuffer;
+
+ outBuffer.frameCount = inBuffer.frameCount;
+
+ if (stream == 0) {
+ ALOGW("UnMerge: registered stream is no longer valid, skipping");
+ continue;
+ }
+
+ ret = stream->getNextBuffer(&outBuffer);
+ if (ret) {
+ ALOGW("UnMerge: failed to get buffer from stream %d", ret);
+ continue;
+ }
+
+ if (!inBuffer.frameCount || !outBuffer.frameCount) {
+ ALOGE("UnMerge: cannot unmerge 0 frames");
+ /* Set error here, so can be propogated to the user */
+ outBuffer.i32 = (int32_t*)(-EINVAL);
+ }
+ else if (inBuffer.frameCount > outBuffer.frameCount) {
+ ALOGE("UnMerge: unable to process whole stream, not enough frames."
+ "Expected %d, Received %d. Performing partial process. ",
+ inBuffer.frameCount, outBuffer.frameCount);
+ if (inBuffer.frameCount < outBuffer.frameCount) {
+ outBuffer.frameCount = inBuffer.frameCount;
+ }
+ }
+
+ unmerge(stream, inBuffer, outBuffer);
+
+ stream->releaseBuffer(&outBuffer);
+ }
+
+ return 0;
+}
+
+} /* namespace tiaudioutils */
diff --git a/audio/utils/src/Pcm.cpp b/audio/utils/src/Pcm.cpp
new file mode 100644
index 0000000..332746c
--- /dev/null
+++ b/audio/utils/src/Pcm.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * 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.
+ */
+
+#include <tinyalsa/asoundlib.h>
+
+#include <tiaudioutils/Pcm.h>
+
+namespace tiaudioutils {
+
+PcmParams::PcmParams()
+ : channels(0), sampleBits(0), sampleRate(0), frameCount(0)
+{
+}
+
+PcmParams::PcmParams(uint32_t chan, uint32_t bits, uint32_t rate, uint32_t frames)
+ : channels(chan), sampleBits(bits), sampleRate(rate), frameCount(frames)
+{
+}
+
+PcmParams::PcmParams(const struct pcm_config &config)
+ : channels(config.channels),
+ sampleRate(config.rate),
+ frameCount(config.period_size)
+{
+ switch (config.format) {
+ case PCM_FORMAT_S32_LE:
+ sampleBits = 32;
+ break;
+ case PCM_FORMAT_S16_LE:
+ default:
+ sampleBits = 16;
+ break;
+ }
+}
+
+PcmParams::PcmParams(const struct audio_config &config, uint32_t frames)
+ : channels(popcount(config.channel_mask)),
+ sampleRate(config.sample_rate),
+ frameCount(frames)
+{
+ switch (config.format) {
+ case AUDIO_FORMAT_PCM_8_BIT:
+ sampleBits = 8;
+ break;
+ case AUDIO_FORMAT_PCM_8_24_BIT:
+ case AUDIO_FORMAT_PCM_32_BIT:
+ sampleBits = 32;
+ break;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ default:
+ sampleBits = 16;
+ break;
+ }
+}
+
+bool PcmParams::isValid() const
+{
+ return (channels && sampleRate && sampleBits);
+}
+
+uint32_t PcmParams::framesToBytes(uint32_t frames) const
+{
+ return (frames * frameSize());
+}
+
+uint32_t PcmParams::bytesToFrames(uint32_t bytes) const
+{
+ return (bytes / frameSize());
+}
+
+void PcmParams::toPcmConfig(struct pcm_config &config) const
+{
+ config.channels = channels;
+ config.rate = sampleRate;
+ config.period_size = frameCount;
+
+ switch (sampleBits) {
+ case 32:
+ config.format = PCM_FORMAT_S32_LE;
+ break;
+ case 16:
+ default:
+ config.format = PCM_FORMAT_S16_LE;
+ break;
+ }
+}
+
+void PcmParams::toAudioConfig(struct audio_config &config, StreamDirection dir) const
+{
+ config.sample_rate = sampleRate;
+
+ if (dir == CAPTURE) {
+ config.channel_mask = audio_channel_in_mask_from_count(channels);
+ } else {
+ config.channel_mask = audio_channel_out_mask_from_count(channels);
+ }
+
+ switch (sampleBits) {
+ case 32:
+ config.format = AUDIO_FORMAT_PCM_32_BIT;
+ break;
+ case 16:
+ default:
+ config.format = AUDIO_FORMAT_PCM_16_BIT;
+ break;
+ }
+}
+
+} /* namespace tiaudioutils */
diff --git a/audio/utils/src/Resampler.cpp b/audio/utils/src/Resampler.cpp
new file mode 100644
index 0000000..f63c69b
--- /dev/null
+++ b/audio/utils/src/Resampler.cpp
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+// #define VERY_VERBOSE_LOGGING
+
+#include <errno.h>
+#include <speex/speex_resampler.h>
+
+#include <tiaudioutils/Log.h>
+#include <tiaudioutils/Resampler.h>
+
+namespace tiaudioutils {
+
+Resampler::Resampler(const PcmParams &params, uint32_t quality)
+ : mInParams(params), mOutParams(params), mQuality(quality),
+ mAvail(0), mBufferFrames(0), mRatioNum(0), mRatioDen(1)
+{
+ createResampler();
+
+ /* initial buffer to hold the equivalent frames of input buffer size */
+ uint32_t frames = (mInParams.frameCount * mRatioNum) / mRatioDen;
+ reallocateBuffer(frames);
+}
+
+Resampler::Resampler(const PcmParams &inParams,
+ const PcmParams &outParams,
+ uint32_t quality)
+ : mInParams(inParams), mOutParams(outParams), mQuality(quality),
+ mAvail(0), mBufferFrames(0), mRatioNum(0), mRatioDen(1)
+{
+ createResampler();
+
+ /* initial buffer to hold the equivalent frames of input buffer size */
+ uint32_t frames = (mInParams.frameCount * mRatioNum) / mRatioDen;
+ reallocateBuffer(frames);
+}
+
+Resampler::~Resampler()
+{
+ if (mBuffer.i16)
+ delete [] mBuffer.i16;
+
+ if (mSpeexRsmp)
+ speex_resampler_destroy(mSpeexRsmp);
+}
+
+bool Resampler::initCheck() const
+{
+ if ((mInParams.sampleBits != 16) || (mOutParams.sampleBits != 16)) {
+ ALOGE("Resampler: %u bits/sample is not supported",
+ mInParams.sampleBits);
+ return false;
+ }
+
+ if (mInParams.channels != mOutParams.channels) {
+ ALOGE("Resampler: channel count mismatch, in %u out %u",
+ mInParams.channels, mOutParams.channels);
+ return false;
+ }
+
+ if (!mSpeexRsmp || !mBuffer.raw)
+ return false;
+
+ return true;
+}
+
+int Resampler::setInSampleRate(uint32_t rate)
+{
+ AutoMutex lock(mLock);
+
+ if (rate == mInParams.sampleRate)
+ return 0;
+
+ ALOGV("Resampler: set new input sample rate %u", rate);
+
+ int ret = speex_resampler_set_rate(mSpeexRsmp,
+ rate,
+ mOutParams.sampleRate);
+ if (ret) {
+ ALOGE("Resampler: failed to set new input sample rate: %s",
+ speex_resampler_strerror(ret));
+ return ret;
+ }
+
+ mInParams.sampleRate = rate;
+ speex_resampler_get_ratio(mSpeexRsmp, &mRatioNum, &mRatioDen);
+
+ uint32_t frames = (mInParams.frameCount * mRatioNum) / mRatioDen;
+ if (frames > mBufferFrames)
+ reallocateBuffer(frames);
+
+ return 0;
+}
+
+int Resampler::setOutSampleRate(uint32_t rate)
+{
+ AutoMutex lock(mLock);
+
+ if (rate == mOutParams.sampleRate)
+ return 0;
+
+ ALOGV("Resampler: set new output sample rate %u", rate);
+
+ int ret = speex_resampler_set_rate(mSpeexRsmp,
+ mInParams.sampleRate,
+ rate);
+ if (ret) {
+ ALOGE("Resampler: failed to set new output sample rate: %s",
+ speex_resampler_strerror(ret));
+ return ret;
+ }
+
+ mOutParams.sampleRate = rate;
+ speex_resampler_get_ratio(mSpeexRsmp, &mRatioNum, &mRatioDen);
+
+ uint32_t frames = (mInParams.frameCount * mRatioNum) / mRatioDen;
+ if (frames > mBuffer.frameCount)
+ reallocateBuffer(frames);
+
+ return 0;
+}
+
+void Resampler::getRatio(uint32_t &num, uint32_t &den) const
+{
+ num = mRatioNum;
+ den = mRatioDen;
+}
+
+int Resampler::resample(BufferProvider &provider,
+ void *outBuffer,
+ uint32_t outFrames)
+{
+ if (!outBuffer)
+ return -EINVAL;
+
+ if (!outFrames)
+ return 0;
+
+ AutoMutex lock(mLock);
+
+ /*
+ * Calculate the number of frames required on the input side to
+ * produce the requested output frames. Intermediate buffer is
+ * resized accordingly.
+ */
+ uint32_t reqInFrames = (outFrames * mRatioNum) / mRatioDen + 1;
+ if (reqInFrames > mBuffer.frameCount)
+ reallocateBuffer(reqInFrames);
+
+ uint32_t written = 0;
+ while (written < outFrames) {
+ if (mAvail < reqInFrames) {
+ BufferProvider::Buffer buf;
+
+ buf.frameCount = reqInFrames - mAvail;
+
+ int ret = provider.getNextBuffer(&buf);
+ if (ret) {
+ ALOGE("Resampler: failed to get next buffer %d", ret);
+ return ret;
+ }
+
+ /* append new buffer to existing frames in local buffer */
+ memcpy(mBuffer.i8 + mOutParams.framesToBytes(mAvail),
+ buf.i8,
+ mOutParams.framesToBytes(buf.frameCount));
+ mAvail += buf.frameCount;
+
+ provider.releaseBuffer(&buf);
+ }
+
+ uint32_t framesIn = mAvail;
+ uint32_t framesOut = outFrames - written;
+ int16_t *bufferIn = mBuffer.i16;
+ int16_t *bufferOut = (int16_t *)outBuffer + (written * mOutParams.channels);
+
+ /* resample */
+ if (mOutParams.channels == 1) {
+ speex_resampler_process_int(mSpeexRsmp,
+ 0,
+ bufferIn,
+ &framesIn,
+ bufferOut,
+ &framesOut);
+ } else {
+ speex_resampler_process_interleaved_int(mSpeexRsmp,
+ bufferIn,
+ &framesIn,
+ bufferOut,
+ &framesOut);
+ }
+ written += framesOut;
+ mAvail -= framesIn;
+
+ /* move samples left to the beginning of the local buffer */
+ if (mAvail) {
+ memmove(mBuffer.raw,
+ mBuffer.i8 + mOutParams.framesToBytes(framesIn),
+ mOutParams.framesToBytes(mAvail));
+ }
+ }
+
+ ALOGW_IF(written != outFrames,
+ "Resampler: frame count mismatch, req %u written %u", outFrames, written);
+
+ return 0;
+}
+
+int Resampler::resample(const void *inBuffer, uint32_t &inFrames,
+ void *outBuffer, uint32_t &outFrames)
+{
+ AutoMutex lock(mLock);
+
+ /* resample */
+ if (mOutParams.channels == 1) {
+ speex_resampler_process_int(mSpeexRsmp,
+ 0,
+ (int16_t *)inBuffer,
+ &inFrames,
+ (int16_t *)outBuffer,
+ &outFrames);
+ } else {
+ speex_resampler_process_interleaved_int(mSpeexRsmp,
+ (int16_t *)inBuffer,
+ &inFrames,
+ (int16_t *)outBuffer,
+ &outFrames);
+ }
+
+ return 0;
+}
+
+void Resampler::createResampler()
+{
+ ALOGV("Resampler: create speex resampler %u to %u Hz",
+ mInParams.sampleRate, mOutParams.sampleRate);
+
+ if (mQuality > SPEEX_RESAMPLER_QUALITY_MAX)
+ mQuality = SPEEX_RESAMPLER_QUALITY_MAX;
+
+ int ret;
+ mSpeexRsmp = speex_resampler_init(mInParams.channels,
+ mInParams.sampleRate,
+ mOutParams.sampleRate,
+ mQuality,
+ &ret);
+ if (!mSpeexRsmp) {
+ ALOGE("Resampler: failed to create Speex resampler: %s",
+ speex_resampler_strerror(ret));
+ return;
+ }
+
+ speex_resampler_reset_mem(mSpeexRsmp);
+ speex_resampler_get_ratio(mSpeexRsmp, &mRatioNum, &mRatioDen);
+}
+
+void Resampler::reallocateBuffer(uint32_t frames)
+{
+ /* current buffer is large enough */
+ if (frames < mBufferFrames)
+ return;
+
+ int16_t *oldBuf = mBuffer.i16;
+ mBufferFrames = frames;
+
+ /* keep the frame count with the headroom frames under the hood */
+ mBuffer.frameCount = mBufferFrames + kHeadRoomFrames;
+ mBuffer.i16 = new int16_t[mBuffer.frameCount * mInParams.channels];
+
+ /* copy frames in the old buffer to the new larger buffer */
+ if (mAvail)
+ memcpy(mBuffer.raw, oldBuf, mInParams.framesToBytes(mAvail));
+
+ if (oldBuf)
+ delete [] oldBuf;
+}
+
+} /* namespace tiaudioutils */
diff --git a/audio/utils/src/SimpleStream.cpp b/audio/utils/src/SimpleStream.cpp
new file mode 100644
index 0000000..5b1ddd5
--- /dev/null
+++ b/audio/utils/src/SimpleStream.cpp
@@ -0,0 +1,605 @@
+/*
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+// #define VERY_VERBOSE_LOGGING
+
+#include <tiaudioutils/Log.h>
+#include <tiaudioutils/SimpleStream.h>
+#include <tiaudioutils/Resampler.h>
+
+namespace tiaudioutils {
+
+SimpleInStream::SimpleInStream(const PcmParams &params)
+ : mParams(params), mReader(NULL), mStarted(false)
+{
+}
+
+SimpleInStream::~SimpleInStream()
+{
+ if (mReader) {
+ ALOGW("SimpleInStream: automatically unregistered");
+ mReader->unregisterStream(this);
+ }
+}
+
+bool SimpleInStream::initCheck() const
+{
+ if (!mParams.isValid()) {
+ ALOGE("SimpleInStream: params are invalid");
+ return false;
+ }
+
+ if (!mParams.frameCount) {
+ ALOGE("SimpleInStream: params.frameCount is invalid");
+ return false;
+ }
+
+ return true;
+}
+
+int SimpleInStream::start()
+{
+ if (!mReader) {
+ ALOGE("SimpleInStream: not registered to reader, cannot start");
+ return -EINVAL;
+ }
+
+ AutoMutex lock(mLock);
+ if (mStarted) {
+ ALOGE("SimpleInStream: stream is already started");
+ return -EBUSY;
+ }
+
+ int ret = mReader->open();
+ if (!ret)
+ mStarted = true;
+ else
+ ALOGE("SimpleInStream: failed to open %d", ret);
+
+ return ret;
+}
+
+void SimpleInStream::stop()
+{
+ if (!mReader) {
+ ALOGE("SimpleInStream: not registered to reader, cannot stop");
+ return;
+ }
+
+ AutoMutex lock(mLock);
+ if (!mStarted) {
+ ALOGE("SimpleInStream: stream is already stopped");
+ return;
+ }
+
+ mStarted = false;
+ mReader->close();
+}
+
+bool SimpleInStream::isStarted() const
+{
+ AutoMutex lock(mLock);
+ return mStarted;
+}
+
+int SimpleInStream::read(void *buffer, size_t frames)
+{
+ if (!mReader) {
+ ALOGE("SimpleInStream: not registered to reader, cannot read");
+ return -EINVAL;
+ }
+
+ AutoMutex lock(mLock);
+ if (!mStarted) {
+ ALOGE("SimpleInStream: stream is not started, cannot read");
+ return -EPERM;
+ }
+
+ return mReader->read(buffer, frames);
+}
+
+/* ---------------------------------------------------------------------------------------- */
+
+SimpleOutStream::SimpleOutStream(const PcmParams &params)
+ : mParams(params), mWriter(NULL), mStarted(false)
+{
+}
+
+SimpleOutStream::~SimpleOutStream()
+{
+ if (mWriter) {
+ ALOGW("SimpleOutStream: automatically unregistered");
+ mWriter->unregisterStream(this);
+ }
+}
+
+bool SimpleOutStream::initCheck() const
+{
+ if (!mParams.isValid()) {
+ ALOGE("SimpleOutStream: params are invalid");
+ return false;
+ }
+
+ if (!mParams.frameCount) {
+ ALOGE("SimpleOutStream: params.frameCount is invalid");
+ return false;
+ }
+
+ return true;
+}
+
+int SimpleOutStream::start()
+{
+ if (!mWriter) {
+ ALOGE("SimpleOutStream: not registered to writer, cannot start");
+ return -EINVAL;
+ }
+
+ AutoMutex lock(mLock);
+ if (mStarted) {
+ ALOGE("SimpleOutStream: stream is already started");
+ return -EBUSY;
+ }
+
+ int ret = mWriter->open();
+ if (!ret)
+ mStarted = true;
+ else
+ ALOGE("SimpleOutStream: failed to open %d", ret);
+
+ return ret;
+}
+
+void SimpleOutStream::stop()
+{
+ if (!mWriter) {
+ ALOGE("SimpleOutStream: not registered to writer, cannot stop");
+ return;
+ }
+
+ AutoMutex lock(mLock);
+ if (!mStarted) {
+ ALOGE("SimpleOutStream: stream is already stopped");
+ return;
+ }
+
+ mStarted = false;
+ mWriter->close();
+}
+
+bool SimpleOutStream::isStarted() const
+{
+ AutoMutex lock(mLock);
+ return mStarted;
+}
+
+int SimpleOutStream::write(const void *buffer, size_t frames)
+{
+ if (!mWriter) {
+ ALOGE("SimpleOutStream: not registered to writer, cannot write");
+ return -EINVAL;
+ }
+
+ AutoMutex lock(mLock);
+ if (!mStarted) {
+ ALOGE("SimpleOutStream: stream is not started, cannot write");
+ return -EPERM;
+ }
+
+ return mWriter->write(buffer, frames);
+}
+
+/* ---------------------------------------------------------------------------------------- */
+
+SimpleReader::SimpleReader(PcmInPort *port, const PcmParams &params)
+ : mPort(port),
+ mParams(params),
+ mStream(NULL),
+ mResampler(NULL),
+ mBufferProvider(*this)
+{
+ /* Intermediate buffer for the frames to be resampled */
+ mBuffer.frameCount = mParams.frameCount;
+ mBuffer.i8 = new int8_t[mParams.bufferSize()];
+}
+
+SimpleReader::~SimpleReader()
+{
+ if (mBuffer.i8)
+ delete [] mBuffer.i8;
+}
+
+bool SimpleReader::initCheck() const
+{
+ if (mPort == NULL) {
+ ALOGE("SimpleReader: invalid PCM input port");
+ return false;
+ }
+
+ if (!mParams.isValid() || !mParams.frameCount) {
+ ALOGE("SimpleReader: params are not valid");
+ return false;
+ }
+
+ if (mBuffer.raw == NULL) {
+ ALOGE("SimpleReader: intermediate buffer allocation failed");
+ return false;
+ }
+
+ return true;
+}
+
+int SimpleReader::registerStream(SimpleInStream *stream)
+{
+ if (!stream) {
+ ALOGE("SimpleReader: stream is invalid, cannot register");
+ return -EINVAL;
+ }
+
+ const PcmParams &outParams = stream->getParams();
+ if (!outParams.isValid() || !outParams.frameCount) {
+ ALOGE("SimpleReader: stream has invalid params");
+ return -EINVAL;
+ }
+
+ ALOGI("SimpleReader: register stream %p", stream);
+
+ AutoMutex lock(mLock);
+ if (mStream) {
+ ALOGE("SimpleReader: reader allows only one stream");
+ return -ENOTSUP;
+ }
+
+ /*
+ * Hardware parameters are not known when the stream is created. It's until
+ * stream registration that we have enough information to determine if the
+ * stream needs resampling, and if so, what the resampling parameters are.
+ * Speex-based resampler is supported only for 16-bits/sample.
+ */
+ if ((mParams.sampleRate != outParams.sampleRate) && (outParams.sampleBits == 16)) {
+ mResampler = new Resampler(mParams, outParams);
+ if (!mResampler) {
+ ALOGE("SimpleReader: failed to create resampler");
+ return -ENODEV;
+ }
+ if (!mResampler->initCheck()) {
+ ALOGE("SimpleReader: failed to initialize resampler");
+ delete mResampler;
+ mResampler = NULL;
+ return -EINVAL;
+ }
+ } else if ((mParams.sampleBits != outParams.sampleBits) ||
+ (mParams.channels != outParams.channels) ||
+ (mParams.sampleRate != outParams.sampleRate)) {
+ ALOGE("SimpleReader: reader doesn't support stream's params");
+ return -EINVAL;
+ }
+
+ stream->mReader = this;
+ mStream = stream;
+
+ return 0;
+}
+
+void SimpleReader::unregisterStream(SimpleInStream *stream)
+{
+ if (!stream) {
+ ALOGE("SimpleReader: stream is invalid, cannot unregister");
+ return;
+ }
+
+ if (stream->isStarted()) {
+ ALOGE("SimpleReader: stream %p is not stopped, cannot unregister", stream);
+ return;
+ }
+
+ ALOGI("SimpleReader: unregister stream %p", stream);
+
+ AutoMutex lock(mLock);
+ if (mStream != stream) {
+ ALOGE("SimpleReader: stream is already unregistered");
+ return;
+ }
+
+ if (mResampler) {
+ delete mResampler;
+ mResampler = NULL;
+ }
+
+ stream->mReader = NULL;
+ mStream = NULL;
+}
+
+int SimpleReader::open()
+{
+ ALOGI("SimpleReader: open PCM port");
+
+ AutoMutex lock(mLock);
+ if (mPort->isOpen()) {
+ ALOGW("SimpleReader: port is already open");
+ return 0;
+ }
+
+ int ret = mPort->open(mParams);
+ if (ret)
+ ALOGE("SimpleReader: failed to open PCM port %d", ret);
+
+ return ret;
+}
+
+void SimpleReader::close()
+{
+ ALOGI("SimpleReader: close PCM port");
+
+ AutoMutex lock(mLock);
+ if (mPort->isOpen())
+ mPort->close();
+ else
+ ALOGW("SimpleReader: PCM port is already closed");
+}
+
+
+int SimpleReader::read(void *buffer, size_t frames)
+{
+ AutoMutex lock(mLock);
+ int ret;
+
+ if (!mPort->isOpen()) {
+ ALOGE("SimpleReader: PCM port is not open");
+ return -ENODEV;
+ }
+
+ if (mResampler) {
+ ret = mResampler->resample(mBufferProvider,
+ buffer, frames);
+ if (ret) {
+ ALOGE("SimpleReader: failed to resample %d", ret);
+ return ret;
+ }
+ ret = frames;
+ } else {
+ ret = mPort->read(buffer, frames);
+ if (ret < 0)
+ ALOGE("SimpleReader: failed to read PCM data %d", ret);
+ }
+
+ return ret;
+}
+
+/* ---------------------------------------------------------------------------------------- */
+
+int SimpleReader::ReadProvider::getNextBuffer(BufferProvider::Buffer *buffer)
+{
+ /* resize buffer if needed */
+ if (buffer->frameCount > mReader.mBuffer.frameCount) {
+ delete [] mReader.mBuffer.i8;
+ mReader.mBuffer.i8 = new int8_t[buffer->frameCount * mReader.mParams.frameSize()];
+ if (mReader.mBuffer.i8) {
+ ALOGE("SimpleReader: failed to resize internal buffer");
+ return -ENOMEM;
+ }
+ mReader.mBuffer.frameCount = buffer->frameCount;
+ }
+
+ int8_t *curBuffer = mReader.mBuffer.i8;
+ uint32_t pending = buffer->frameCount;
+ while (pending > 0) {
+ int read = mReader.mPort->read(curBuffer, pending);
+ if (read < 0) {
+ ALOGE("SimpleReader: failed to read PCM data %d", read);
+ break;
+ }
+ pending -= read;
+ curBuffer += read * mReader.mParams.frameSize();
+ }
+
+ buffer->raw = mReader.mBuffer.raw;
+ buffer->frameCount -= pending;
+
+ ALOGW_IF(pending, "SimpleReader: could not read %d frames", pending);
+
+ return 0;
+}
+
+void SimpleReader::ReadProvider::releaseBuffer(BufferProvider::Buffer *buffer)
+{
+ /* Nothing to do to release the buffer, but must be implemented */
+}
+
+/* ---------------------------------------------------------------------------------------- */
+
+SimpleWriter::SimpleWriter(PcmOutPort *port, const PcmParams &params)
+ : mPort(port),
+ mParams(params),
+ mStream(NULL),
+ mResampler(NULL)
+{
+ /* Intermediate buffer for the resampled frames */
+ mBuffer.frameCount = mParams.frameCount;
+ mBuffer.i8 = new int8_t[mParams.bufferSize()];
+}
+
+SimpleWriter::~SimpleWriter()
+{
+ if (mBuffer.i8)
+ delete [] mBuffer.i8;
+}
+
+bool SimpleWriter::initCheck() const
+{
+ if (mPort == NULL) {
+ ALOGE("SimpleWriter: invalid PCM output port");
+ return false;
+ }
+
+ if (!mParams.isValid() || !mParams.frameCount) {
+ ALOGE("SimpleWriter: params are not valid");
+ return false;
+ }
+
+ if (mBuffer.raw == NULL) {
+ ALOGE("SimpleWriter: intermediate buffer allocation failed");
+ return false;
+ }
+
+ return true;
+}
+
+int SimpleWriter::registerStream(SimpleOutStream *stream)
+{
+ if (!stream) {
+ ALOGE("SimpleWriter: stream is invalid, cannot register");
+ return -EINVAL;
+ }
+
+ const PcmParams &inParams = stream->getParams();
+ if (!inParams.isValid() || !inParams.frameCount) {
+ ALOGE("SimpleWriter: stream has invalid params");
+ return -EINVAL;
+ }
+
+ ALOGI("SimpleWriter: register stream %p", stream);
+
+ AutoMutex lock(mLock);
+ if (mStream) {
+ ALOGE("SimpleWriter: writer allows only one stream");
+ return -ENOTSUP;
+ }
+
+ /*
+ * Hardware parameters are not known when the stream is created. It's until
+ * stream registration that we have enough information to determine if the
+ * stream needs resampling, and if so, what the resampling parameters are.
+ * Speex-based resampler is supported only for 16-bits/sample.
+ */
+ if ((mParams.sampleRate != inParams.sampleRate) && (inParams.sampleBits == 16)) {
+ mResampler = new Resampler(inParams, mParams);
+ if (!mResampler) {
+ ALOGE("SimpleWriter: failed to create resampler");
+ return -ENODEV;
+ }
+ if (!mResampler->initCheck()) {
+ ALOGE("SimpleWriter: failed to initialize resampler");
+ delete mResampler;
+ mResampler = NULL;
+ return -EINVAL;
+ }
+ } else if ((mParams.sampleBits != inParams.sampleBits) ||
+ (mParams.channels != inParams.channels) ||
+ (mParams.sampleRate != inParams.sampleRate)) {
+ ALOGE("SimpleWriter: writer doesn't support stream's params");
+ return -EINVAL;
+ }
+
+ stream->mWriter = this;
+ mStream = stream;
+
+ return 0;
+}
+
+void SimpleWriter::unregisterStream(SimpleOutStream *stream)
+{
+ if (!stream) {
+ ALOGE("SimpleWriter: stream is invalid, cannot unregister");
+ return;
+ }
+
+ if (stream->isStarted()) {
+ ALOGE("SimpleWriter: stream %p is not stopped, cannot unregister", stream);
+ return;
+ }
+
+ ALOGI("SimpleWriter: unregister stream %p", stream);
+
+ AutoMutex lock(mLock);
+ if (mStream != stream) {
+ ALOGE("SimpleWriter: stream is already unregistered");
+ return;
+ }
+
+ if (mResampler) {
+ delete mResampler;
+ mResampler = NULL;
+ }
+
+ stream->mWriter = NULL;
+ mStream = NULL;
+}
+
+int SimpleWriter::open()
+{
+ ALOGI("SimpleWriter: open PCM port");
+
+ AutoMutex lock(mLock);
+ if (mPort->isOpen()) {
+ ALOGW("SimpleWriter: port is already open");
+ return 0;
+ }
+
+ int ret = mPort->open(mParams);
+ if (ret)
+ ALOGE("SimpleWriter: failed to open PCM port %d", ret);
+
+ return ret;
+}
+
+void SimpleWriter::close()
+{
+ ALOGI("SimpleWriter: close PCM port");
+
+ AutoMutex lock(mLock);
+ if (mPort->isOpen())
+ mPort->close();
+ else
+ ALOGW("SimpleWriter: PCM port is already closed");
+}
+
+
+int SimpleWriter::write(const void *buffer, size_t frames)
+{
+ AutoMutex lock(mLock);
+
+ if (!mPort->isOpen()) {
+ ALOGE("SimpleWriter: PCM port is not open");
+ return -ENODEV;
+ }
+
+ const void *outBuffer;
+ uint32_t outFrames;
+ int ret;
+
+ if (mResampler) {
+ outBuffer = mBuffer.raw;
+ outFrames = mBuffer.frameCount;
+ ret = mResampler->resample(buffer, frames, mBuffer.raw, outFrames);
+ if (ret) {
+ ALOGE("SimpleWriter: failed to resample %d", ret);
+ return ret;
+ }
+ } else {
+ outBuffer = buffer;
+ outFrames = frames;
+ }
+
+ ret = mPort->write(outBuffer, outFrames);
+ if (ret < 0)
+ ALOGE("SimpleWriter: failed to write PCM data %d", ret);
+
+ return ret;
+}
+
+} /* namespace tiaudioutils */
diff --git a/audio/utils/src/Stream.cpp b/audio/utils/src/Stream.cpp
new file mode 100644
index 0000000..dad252d
--- /dev/null
+++ b/audio/utils/src/Stream.cpp
@@ -0,0 +1,1088 @@
+/*
+ * Copyright (C) 2013 Texas Instruments
+ *
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+// #define VERY_VERBOSE_LOGGING
+
+#include <tiaudioutils/Log.h>
+#include <tiaudioutils/Stream.h>
+#include <tiaudioutils/Resampler.h>
+
+namespace tiaudioutils {
+
+BufferAdaptor::BufferAdaptor()
+ : mStatus(0)
+{
+ sem_init(&mFill, 0, 0);
+ sem_init(&mEmpty, 0, 1);
+ sem_init(&mDone, 0, 0);
+ mUnderRuns = 0;
+}
+
+BufferAdaptor::~BufferAdaptor()
+{
+ sem_destroy(&mFill);
+ sem_destroy(&mEmpty);
+ sem_destroy(&mDone);
+}
+
+int BufferAdaptor::read(void * const buffer, size_t frames)
+{
+ sem_wait(&mEmpty);
+
+ mMutex.lock();
+ mBuffer = buffer;
+ mFrames = frames;
+ mMutex.unlock();
+
+ sem_post(&mFill);
+
+ sem_wait(&mDone);
+
+ return mStatus;
+}
+
+int BufferAdaptor::write(const void *buffer, size_t frames)
+{
+ sem_wait(&mEmpty);
+
+ mMutex.lock();
+ mBuffer = buffer;
+ mFrames = frames;
+ mMutex.unlock();
+
+ sem_post(&mFill);
+
+ sem_wait(&mDone);
+
+ return mStatus;
+}
+
+int BufferAdaptor::getNextBuffer(BufferProvider::Buffer* buffer)
+{
+ timespec ts;
+
+ /*
+ * Set a 200ms timeout for the semaphore wait call. Waiting indefinitely
+ * may result in a deadlock condition, and waiting for too short a period
+ * can result in glitches in the audio. A value is chosen that is large
+ * enough to give sufficient time to receive the next read/write call and
+ * yet not so large as to noticably block the user in case of error. A
+ * timeout of 200ms should be sufficient to receive the next audio buffer
+ * during normal playback and record.
+ */
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_nsec += (200 * 1000000); /* 200ms timeout */
+ if (ts.tv_nsec > 1000000000) { /* check for wraparound */
+ ts.tv_sec += 1;
+ ts.tv_nsec -= 1000000000;
+ }
+
+ int ret = sem_timedwait(&mFill, &ts);
+
+ mMutex.lock();
+ if (ret) {
+ ALOGW("getNextBuffer: failed to get buffer [%s]", strerror(errno));
+ buffer->raw = NULL;
+ buffer->frameCount = 0;
+ mStatus = ret;
+ mUnderRuns++;
+ }
+ else {
+ buffer->raw = const_cast<void *>(mBuffer);
+ if (buffer->frameCount > mFrames)
+ buffer->frameCount = mFrames;
+ }
+ mMutex.unlock();
+
+ return ret;
+}
+
+void BufferAdaptor::releaseBuffer(BufferProvider::Buffer* buffer)
+{
+ sem_post(&mEmpty);
+
+ /*
+ * Pass the number of read/written frames in case of success,
+ * or the error code otherwise.
+ *
+ * NOTE: The rule is that the buffer's frame count doesn't change
+ * between getNextBuffer() and releaseBuffer().
+ * The exception to that rule is when the frame count is not known
+ * at the time the buffer is acquired.
+ * Specifically, that's the case of resampling with fractional ratio
+ * where the "actual" resampled frame count might vary from one
+ * iteration to another. So, if the resampler produced less frames
+ * than we expected (and got the buffer for), simply update the amount
+ * of frames here.
+ * This is not fair for all providers (particularly write providers)
+ * that would otherwise have to "rewind" to update the actual number
+ * of frames. Read providers only provide a new empty buffer to be filled,
+ * so the "rewind" operation has no side effect.
+ */
+ if (buffer->frameCount)
+ mStatus = buffer->frameCount;
+ else
+ mStatus = (int)buffer->i32;
+
+ sem_post(&mDone);
+}
+
+/* ---------------------------------------------------------------------------------------- */
+
+InStream::InStream(const PcmParams &params,
+ BufferProvider *provider)
+ : mBufferProvider(provider),
+ mParams(params),
+ mMap((1U << params.channels) - 1),
+ mReader(NULL),
+ mResampler(NULL),
+ mStarted(false)
+{
+}
+
+InStream::InStream(const PcmParams &params,
+ const SlotMap &map,
+ BufferProvider *provider)
+ : mBufferProvider(provider),
+ mParams(params),
+ mMap(map),
+ mReader(NULL),
+ mResampler(NULL),
+ mStarted(false)
+{
+}
+
+InStream::~InStream()
+{
+ if (mReader) {
+ ALOGW("InStream: automatically unregistered");
+ if (isStarted())
+ stop();
+ sp<InStream> stream = this;
+ mReader->unregisterStream(stream);
+ }
+}
+
+bool InStream::initCheck() const
+{
+ if (mBufferProvider == NULL) {
+ ALOGE("InStream: buffer provider is invalid");
+ return false;
+ }
+
+ if (!mMap.isValid()) {
+ ALOGE("InStream: slot map is invalid");
+ return false;
+ }
+
+ if (!mParams.isValid()) {
+ ALOGE("InStream: params are invalid");
+ return false;
+ }
+
+ if (!mParams.frameCount) {
+ ALOGE("InStream: params.frameCount is invalid");
+ return false;
+ }
+
+ if (mMap.getDstMask() >= (1U << mParams.channels)) {
+ ALOGE("InStream: dest mask is invalid (too many channels %u)",
+ mMap.getChannelCount());
+ return false;
+ }
+
+ return true;
+}
+
+int InStream::start()
+{
+ if (!mReader) {
+ ALOGE("InStream: not registered to reader, cannot start");
+ return -EINVAL;
+ }
+
+ AutoMutex lock(mLock);
+ if (mStarted) {
+ ALOGE("InStream: stream is already started");
+ return -EBUSY;
+ }
+
+ int ret = mReader->open();
+ if (!ret)
+ mStarted = true;
+ else
+ ALOGE("InStream: failed to open %d", ret);
+
+ return ret;
+}
+
+void InStream::stop()
+{
+ if (!mReader) {
+ ALOGE("InStream: not registered to reader, cannot stop");
+ return;
+ }
+
+ AutoMutex lock(mLock);
+ if (!mStarted) {
+ ALOGE("InStream: stream is already stopped");
+ return;
+ }
+
+ mStarted = false;
+ mReader->close();
+}
+
+bool InStream::isStarted() const
+{
+ AutoMutex lock(mLock);
+ return mStarted;
+}
+
+int InStream::read(void *buffer, size_t frames)
+{
+ ALOGE("InStream: not supported by the stream type");
+ return -ENOTSUP;
+}
+
+int InStream::getNextBuffer(BufferProvider::Buffer *buffer)
+{
+ int ret;
+
+ if (canResample())
+ ret = getNextBufferForResample(buffer);
+ else
+ ret = mBufferProvider->getNextBuffer(buffer);
+
+ return ret;
+}
+
+void InStream::releaseBuffer(BufferProvider::Buffer *buffer)
+{
+ if (canResample())
+ releaseResampledBuffer(buffer);
+ else
+ mBufferProvider->releaseBuffer(buffer);
+}
+
+int InStream::getNextBufferForResample(BufferProvider::Buffer *buffer)
+{
+ /*
+ * The buffer requested to this method is in the native sample rate
+ * (at which PcmReader or UnMerge operate). We pass our internal
+ * temporary buffer from which we resample after it has been filled up.
+ */
+ buffer->raw = mRsmpBuffer.raw;
+ buffer->frameCount = mRsmpBuffer.frameCount;
+
+ return 0;
+}
+
+void InStream::releaseResampledBuffer(BufferProvider::Buffer *buffer)
+{
+ BufferProvider::Buffer outBuffer;
+ int8_t *curBuffer = buffer->i8;
+ int32_t pending = buffer->frameCount;
+ uint32_t ratioNum;
+ uint32_t ratioDen;
+
+ mResampler->getRatio(ratioNum, ratioDen);
+
+ /* resample all frames in the released output buffer */
+ while (pending > 0) {
+ /*
+ * Request a buffer to our internal provider, whose size is proportional
+ * to the resampling ratio.
+ */
+ outBuffer.frameCount = (pending * ratioDen) / ratioNum + 1;
+ int ret = mBufferProvider->getNextBuffer(&outBuffer);
+ if (ret) {
+ ALOGE("InStream: failed to get buffer from provider %d", ret);
+ return;
+ }
+
+ uint32_t inFrames = pending;
+ uint32_t outFrames = outBuffer.frameCount;
+ ret = mResampler->resample(curBuffer, inFrames, outBuffer.raw, outFrames);
+ if (ret) {
+ ALOGW("InStream: failed to resample %d", ret);
+ outBuffer.i32 = (int32_t*)ret;
+ outBuffer.frameCount = 0;
+ } else {
+ /*
+ * NOTE: Consciously change the frame count of the acquired buffer.
+ * This is an exception for read providers, otherwise we would have
+ * to resample in a temporary buffer and once the "produced" number
+ * of samples is known, acquire the buffer and copy the audio frames.
+ */
+ outBuffer.frameCount = outFrames;
+ pending -= inFrames;
+ curBuffer += mParams.framesToBytes(inFrames);
+ }
+
+ mBufferProvider->releaseBuffer(&outBuffer);
+ }
+
+ ALOGW_IF(pending, "InStream: unexpected pending frame count %d", pending);
+}
+
+/* ---------------------------------------------------------------------------------------- */
+
+AdaptedInStream::AdaptedInStream(const PcmParams &params)
+ : InStream(params, &mAdaptor)
+{
+}
+
+AdaptedInStream::AdaptedInStream(const PcmParams &params,
+ const SlotMap &map)
+ : InStream(params, map, &mAdaptor)
+{
+}
+
+int AdaptedInStream::read(void *buffer, size_t frames)
+{
+ AutoMutex lock(mLock);
+ if (!mStarted) {
+ ALOGE("AdaptedInStream: stream is not started, cannot read");
+ return -EPERM;
+ }
+
+ return mAdaptor.read(buffer, frames);
+}
+
+/* ---------------------------------------------------------------------------------------- */
+
+BufferedInStream::BufferedInStream(const PcmParams &params,
+ uint32_t frames)
+ : InStream(params), mPipe(params, frames), mPipeWriter(&mPipe)
+{
+ mBufferProvider = &mPipeWriter;
+}
+
+BufferedInStream::BufferedInStream(const PcmParams &params,
+ const SlotMap &map,
+ uint32_t frames)
+ : InStream(params, map), mPipe(params, frames), mPipeWriter(&mPipe)
+{
+ mBufferProvider = &mPipeWriter;
+}
+
+bool BufferedInStream::initCheck() const
+{
+ return (InStream::initCheck() &&
+ mPipe.initCheck() &&
+ mPipeWriter.initCheck());
+}
+
+int BufferedInStream::read(void *buffer, size_t frames)
+{
+ AutoMutex lock(mLock);
+ if (!mStarted) {
+ ALOGE("BufferedInStream: stream is not started, cannot read");
+ return -EPERM;
+ }
+
+ return mPipe.read(buffer, frames);
+}
+
+/* ---------------------------------------------------------------------------------------- */
+
+OutStream::OutStream(const PcmParams &params,
+ BufferProvider *provider)
+ : mBufferProvider(provider),
+ mParams(params),
+ mMap((1U << params.channels) - 1),
+ mWriter(NULL),
+ mResampler(NULL),
+ mStarted(false)
+{
+}
+
+OutStream::OutStream(const PcmParams &params,
+ const SlotMap &map,
+ BufferProvider *provider)
+ : mBufferProvider(provider),
+ mParams(params),
+ mMap(map),
+ mWriter(NULL),
+ mResampler(NULL),
+ mStarted(false)
+{
+}
+
+OutStream::~OutStream()
+{
+ if (mWriter) {
+ ALOGW("OutStream: automatically unregistered");
+ if (isStarted())
+ stop();
+ sp<OutStream> stream = this;
+ mWriter->unregisterStream(stream);
+ }
+}
+
+bool OutStream::initCheck() const
+{
+ if (mBufferProvider == NULL) {
+ ALOGE("OutStream: buffer provider is invalid");
+ return false;
+ }
+
+ if (!mMap.isValid()) {
+ ALOGE("OutStream: slot map is invalid");
+ return false;
+ }
+
+ if (!mParams.isValid()) {
+ ALOGE("OutStream: params are invalid");
+ return false;
+ }
+
+ if (!mParams.frameCount) {
+ ALOGE("OutStream: params.frameCount is invalid");
+ return false;
+ }
+
+ if (mMap.getSrcMask() >= (1U << mParams.channels)) {
+ ALOGE("OutStream: source mask is invalid (too many channels %u)",
+ mMap.getChannelCount());
+ return false;
+ }
+
+ return true;
+}
+
+int OutStream::start()
+{
+ if (!mWriter) {
+ ALOGE("OutStream: not registered to writer, cannot start");
+ return -EINVAL;
+ }
+
+ AutoMutex lock(mLock);
+ if (mStarted) {
+ ALOGE("OutStream: stream is already started");
+ return -EBUSY;
+ }
+
+ int ret = mWriter->open();
+ if (!ret)
+ mStarted = true;
+ else
+ ALOGE("OutStream: failed to open %d", ret);
+
+ return ret;
+}
+
+void OutStream::stop()
+{
+ if (!mWriter) {
+ ALOGE("OutStream: not registered to writer, cannot stop");
+ return;
+ }
+
+ AutoMutex lock(mLock);
+ if (!mStarted) {
+ ALOGE("OutStream: stream is already stopped");
+ return;
+ }
+
+ mStarted = false;
+ mWriter->close();
+}
+
+bool OutStream::isStarted() const
+{
+ AutoMutex lock(mLock);
+ return mStarted;
+}
+
+int OutStream::write(const void *buffer, size_t frames)
+{
+ ALOGE("OutStream: not supported by the stream type");
+ return -ENOTSUP;
+}
+
+int OutStream::getNextBuffer(BufferProvider::Buffer *buffer)
+{
+ int ret;
+
+ if (canResample())
+ ret = getNextResampledBuffer(buffer);
+ else
+ ret = mBufferProvider->getNextBuffer(buffer);
+
+ return ret;
+}
+
+void OutStream::releaseBuffer(BufferProvider::Buffer *buffer)
+{
+ if (!canResample())
+ mBufferProvider->releaseBuffer(buffer);
+}
+
+int OutStream::getNextResampledBuffer(BufferProvider::Buffer *buffer)
+{
+ int ret;
+
+ if (buffer->frameCount > mRsmpBuffer.frameCount)
+ buffer->frameCount = mRsmpBuffer.frameCount;
+
+ ret = mResampler->resample(*mBufferProvider,
+ mRsmpBuffer.raw,
+ mRsmpBuffer.frameCount);
+ if (ret) {
+ ALOGE("OutStream: failed to resample %d", ret);
+ return ret;
+ }
+
+ buffer->raw = mRsmpBuffer.raw;
+ buffer->frameCount = mRsmpBuffer.frameCount;
+
+ return ret;
+}
+
+/* ---------------------------------------------------------------------------------------- */
+
+AdaptedOutStream::AdaptedOutStream(const PcmParams &params)
+ : OutStream(params, &mAdaptor)
+{
+}
+
+AdaptedOutStream::AdaptedOutStream(const PcmParams &params,
+ const SlotMap &map)
+ : OutStream(params, map, &mAdaptor)
+{
+}
+
+int AdaptedOutStream::write(const void *buffer, size_t frames)
+{
+ AutoMutex lock(mLock);
+ if (!mStarted) {
+ ALOGE("AdaptedOutStream: stream is not started, cannot write");
+ return -EPERM;
+ }
+
+ return mAdaptor.write(buffer, frames);
+}
+
+/* ---------------------------------------------------------------------------------------- */
+
+BufferedOutStream::BufferedOutStream(const PcmParams &params,
+ uint32_t frames)
+ : OutStream(params), mPipe(params, frames), mPipeReader(&mPipe)
+{
+ mBufferProvider = &mPipeReader;
+}
+
+BufferedOutStream::BufferedOutStream(const PcmParams &params,
+ const SlotMap &map,
+ uint32_t frames)
+ : OutStream(params, map), mPipe(params, frames), mPipeReader(&mPipe)
+{
+ mBufferProvider = &mPipeReader;
+}
+
+bool BufferedOutStream::initCheck() const
+{
+ return (OutStream::initCheck() &&
+ mPipe.initCheck() &&
+ mPipeReader.initCheck());
+}
+
+int BufferedOutStream::write(const void *buffer, size_t frames)
+{
+ AutoMutex lock(mLock);
+ if (!mStarted) {
+ ALOGE("BufferedOutStream: stream is not started, cannot write");
+ return -EPERM;
+ }
+
+ return mPipe.write(buffer, frames);
+}
+
+/* ---------------------------------------------------------------------------------------- */
+
+PcmReader::PcmReader(PcmInPort *port, const PcmParams &params)
+ : ThreadBase("PcmReader"),
+ mPort(port),
+ mParams(params),
+ mUnMerge(params),
+ mUsers(0)
+{
+ mBuffer.i8 = new int8_t[mParams.bufferSize()];
+ mBuffer.frameCount = mParams.frameCount;
+}
+
+PcmReader::~PcmReader()
+{
+ for (StreamSet::iterator i = mStreams.begin(); i != mStreams.end(); ++i) {
+ sp<InStream> stream = (*i).promote();
+ if (stream == 0)
+ continue;
+ ALOGW("PcmReader: automatically unregistering stream %p", stream.get());
+ unregisterStream(stream);
+ }
+
+ if (mPort && mPort->isOpen())
+ mPort->close();
+
+ if (mBuffer.i8) {
+ delete [] mBuffer.i8;
+ mBuffer.i8 = NULL;
+ }
+}
+
+bool PcmReader::initCheck() const
+{
+ if (mPort == NULL) {
+ ALOGE("PcmReader: invalid PCM input port");
+ return false;
+ }
+
+ if (!mParams.isValid() || !mParams.frameCount) {
+ ALOGE("PcmReader: params are not valid");
+ return false;
+ }
+
+ if (mBuffer.raw == NULL) {
+ ALOGE("PcmReader: intermediate buffer allocation failed");
+ return false;
+ }
+
+ if (!mUnMerge.initCheck()) {
+ ALOGE("PcmReader: un-merge failed to initialize");
+ return false;
+ }
+
+ return true;
+}
+
+int PcmReader::registerStream(sp<InStream>& stream)
+{
+ if (stream == NULL) {
+ ALOGE("PcmReader: stream is invalid, cannot register");
+ return -EINVAL;
+ }
+
+ const PcmParams &outParams = stream->getParams();
+ if (!outParams.isValid() || !outParams.frameCount) {
+ ALOGE("PcmReader: stream has invalid params");
+ return -EINVAL;
+ }
+
+ ALOGI("PcmReader: register stream %p src 0x%04x dst 0x%04x",
+ stream.get(), stream->mMap.getSrcMask(), stream->mMap.getDstMask());
+
+ AutoMutex lock(mLock);
+ if (mStreams.find(stream) != mStreams.end()) {
+ ALOGE("PcmReader: stream is already registered");
+ return -EINVAL;
+ }
+
+ /*
+ * Hardware parameters are not known when the stream is created. It's not
+ * until stream registration that we have enough information to determine
+ * if the stream needs resampling, and if so, what the resampling parameters
+ * are. Speex-based resampler is supported only for 16-bits/sample.
+ */
+ if ((mParams.sampleRate != outParams.sampleRate) && (outParams.sampleBits == 16)) {
+ int ret = attachResampler(stream.get());
+ if (ret) {
+ ALOGE("PcmReader: failed to create and attached resampler");
+ return ret;
+ }
+ } else if ((mParams.sampleBits != outParams.sampleBits) ||
+ (mParams.sampleRate != outParams.sampleRate)) {
+ /* Channels of the reader and stream are different if un-merge is used */
+ ALOGE("PcmReader: reader doesn't support stream's params");
+ return -EINVAL;
+ }
+
+ int ret = mUnMerge.registerStream(stream);
+ if (ret) {
+ ALOGE("PcmReader: failed to register stream %d", ret);
+ detachResampler(stream.get());
+ return ret;
+ }
+
+ stream->mReader = this;
+
+ mStreams.insert(stream);
+
+ return 0;
+}
+
+void PcmReader::unregisterStream(sp<InStream>& stream)
+{
+ if (stream == NULL) {
+ ALOGE("PcmReader: stream is invalid, cannot unregister");
+ return;
+ }
+
+ if (stream->isStarted()) {
+ ALOGE("PcmReader: stream %p is not stopped, cannot unregister",
+ stream.get());
+ return;
+ }
+
+ ALOGI("PcmReader: unregister stream %p src 0x%04x dst 0x%04x",
+ stream.get(), stream->mMap.getSrcMask(), stream->mMap.getDstMask());
+
+ AutoMutex lock(mLock);
+ if (mStreams.find(stream) != mStreams.end()) {
+ mUnMerge.unregisterStream(stream);
+ detachResampler(stream.get());
+ stream->mReader = NULL;
+ mStreams.erase(stream);
+ } else {
+ ALOGE("PcmReader: stream is not registered or already unregistered");
+ }
+}
+
+int PcmReader::open()
+{
+ AutoMutex lock(mLock);
+
+ ALOGV("PcmReader: users %d->%d", mUsers, mUsers+1);
+ if (mUsers++)
+ return 0;
+
+ ALOGV("PcmReader: open PCM port and start reader thread");
+ int ret = mPort->open(mParams);
+ if (ret) {
+ ALOGE("PcmReader: failed to open PCM port %d", ret);
+ return ret;
+ }
+
+ /* Start the reader thread */
+ ret = run();
+ if (ret)
+ ALOGE("PcmReader: failed to start reader thread %d", ret);
+
+ return ret;
+}
+
+void PcmReader::close()
+{
+ AutoMutex lock(mLock);
+
+ ALOGV("PcmReader: users %d->%d", mUsers, mUsers-1);
+ ALOG_ASSERT(mUsers);
+ if (--mUsers)
+ return;
+
+ ALOGV("PcmReader: stop reader thread and close PCM port");
+ stop();
+ mPort->close();
+}
+
+int PcmReader::threadFunc()
+{
+ int ret = mPort->read(mBuffer.raw, mParams.frameCount);
+ if (ret < 0) {
+ ALOGE("PcmReader: failed to read PCM data %d", ret);
+ /*
+ * Set frameCount to 0, this will result in an error being returned to
+ * the buffer adaptor read call after the unmerge process call
+ */
+ mBuffer.frameCount = 0;
+ }
+ else {
+ if (ret != (int)mBuffer.frameCount) {
+ ALOGW("PcmReader: read fewer PCM frames than requested %d", ret);
+ }
+ /* Set the frame count to the actual amount read */
+ mBuffer.frameCount = (size_t)ret;
+ }
+
+ ret = mUnMerge.process(mBuffer);
+ if (ret) {
+ ALOGE("PcmReader: unmerge failed %d", ret);
+ }
+
+ return 0;
+}
+
+int PcmReader::attachResampler(InStream *stream)
+{
+ PcmParams inParams = mParams;
+ const PcmParams &outParams = stream->getParams();
+
+ inParams.channels = outParams.channels;
+
+ /* Intermediate buffer for the frames to be resampled */
+ stream->mRsmpBuffer.frameCount = inParams.frameCount;
+ stream->mRsmpBuffer.i8 = new int8_t[inParams.frameCount * inParams.frameSize()];
+ if (!stream->mRsmpBuffer.i8)
+ return -ENOMEM;
+
+ stream->mResampler = new Resampler(inParams, outParams);
+ if (!stream->mResampler || !stream->mResampler->initCheck()) {
+ detachResampler(stream);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+void PcmReader::detachResampler(InStream *stream)
+{
+ if (stream->mResampler) {
+ delete stream->mResampler;
+ stream->mResampler = NULL;
+ }
+
+ if (stream->mRsmpBuffer.i8) {
+ delete [] stream->mRsmpBuffer.i8;
+ stream->mRsmpBuffer.i8 = NULL;
+ }
+}
+
+/* ---------------------------------------------------------------------------------------- */
+
+PcmWriter::PcmWriter(PcmOutPort *port, const PcmParams &params)
+ : ThreadBase("PcmWriter"),
+ mPort(port),
+ mParams(params),
+ mMerge(params),
+ mUsers(0)
+{
+ mBuffer.i8 = new int8_t[mParams.bufferSize()];
+ mBuffer.frameCount = mParams.frameCount;
+}
+
+PcmWriter::~PcmWriter()
+{
+ for (StreamSet::iterator i = mStreams.begin(); i != mStreams.end(); ++i) {
+ sp<OutStream> stream = (*i).promote();
+ if (stream == 0)
+ continue;
+ ALOGW("PcmWriter: automatically unregistering stream %p", stream.get());
+ unregisterStream(stream);
+ }
+
+ if (mPort && mPort->isOpen())
+ mPort->close();
+
+ if (mBuffer.i8) {
+ delete [] mBuffer.i8;
+ mBuffer.i8 = NULL;
+ }
+}
+
+bool PcmWriter::initCheck() const
+{
+ if (mPort == NULL) {
+ ALOGE("PcmWriter: invalid PCM input port");
+ return false;
+ }
+
+ if (!mParams.isValid() || !mParams.frameCount) {
+ ALOGE("PcmWriter: params are not valid");
+ return false;
+ }
+
+ if (mBuffer.raw == NULL) {
+ ALOGE("PcmWriter: intermediate buffer allocation failed");
+ return false;
+ }
+
+ if (!mMerge.initCheck()) {
+ ALOGE("PcmWriter: merge failed to initialize");
+ return false;
+ }
+
+ return true;
+}
+
+int PcmWriter::registerStream(sp<OutStream>& stream)
+{
+ if (stream == NULL) {
+ ALOGE("PcmWriter: stream is invalid, cannot register");
+ return -EINVAL;
+ }
+
+ const PcmParams &inParams = stream->getParams();
+ if (!inParams.isValid() || !inParams.frameCount) {
+ ALOGE("PcmWriter: stream has invalid params");
+ return -EINVAL;
+ }
+
+ ALOGI("PcmWriter: register stream %p src 0x%04x dst 0x%04x",
+ stream.get(), stream->mMap.getSrcMask(), stream->mMap.getDstMask());
+
+ AutoMutex lock(mLock);
+ if (mStreams.find(stream) != mStreams.end()) {
+ ALOGE("PcmWriter: stream is already registered");
+ return -EINVAL;
+ }
+
+ /*
+ * Hardware parameters are not known when the stream is created. It's not
+ * until stream registration that we have enough information to determine if
+ * the stream needs resampling, and if so, what the resampling parameters
+ * are. Speex-based resampler is supported only for 16-bits/sample.
+ */
+ if ((mParams.sampleRate != inParams.sampleRate) && (inParams.sampleBits == 16)) {
+ int ret = attachResampler(stream.get());
+ if (ret) {
+ ALOGE("PcmWriter: failed to create and attached resampler");
+ return ret;
+ }
+ } else if ((mParams.sampleBits != inParams.sampleBits) ||
+ (mParams.sampleRate != inParams.sampleRate)) {
+ /* Channels of the writer and stream are different if merge is used */
+ ALOGE("PcmWriter: writer doesn't support stream's params");
+ return -EINVAL;
+ }
+
+ int ret = mMerge.registerStream(stream);
+ if (ret) {
+ ALOGE("PcmWriter: failed to register stream %d", ret);
+ detachResampler(stream.get());
+ return ret;
+ }
+
+ stream->mWriter = this;
+
+ mStreams.insert(stream);
+
+ return 0;
+}
+
+void PcmWriter::unregisterStream(sp<OutStream>& stream)
+{
+ if (stream == NULL) {
+ ALOGE("PcmWriter: stream is invalid, cannot unregister");
+ return;
+ }
+
+ if (stream->isStarted()) {
+ ALOGE("PcmWriter: stream %p is not stopped, cannot unregister",
+ stream.get());
+ return;
+ }
+
+ ALOGI("PcmWriter: unregister stream %p src 0x%04x dst 0x%04x",
+ stream.get(), stream->mMap.getSrcMask(), stream->mMap.getDstMask());
+
+ AutoMutex lock(mLock);
+ if (mStreams.find(stream) != mStreams.end()) {
+ mMerge.unregisterStream(stream);
+ detachResampler(stream.get());
+ stream->mWriter = NULL;
+ mStreams.erase(stream);
+ } else {
+ ALOGE("PcmWriter: stream is not registered or already unregistered");
+ }
+}
+
+int PcmWriter::open()
+{
+ AutoMutex lock(mLock);
+
+ ALOGV("PcmWriter: users %d->%d", mUsers, mUsers+1);
+ if (mUsers++)
+ return 0;
+
+ ALOGV("PcmWriter: open PCM port and start writer thread");
+ int ret = mPort->open(mParams);
+ if (ret) {
+ ALOGE("PcmWriter: failed to open PCM port %d", ret);
+ return ret;
+ }
+
+ /* Start the writer thread */
+ ret = run();
+ if (ret)
+ ALOGE("PcmWriter: failed to start writer thread %d", ret);
+
+ return ret;
+}
+
+void PcmWriter::close()
+{
+ AutoMutex lock(mLock);
+
+ ALOGV("PcmWriter: users %d->%d", mUsers, mUsers-1);
+ ALOG_ASSERT(mUsers);
+ if (--mUsers)
+ return;
+
+ ALOGV("PcmWriter: stop writer thread and close PCM port");
+ stop();
+ mPort->close();
+}
+
+int PcmWriter::threadFunc()
+{
+ int ret = mMerge.process(mBuffer);
+ if (ret) {
+ ALOGE("PcmWriter: error processing data from provider %d", ret);
+ }
+
+ ret = mPort->write(mBuffer.raw, mBuffer.frameCount);
+ if (ret < 0) {
+ ALOGE("PcmWriter: failed to write PCM data %d", ret);
+ /*
+ * mPort->write errors will not propagate to the user through the buffer
+ * adaptor. So, perform the sleep here to lessen the number of errors.
+ */
+ uint32_t usecs = (mBuffer.frameCount * 1000) / mParams.sampleRate;
+ usleep(usecs);
+ }
+
+ return 0;
+}
+
+int PcmWriter::attachResampler(OutStream *stream)
+{
+ const PcmParams &inParams = stream->getParams();
+ PcmParams outParams = mParams;
+
+ outParams.channels = inParams.channels;
+
+ /* Intermediate buffer for the resampled frames */
+ stream->mRsmpBuffer.frameCount = outParams.frameCount;
+ stream->mRsmpBuffer.i8 = new int8_t[outParams.frameCount * outParams.frameSize()];
+ if (!stream->mRsmpBuffer.i8)
+ return -ENOMEM;
+
+ stream->mResampler = new Resampler(inParams, outParams);
+ if (!stream->mResampler || !stream->mResampler->initCheck()) {
+ detachResampler(stream);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+void PcmWriter::detachResampler(OutStream *stream)
+{
+ if (stream->mResampler) {
+ delete stream->mResampler;
+ stream->mResampler = NULL;
+ }
+
+ if (stream->mRsmpBuffer.i8) {
+ delete [] stream->mRsmpBuffer.i8;
+ stream->mRsmpBuffer.i8 = NULL;
+ }
+}
+
+} /* namespace tiaudioutils */