diff options
author | Misael Lopez Cruz <misael.lopez@ti.com> | 2013-10-23 01:32:27 -0500 |
---|---|---|
committer | Angela Stegmaier <a0866189@ti.com> | 2013-11-01 11:42:55 -0500 |
commit | 5aadbddedac5f56c83535c45c6c940ef67c96b5d (patch) | |
tree | 8440df0f50fec7fe4758cc8e365096ad32c4f255 | |
parent | c81ce74a427ed2112cf82a9d012b932d929a5afa (diff) | |
download | common-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>
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 ¶ms); + + /** + * \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 ¶ms); + + /** + * \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 ¶ms, 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 ¶ms); + + /** + * \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 ¶ms); + + /** + * \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 ¶ms) = 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 ¶ms, + 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 ¶ms); + + /** + * \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 ¶ms); + + /** + * \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 ¶ms); + + /** + * \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 ¶ms); + + /** + * \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 ¶ms, + 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 ¶ms, + 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 ¶ms); + + /** + * \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 ¶ms, + 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 ¶ms, + 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 ¶ms, + 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 ¶ms, + 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 ¶ms, + 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 ¶ms); + + /** + * \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 ¶ms, + 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 ¶ms, + 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 ¶ms, + 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 ¶ms); + + /** + * \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 ¶ms); + + /** + * \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 ¶ms) +{ + 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 ¶ms) +{ + 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 ¶ms, 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 ¶ms) + : 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 ¶ms = 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 ¶ms) + : 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 ¶ms = 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 ¶ms, 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 ¶ms) + : 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 ¶ms) + : 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 ¶ms) + : 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 ¶ms) + : 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 ¶ms, + BufferProvider *provider) + : mBufferProvider(provider), + mParams(params), + mMap((1U << params.channels) - 1), + mReader(NULL), + mResampler(NULL), + mStarted(false) +{ +} + +InStream::InStream(const PcmParams ¶ms, + 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 ¶ms) + : InStream(params, &mAdaptor) +{ +} + +AdaptedInStream::AdaptedInStream(const PcmParams ¶ms, + 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 ¶ms, + uint32_t frames) + : InStream(params), mPipe(params, frames), mPipeWriter(&mPipe) +{ + mBufferProvider = &mPipeWriter; +} + +BufferedInStream::BufferedInStream(const PcmParams ¶ms, + 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 ¶ms, + BufferProvider *provider) + : mBufferProvider(provider), + mParams(params), + mMap((1U << params.channels) - 1), + mWriter(NULL), + mResampler(NULL), + mStarted(false) +{ +} + +OutStream::OutStream(const PcmParams ¶ms, + 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 ¶ms) + : OutStream(params, &mAdaptor) +{ +} + +AdaptedOutStream::AdaptedOutStream(const PcmParams ¶ms, + 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 ¶ms, + uint32_t frames) + : OutStream(params), mPipe(params, frames), mPipeReader(&mPipe) +{ + mBufferProvider = &mPipeReader; +} + +BufferedOutStream::BufferedOutStream(const PcmParams ¶ms, + 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 ¶ms) + : 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 ¶ms) + : 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 */ |