├── .gitignore ├── AUTHORS ├── Android.mk ├── COPYING ├── ChangeLog ├── INSTALL ├── Makefile.am ├── NEWS ├── README ├── autogen.sh ├── configure.ac ├── gitcompile ├── include ├── Makefile.am ├── sound │ ├── compress_offload.h │ └── compress_params.h └── tinycompress │ ├── compress_ops.h │ ├── tinycompress.h │ ├── tinymp3.h │ ├── tinywave.h │ └── version.h ├── m4 └── .place_holder ├── src ├── Makefile.am ├── lib │ ├── Makefile.am │ ├── compress.c │ └── compress_hw.c ├── utils-lgpl │ ├── Makefile.am │ └── fcplay.c └── utils │ ├── Makefile.am │ ├── cplay.c │ ├── crecord.c │ └── wave.c └── tinycompress.pc.in /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | *.o 3 | *.so 4 | *.la 5 | *.lo 6 | *~ 7 | configure 8 | config.log 9 | config.cache 10 | config.status 11 | config.guess 12 | config.sub 13 | config.h 14 | config.h.in 15 | Makefile 16 | Makefile.in 17 | libtool 18 | depcomp 19 | ltmain.sh 20 | aclocal.m4 21 | autom4te.cache 22 | compile 23 | install-sh 24 | missing 25 | stamp-h1 26 | m4/libtool.m4 27 | m4/ltoptions.m4 28 | m4/ltsugar.m4 29 | m4/ltversion.m4 30 | m4/lt~obsolete.m4 31 | 32 | # binary ignores 33 | cplay 34 | crecord 35 | fcplay 36 | 37 | # other ignores 38 | *.patch 39 | cscope.* 40 | tags 41 | tinycompress.pc 42 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | To see author of various changes in this project, please run the below git 2 | command 3 | 4 | $ git log --format='%aN'| sort -u 5 | -------------------------------------------------------------------------------- /Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH:= $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | LOCAL_C_INCLUDES:= $(LOCAL_PATH)/include 5 | LOCAL_SRC_FILES:= src/lib/compress.c src/lib/compress_hw.c 6 | LOCAL_MODULE := libtinycompress 7 | LOCAL_SHARED_LIBRARIES:= libcutils libutils 8 | LOCAL_MODULE_TAGS := optional 9 | LOCAL_PRELINK_MODULE := false 10 | 11 | include $(BUILD_SHARED_LIBRARY) 12 | 13 | include $(CLEAR_VARS) 14 | 15 | LOCAL_C_INCLUDES:= $(LOCAL_PATH)/include 16 | LOCAL_SRC_FILES:= src/utils/cplay.c 17 | LOCAL_MODULE := cplay 18 | LOCAL_SHARED_LIBRARIES:= libcutils libutils libtinycompress 19 | LOCAL_MODULE_TAGS := optional 20 | 21 | include $(BUILD_EXECUTABLE) 22 | 23 | include $(CLEAR_VARS) 24 | 25 | LOCAL_C_INCLUDES:= $(LOCAL_PATH)/include 26 | LOCAL_SRC_FILES:= src/utils/crec.c 27 | LOCAL_MODULE := crec 28 | LOCAL_SHARED_LIBRARIES:= libcutils libutils libtinycompress 29 | LOCAL_MODULE_TAGS := optional 30 | 31 | include $(BUILD_EXECUTABLE) 32 | 33 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | tinycompress is provided under a dual BSD/LGPLv2.1 license. When using or 2 | redistributing tinycompress, you may do so under either license. 3 | 4 | BSD LICENSE 5 | 6 | tinycompress library for compress audio offload in alsa 7 | Copyright (c) 2011-2012, Intel Corporation 8 | All rights reserved. 9 | 10 | Author: Vinod Koul 11 | 12 | Redistribution and use in source and binary forms, with or without 13 | modification, are permitted provided that the following conditions are met: 14 | 15 | Redistributions of source code must retain the above copyright notice, 16 | this list of conditions and the following disclaimer. 17 | Redistributions in binary form must reproduce the above copyright notice, 18 | this list of conditions and the following disclaimer in the documentation 19 | and/or other materials provided with the distribution. 20 | Neither the name of Intel Corporation nor the names of its contributors 21 | may be used to endorse or promote products derived from this software 22 | without specific prior written permission. 23 | 24 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 28 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 29 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 31 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 32 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 34 | THE POSSIBILITY OF SUCH DAMAGE. 35 | 36 | LGPL LICENSE 37 | 38 | tinycompress library for compress audio offload in alsa 39 | Copyright (c) 2011-2012, Intel Corporation. 40 | 41 | 42 | This program is free software; you can redistribute it and/or modify it 43 | under the terms and conditions of the GNU Lesser General Public License, 44 | version 2.1, as published by the Free Software Foundation. 45 | 46 | This program is distributed in the hope it will be useful, but WITHOUT 47 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 48 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 49 | License for more details. 50 | 51 | You should have received a copy of the GNU Lesser General Public License 52 | along with this program; if not, write to 53 | the Free Software Foundation, Inc., 54 | 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 55 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | For changelog please see the git log of the project 2 | 3 | $ git log 4 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | 2 | Tinycompress Installation Instructions 3 | ************************************** 4 | 5 | Install from tarball 6 | ==================== 7 | 8 | For installation you can use these commands: 9 | 10 | ./configure 11 | make install 12 | 13 | 14 | Compilation from Git 15 | ==================== 16 | 17 | gitcompile script allows to build and then install 18 | 19 | ./gitcompile 20 | make install 21 | 22 | 23 | Compilation for Android 24 | ======================= 25 | 26 | Android makefile Android.mk is included in this project and should be 27 | packaged into Android build system 28 | 29 | 30 | Configure help 31 | ============== 32 | 33 | Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, 34 | Inc. 35 | 36 | Copying and distribution of this file, with or without modification, 37 | are permitted in any medium without royalty provided the copyright 38 | notice and this notice are preserved. This file is offered as-is, 39 | without warranty of any kind. 40 | 41 | Basic Installation 42 | ================== 43 | 44 | Briefly, the shell command `./configure && make && make install' 45 | should configure, build, and install this package. The following 46 | more-detailed instructions are generic; see the `README' file for 47 | instructions specific to this package. Some packages provide this 48 | `INSTALL' file but do not implement all of the features documented 49 | below. The lack of an optional feature in a given package is not 50 | necessarily a bug. More recommendations for GNU packages can be found 51 | in *note Makefile Conventions: (standards)Makefile Conventions. 52 | 53 | The `configure' shell script attempts to guess correct values for 54 | various system-dependent variables used during compilation. It uses 55 | those values to create a `Makefile' in each directory of the package. 56 | It may also create one or more `.h' files containing system-dependent 57 | definitions. Finally, it creates a shell script `config.status' that 58 | you can run in the future to recreate the current configuration, and a 59 | file `config.log' containing compiler output (useful mainly for 60 | debugging `configure'). 61 | 62 | It can also use an optional file (typically called `config.cache' 63 | and enabled with `--cache-file=config.cache' or simply `-C') that saves 64 | the results of its tests to speed up reconfiguring. Caching is 65 | disabled by default to prevent problems with accidental use of stale 66 | cache files. 67 | 68 | If you need to do unusual things to compile the package, please try 69 | to figure out how `configure' could check whether to do them, and mail 70 | diffs or instructions to the address given in the `README' so they can 71 | be considered for the next release. If you are using the cache, and at 72 | some point `config.cache' contains results you don't want to keep, you 73 | may remove or edit it. 74 | 75 | The file `configure.ac' (or `configure.in') is used to create 76 | `configure' by a program called `autoconf'. You need `configure.ac' if 77 | you want to change it or regenerate `configure' using a newer version 78 | of `autoconf'. 79 | 80 | The simplest way to compile this package is: 81 | 82 | 1. `cd' to the directory containing the package's source code and type 83 | `./configure' to configure the package for your system. 84 | 85 | Running `configure' might take a while. While running, it prints 86 | some messages telling which features it is checking for. 87 | 88 | 2. Type `make' to compile the package. 89 | 90 | 3. Optionally, type `make check' to run any self-tests that come with 91 | the package, generally using the just-built uninstalled binaries. 92 | 93 | 4. Type `make install' to install the programs and any data files and 94 | documentation. When installing into a prefix owned by root, it is 95 | recommended that the package be configured and built as a regular 96 | user, and only the `make install' phase executed with root 97 | privileges. 98 | 99 | 5. Optionally, type `make installcheck' to repeat any self-tests, but 100 | this time using the binaries in their final installed location. 101 | This target does not install anything. Running this target as a 102 | regular user, particularly if the prior `make install' required 103 | root privileges, verifies that the installation completed 104 | correctly. 105 | 106 | 6. You can remove the program binaries and object files from the 107 | source code directory by typing `make clean'. To also remove the 108 | files that `configure' created (so you can compile the package for 109 | a different kind of computer), type `make distclean'. There is 110 | also a `make maintainer-clean' target, but that is intended mainly 111 | for the package's developers. If you use it, you may have to get 112 | all sorts of other programs in order to regenerate files that came 113 | with the distribution. 114 | 115 | 7. Often, you can also type `make uninstall' to remove the installed 116 | files again. In practice, not all packages have tested that 117 | uninstallation works correctly, even though it is required by the 118 | GNU Coding Standards. 119 | 120 | 8. Some packages, particularly those that use Automake, provide `make 121 | distcheck', which can by used by developers to test that all other 122 | targets like `make install' and `make uninstall' work correctly. 123 | This target is generally not run by end users. 124 | 125 | Compilers and Options 126 | ===================== 127 | 128 | Some systems require unusual options for compilation or linking that 129 | the `configure' script does not know about. Run `./configure --help' 130 | for details on some of the pertinent environment variables. 131 | 132 | You can give `configure' initial values for configuration parameters 133 | by setting variables in the command line or in the environment. Here 134 | is an example: 135 | 136 | ./configure CC=c99 CFLAGS=-g LIBS=-lposix 137 | 138 | *Note Defining Variables::, for more details. 139 | 140 | Compiling For Multiple Architectures 141 | ==================================== 142 | 143 | You can compile the package for more than one kind of computer at the 144 | same time, by placing the object files for each architecture in their 145 | own directory. To do this, you can use GNU `make'. `cd' to the 146 | directory where you want the object files and executables to go and run 147 | the `configure' script. `configure' automatically checks for the 148 | source code in the directory that `configure' is in and in `..'. This 149 | is known as a "VPATH" build. 150 | 151 | With a non-GNU `make', it is safer to compile the package for one 152 | architecture at a time in the source code directory. After you have 153 | installed the package for one architecture, use `make distclean' before 154 | reconfiguring for another architecture. 155 | 156 | On MacOS X 10.5 and later systems, you can create libraries and 157 | executables that work on multiple system types--known as "fat" or 158 | "universal" binaries--by specifying multiple `-arch' options to the 159 | compiler but only a single `-arch' option to the preprocessor. Like 160 | this: 161 | 162 | ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ 163 | CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ 164 | CPP="gcc -E" CXXCPP="g++ -E" 165 | 166 | This is not guaranteed to produce working output in all cases, you 167 | may have to build one architecture at a time and combine the results 168 | using the `lipo' tool if you have problems. 169 | 170 | Installation Names 171 | ================== 172 | 173 | By default, `make install' installs the package's commands under 174 | `/usr/local/bin', include files under `/usr/local/include', etc. You 175 | can specify an installation prefix other than `/usr/local' by giving 176 | `configure' the option `--prefix=PREFIX', where PREFIX must be an 177 | absolute file name. 178 | 179 | You can specify separate installation prefixes for 180 | architecture-specific files and architecture-independent files. If you 181 | pass the option `--exec-prefix=PREFIX' to `configure', the package uses 182 | PREFIX as the prefix for installing programs and libraries. 183 | Documentation and other data files still use the regular prefix. 184 | 185 | In addition, if you use an unusual directory layout you can give 186 | options like `--bindir=DIR' to specify different values for particular 187 | kinds of files. Run `configure --help' for a list of the directories 188 | you can set and what kinds of files go in them. In general, the 189 | default for these options is expressed in terms of `${prefix}', so that 190 | specifying just `--prefix' will affect all of the other directory 191 | specifications that were not explicitly provided. 192 | 193 | The most portable way to affect installation locations is to pass the 194 | correct locations to `configure'; however, many packages provide one or 195 | both of the following shortcuts of passing variable assignments to the 196 | `make install' command line to change installation locations without 197 | having to reconfigure or recompile. 198 | 199 | The first method involves providing an override variable for each 200 | affected directory. For example, `make install 201 | prefix=/alternate/directory' will choose an alternate location for all 202 | directory configuration variables that were expressed in terms of 203 | `${prefix}'. Any directories that were specified during `configure', 204 | but not in terms of `${prefix}', must each be overridden at install 205 | time for the entire installation to be relocated. The approach of 206 | makefile variable overrides for each directory variable is required by 207 | the GNU Coding Standards, and ideally causes no recompilation. 208 | However, some platforms have known limitations with the semantics of 209 | shared libraries that end up requiring recompilation when using this 210 | method, particularly noticeable in packages that use GNU Libtool. 211 | 212 | The second method involves providing the `DESTDIR' variable. For 213 | example, `make install DESTDIR=/alternate/directory' will prepend 214 | `/alternate/directory' before all installation names. The approach of 215 | `DESTDIR' overrides is not required by the GNU Coding Standards, and 216 | does not work on platforms that have drive letters. On the other hand, 217 | it does better at avoiding recompilation issues, and works well even 218 | when some directory options were not specified in terms of `${prefix}' 219 | at `configure' time. 220 | 221 | Optional Features 222 | ================= 223 | 224 | If the package supports it, you can cause programs to be installed 225 | with an extra prefix or suffix on their names by giving `configure' the 226 | option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. 227 | 228 | Some packages pay attention to `--enable-FEATURE' options to 229 | `configure', where FEATURE indicates an optional part of the package. 230 | They may also pay attention to `--with-PACKAGE' options, where PACKAGE 231 | is something like `gnu-as' or `x' (for the X Window System). The 232 | `README' should mention any `--enable-' and `--with-' options that the 233 | package recognizes. 234 | 235 | For packages that use the X Window System, `configure' can usually 236 | find the X include and library files automatically, but if it doesn't, 237 | you can use the `configure' options `--x-includes=DIR' and 238 | `--x-libraries=DIR' to specify their locations. 239 | 240 | Some packages offer the ability to configure how verbose the 241 | execution of `make' will be. For these packages, running `./configure 242 | --enable-silent-rules' sets the default to minimal output, which can be 243 | overridden with `make V=1'; while running `./configure 244 | --disable-silent-rules' sets the default to verbose, which can be 245 | overridden with `make V=0'. 246 | 247 | Particular systems 248 | ================== 249 | 250 | On HP-UX, the default C compiler is not ANSI C compatible. If GNU 251 | CC is not installed, it is recommended to use the following options in 252 | order to use an ANSI C compiler: 253 | 254 | ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" 255 | 256 | and if that doesn't work, install pre-built binaries of GCC for HP-UX. 257 | 258 | HP-UX `make' updates targets which have the same time stamps as 259 | their prerequisites, which makes it generally unusable when shipped 260 | generated files such as `configure' are involved. Use GNU `make' 261 | instead. 262 | 263 | On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot 264 | parse its `' header file. The option `-nodtk' can be used as 265 | a workaround. If GNU CC is not installed, it is therefore recommended 266 | to try 267 | 268 | ./configure CC="cc" 269 | 270 | and if that doesn't work, try 271 | 272 | ./configure CC="cc -nodtk" 273 | 274 | On Solaris, don't put `/usr/ucb' early in your `PATH'. This 275 | directory contains several dysfunctional programs; working variants of 276 | these programs are available in `/usr/bin'. So, if you need `/usr/ucb' 277 | in your `PATH', put it _after_ `/usr/bin'. 278 | 279 | On Haiku, software installed for all users goes in `/boot/common', 280 | not `/usr/local'. It is recommended to use the following options: 281 | 282 | ./configure --prefix=/boot/common 283 | 284 | Specifying the System Type 285 | ========================== 286 | 287 | There may be some features `configure' cannot figure out 288 | automatically, but needs to determine by the type of machine the package 289 | will run on. Usually, assuming the package is built to be run on the 290 | _same_ architectures, `configure' can figure that out, but if it prints 291 | a message saying it cannot guess the machine type, give it the 292 | `--build=TYPE' option. TYPE can either be a short name for the system 293 | type, such as `sun4', or a canonical name which has the form: 294 | 295 | CPU-COMPANY-SYSTEM 296 | 297 | where SYSTEM can have one of these forms: 298 | 299 | OS 300 | KERNEL-OS 301 | 302 | See the file `config.sub' for the possible values of each field. If 303 | `config.sub' isn't included in this package, then this package doesn't 304 | need to know the machine type. 305 | 306 | If you are _building_ compiler tools for cross-compiling, you should 307 | use the option `--target=TYPE' to select the type of system they will 308 | produce code for. 309 | 310 | If you want to _use_ a cross compiler, that generates code for a 311 | platform different from the build platform, you should specify the 312 | "host" platform (i.e., that on which the generated programs will 313 | eventually be run) with `--host=TYPE'. 314 | 315 | Sharing Defaults 316 | ================ 317 | 318 | If you want to set default values for `configure' scripts to share, 319 | you can create a site shell script called `config.site' that gives 320 | default values for variables like `CC', `cache_file', and `prefix'. 321 | `configure' looks for `PREFIX/share/config.site' if it exists, then 322 | `PREFIX/etc/config.site' if it exists. Or, you can set the 323 | `CONFIG_SITE' environment variable to the location of the site script. 324 | A warning: not all `configure' scripts look for a site script. 325 | 326 | Defining Variables 327 | ================== 328 | 329 | Variables not defined in a site shell script can be set in the 330 | environment passed to `configure'. However, some packages may run 331 | configure again during the build, and the customized values of these 332 | variables may be lost. In order to avoid this problem, you should set 333 | them in the `configure' command line, using `VAR=value'. For example: 334 | 335 | ./configure CC=/usr/local2/bin/gcc 336 | 337 | causes the specified `gcc' to be used as the C compiler (unless it is 338 | overridden in the site shell script). 339 | 340 | Unfortunately, this technique does not work for `CONFIG_SHELL' due to 341 | an Autoconf limitation. Until the limitation is lifted, you can use 342 | this workaround: 343 | 344 | CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash 345 | 346 | `configure' Invocation 347 | ====================== 348 | 349 | `configure' recognizes the following options to control how it 350 | operates. 351 | 352 | `--help' 353 | `-h' 354 | Print a summary of all of the options to `configure', and exit. 355 | 356 | `--help=short' 357 | `--help=recursive' 358 | Print a summary of the options unique to this package's 359 | `configure', and exit. The `short' variant lists options used 360 | only in the top level, while the `recursive' variant lists options 361 | also present in any nested packages. 362 | 363 | `--version' 364 | `-V' 365 | Print the version of Autoconf used to generate the `configure' 366 | script, and exit. 367 | 368 | `--cache-file=FILE' 369 | Enable the cache: use and save the results of the tests in FILE, 370 | traditionally `config.cache'. FILE defaults to `/dev/null' to 371 | disable caching. 372 | 373 | `--config-cache' 374 | `-C' 375 | Alias for `--cache-file=config.cache'. 376 | 377 | `--quiet' 378 | `--silent' 379 | `-q' 380 | Do not print messages saying which checks are being made. To 381 | suppress all normal output, redirect it to `/dev/null' (any error 382 | messages will still be shown). 383 | 384 | `--srcdir=DIR' 385 | Look for the package's source code in directory DIR. Usually 386 | `configure' can determine that directory automatically. 387 | 388 | `--prefix=DIR' 389 | Use DIR as the installation prefix. *note Installation Names:: 390 | for more details, including other options available for fine-tuning 391 | the installation locations. 392 | 393 | `--no-create' 394 | `-n' 395 | Run the configure checks, but stop before creating any output 396 | files. 397 | 398 | `configure' also accepts some other, not widely useful, options. Run 399 | `configure --help' for more details. 400 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = include src 2 | 3 | pkgconfig_DATA = tinycompress.pc 4 | 5 | ACLOCAL_AMFLAGS = -I m4 6 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alsa-project/tinycompress/ea5c7245beb0b6aec1565cfae0454d6ba374dfdd/NEWS -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | README for tinycompress 2 | ======================= 3 | vkoul@kernel.org 4 | ================ 5 | 6 | 1. WHAT 7 | tinycompress is a userspace library for anyone who wants to use the ALSA 8 | compressed APIs introduced in Linux 3.3 9 | This library provides the APIs to open a ALSA compressed device and read/write 10 | compressed data like MP3 etc to it. 11 | 12 | This also includes a utility command line player (cplay) which demonstrates 13 | the usage of this API. Currently this contains support for playing the mp3 format 14 | 15 | 2. WHERE 16 | The library can found in github at: 17 | https://github.com/alsa-project/tinycompress 18 | 19 | The official mirror on the ALSA server also can be used: 20 | Git: git clone git://git.alsa-project.org/tinycompress.git 21 | Http: http://git.alsa-project.org/?p=tinycompress.git 22 | 23 | 3. PATCHES 24 | Please send any enhancements/fixes to alsa developer mailing list at: 25 | alsa-devel@alsa-project.org OR send a PULL-REQUEST using github 26 | 27 | 4. LICENSE 28 | tinycompress is provided under a dual BSD/LGPLv2.1 license. When using or 29 | redistributing tinycompress, you may do so under either license. 30 | 31 | 5. CREDITS 32 | - Pierre-Louis Bossart for library design 33 | - Navjot Singh for writing the mp3 parser code 34 | 35 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | autoreconf --verbose --force --install || { 4 | echo 'autogen.sh failed'; 5 | exit 1; 6 | } 7 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_PREREQ([2.63]) 5 | AC_INIT([tinycompress], [1.2.13]) 6 | AC_CONFIG_HEADERS([config.h]) 7 | AC_CONFIG_MACRO_DIR([m4]) 8 | 9 | AM_INIT_AUTOMAKE(1.10) 10 | LT_INIT(disable-static) 11 | 12 | AC_ARG_ENABLE(fcplay, 13 | AS_HELP_STRING([--enable-fcplay], [enable the fcplay component]), 14 | [build_fcplay="$enableval"], [build_fcplay="no"]) 15 | AC_ARG_ENABLE(pcm, 16 | AS_HELP_STRING([--enable-pcm], [enable PCM compress playback support(used for debugging)]), 17 | [enable_pcm="$enableval"], [enable_pcm="no"]) 18 | 19 | AM_CONDITIONAL([BUILD_FCPLAY], [test x$build_fcplay = xyes]) 20 | AM_CONDITIONAL([ENABLE_PCM], [test x$enable_pcm = xyes]) 21 | 22 | #if test "$build_fcplay" = "yes"; then 23 | # AC_DEFINE([BUILD_FCPLAY], "1", [Build Fcplay component]) 24 | #fi 25 | 26 | if test "$enable_pcm" = "yes"; then 27 | AC_DEFINE([ENABLE_PCM], 1, [Enable PCM compress playback support (used for debugging)]) 28 | fi 29 | 30 | # Checks for programs. 31 | AC_PROG_CXX 32 | AC_PROG_CC 33 | AC_PROG_CPP 34 | AC_PROG_INSTALL 35 | AC_PROG_MAKE_SET 36 | AM_PROG_CC_C_O 37 | PKG_INSTALLDIR 38 | 39 | # Checks for libraries. 40 | AS_IF([test "x$build_fcplay" = "xyes"], [ 41 | PKG_CHECK_MODULES([AVCODEC], [libavcodec >= 3.0.7]) 42 | PKG_CHECK_MODULES([AVFORMAT], [libavformat >= 3.0.7]) 43 | PKG_CHECK_MODULES([AVUTIL], [libavutil >= 3.0.7]) 44 | ]) 45 | 46 | # Checks for typedefs, structures, and compiler characteristics. 47 | 48 | # Checks for library functions. 49 | 50 | AC_CONFIG_FILES([ 51 | Makefile 52 | include/Makefile 53 | src/Makefile 54 | src/lib/Makefile 55 | src/utils/Makefile 56 | src/utils-lgpl/Makefile 57 | tinycompress.pc]) 58 | AC_OUTPUT 59 | -------------------------------------------------------------------------------- /gitcompile: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | libtoolize --force --copy --automake 4 | aclocal $alsa_m4_flags $ACLOCAL_FLAGS 5 | # save original files to avoid stupid modifications by gettextize 6 | autoheader 7 | automake --foreign --copy --add-missing 8 | touch depcomp # for older automake 9 | autoconf 10 | export CFLAGS='-O2 -Wall -pipe -g' 11 | echo "CFLAGS=$CFLAGS" 12 | echo "./configure $@" 13 | ./configure $@ || exit 1 14 | unset CFLAGS 15 | if [ -z "$GITCOMPILE_NO_MAKE" ]; then 16 | make 17 | fi 18 | -------------------------------------------------------------------------------- /include/Makefile.am: -------------------------------------------------------------------------------- 1 | nobase_include_HEADERS = tinycompress/tinycompress.h 2 | 3 | noinst_HEADERS = sound/compress_offload.h \ 4 | sound/compress_params.h \ 5 | tinycompress/version.h \ 6 | tinycompress/compress_ops.h \ 7 | tinycompress/tinymp3.h \ 8 | tinycompress/tinywave.h 9 | -------------------------------------------------------------------------------- /include/sound/compress_offload.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | **************************************************************************** 3 | *** 4 | *** This header was automatically generated from a Linux kernel header 5 | *** of the same name, to make information necessary for userspace to 6 | *** call into the kernel available to libc. It contains only constants, 7 | *** structures, and macros generated from the original header, and thus, 8 | *** contains no copyrightable information. 9 | *** 10 | *** To edit the content of this header, modify the corresponding 11 | *** source file (e.g. under external/kernel-headers/original/) then 12 | *** run bionic/libc/kernel/tools/update_all.py 13 | *** 14 | *** Any manual change here will be lost the next time this script will 15 | *** be run. You've been warned! 16 | *** 17 | **************************************************************************** 18 | ****************************************************************************/ 19 | #ifndef __COMPRESS_OFFLOAD_H 20 | #define __COMPRESS_OFFLOAD_H 21 | #include 22 | #include 23 | #include 24 | #define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 2, 0) 25 | 26 | struct snd_compressed_buffer { 27 | __u32 fragment_size; 28 | __u32 fragments; 29 | }__attribute__((packed, aligned(4))); 30 | 31 | struct snd_compr_params { 32 | struct snd_compressed_buffer buffer; 33 | struct snd_codec codec; 34 | __u8 no_wake_mode; 35 | }__attribute__((packed, aligned(4))); 36 | 37 | struct snd_compr_tstamp { 38 | __u32 byte_offset; 39 | __u32 copied_total; 40 | __u32 pcm_frames; 41 | __u32 pcm_io_frames; 42 | __u32 sampling_rate; 43 | }__attribute__((packed, aligned(4))); 44 | 45 | struct snd_compr_avail { 46 | __u64 avail; 47 | struct snd_compr_tstamp tstamp; 48 | }__attribute__((packed, aligned(4))); 49 | 50 | enum snd_compr_direction { 51 | SND_COMPRESS_PLAYBACK = 0, 52 | SND_COMPRESS_CAPTURE 53 | }; 54 | 55 | struct snd_compr_caps { 56 | __u32 num_codecs; 57 | __u32 direction; 58 | __u32 min_fragment_size; 59 | __u32 max_fragment_size; 60 | __u32 min_fragments; 61 | __u32 max_fragments; 62 | __u32 codecs[MAX_NUM_CODECS]; 63 | __u32 reserved[11]; 64 | }__attribute__((packed, aligned(4))); 65 | 66 | struct snd_compr_codec_caps { 67 | __u32 codec; 68 | __u32 num_descriptors; 69 | struct snd_codec_desc descriptor[MAX_NUM_CODEC_DESCRIPTORS]; 70 | }__attribute__((packed, aligned(4))); 71 | 72 | enum { 73 | SNDRV_COMPRESS_ENCODER_PADDING = 1, 74 | SNDRV_COMPRESS_ENCODER_DELAY = 2, 75 | }; 76 | 77 | struct snd_compr_metadata { 78 | __u32 key; 79 | __u32 value[8]; 80 | }__attribute__((packed, aligned(4))); 81 | 82 | #define SNDRV_COMPRESS_IOCTL_VERSION _IOR('C', 0x00, int) 83 | #define SNDRV_COMPRESS_GET_CAPS _IOWR('C', 0x10, struct snd_compr_caps) 84 | #define SNDRV_COMPRESS_GET_CODEC_CAPS _IOWR('C', 0x11, struct snd_compr_codec_caps) 85 | #define SNDRV_COMPRESS_SET_PARAMS _IOW('C', 0x12, struct snd_compr_params) 86 | #define SNDRV_COMPRESS_GET_PARAMS _IOR('C', 0x13, struct snd_codec) 87 | #define SNDRV_COMPRESS_SET_METADATA _IOW('C', 0x14, struct snd_compr_metadata) 88 | #define SNDRV_COMPRESS_GET_METADATA _IOWR('C', 0x15, struct snd_compr_metadata) 89 | #define SNDRV_COMPRESS_TSTAMP _IOR('C', 0x20, struct snd_compr_tstamp) 90 | #define SNDRV_COMPRESS_AVAIL _IOR('C', 0x21, struct snd_compr_avail) 91 | #define SNDRV_COMPRESS_PAUSE _IO('C', 0x30) 92 | #define SNDRV_COMPRESS_RESUME _IO('C', 0x31) 93 | #define SNDRV_COMPRESS_START _IO('C', 0x32) 94 | #define SNDRV_COMPRESS_STOP _IO('C', 0x33) 95 | #define SNDRV_COMPRESS_DRAIN _IO('C', 0x34) 96 | #define SNDRV_COMPRESS_NEXT_TRACK _IO('C', 0x35) 97 | #define SNDRV_COMPRESS_PARTIAL_DRAIN _IO('C', 0x36) 98 | #define SND_COMPR_TRIGGER_DRAIN 7 99 | #define SND_COMPR_TRIGGER_NEXT_TRACK 8 100 | #define SND_COMPR_TRIGGER_PARTIAL_DRAIN 9 101 | #endif 102 | -------------------------------------------------------------------------------- /include/sound/compress_params.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | **************************************************************************** 3 | *** 4 | *** This header was automatically generated from a Linux kernel header 5 | *** of the same name, to make information necessary for userspace to 6 | *** call into the kernel available to libc. It contains only constants, 7 | *** structures, and macros generated from the original header, and thus, 8 | *** contains no copyrightable information. 9 | *** 10 | *** To edit the content of this header, modify the corresponding 11 | *** source file (e.g. under external/kernel-headers/original/) then 12 | *** run bionic/libc/kernel/tools/update_all.py 13 | *** 14 | *** Any manual change here will be lost the next time this script will 15 | *** be run. You've been warned! 16 | *** 17 | **************************************************************************** 18 | ****************************************************************************/ 19 | #ifndef __SND_COMPRESS_PARAMS_H 20 | #define __SND_COMPRESS_PARAMS_H 21 | 22 | #include 23 | #define MAX_NUM_CODECS 32 24 | #define MAX_NUM_CODEC_DESCRIPTORS 32 25 | #define MAX_NUM_BITRATES 32 26 | #define MAX_NUM_SAMPLE_RATES 32 27 | 28 | #define SND_AUDIOCODEC_PCM ((__u32) 0x00000001) 29 | #define SND_AUDIOCODEC_MP3 ((__u32) 0x00000002) 30 | #define SND_AUDIOCODEC_AMR ((__u32) 0x00000003) 31 | #define SND_AUDIOCODEC_AMRWB ((__u32) 0x00000004) 32 | #define SND_AUDIOCODEC_AMRWBPLUS ((__u32) 0x00000005) 33 | #define SND_AUDIOCODEC_AAC ((__u32) 0x00000006) 34 | #define SND_AUDIOCODEC_WMA ((__u32) 0x00000007) 35 | #define SND_AUDIOCODEC_REAL ((__u32) 0x00000008) 36 | #define SND_AUDIOCODEC_VORBIS ((__u32) 0x00000009) 37 | #define SND_AUDIOCODEC_FLAC ((__u32) 0x0000000A) 38 | #define SND_AUDIOCODEC_IEC61937 ((__u32) 0x0000000B) 39 | #define SND_AUDIOCODEC_G723_1 ((__u32) 0x0000000C) 40 | #define SND_AUDIOCODEC_G729 ((__u32) 0x0000000D) 41 | #define SND_AUDIOCODEC_BESPOKE ((__u32) 0x0000000E) 42 | #define SND_AUDIOCODEC_ALAC ((__u32) 0x0000000F) 43 | #define SND_AUDIOCODEC_APE ((__u32) 0x00000010) 44 | 45 | #define SND_AUDIOCODEC_MAX SND_AUDIOCODEC_APE 46 | #define SND_AUDIOPROFILE_PCM ((__u32) 0x00000001) 47 | 48 | #define SND_AUDIOCHANMODE_MP3_MONO ((__u32) 0x00000001) 49 | #define SND_AUDIOCHANMODE_MP3_STEREO ((__u32) 0x00000002) 50 | #define SND_AUDIOCHANMODE_MP3_JOINTSTEREO ((__u32) 0x00000004) 51 | #define SND_AUDIOCHANMODE_MP3_DUAL ((__u32) 0x00000008) 52 | 53 | #define SND_AUDIOPROFILE_AMR ((__u32) 0x00000001) 54 | 55 | #define SND_AUDIOMODE_AMR_DTX_OFF ((__u32) 0x00000001) 56 | #define SND_AUDIOMODE_AMR_VAD1 ((__u32) 0x00000002) 57 | #define SND_AUDIOMODE_AMR_VAD2 ((__u32) 0x00000004) 58 | 59 | #define SND_AUDIOSTREAMFORMAT_UNDEFINED ((__u32) 0x00000000) 60 | #define SND_AUDIOSTREAMFORMAT_CONFORMANCE ((__u32) 0x00000001) 61 | #define SND_AUDIOSTREAMFORMAT_IF1 ((__u32) 0x00000002) 62 | #define SND_AUDIOSTREAMFORMAT_IF2 ((__u32) 0x00000004) 63 | #define SND_AUDIOSTREAMFORMAT_FSF ((__u32) 0x00000008) 64 | #define SND_AUDIOSTREAMFORMAT_RTPPAYLOAD ((__u32) 0x00000010) 65 | #define SND_AUDIOSTREAMFORMAT_ITU ((__u32) 0x00000020) 66 | 67 | #define SND_AUDIOPROFILE_AMRWB ((__u32) 0x00000001) 68 | 69 | #define SND_AUDIOMODE_AMRWB_DTX_OFF ((__u32) 0x00000001) 70 | #define SND_AUDIOMODE_AMRWB_VAD1 ((__u32) 0x00000002) 71 | #define SND_AUDIOMODE_AMRWB_VAD2 ((__u32) 0x00000004) 72 | 73 | #define SND_AUDIOPROFILE_AMRWBPLUS ((__u32) 0x00000001) 74 | 75 | #define SND_AUDIOPROFILE_AAC ((__u32) 0x00000001) 76 | 77 | #define SND_AUDIOMODE_AAC_MAIN ((__u32) 0x00000001) 78 | #define SND_AUDIOMODE_AAC_LC ((__u32) 0x00000002) 79 | #define SND_AUDIOMODE_AAC_SSR ((__u32) 0x00000004) 80 | #define SND_AUDIOMODE_AAC_LTP ((__u32) 0x00000008) 81 | #define SND_AUDIOMODE_AAC_HE ((__u32) 0x00000010) 82 | #define SND_AUDIOMODE_AAC_SCALABLE ((__u32) 0x00000020) 83 | #define SND_AUDIOMODE_AAC_ERLC ((__u32) 0x00000040) 84 | #define SND_AUDIOMODE_AAC_LD ((__u32) 0x00000080) 85 | #define SND_AUDIOMODE_AAC_HE_PS ((__u32) 0x00000100) 86 | #define SND_AUDIOMODE_AAC_HE_MPS ((__u32) 0x00000200) 87 | 88 | #define SND_AUDIOSTREAMFORMAT_MP2ADTS ((__u32) 0x00000001) 89 | #define SND_AUDIOSTREAMFORMAT_MP4ADTS ((__u32) 0x00000002) 90 | #define SND_AUDIOSTREAMFORMAT_MP4LOAS ((__u32) 0x00000004) 91 | #define SND_AUDIOSTREAMFORMAT_MP4LATM ((__u32) 0x00000008) 92 | #define SND_AUDIOSTREAMFORMAT_ADIF ((__u32) 0x00000010) 93 | #define SND_AUDIOSTREAMFORMAT_MP4FF ((__u32) 0x00000020) 94 | #define SND_AUDIOSTREAMFORMAT_RAW ((__u32) 0x00000040) 95 | 96 | #define SND_AUDIOPROFILE_WMA7 ((__u32) 0x00000001) 97 | #define SND_AUDIOPROFILE_WMA8 ((__u32) 0x00000002) 98 | #define SND_AUDIOPROFILE_WMA9 ((__u32) 0x00000004) 99 | #define SND_AUDIOPROFILE_WMA10 ((__u32) 0x00000008) 100 | #define SND_AUDIOPROFILE_WMA9_PRO ((__u32) 0x00000010) 101 | #define SND_AUDIOPROFILE_WMA9_LOSSLESS ((__u32) 0x00000020) 102 | #define SND_AUDIOPROFILE_WMA10_LOSSLESS ((__u32) 0x00000040) 103 | 104 | 105 | #define SND_AUDIOMODE_WMA_LEVEL1 ((__u32) 0x00000001) 106 | #define SND_AUDIOMODE_WMA_LEVEL2 ((__u32) 0x00000002) 107 | #define SND_AUDIOMODE_WMA_LEVEL3 ((__u32) 0x00000004) 108 | #define SND_AUDIOMODE_WMA_LEVEL4 ((__u32) 0x00000008) 109 | #define SND_AUDIOMODE_WMAPRO_LEVELM0 ((__u32) 0x00000010) 110 | #define SND_AUDIOMODE_WMAPRO_LEVELM1 ((__u32) 0x00000020) 111 | #define SND_AUDIOMODE_WMAPRO_LEVELM2 ((__u32) 0x00000040) 112 | #define SND_AUDIOMODE_WMAPRO_LEVELM3 ((__u32) 0x00000080) 113 | 114 | #define SND_AUDIOSTREAMFORMAT_WMA_ASF ((__u32) 0x00000001) 115 | 116 | #define SND_AUDIOSTREAMFORMAT_WMA_NOASF_HDR ((__u32) 0x00000002) 117 | 118 | #define SND_AUDIOPROFILE_REALAUDIO ((__u32) 0x00000001) 119 | 120 | #define SND_AUDIOMODE_REALAUDIO_G2 ((__u32) 0x00000001) 121 | #define SND_AUDIOMODE_REALAUDIO_8 ((__u32) 0x00000002) 122 | #define SND_AUDIOMODE_REALAUDIO_10 ((__u32) 0x00000004) 123 | #define SND_AUDIOMODE_REALAUDIO_SURROUND ((__u32) 0x00000008) 124 | 125 | #define SND_AUDIOPROFILE_VORBIS ((__u32) 0x00000001) 126 | 127 | #define SND_AUDIOMODE_VORBIS ((__u32) 0x00000001) 128 | 129 | #define SND_AUDIOPROFILE_FLAC ((__u32) 0x00000001) 130 | 131 | #define SND_AUDIOMODE_FLAC_LEVEL0 ((__u32) 0x00000001) 132 | #define SND_AUDIOMODE_FLAC_LEVEL1 ((__u32) 0x00000002) 133 | #define SND_AUDIOMODE_FLAC_LEVEL2 ((__u32) 0x00000004) 134 | #define SND_AUDIOMODE_FLAC_LEVEL3 ((__u32) 0x00000008) 135 | #define SND_AUDIOMODE_FLAC_LEVEL4 ((__u32) 0x00000010) 136 | #define SND_AUDIOMODE_FLAC_LEVEL5 ((__u32) 0x00000020) 137 | #define SND_AUDIOMODE_FLAC_LEVEL6 ((__u32) 0x00000040) 138 | #define SND_AUDIOMODE_FLAC_LEVEL7 ((__u32) 0x00000080) 139 | #define SND_AUDIOMODE_FLAC_LEVEL8 ((__u32) 0x00000100) 140 | 141 | #define SND_AUDIOSTREAMFORMAT_FLAC ((__u32) 0x00000001) 142 | #define SND_AUDIOSTREAMFORMAT_FLAC_OGG ((__u32) 0x00000002) 143 | 144 | #define SND_AUDIOPROFILE_IEC61937 ((__u32) 0x00000001) 145 | 146 | #define SND_AUDIOPROFILE_IEC61937_SPDIF ((__u32) 0x00000002) 147 | 148 | #define SND_AUDIOMODE_IEC_REF_STREAM_HEADER ((__u32) 0x00000000) 149 | #define SND_AUDIOMODE_IEC_LPCM ((__u32) 0x00000001) 150 | #define SND_AUDIOMODE_IEC_AC3 ((__u32) 0x00000002) 151 | #define SND_AUDIOMODE_IEC_MPEG1 ((__u32) 0x00000004) 152 | #define SND_AUDIOMODE_IEC_MP3 ((__u32) 0x00000008) 153 | #define SND_AUDIOMODE_IEC_MPEG2 ((__u32) 0x00000010) 154 | #define SND_AUDIOMODE_IEC_AACLC ((__u32) 0x00000020) 155 | #define SND_AUDIOMODE_IEC_DTS ((__u32) 0x00000040) 156 | #define SND_AUDIOMODE_IEC_ATRAC ((__u32) 0x00000080) 157 | #define SND_AUDIOMODE_IEC_SACD ((__u32) 0x00000100) 158 | #define SND_AUDIOMODE_IEC_EAC3 ((__u32) 0x00000200) 159 | #define SND_AUDIOMODE_IEC_DTS_HD ((__u32) 0x00000400) 160 | #define SND_AUDIOMODE_IEC_MLP ((__u32) 0x00000800) 161 | #define SND_AUDIOMODE_IEC_DST ((__u32) 0x00001000) 162 | #define SND_AUDIOMODE_IEC_WMAPRO ((__u32) 0x00002000) 163 | #define SND_AUDIOMODE_IEC_REF_CXT ((__u32) 0x00004000) 164 | #define SND_AUDIOMODE_IEC_HE_AAC ((__u32) 0x00008000) 165 | #define SND_AUDIOMODE_IEC_HE_AAC2 ((__u32) 0x00010000) 166 | #define SND_AUDIOMODE_IEC_MPEG_SURROUND ((__u32) 0x00020000) 167 | 168 | #define SND_AUDIOPROFILE_G723_1 ((__u32) 0x00000001) 169 | 170 | #define SND_AUDIOMODE_G723_1_ANNEX_A ((__u32) 0x00000001) 171 | #define SND_AUDIOMODE_G723_1_ANNEX_B ((__u32) 0x00000002) 172 | #define SND_AUDIOMODE_G723_1_ANNEX_C ((__u32) 0x00000004) 173 | 174 | #define SND_AUDIOPROFILE_G729 ((__u32) 0x00000001) 175 | 176 | #define SND_AUDIOMODE_G729_ANNEX_A ((__u32) 0x00000001) 177 | #define SND_AUDIOMODE_G729_ANNEX_B ((__u32) 0x00000002) 178 | 179 | #define SND_RATECONTROLMODE_CONSTANTBITRATE ((__u32) 0x00000001) 180 | #define SND_RATECONTROLMODE_VARIABLEBITRATE ((__u32) 0x00000002) 181 | 182 | struct snd_enc_wma { 183 | __u32 super_block_align; 184 | }; 185 | 186 | struct snd_enc_vorbis { 187 | __s32 quality; 188 | __u32 managed; 189 | __u32 max_bit_rate; 190 | __u32 min_bit_rate; 191 | __u32 downmix; 192 | }__attribute__((packed, aligned(4))); 193 | 194 | struct snd_enc_real { 195 | __u32 quant_bits; 196 | __u32 start_region; 197 | __u32 num_regions; 198 | }__attribute__((packed, aligned(4))); 199 | 200 | struct snd_enc_flac { 201 | __u32 num; 202 | __u32 gain; 203 | }__attribute__((packed, aligned(4))); 204 | 205 | struct snd_enc_generic { 206 | __u32 bw; 207 | __s32 reserved[15]; 208 | }__attribute__((packed, aligned(4))); 209 | 210 | struct snd_dec_flac { 211 | __u16 sample_size; 212 | __u16 min_blk_size; 213 | __u16 max_blk_size; 214 | __u16 min_frame_size; 215 | __u16 max_frame_size; 216 | __u16 reserved; 217 | } __attribute__((packed, aligned(4))); 218 | 219 | struct snd_dec_wma { 220 | __u32 encoder_option; 221 | __u32 adv_encoder_option; 222 | __u32 adv_encoder_option2; 223 | __u32 reserved; 224 | } __attribute__((packed, aligned(4))); 225 | 226 | struct snd_dec_alac { 227 | __u32 frame_length; 228 | __u8 compatible_version; 229 | __u8 pb; 230 | __u8 mb; 231 | __u8 kb; 232 | __u32 max_run; 233 | __u32 max_frame_bytes; 234 | } __attribute__((packed, aligned(4))); 235 | 236 | struct snd_dec_ape { 237 | __u16 compatible_version; 238 | __u16 compression_level; 239 | __u32 format_flags; 240 | __u32 blocks_per_frame; 241 | __u32 final_frame_blocks; 242 | __u32 total_frames; 243 | __u32 seek_table_present; 244 | } __attribute__((packed, aligned(4))); 245 | 246 | union snd_codec_options { 247 | struct snd_enc_wma wma; 248 | struct snd_enc_vorbis vorbis; 249 | struct snd_enc_real real; 250 | struct snd_enc_flac flac; 251 | struct snd_enc_generic generic; 252 | struct snd_dec_flac flac_d; 253 | struct snd_dec_wma wma_d; 254 | struct snd_dec_alac alac_d; 255 | struct snd_dec_ape ape_d; 256 | }__attribute__((packed, aligned(4))); 257 | 258 | struct snd_codec_desc { 259 | __u32 max_ch; 260 | __u32 sample_rates[MAX_NUM_SAMPLE_RATES]; 261 | __u32 num_sample_rates; 262 | __u32 bit_rate[MAX_NUM_BITRATES]; 263 | __u32 num_bitrates; 264 | __u32 rate_control; 265 | __u32 profiles; 266 | __u32 modes; 267 | __u32 formats; 268 | __u32 min_buffer; 269 | __u32 reserved[15]; 270 | }__attribute__((packed, aligned(4))); 271 | 272 | struct snd_codec { 273 | __u32 id; 274 | __u32 ch_in; 275 | __u32 ch_out; 276 | __u32 sample_rate; 277 | __u32 bit_rate; 278 | __u32 rate_control; 279 | __u32 profile; 280 | __u32 level; 281 | __u32 ch_mode; 282 | __u32 format; 283 | __u32 align; 284 | union snd_codec_options options; 285 | __u32 reserved[3]; 286 | }__attribute__((packed, aligned(4))); 287 | 288 | #endif 289 | -------------------------------------------------------------------------------- /include/tinycompress/compress_ops.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1-only OR BSD-3-Clause) */ 2 | /* Copyright (c) 2020 The Linux Foundation. All rights reserved. */ 3 | 4 | #ifndef __COMPRESS_OPS_H__ 5 | #define __COMPRESS_OPS_H__ 6 | 7 | #include "sound/compress_params.h" 8 | #include "sound/compress_offload.h" 9 | #include "tinycompress.h" 10 | 11 | /* 12 | * struct compress_ops: 13 | * ops structure containing ops corresponding to exposed 14 | * compress APIs, needs to be implemented by plugin lib for 15 | * virtual compress nodes. Real compress node handling is 16 | * done in compress_hw.c 17 | */ 18 | struct compress_ops { 19 | void *(*open_by_name)(const char *name, 20 | unsigned int flags, struct compr_config *config); 21 | void (*close)(void *compress_data); 22 | int (*get_hpointer)(void *compress_data, 23 | unsigned int *avail, struct timespec *tstamp); 24 | int (*get_tstamp)(void *compress_data, 25 | unsigned int *samples, unsigned int *sampling_rate); 26 | int (*write)(void *compress_data, const void *buf, size_t size); 27 | int (*read)(void *compress_data, void *buf, size_t size); 28 | int (*start)(void *compress_data); 29 | int (*stop)(void *compress_data); 30 | int (*pause)(void *compress_data); 31 | int (*resume)(void *compress_data); 32 | int (*drain)(void *compress_data); 33 | int (*partial_drain)(void *compress_data); 34 | int (*next_track)(void *compress_data); 35 | int (*set_gapless_metadata)(void *compress_data, 36 | struct compr_gapless_mdata *mdata); 37 | void (*set_max_poll_wait)(void *compress_data, int milliseconds); 38 | void (*set_nonblock)(void *compress_data, int nonblock); 39 | int (*wait)(void *compress_data, int timeout_ms); 40 | bool (*is_codec_supported_by_name) (const char *name, 41 | unsigned int flags, struct snd_codec *codec); 42 | int (*is_compress_running)(void *compress_data); 43 | int (*is_compress_ready)(void *compress_data); 44 | const char *(*get_error)(void *compress_data); 45 | int (*set_codec_params)(void *compress_data, struct snd_codec *codec); 46 | }; 47 | 48 | #endif /* end of __COMPRESS_OPS_H__ */ 49 | -------------------------------------------------------------------------------- /include/tinycompress/tinycompress.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is provided under a dual BSD/LGPLv2.1 license. When using or 3 | * redistributing this file, you may do so under either license. 4 | * 5 | * BSD LICENSE 6 | * 7 | * Copyright (c) 2011-2012, Intel Corporation 8 | * All rights reserved. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * Redistributions of source code must retain the above copyright notice, 14 | * this list of conditions and the following disclaimer. 15 | * Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * Neither the name of Intel Corporation nor the names of its contributors 19 | * may be used to endorse or promote products derived from this software 20 | * without specific prior written permission. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 26 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 32 | * THE POSSIBILITY OF SUCH DAMAGE. 33 | * 34 | * LGPL LICENSE 35 | * 36 | * tinycompress library for compress audio offload in alsa 37 | * Copyright (c) 2011-2012, Intel Corporation. 38 | * 39 | * 40 | * This program is free software; you can redistribute it and/or modify it 41 | * under the terms and conditions of the GNU Lesser General Public License, 42 | * version 2.1, as published by the Free Software Foundation. 43 | * 44 | * This program is distributed in the hope it will be useful, but WITHOUT 45 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 46 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 47 | * License for more details. 48 | * 49 | * You should have received a copy of the GNU Lesser General Public License 50 | * along with this program; if not, write to 51 | * the Free Software Foundation, Inc., 52 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 53 | */ 54 | 55 | 56 | #ifndef __TINYCOMPRESS_H 57 | #define __TINYCOMPRESS_H 58 | 59 | #include 60 | #include 61 | 62 | #if defined(__cplusplus) 63 | extern "C" { 64 | #endif 65 | /* 66 | * struct compr_config: config structure, needs to be filled by app 67 | * If fragment_size or fragments are zero, this means "don't care" 68 | * and tinycompress will choose values that the driver supports 69 | * 70 | * @fragment_size: size of fragment requested, in bytes 71 | * @fragments: number of fragments 72 | * @codec: codec type and parameters requested 73 | */ 74 | struct compr_config { 75 | __u32 fragment_size; 76 | __u32 fragments; 77 | struct snd_codec *codec; 78 | }; 79 | 80 | struct compr_gapless_mdata { 81 | __u32 encoder_delay; 82 | __u32 encoder_padding; 83 | }; 84 | 85 | #define COMPRESS_OUT 0x20000000 86 | #define COMPRESS_IN 0x10000000 87 | 88 | struct compress; 89 | struct snd_compr_tstamp; 90 | 91 | /* 92 | * compress_open: open a new compress stream 93 | * returns the valid struct compress on success, NULL on failure 94 | * If config does not specify a requested fragment size, on return 95 | * it will be updated with the size and number of fragments that 96 | * were configured 97 | * 98 | * @card: sound card number 99 | * @device: device number 100 | * @flags: device flags can be COMPRESS_OUT or COMPRESS_IN 101 | * @config: stream config requested. Returns actual fragment config 102 | */ 103 | struct compress *compress_open(unsigned int card, unsigned int device, 104 | unsigned int flags, struct compr_config *config); 105 | 106 | /* 107 | * compress_open_by_name: open a new compress stream 108 | * returns the valid struct compress on success, NULL on failure 109 | * If config does not specify a requested fragment size, on return 110 | * it will be updated with the size and number of fragments that 111 | * were configured. 112 | * format of name is : 113 | * hw:, for real hw compress node 114 | * : for virtual compress node 115 | * 116 | * @name: name of the compress node 117 | * @flags: device flags can be COMPRESS_OUT or COMPRESS_IN 118 | * @config: stream config requested. Returns actual fragment config 119 | */ 120 | 121 | struct compress *compress_open_by_name(const char *name, 122 | unsigned int flags, struct compr_config *config); 123 | /* 124 | * compress_close: close the compress stream 125 | * 126 | * @compress: compress stream to be closed 127 | */ 128 | void compress_close(struct compress *compress); 129 | 130 | /* 131 | * compress_get_hpointer: get the hw timestamp 132 | * return 0 on success, negative on error 133 | * 134 | * @compress: compress stream on which query is made 135 | * @avail: buffer availble for write/read, in bytes 136 | * @tstamp: hw time 137 | */ 138 | int compress_get_hpointer(struct compress *compress, 139 | unsigned int *avail, struct timespec *tstamp); 140 | 141 | 142 | /* 143 | * compress_get_tstamp: get the raw hw timestamp 144 | * return 0 on success, negative on error 145 | * 146 | * @compress: compress stream on which query is made 147 | * @samples: number of decoded samples played 148 | * @sampling_rate: sampling rate of decoded samples 149 | */ 150 | int compress_get_tstamp(struct compress *compress, 151 | unsigned int *samples, unsigned int *sampling_rate); 152 | 153 | /* 154 | * compress_write: write data to the compress stream 155 | * return bytes written on success, negative on error 156 | * By default this is a blocking call and will not return 157 | * until all bytes have been written or there was a 158 | * write error. 159 | * If non-blocking mode has been enabled with compress_nonblock(), 160 | * this function will write all bytes that can be written without 161 | * blocking and will then return the number of bytes successfully 162 | * written. If the return value is not an error and is < size 163 | * the caller can use compress_wait() to block until the driver 164 | * is ready for more data. 165 | * 166 | * @compress: compress stream to be written to 167 | * @buf: pointer to data 168 | * @size: number of bytes to be written 169 | */ 170 | int compress_write(struct compress *compress, const void *buf, unsigned int size); 171 | 172 | /* 173 | * compress_read: read data from the compress stream 174 | * return bytes read on success, negative on error 175 | * By default this is a blocking call and will block until 176 | * size bytes have been written or there was a read error. 177 | * If non-blocking mode was enabled using compress_nonblock() 178 | * the behaviour will change to read only as many bytes as 179 | * are currently available (if no bytes are available it 180 | * will return immediately). The caller can then use 181 | * compress_wait() to block until more bytes are available. 182 | * 183 | * @compress: compress stream from where data is to be read 184 | * @buf: pointer to data buffer 185 | * @size: size of given buffer 186 | */ 187 | int compress_read(struct compress *compress, void *buf, unsigned int size); 188 | 189 | /* 190 | * compress_start: start the compress stream 191 | * return 0 on success, negative on error 192 | * 193 | * @compress: compress stream to be started 194 | */ 195 | int compress_start(struct compress *compress); 196 | 197 | /* 198 | * compress_stop: stop the compress stream 199 | * return 0 on success, negative on error 200 | * 201 | * @compress: compress stream to be stopped 202 | */ 203 | int compress_stop(struct compress *compress); 204 | 205 | /* 206 | * compress_pause: pause the compress stream 207 | * return 0 on success, negative on error 208 | * 209 | * @compress: compress stream to be paused 210 | */ 211 | int compress_pause(struct compress *compress); 212 | 213 | /* 214 | * compress_resume: resume the compress stream 215 | * return 0 on success, negative on error 216 | * 217 | * @compress: compress stream to be resumed 218 | */ 219 | int compress_resume(struct compress *compress); 220 | 221 | /* 222 | * compress_drain: drain the compress stream 223 | * return 0 on success, negative on error 224 | * 225 | * @compress: compress stream to be drain 226 | */ 227 | int compress_drain(struct compress *compress); 228 | 229 | /* 230 | * compress_next_track: set the next track for stream 231 | * 232 | * return 0 on success, negative on error 233 | * 234 | * @compress: compress stream to be transistioned to next track 235 | */ 236 | int compress_next_track(struct compress *compress); 237 | 238 | /* 239 | * compress_partial_drain: drain will return after the last frame is decoded 240 | * by DSP and will play the , All the data written into compressed 241 | * ring buffer is decoded 242 | * 243 | * return 0 on success, negative on error 244 | * 245 | * @compress: compress stream to be drain 246 | */ 247 | int compress_partial_drain(struct compress *compress); 248 | 249 | /* 250 | * compress_set_gapless_metadata: set gapless metadata of a compress strem 251 | * 252 | * return 0 on success, negative on error 253 | * 254 | * @compress: compress stream for which metadata has to set 255 | * @mdata: metadata encoder delay and padding 256 | */ 257 | 258 | int compress_set_gapless_metadata(struct compress *compress, 259 | struct compr_gapless_mdata *mdata); 260 | 261 | /* 262 | * is_codec_supported:check if the given codec is supported 263 | * returns true when supported, false if not 264 | * 265 | * @card: sound card number 266 | * @device: device number 267 | * @flags: stream flags 268 | * @codec: codec type and parameters to be checked 269 | */ 270 | bool is_codec_supported(unsigned int card, unsigned int device, 271 | unsigned int flags, struct snd_codec *codec); 272 | 273 | /* 274 | * is_codec_supported_by_name:check if the given codec is supported 275 | * returns true when supported, false if not. 276 | * format of name is : 277 | * hw:, for real hw compress node 278 | * : for virtual compress node 279 | * 280 | * @name: name of the compress node 281 | * @flags: stream flags 282 | * @codec: codec type and parameters to be checked 283 | */ 284 | bool is_codec_supported_by_name(const char *name, 285 | unsigned int flags, struct snd_codec *codec); 286 | 287 | /* 288 | * compress_set_max_poll_wait: set the maximum time tinycompress 289 | * will wait for driver to signal a poll(). Interval is in 290 | * milliseconds. 291 | * Pass interval of -1 to disable timeout and make poll() wait 292 | * until driver signals. 293 | * If this function is not used the timeout defaults to 20 seconds. 294 | */ 295 | void compress_set_max_poll_wait(struct compress *compress, int milliseconds); 296 | 297 | /* Enable or disable non-blocking mode for write and read */ 298 | void compress_nonblock(struct compress *compress, int nonblock); 299 | 300 | /* Wait for ring buffer to ready for next read or write */ 301 | int compress_wait(struct compress *compress, int timeout_ms); 302 | 303 | int is_compress_running(struct compress *compress); 304 | 305 | int is_compress_ready(struct compress *compress); 306 | 307 | /* Returns a human readable reason for the last error */ 308 | const char *compress_get_error(struct compress *compress); 309 | 310 | /* 311 | * compress_set_param: set codec config intended for next track 312 | * if DSP has support to switch CODEC config during gapless playback 313 | * 314 | * return 0 on success, negative on error 315 | * 316 | * @compress: compress stream for which metadata has to set 317 | * @config: stream config for next track 318 | */ 319 | int compress_set_codec_params(struct compress *compress, struct snd_codec *codec); 320 | 321 | #if defined(__cplusplus) 322 | } // extern "C" 323 | #endif 324 | 325 | #endif 326 | -------------------------------------------------------------------------------- /include/tinycompress/tinymp3.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is provided under a dual BSD/LGPLv2.1 license. When using or 3 | * redistributing this file, you may do so under either license. 4 | * 5 | * BSD LICENSE 6 | * 7 | * mp3 header and prasing 8 | * Copyright (c) 2011-2012, Intel Corporation 9 | * All rights reserved. 10 | * 11 | * Author: Vinod Koul 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that the following conditions are met: 15 | * 16 | * Redistributions of source code must retain the above copyright notice, 17 | * this list of conditions and the following disclaimer. 18 | * Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * Neither the name of Intel Corporation nor the names of its contributors 22 | * may be used to endorse or promote products derived from this software 23 | * without specific prior written permission. 24 | * 25 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 26 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 29 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 35 | * THE POSSIBILITY OF SUCH DAMAGE. 36 | * 37 | * LGPL LICENSE 38 | * 39 | * mp3 header and parsing 40 | * Copyright (c) 2011-2012, Intel Corporation. 41 | * 42 | * 43 | * This program is free software; you can redistribute it and/or modify it 44 | * under the terms and conditions of the GNU Lesser General Public License, 45 | * version 2.1, as published by the Free Software Foundation. 46 | * 47 | * This program is distributed in the hope it will be useful, but WITHOUT 48 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 49 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 50 | * License for more details. 51 | * 52 | * You should have received a copy of the GNU Lesser General Public License 53 | * along with this program; if not, write to 54 | * the Free Software Foundation, Inc., 55 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 56 | */ 57 | 58 | 59 | #ifndef __TINYMP3_H 60 | #define __TINYMP3_H 61 | 62 | #if defined(__cplusplus) 63 | extern "C" { 64 | #endif 65 | 66 | 67 | #define MP3_SYNC 0xe0ff 68 | 69 | static const int mp3_sample_rates[3][3] = { 70 | {44100, 48000, 32000}, /* MPEG-1 */ 71 | {22050, 24000, 16000}, /* MPEG-2 */ 72 | {11025, 12000, 8000}, /* MPEG-2.5 */ 73 | }; 74 | 75 | static const int mp3_bit_rates[3][3][15] = { 76 | { 77 | /* MPEG-1 */ 78 | { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448}, /* Layer 1 */ 79 | { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384}, /* Layer 2 */ 80 | { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320}, /* Layer 3 */ 81 | }, 82 | { 83 | /* MPEG-2 */ 84 | { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256}, /* Layer 1 */ 85 | { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}, /* Layer 2 */ 86 | { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}, /* Layer 3 */ 87 | }, 88 | { 89 | /* MPEG-2.5 */ 90 | { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256}, /* Layer 1 */ 91 | { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}, /* Layer 2 */ 92 | { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}, /* Layer 3 */ 93 | }, 94 | }; 95 | 96 | enum mpeg_version { 97 | MPEG1 = 0, 98 | MPEG2 = 1, 99 | MPEG25 = 2 100 | }; 101 | 102 | enum mp3_stereo_mode { 103 | STEREO = 0x00, 104 | JOINT = 0x01, 105 | DUAL = 0x02, 106 | MONO = 0x03 107 | }; 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /include/tinycompress/tinywave.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1-only OR BSD-3-Clause) */ 2 | /* 3 | * wave header and parsing 4 | * 5 | * Copyright 2020 NXP 6 | */ 7 | 8 | #ifndef __TINYWAVE_H 9 | #define __TINYWAVE_H 10 | 11 | struct riff_chunk { 12 | char desc[4]; 13 | uint32_t size; 14 | } __attribute__((__packed__)); 15 | 16 | struct wave_header { 17 | struct { 18 | struct riff_chunk chunk; 19 | char format[4]; 20 | } __attribute__((__packed__)) riff; 21 | 22 | struct { 23 | struct riff_chunk chunk; 24 | uint16_t type; 25 | uint16_t channels; 26 | uint32_t rate; 27 | uint32_t byterate; 28 | uint16_t blockalign; 29 | uint16_t samplebits; 30 | } __attribute__((__packed__)) fmt; 31 | 32 | struct { 33 | struct riff_chunk chunk; 34 | } __attribute__((__packed__)) data; 35 | } __attribute__((__packed__)); 36 | 37 | void init_wave_header(struct wave_header *header, uint16_t channels, 38 | uint32_t rate, uint16_t samplebits); 39 | void size_wave_header(struct wave_header *header, uint32_t size); 40 | 41 | int parse_wave_header(struct wave_header *header, unsigned int *channels, 42 | unsigned int *rate, unsigned int *format); 43 | #endif 44 | -------------------------------------------------------------------------------- /include/tinycompress/version.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is provided under a dual BSD/LGPLv2.1 license. When using or 3 | * redistributing this file, you may do so under either license. 4 | * 5 | * BSD LICENSE 6 | * 7 | * Copyright (c) 2011-2012, Intel Corporation 8 | * All rights reserved. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * Redistributions of source code must retain the above copyright notice, 14 | * this list of conditions and the following disclaimer. 15 | * Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * Neither the name of Intel Corporation nor the names of its contributors 19 | * may be used to endorse or promote products derived from this software 20 | * without specific prior written permission. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 26 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 32 | * THE POSSIBILITY OF SUCH DAMAGE. 33 | * 34 | * LGPL LICENSE 35 | * 36 | * tinycompress library for compress audio offload in alsa 37 | * Copyright (c) 2011-2012, Intel Corporation. 38 | * 39 | * 40 | * This program is free software; you can redistribute it and/or modify it 41 | * under the terms and conditions of the GNU Lesser General Public License, 42 | * version 2.1, as published by the Free Software Foundation. 43 | * 44 | * This program is distributed in the hope it will be useful, but WITHOUT 45 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 46 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 47 | * License for more details. 48 | * 49 | * You should have received a copy of the GNU Lesser General Public License 50 | * along with this program; if not, write to 51 | * the Free Software Foundation, Inc., 52 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 53 | */ 54 | 55 | 56 | #ifndef __VERSION_H 57 | #define __VERSION_H 58 | 59 | 60 | #define TINYCOMPRESS_LIB_MAJOR 0 /* major number of library version */ 61 | #define TINYCOMPRESS_LIB_MINOR 2 /* minor number of library version */ 62 | #define TINYCOMPRESS_LIB_SUBMINOR 0 /* subminor number of library version */ 63 | 64 | /** library version */ 65 | #define TINYCOMPRESS_LIB_VERSION \ 66 | ((TINYCOMPRESS_LIB_MAJOR<<16)|\ 67 | (TINYCOMPRESS_LIB_MINOR<<8)|\ 68 | TINYCOMPRESS_LIB_SUBMINOR) 69 | 70 | /** library version (string) */ 71 | #define TINYCOMPRESS_LIB_VERSION_STR "0.2.0" 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /m4/.place_holder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alsa-project/tinycompress/ea5c7245beb0b6aec1565cfae0454d6ba374dfdd/m4/.place_holder -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = lib utils 2 | if BUILD_FCPLAY 3 | SUBDIRS += utils-lgpl 4 | endif 5 | -------------------------------------------------------------------------------- /src/lib/Makefile.am: -------------------------------------------------------------------------------- 1 | tinycompressdir = $(libdir) 2 | 3 | tinycompress_LTLIBRARIES = libtinycompress.la 4 | libtinycompress_la_SOURCES = compress.c compress_hw.c 5 | libtinycompress_la_CFLAGS = -I$(top_srcdir)/include 6 | libtinycompress_la_LIBADD = -ldl 7 | -------------------------------------------------------------------------------- /src/lib/compress.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is provided under a dual BSD/LGPLv2.1 license. When using or 3 | * redistributing this file, you may do so under either license. 4 | * 5 | * BSD LICENSE 6 | * 7 | * tinycompress library for compress audio offload in alsa 8 | * Copyright (c) 2011-2012, Intel Corporation 9 | * All rights reserved. 10 | * 11 | * Author: Vinod Koul 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that the following conditions are met: 15 | * 16 | * Redistributions of source code must retain the above copyright notice, 17 | * this list of conditions and the following disclaimer. 18 | * Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * Neither the name of Intel Corporation nor the names of its contributors 22 | * may be used to endorse or promote products derived from this software 23 | * without specific prior written permission. 24 | * 25 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 26 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 29 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 35 | * THE POSSIBILITY OF SUCH DAMAGE. 36 | * 37 | * LGPL LICENSE 38 | * 39 | * tinycompress library for compress audio offload in alsa 40 | * Copyright (c) 2011-2012, Intel Corporation. 41 | * 42 | * 43 | * This program is free software; you can redistribute it and/or modify it 44 | * under the terms and conditions of the GNU Lesser General Public License, 45 | * version 2.1, as published by the Free Software Foundation. 46 | * 47 | * This program is distributed in the hope it will be useful, but WITHOUT 48 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 49 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 50 | * License for more details. 51 | * 52 | * You should have received a copy of the GNU Lesser General Public License 53 | * along with this program; if not, write to 54 | * the Free Software Foundation, Inc., 55 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 56 | */ 57 | 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include "tinycompress/tinycompress.h" 63 | #include "tinycompress/compress_ops.h" 64 | 65 | #ifndef TINYCOMPRESS_PLUGIN_DIR 66 | #define TINYCOMPRESS_PLUGIN_DIR "/usr/lib/tinycompress-lib/" 67 | #endif 68 | 69 | struct compress { 70 | struct compress_ops *ops; 71 | void *data; 72 | void *dl_hdl; 73 | }; 74 | 75 | extern struct compress_ops compress_hw_ops; 76 | 77 | const char *compress_get_error(struct compress *compress) 78 | { 79 | return compress->ops->get_error(compress->data); 80 | } 81 | 82 | int is_compress_running(struct compress *compress) 83 | { 84 | return compress->ops->is_compress_running(compress->data); 85 | } 86 | 87 | int is_compress_ready(struct compress *compress) 88 | { 89 | return compress->ops->is_compress_ready(compress->data); 90 | } 91 | 92 | struct compress *compress_open(unsigned int card, unsigned int device, 93 | unsigned int flags, struct compr_config *config) 94 | { 95 | struct compress *compress; 96 | char name[128]; 97 | 98 | snprintf(name, sizeof(name), "hw:%u,%u", card, device); 99 | compress = calloc(1, sizeof(struct compress)); 100 | if (!compress) 101 | return NULL; 102 | 103 | compress->ops = &compress_hw_ops; 104 | compress->data = compress->ops->open_by_name(name, flags, config); 105 | if (compress->data == NULL) { 106 | free(compress); 107 | return NULL; 108 | } 109 | return compress; 110 | } 111 | 112 | static int populate_compress_plugin_ops(struct compress *compress, const char *name) 113 | { 114 | unsigned int ret = -1; 115 | char *token, *token_saveptr; 116 | char *compr_name; 117 | char lib_name[128]; 118 | void *dl_hdl; 119 | const char *err = NULL; 120 | const char *s; 121 | 122 | token = strdup(name); 123 | compr_name = strtok_r(token, ":", &token_saveptr); 124 | 125 | s = getenv("TINYCOMPRESS_PLUGIN_DIR"); 126 | if (s == NULL) 127 | s = TINYCOMPRESS_PLUGIN_DIR; 128 | 129 | snprintf(lib_name, sizeof(lib_name), "%slibtinycompress_module_%s.so", s, compr_name); 130 | 131 | free(token); 132 | dl_hdl = dlopen(lib_name, RTLD_NOW); 133 | if (!dl_hdl) { 134 | fprintf(stderr, "%s: unable to open %s, error: %s\n", 135 | __func__, lib_name, dlerror()); 136 | return ret; 137 | } 138 | 139 | compress->ops = dlsym(dl_hdl, "compress_plugin_ops"); 140 | err = dlerror(); 141 | if (err) { 142 | fprintf(stderr, "%s: dlsym to ops failed, err = '%s'\n", 143 | __func__, err); 144 | dlclose(dl_hdl); 145 | return ret; 146 | } 147 | compress->dl_hdl = dl_hdl; 148 | return 0; 149 | } 150 | 151 | /* 152 | * Format of name is : 153 | * 'hw:,'for hw compress nodes and 154 | * ':' for virtual compress nodes. 155 | * It dynamically loads the plugin library whose name is 156 | * libtinycompress_module_.so. Plugin library 157 | * needs to implement/expose compress_plugin_ops. 158 | */ 159 | struct compress *compress_open_by_name(const char *name, 160 | unsigned int flags, struct compr_config *config) 161 | { 162 | struct compress *compress; 163 | 164 | compress = calloc(1, sizeof(struct compress)); 165 | if (!compress) 166 | return NULL; 167 | 168 | if ((name[0] == 'h') || (name[1] == 'w') || (name[2] == ':')) { 169 | compress->ops = &compress_hw_ops; 170 | } else { 171 | if (populate_compress_plugin_ops(compress, name)) { 172 | free(compress); 173 | return NULL; 174 | } 175 | } 176 | 177 | compress->data = compress->ops->open_by_name(name, flags, config); 178 | if (compress->data == NULL) { 179 | if (compress->dl_hdl) 180 | dlclose(compress->dl_hdl); 181 | free(compress); 182 | return NULL; 183 | } 184 | return compress; 185 | } 186 | 187 | void compress_close(struct compress *compress) 188 | { 189 | compress->ops->close(compress->data); 190 | if (compress->dl_hdl) 191 | dlclose(compress->dl_hdl); 192 | 193 | free(compress); 194 | } 195 | 196 | int compress_get_hpointer(struct compress *compress, 197 | unsigned int *avail, struct timespec *tstamp) 198 | { 199 | return compress->ops->get_hpointer(compress->data, avail, tstamp); 200 | } 201 | 202 | int compress_get_tstamp(struct compress *compress, 203 | unsigned int *samples, unsigned int *sampling_rate) 204 | { 205 | return compress->ops->get_tstamp(compress->data, samples, sampling_rate); 206 | } 207 | 208 | int compress_write(struct compress *compress, const void *buf, unsigned int size) 209 | { 210 | return compress->ops->write(compress->data, buf, size); 211 | } 212 | 213 | int compress_read(struct compress *compress, void *buf, unsigned int size) 214 | { 215 | return compress->ops->read(compress->data, buf, size); 216 | } 217 | 218 | int compress_start(struct compress *compress) 219 | { 220 | return compress->ops->start(compress->data); 221 | } 222 | 223 | int compress_stop(struct compress *compress) 224 | { 225 | return compress->ops->stop(compress->data); 226 | } 227 | 228 | int compress_pause(struct compress *compress) 229 | { 230 | return compress->ops->pause(compress->data); 231 | } 232 | 233 | int compress_resume(struct compress *compress) 234 | { 235 | return compress->ops->resume(compress->data); 236 | } 237 | 238 | int compress_drain(struct compress *compress) 239 | { 240 | return compress->ops->drain(compress->data); 241 | } 242 | 243 | int compress_partial_drain(struct compress *compress) 244 | { 245 | return compress->ops->partial_drain(compress->data); 246 | } 247 | 248 | int compress_next_track(struct compress *compress) 249 | { 250 | return compress->ops->next_track(compress->data); 251 | } 252 | 253 | int compress_set_gapless_metadata(struct compress *compress, 254 | struct compr_gapless_mdata *mdata) 255 | { 256 | return compress->ops->set_gapless_metadata(compress->data, mdata); 257 | } 258 | 259 | bool is_codec_supported(unsigned int card, unsigned int device, 260 | unsigned int flags, struct snd_codec *codec) 261 | { 262 | struct compress_ops *ops = &compress_hw_ops; 263 | char name[128]; 264 | 265 | snprintf(name, sizeof(name), "hw:%u,%u", card, device); 266 | 267 | return ops->is_codec_supported_by_name(name, flags, codec); 268 | } 269 | 270 | bool is_codec_supported_by_name(const char *name, 271 | unsigned int flags, struct snd_codec *codec) 272 | { 273 | struct compress *compress; 274 | bool ret; 275 | 276 | compress = calloc(1, sizeof(struct compress)); 277 | if (!compress) 278 | return false; 279 | 280 | if ((name[0] == 'h') || (name[1] == 'w') || (name[2] == ':')) { 281 | compress->ops = &compress_hw_ops; 282 | } else { 283 | if (populate_compress_plugin_ops(compress, name)) { 284 | free(compress); 285 | return NULL; 286 | } 287 | } 288 | 289 | ret = compress->ops->is_codec_supported_by_name(name, flags, codec); 290 | 291 | if (compress->dl_hdl) 292 | dlclose(compress->dl_hdl); 293 | free(compress); 294 | 295 | return ret; 296 | } 297 | 298 | void compress_set_max_poll_wait(struct compress *compress, int milliseconds) 299 | { 300 | compress->ops->set_max_poll_wait(compress->data, milliseconds); 301 | } 302 | 303 | void compress_nonblock(struct compress *compress, int nonblock) 304 | { 305 | compress->ops->set_nonblock(compress->data, nonblock); 306 | } 307 | 308 | int compress_wait(struct compress *compress, int timeout_ms) 309 | { 310 | return compress->ops->wait(compress->data, timeout_ms); 311 | } 312 | 313 | int compress_set_codec_params(struct compress *compress, struct snd_codec *codec) 314 | { 315 | return compress->ops->set_codec_params(compress->data, codec); 316 | } 317 | -------------------------------------------------------------------------------- /src/lib/compress_hw.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1-only OR BSD-3-Clause) */ 2 | /* Copyright (c) 2011-2012, Intel Corporation. All rights reserved. */ 3 | /* Copyright (c) 2020 The Linux Foundation. All rights reserved. */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #define __force 22 | #define __bitwise 23 | #define __user 24 | #include 25 | #include "sound/compress_params.h" 26 | #include "sound/compress_offload.h" 27 | #include "tinycompress/tinycompress.h" 28 | #include "tinycompress/compress_ops.h" 29 | 30 | #define COMPR_ERR_MAX 128 31 | 32 | /* Default maximum time we will wait in a poll() - 20 seconds */ 33 | #define DEFAULT_MAX_POLL_WAIT_MS 20000 34 | 35 | struct compress_hw_data { 36 | int fd; 37 | unsigned int flags; 38 | char error[COMPR_ERR_MAX]; 39 | struct compr_config *config; 40 | int running; 41 | int max_poll_wait_ms; 42 | int nonblocking; 43 | unsigned int gapless_metadata; 44 | unsigned int next_track; 45 | }; 46 | 47 | static int oops(struct compress_hw_data *compress, int e, const char *fmt, ...) 48 | { 49 | va_list ap; 50 | int sz; 51 | 52 | va_start(ap, fmt); 53 | vsnprintf(compress->error, COMPR_ERR_MAX, fmt, ap); 54 | va_end(ap); 55 | sz = strlen(compress->error); 56 | 57 | snprintf(compress->error + sz, COMPR_ERR_MAX - sz, 58 | ": %s", strerror(e)); 59 | errno = e; 60 | 61 | return -1; 62 | } 63 | 64 | static struct compress_hw_data bad_compress = { 65 | .fd = -1, 66 | }; 67 | 68 | static const char *compress_hw_get_error(void *data) 69 | { 70 | struct compress_hw_data *compress = (struct compress_hw_data *)data; 71 | 72 | return compress->error; 73 | } 74 | 75 | static int is_compress_hw_running(void *data) 76 | { 77 | struct compress_hw_data *compress = (struct compress_hw_data *)data; 78 | 79 | return ((compress->fd > 0) && compress->running) ? 1 : 0; 80 | } 81 | 82 | static int is_compress_hw_ready(void *data) 83 | { 84 | struct compress_hw_data *compress = (struct compress_hw_data *)data; 85 | 86 | return (compress->fd > 0) ? 1 : 0; 87 | } 88 | 89 | static int get_compress_hw_version(struct compress_hw_data *compress) 90 | { 91 | int version = 0; 92 | 93 | if (ioctl(compress->fd, SNDRV_COMPRESS_IOCTL_VERSION, &version)) { 94 | oops(compress, errno, "cant read version"); 95 | return -1; 96 | } 97 | return version; 98 | } 99 | 100 | static bool _is_codec_type_supported(int fd, struct snd_codec *codec) 101 | { 102 | struct snd_compr_caps caps; 103 | bool found = false; 104 | unsigned int i; 105 | 106 | if (ioctl(fd, SNDRV_COMPRESS_GET_CAPS, &caps)) { 107 | oops(&bad_compress, errno, "cannot get device caps"); 108 | return false; 109 | } 110 | 111 | for (i = 0; i < caps.num_codecs; i++) { 112 | if (caps.codecs[i] == codec->id) { 113 | /* found the codec */ 114 | found = true; 115 | break; 116 | } 117 | } 118 | /* TODO: match the codec properties */ 119 | return found; 120 | } 121 | 122 | static inline void 123 | fill_compress_hw_params(struct compr_config *config, struct snd_compr_params *params) 124 | { 125 | params->buffer.fragment_size = config->fragment_size; 126 | params->buffer.fragments = config->fragments; 127 | memcpy(¶ms->codec, config->codec, sizeof(params->codec)); 128 | } 129 | 130 | static void *compress_hw_open_by_name(const char *name, 131 | unsigned int flags, struct compr_config *config) 132 | { 133 | struct compress_hw_data *compress; 134 | struct snd_compr_params params; 135 | struct snd_compr_caps caps; 136 | unsigned int card, device; 137 | char fn[256]; 138 | 139 | if (!config) { 140 | oops(&bad_compress, EINVAL, "passed bad config"); 141 | return &bad_compress; 142 | } 143 | 144 | if (sscanf(&name[3], "%u,%u", &card, &device) != 2) { 145 | oops(&bad_compress, errno, "Invalid device name %s", name); 146 | return &bad_compress; 147 | } 148 | 149 | compress = calloc(1, sizeof(struct compress_hw_data)); 150 | if (!compress) { 151 | oops(&bad_compress, errno, "cannot allocate compress object"); 152 | return &bad_compress; 153 | } 154 | 155 | compress->next_track = 0; 156 | compress->gapless_metadata = 0; 157 | compress->config = calloc(1, sizeof(*config)); 158 | if (!compress->config) 159 | goto input_fail; 160 | 161 | snprintf(fn, sizeof(fn), "/dev/snd/comprC%uD%u", card, device); 162 | 163 | compress->max_poll_wait_ms = DEFAULT_MAX_POLL_WAIT_MS; 164 | 165 | compress->flags = flags; 166 | if (!((flags & COMPRESS_OUT) || (flags & COMPRESS_IN))) { 167 | oops(&bad_compress, EINVAL, "can't deduce device direction from given flags"); 168 | goto config_fail; 169 | } 170 | 171 | if (flags & COMPRESS_OUT) { 172 | compress->fd = open(fn, O_RDONLY); 173 | } else { 174 | compress->fd = open(fn, O_WRONLY); 175 | } 176 | if (compress->fd < 0) { 177 | oops(&bad_compress, errno, "cannot open device '%s'", fn); 178 | goto config_fail; 179 | } 180 | 181 | if (ioctl(compress->fd, SNDRV_COMPRESS_GET_CAPS, &caps)) { 182 | oops(compress, errno, "cannot get device caps"); 183 | goto codec_fail; 184 | } 185 | 186 | /* If caller passed "don't care" fill in default values */ 187 | if ((config->fragment_size == 0) || (config->fragments == 0)) { 188 | config->fragment_size = caps.min_fragment_size; 189 | config->fragments = caps.max_fragments; 190 | } 191 | 192 | memcpy(compress->config, config, sizeof(*compress->config)); 193 | fill_compress_hw_params(config, ¶ms); 194 | 195 | if (ioctl(compress->fd, SNDRV_COMPRESS_SET_PARAMS, ¶ms)) { 196 | oops(&bad_compress, errno, "cannot set device"); 197 | goto codec_fail; 198 | } 199 | 200 | return compress; 201 | 202 | codec_fail: 203 | close(compress->fd); 204 | compress->fd = -1; 205 | config_fail: 206 | free(compress->config); 207 | input_fail: 208 | free(compress); 209 | return &bad_compress; 210 | } 211 | 212 | static void compress_hw_close(void *data) 213 | { 214 | struct compress_hw_data *compress = (struct compress_hw_data *)data; 215 | 216 | if (compress == &bad_compress) 217 | return; 218 | 219 | if (compress->fd >= 0) 220 | close(compress->fd); 221 | compress->running = 0; 222 | compress->fd = -1; 223 | free(compress->config); 224 | free(compress); 225 | } 226 | 227 | static int compress_hw_get_hpointer(void *data, 228 | unsigned int *avail, struct timespec *tstamp) 229 | { 230 | struct compress_hw_data *compress = (struct compress_hw_data *)data; 231 | struct snd_compr_avail kavail; 232 | __u64 time; 233 | 234 | if (!is_compress_hw_ready(compress)) 235 | return oops(compress, ENODEV, "device not ready"); 236 | 237 | if (ioctl(compress->fd, SNDRV_COMPRESS_AVAIL, &kavail)) 238 | return oops(compress, errno, "cannot get avail"); 239 | if (0 == kavail.tstamp.sampling_rate) 240 | return oops(compress, ENODATA, "sample rate unknown"); 241 | *avail = (unsigned int)kavail.avail; 242 | time = kavail.tstamp.pcm_io_frames / kavail.tstamp.sampling_rate; 243 | tstamp->tv_sec = time; 244 | time = kavail.tstamp.pcm_io_frames % kavail.tstamp.sampling_rate; 245 | tstamp->tv_nsec = time * 1000000000 / kavail.tstamp.sampling_rate; 246 | return 0; 247 | } 248 | 249 | static int compress_hw_get_tstamp(void *data, 250 | unsigned int *samples, unsigned int *sampling_rate) 251 | { 252 | struct compress_hw_data *compress = (struct compress_hw_data *)data; 253 | struct snd_compr_tstamp ktstamp; 254 | 255 | if (!is_compress_hw_ready(compress)) 256 | return oops(compress, ENODEV, "device not ready"); 257 | 258 | if (ioctl(compress->fd, SNDRV_COMPRESS_TSTAMP, &ktstamp)) 259 | return oops(compress, errno, "cannot get tstamp"); 260 | 261 | *samples = ktstamp.pcm_io_frames; 262 | *sampling_rate = ktstamp.sampling_rate; 263 | return 0; 264 | } 265 | 266 | static int compress_hw_write(void *data, const void *buf, size_t size) 267 | { 268 | struct compress_hw_data *compress = (struct compress_hw_data *)data; 269 | struct snd_compr_avail avail; 270 | struct pollfd fds; 271 | int to_write = 0; /* zero indicates we haven't written yet */ 272 | int written, total = 0, ret; 273 | const char* cbuf = buf; 274 | const unsigned int frag_size = compress->config->fragment_size; 275 | 276 | if (!(compress->flags & COMPRESS_IN)) 277 | return oops(compress, EINVAL, "Invalid flag set"); 278 | if (!is_compress_hw_ready(compress)) 279 | return oops(compress, ENODEV, "device not ready"); 280 | fds.fd = compress->fd; 281 | fds.events = POLLOUT; 282 | 283 | /*TODO: treat auto start here first */ 284 | while (size) { 285 | if (ioctl(compress->fd, SNDRV_COMPRESS_AVAIL, &avail)) 286 | return oops(compress, errno, "cannot get avail"); 287 | 288 | /* We can write if we have at least one fragment available 289 | * or there is enough space for all remaining data 290 | */ 291 | if ((avail.avail < frag_size) && (avail.avail < size)) { 292 | 293 | if (compress->nonblocking) 294 | return total; 295 | 296 | ret = poll(&fds, 1, compress->max_poll_wait_ms); 297 | if (fds.revents & POLLERR) { 298 | return oops(compress, EIO, "poll returned error!"); 299 | } 300 | /* A pause will cause -EBADFD or zero. 301 | * This is not an error, just stop writing */ 302 | if ((ret == 0) || (ret < 0 && errno == EBADFD)) 303 | break; 304 | if (ret < 0) 305 | return oops(compress, errno, "poll error"); 306 | if (fds.revents & POLLOUT) { 307 | continue; 308 | } 309 | } 310 | /* write avail bytes */ 311 | if (size > avail.avail) 312 | to_write = avail.avail; 313 | else 314 | to_write = size; 315 | written = write(compress->fd, cbuf, to_write); 316 | if (written < 0) { 317 | /* If play was paused the write returns -EBADFD */ 318 | if (errno == EBADFD) 319 | break; 320 | return oops(compress, errno, "write failed!"); 321 | } 322 | 323 | size -= written; 324 | cbuf += written; 325 | total += written; 326 | } 327 | return total; 328 | } 329 | 330 | static int compress_hw_read(void *data, void *buf, size_t size) 331 | { 332 | struct compress_hw_data *compress = (struct compress_hw_data *)data; 333 | struct snd_compr_avail avail; 334 | struct pollfd fds; 335 | int to_read = 0; 336 | int num_read, total = 0, ret; 337 | char* cbuf = buf; 338 | const unsigned int frag_size = compress->config->fragment_size; 339 | 340 | if (!(compress->flags & COMPRESS_OUT)) 341 | return oops(compress, EINVAL, "Invalid flag set"); 342 | if (!is_compress_hw_ready(compress)) 343 | return oops(compress, ENODEV, "device not ready"); 344 | fds.fd = compress->fd; 345 | fds.events = POLLIN; 346 | 347 | while (size) { 348 | if (ioctl(compress->fd, SNDRV_COMPRESS_AVAIL, &avail)) 349 | return oops(compress, errno, "cannot get avail"); 350 | 351 | if ( (avail.avail < frag_size) && (avail.avail < size) ) { 352 | /* Less than one fragment available and not at the 353 | * end of the read, so poll 354 | */ 355 | if (compress->nonblocking) 356 | return total; 357 | 358 | ret = poll(&fds, 1, compress->max_poll_wait_ms); 359 | if (fds.revents & POLLERR) { 360 | return oops(compress, EIO, "poll returned error!"); 361 | } 362 | /* A pause will cause -EBADFD or zero. 363 | * This is not an error, just stop reading */ 364 | if ((ret == 0) || (ret < 0 && errno == EBADFD)) 365 | break; 366 | if (ret < 0) 367 | return oops(compress, errno, "poll error"); 368 | if (fds.revents & POLLIN) { 369 | continue; 370 | } 371 | } 372 | /* read avail bytes */ 373 | if (size > avail.avail) 374 | to_read = avail.avail; 375 | else 376 | to_read = size; 377 | num_read = read(compress->fd, cbuf, to_read); 378 | if (num_read < 0) { 379 | /* If play was paused the read returns -EBADFD */ 380 | if (errno == EBADFD) 381 | break; 382 | return oops(compress, errno, "read failed!"); 383 | } 384 | 385 | size -= num_read; 386 | cbuf += num_read; 387 | total += num_read; 388 | } 389 | 390 | return total; 391 | } 392 | 393 | static int compress_hw_start(void *data) 394 | { 395 | struct compress_hw_data *compress = (struct compress_hw_data *)data; 396 | 397 | if (!is_compress_hw_ready(compress)) 398 | return oops(compress, ENODEV, "device not ready"); 399 | if (ioctl(compress->fd, SNDRV_COMPRESS_START)) 400 | return oops(compress, errno, "cannot start the stream"); 401 | compress->running = 1; 402 | return 0; 403 | 404 | } 405 | 406 | static int compress_hw_stop(void *data) 407 | { 408 | struct compress_hw_data *compress = (struct compress_hw_data *)data; 409 | 410 | if (!is_compress_hw_running(compress)) 411 | return oops(compress, ENODEV, "device not ready"); 412 | if (ioctl(compress->fd, SNDRV_COMPRESS_STOP)) 413 | return oops(compress, errno, "cannot stop the stream"); 414 | return 0; 415 | } 416 | 417 | static int compress_hw_pause(void *data) 418 | { 419 | struct compress_hw_data *compress = (struct compress_hw_data *)data; 420 | 421 | if (!is_compress_hw_running(compress)) 422 | return oops(compress, ENODEV, "device not ready"); 423 | if (ioctl(compress->fd, SNDRV_COMPRESS_PAUSE)) 424 | return oops(compress, errno, "cannot pause the stream"); 425 | return 0; 426 | } 427 | 428 | static int compress_hw_resume(void *data) 429 | { 430 | struct compress_hw_data *compress = (struct compress_hw_data *)data; 431 | 432 | if (ioctl(compress->fd, SNDRV_COMPRESS_RESUME)) 433 | return oops(compress, errno, "cannot resume the stream"); 434 | return 0; 435 | } 436 | 437 | static int compress_hw_drain(void *data) 438 | { 439 | struct compress_hw_data *compress = (struct compress_hw_data *)data; 440 | 441 | if (!is_compress_hw_running(compress)) 442 | return oops(compress, ENODEV, "device not ready"); 443 | if (ioctl(compress->fd, SNDRV_COMPRESS_DRAIN)) 444 | return oops(compress, errno, "cannot drain the stream"); 445 | return 0; 446 | } 447 | 448 | static int compress_hw_partial_drain(void *data) 449 | { 450 | struct compress_hw_data *compress = (struct compress_hw_data *)data; 451 | 452 | if (!is_compress_hw_running(compress)) 453 | return oops(compress, ENODEV, "device not ready"); 454 | 455 | if (!compress->next_track) 456 | return oops(compress, EPERM, "next track not signalled"); 457 | if (ioctl(compress->fd, SNDRV_COMPRESS_PARTIAL_DRAIN)) 458 | return oops(compress, errno, "cannot drain the stream\n"); 459 | compress->next_track = 0; 460 | return 0; 461 | } 462 | 463 | static int compress_hw_next_track(void *data) 464 | { 465 | struct compress_hw_data *compress = (struct compress_hw_data *)data; 466 | 467 | if (!is_compress_hw_running(compress)) 468 | return oops(compress, ENODEV, "device not ready"); 469 | 470 | if (!compress->gapless_metadata) 471 | return oops(compress, EPERM, "metadata not set"); 472 | if (ioctl(compress->fd, SNDRV_COMPRESS_NEXT_TRACK)) 473 | return oops(compress, errno, "cannot set next track\n"); 474 | compress->next_track = 1; 475 | compress->gapless_metadata = 0; 476 | return 0; 477 | } 478 | 479 | static int compress_hw_set_gapless_metadata(void *data, 480 | struct compr_gapless_mdata *mdata) 481 | { 482 | struct compress_hw_data *compress = (struct compress_hw_data *)data; 483 | struct snd_compr_metadata metadata; 484 | int version; 485 | 486 | if (!is_compress_hw_ready(compress)) 487 | return oops(compress, ENODEV, "device not ready"); 488 | 489 | version = get_compress_hw_version(compress); 490 | if (version <= 0) 491 | return -1; 492 | 493 | if (version < SNDRV_PROTOCOL_VERSION(0, 1, 1)) 494 | return oops(compress, ENXIO, "gapless apis not supported in kernel"); 495 | 496 | metadata.key = SNDRV_COMPRESS_ENCODER_PADDING; 497 | metadata.value[0] = mdata->encoder_padding; 498 | if (ioctl(compress->fd, SNDRV_COMPRESS_SET_METADATA, &metadata)) 499 | return oops(compress, errno, "can't set metadata for stream\n"); 500 | 501 | metadata.key = SNDRV_COMPRESS_ENCODER_DELAY; 502 | metadata.value[0] = mdata->encoder_delay; 503 | if (ioctl(compress->fd, SNDRV_COMPRESS_SET_METADATA, &metadata)) 504 | return oops(compress, errno, "can't set metadata for stream\n"); 505 | compress->gapless_metadata = 1; 506 | return 0; 507 | } 508 | 509 | static bool compress_hw_is_codec_supported_by_name(const char *name, 510 | unsigned int flags, struct snd_codec *codec) 511 | { 512 | unsigned int card, device; 513 | unsigned int dev_flag; 514 | bool ret; 515 | int fd; 516 | char fn[256]; 517 | 518 | if (sscanf(&name[3], "%u,%u", &card, &device) != 2) 519 | return false; 520 | 521 | snprintf(fn, sizeof(fn), "/dev/snd/comprC%uD%u", card, device); 522 | 523 | if (flags & COMPRESS_OUT) 524 | dev_flag = O_RDONLY; 525 | else 526 | dev_flag = O_WRONLY; 527 | 528 | fd = open(fn, dev_flag); 529 | if (fd < 0) 530 | return oops(&bad_compress, errno, "cannot open device '%s'", fn); 531 | 532 | ret = _is_codec_type_supported(fd, codec); 533 | 534 | close(fd); 535 | return ret; 536 | } 537 | 538 | static void compress_hw_set_max_poll_wait(void *data, int milliseconds) 539 | { 540 | struct compress_hw_data *compress = (struct compress_hw_data *)data; 541 | 542 | compress->max_poll_wait_ms = milliseconds; 543 | } 544 | 545 | static void compress_hw_set_nonblock(void *data, int nonblock) 546 | { 547 | struct compress_hw_data *compress = (struct compress_hw_data *)data; 548 | 549 | compress->nonblocking = !!nonblock; 550 | } 551 | 552 | static int compress_hw_wait(void *data, int timeout_ms) 553 | { 554 | struct pollfd fds; 555 | int ret; 556 | struct compress_hw_data *compress = (struct compress_hw_data *)data; 557 | 558 | fds.fd = compress->fd; 559 | fds.events = POLLOUT | POLLIN; 560 | 561 | ret = poll(&fds, 1, timeout_ms); 562 | if (ret > 0) { 563 | if (fds.revents & POLLERR) 564 | return oops(compress, EIO, "poll returned error!"); 565 | if (fds.revents & (POLLOUT | POLLIN)) 566 | return 0; 567 | } 568 | if (ret == 0) 569 | return oops(compress, ETIME, "poll timed out"); 570 | if (ret < 0) 571 | return oops(compress, errno, "poll error"); 572 | 573 | return oops(compress, EIO, "poll signalled unhandled event"); 574 | } 575 | 576 | static int compress_hw_set_codec_params(void *data, struct snd_codec *codec) 577 | { 578 | struct compress_hw_data *compress = (struct compress_hw_data *)data; 579 | struct snd_compr_params params; 580 | 581 | if (!is_compress_hw_ready(compress)) 582 | return oops(compress, ENODEV, "device not ready\n"); 583 | 584 | if (!codec) 585 | return oops(compress, EINVAL, "passed bad config\n"); 586 | 587 | if (!compress->next_track) 588 | return oops(compress, EPERM, 589 | "set CODEC params while next track not signalled is not allowed"); 590 | 591 | params.buffer.fragment_size = compress->config->fragment_size; 592 | params.buffer.fragments = compress->config->fragments; 593 | memcpy(¶ms.codec, codec, sizeof(params.codec)); 594 | 595 | if (ioctl(compress->fd, SNDRV_COMPRESS_SET_PARAMS, ¶ms)) 596 | return oops(compress, errno, "cannot set param for next track\n"); 597 | 598 | return 0; 599 | } 600 | 601 | struct compress_ops compress_hw_ops = { 602 | .open_by_name = compress_hw_open_by_name, 603 | .close = compress_hw_close, 604 | .get_hpointer = compress_hw_get_hpointer, 605 | .get_tstamp = compress_hw_get_tstamp, 606 | .write = compress_hw_write, 607 | .read = compress_hw_read, 608 | .start = compress_hw_start, 609 | .stop = compress_hw_stop, 610 | .pause = compress_hw_pause, 611 | .resume = compress_hw_resume, 612 | .drain = compress_hw_drain, 613 | .partial_drain = compress_hw_partial_drain, 614 | .next_track = compress_hw_next_track, 615 | .set_gapless_metadata = compress_hw_set_gapless_metadata, 616 | .set_max_poll_wait = compress_hw_set_max_poll_wait, 617 | .set_nonblock = compress_hw_set_nonblock, 618 | .wait = compress_hw_wait, 619 | .is_codec_supported_by_name = compress_hw_is_codec_supported_by_name, 620 | .is_compress_running = is_compress_hw_running, 621 | .is_compress_ready = is_compress_hw_ready, 622 | .get_error = compress_hw_get_error, 623 | .set_codec_params = compress_hw_set_codec_params, 624 | }; 625 | 626 | -------------------------------------------------------------------------------- /src/utils-lgpl/Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS = fcplay 2 | 3 | fcplay_SOURCES = fcplay.c 4 | 5 | fcplay_CFLAGS = -I$(top_srcdir)/include \ 6 | $(AVCODEC_CFLAGS) \ 7 | $(AVFORMAT_CFLAGS) \ 8 | $(AVUTIL_CFLAGS) \ 9 | $(LIBDRM_CFLAGS) 10 | 11 | fcplay_LDADD = $(top_builddir)/src/lib/libtinycompress.la \ 12 | $(AVCODEC_LIBS) \ 13 | $(AVFORMAT_LIBS) \ 14 | $(AVUTIL_LIBS) \ 15 | $(LIBDRM_LIBS) 16 | -------------------------------------------------------------------------------- /src/utils-lgpl/fcplay.c: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: LGPL-2.1-only 2 | 3 | //Copyright (c) 2011-2012, Intel Corporation 4 | //Copyright (c) 2018-2019, Linaro Ltd 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #define __force 19 | #define __user 20 | #include "sound/compress_params.h" 21 | #include "tinycompress/tinycompress.h" 22 | #include 23 | #include 24 | 25 | static int verbose; 26 | 27 | static const struct { 28 | const char *name; 29 | unsigned int id; 30 | } codec_ids[] = { 31 | { "PCM", SND_AUDIOCODEC_PCM }, 32 | { "MP3", SND_AUDIOCODEC_MP3 }, 33 | { "AMR", SND_AUDIOCODEC_AMR }, 34 | { "AMRWB", SND_AUDIOCODEC_AMRWB }, 35 | { "AMRWBPLUS", SND_AUDIOCODEC_AMRWBPLUS }, 36 | { "AAC", SND_AUDIOCODEC_AAC }, 37 | { "WMA", SND_AUDIOCODEC_WMA }, 38 | { "REAL", SND_AUDIOCODEC_REAL }, 39 | { "VORBIS", SND_AUDIOCODEC_VORBIS }, 40 | { "FLAC", SND_AUDIOCODEC_FLAC }, 41 | { "IEC61937", SND_AUDIOCODEC_IEC61937 }, 42 | { "G723_1", SND_AUDIOCODEC_G723_1 }, 43 | { "G729", SND_AUDIOCODEC_G729 }, 44 | /* BESPOKE isn't defined on older kernels */ 45 | #ifdef SND_AUDIOCODEC_BESPOKE 46 | { "BESPOKE", SND_AUDIOCODEC_BESPOKE }, 47 | #endif 48 | }; 49 | #define CPLAY_NUM_CODEC_IDS (sizeof(codec_ids) / sizeof(codec_ids[0])) 50 | 51 | static void usage(void) 52 | { 53 | int i; 54 | 55 | fprintf(stderr, "usage: cplay [OPTIONS] filename\n" 56 | "-c\tcard number\n" 57 | "-d\tdevice node\n" 58 | "-I\tspecify codec ID (default is mp3)\n" 59 | "-b\tbuffer size\n" 60 | "-f\tfragments\n" 61 | "-g\tgapless play\n\n" 62 | "-v\tverbose mode\n" 63 | "-h\tPrints this help list\n\n" 64 | "Example:\n" 65 | "\tfcplay -c 1 -d 2 test.mp3\n" 66 | "\tfcplay -f 5 test.mp3\n" 67 | "\tfcplay -c 1 -d 2 test1.mp3 test2.mp3\n" 68 | "\tGapless:\n" 69 | "\t\tfcplay -c 1 -d 2 -g 1 test1.mp3 test2.mp3\n\n" 70 | "Valid codec IDs:\n"); 71 | 72 | for (i = 0; i < CPLAY_NUM_CODEC_IDS; ++i) 73 | fprintf(stderr, "%s%c", codec_ids[i].name, 74 | ((i + 1) % 8) ? ' ' : '\n'); 75 | 76 | fprintf(stderr, "\nor the value in decimal or hex\n"); 77 | 78 | exit(EXIT_FAILURE); 79 | } 80 | 81 | void play_samples(char **files, unsigned int card, unsigned int device, 82 | unsigned long buffer_size, unsigned int frag, 83 | unsigned long codec_id, unsigned int file_count, unsigned int gapless); 84 | 85 | static int print_time(struct compress *compress) 86 | { 87 | unsigned int avail; 88 | struct timespec tstamp; 89 | 90 | if (compress_get_hpointer(compress, &avail, &tstamp) != 0) { 91 | fprintf(stderr, "Error querying timestamp\n"); 92 | fprintf(stderr, "ERR: %s\n", compress_get_error(compress)); 93 | return -1; 94 | } else 95 | fprintf(stderr, "DSP played %jd.%jd\n", (intmax_t)tstamp.tv_sec, (intmax_t)tstamp.tv_nsec*1000); 96 | return 0; 97 | } 98 | 99 | int main(int argc, char **argv) 100 | { 101 | char **file; 102 | unsigned long buffer_size = 0; 103 | int c, i; 104 | unsigned int card = 0, device = 0, frag = 0; 105 | unsigned int codec_id = SND_AUDIOCODEC_MP3; 106 | unsigned int file_count = 0, gapless = 0; 107 | 108 | if (argc < 2) 109 | usage(); 110 | 111 | verbose = 0; 112 | while ((c = getopt(argc, argv, "hvb:f:c:d:I:g:")) != -1) { 113 | switch (c) { 114 | case 'h': 115 | usage(); 116 | break; 117 | case 'b': 118 | buffer_size = strtol(optarg, NULL, 0); 119 | break; 120 | case 'f': 121 | frag = strtol(optarg, NULL, 10); 122 | break; 123 | case 'c': 124 | card = strtol(optarg, NULL, 10); 125 | break; 126 | case 'd': 127 | device = strtol(optarg, NULL, 10); 128 | break; 129 | case 'g': 130 | gapless = strtol(optarg, NULL, 10); 131 | break; 132 | case 'I': 133 | if (optarg[0] == '0') { 134 | codec_id = strtol(optarg, NULL, 0); 135 | } else { 136 | for (i = 0; i < CPLAY_NUM_CODEC_IDS; ++i) { 137 | if (strcmp(optarg, 138 | codec_ids[i].name) == 0) { 139 | codec_id = codec_ids[i].id; 140 | break; 141 | } 142 | } 143 | 144 | if (i == CPLAY_NUM_CODEC_IDS) { 145 | fprintf(stderr, "Unrecognised ID: %s\n", 146 | optarg); 147 | usage(); 148 | } 149 | } 150 | break; 151 | case 'v': 152 | verbose = 1; 153 | break; 154 | default: 155 | exit(EXIT_FAILURE); 156 | } 157 | } 158 | if (optind >= argc) 159 | usage(); 160 | 161 | file = &argv[optind]; 162 | 163 | //file_count represents total number of files to play 164 | file_count = argc - optind; 165 | 166 | play_samples(file, card, device, buffer_size, frag, codec_id, 167 | file_count, gapless); 168 | 169 | fprintf(stderr, "Finish Playing.... Close Normally\n"); 170 | exit(EXIT_SUCCESS); 171 | } 172 | 173 | static int get_codec_id(int codec_id) 174 | { 175 | switch (codec_id) { 176 | case AV_CODEC_ID_MP3: 177 | return SND_AUDIOCODEC_MP3; 178 | case AV_CODEC_ID_AAC: 179 | return SND_AUDIOCODEC_AAC; 180 | case AV_CODEC_ID_WMAV1: 181 | case AV_CODEC_ID_WMAV2: 182 | return SND_AUDIOCODEC_WMA; 183 | case AV_CODEC_ID_VORBIS: 184 | return SND_AUDIOCODEC_VORBIS; 185 | case AV_CODEC_ID_FLAC: 186 | return SND_AUDIOCODEC_FLAC; 187 | case AV_CODEC_ID_RA_144: 188 | case AV_CODEC_ID_RA_288: 189 | return SND_AUDIOCODEC_REAL; 190 | case AV_CODEC_ID_AMR_NB: 191 | return SND_AUDIOCODEC_AMR; 192 | case AV_CODEC_ID_AMR_WB: 193 | return SND_AUDIOCODEC_AMRWB; 194 | case AV_CODEC_ID_PCM_S16LE ... AV_CODEC_ID_PCM_S16BE_PLANAR: 195 | return SND_AUDIOCODEC_PCM; 196 | default: 197 | fprintf(stderr, "Not supported AVcodec: %d\n", codec_id); 198 | exit(EXIT_FAILURE); 199 | } 200 | } 201 | 202 | static int parse_file(char *file, struct snd_codec *codec) 203 | { 204 | AVFormatContext *ctx = NULL; 205 | AVStream *stream; 206 | char errbuf[50]; 207 | int err = 0, i, filled = 0; 208 | 209 | err = avformat_open_input(&ctx, file, NULL, NULL); 210 | if (err < 0) { 211 | av_strerror(err, errbuf, sizeof(errbuf)); 212 | fprintf(stderr, "Unable to open %s: %s\n", file, errbuf); 213 | exit(EXIT_FAILURE); 214 | } 215 | 216 | err = avformat_find_stream_info(ctx, NULL); 217 | if (err < 0) { 218 | av_strerror(err, errbuf, sizeof(errbuf)); 219 | fprintf(stderr, "Unable to identify %s: %s\n", file, errbuf); 220 | goto exit; 221 | } 222 | 223 | if (ctx->nb_streams < 1) { 224 | fprintf(stderr, "No streams found in %s\n", file); 225 | goto exit; 226 | } 227 | 228 | if (verbose) 229 | fprintf(stderr, "Streams: %d\n", ctx->nb_streams); 230 | 231 | for (i = 0; i < ctx->nb_streams; i++) { 232 | stream = ctx->streams[i]; 233 | 234 | if (verbose) 235 | fprintf(stderr, "StreamType: %d", stream->codecpar->codec_type); 236 | 237 | if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { 238 | fprintf(stderr, "Stream:%d is audio type\n", i); 239 | 240 | if (!filled) { 241 | /* we fill params from 1st audio stream */ 242 | codec->id = get_codec_id(stream->codecpar->codec_id); 243 | codec->ch_in = stream->codecpar->ch_layout.nb_channels; 244 | codec->ch_out = stream->codecpar->ch_layout.nb_channels; 245 | codec->sample_rate = stream->codecpar->sample_rate; 246 | codec->bit_rate = ctx->bit_rate; 247 | codec->profile = stream->codecpar->profile; 248 | codec->format = 0; /* need codec format type */ 249 | codec->align = stream->codecpar->block_align; 250 | codec->level = 0; 251 | codec->rate_control = 0; 252 | codec->ch_mode = 0; 253 | filled = 1; 254 | 255 | if (codec->id == SND_AUDIOCODEC_FLAC) { 256 | codec->options.flac_d.sample_size = stream->codecpar->bits_per_raw_sample; 257 | /* use these values from libav/flac* where 16 is mind block size for 258 | * flac and 64K max. 11 is min frame and avg is 8192, so take 4 times 259 | * that 260 | */ 261 | codec->options.flac_d.min_blk_size = 16; 262 | codec->options.flac_d.max_blk_size = 65535; 263 | codec->options.flac_d.min_frame_size = 11; 264 | codec->options.flac_d.max_frame_size = 8192*4; 265 | } 266 | } 267 | 268 | if (verbose) { 269 | fprintf(stderr, "Stream:%d", i); 270 | fprintf(stderr, " Codec: %d", stream->codecpar->codec_id); 271 | fprintf(stderr, " Format: %d", stream->codecpar->format); 272 | fprintf(stderr, " Bit Rate: %ld", stream->codecpar->bit_rate); 273 | fprintf(stderr, " Bits coded: %d", stream->codecpar->bits_per_coded_sample); 274 | fprintf(stderr, " Profile: %d", stream->codecpar->profile); 275 | fprintf(stderr, " Codec tag: %d", stream->codecpar->codec_tag); 276 | fprintf(stderr, " Channels: %d", stream->codecpar->ch_layout.nb_channels); 277 | fprintf(stderr, " Sample rate: %d", stream->codecpar->sample_rate); 278 | fprintf(stderr, " block_align: %d", stream->codecpar->block_align); 279 | if (codec->id == SND_AUDIOCODEC_FLAC) { 280 | fprintf(stderr, " Sample Size %d", codec->options.flac_d.sample_size); 281 | fprintf(stderr, " Min Block Size %d", codec->options.flac_d.min_blk_size); 282 | fprintf(stderr, " Max Block Size %d", codec->options.flac_d.max_blk_size); 283 | fprintf(stderr, " Min Frame Size %d", codec->options.flac_d.min_frame_size); 284 | fprintf(stderr, " Max Frame Size %d", codec->options.flac_d.max_frame_size); 285 | 286 | } 287 | fprintf(stderr, "\n"); 288 | } 289 | } 290 | } 291 | 292 | if (verbose) 293 | av_dump_format(ctx, 0, file, 0); 294 | 295 | avformat_close_input(&ctx); 296 | return 0; 297 | 298 | exit: 299 | avformat_close_input(&ctx); 300 | exit(EXIT_FAILURE); 301 | 302 | } 303 | 304 | void play_samples(char **files, unsigned int card, unsigned int device, 305 | unsigned long buffer_size, unsigned int frag, 306 | unsigned long codec_id, unsigned int file_count, unsigned int gapless) 307 | { 308 | struct compr_config config; 309 | struct snd_codec codec; 310 | struct compress *compress; 311 | struct compr_gapless_mdata mdata; 312 | FILE *file; 313 | char *buffer; 314 | char *name; 315 | int size, num_read, wrote; 316 | unsigned int file_idx = 0, rc = 0; 317 | 318 | if (verbose) 319 | printf("%s: entry\n", __func__); 320 | 321 | name = files[file_idx]; 322 | file = fopen(name, "rb"); 323 | if (!file) { 324 | fprintf(stderr, "Unable to open file '%s'\n", name); 325 | exit(EXIT_FAILURE); 326 | } 327 | 328 | memset(&codec, 0, sizeof(codec)); 329 | memset(&config, 0, sizeof(config)); 330 | memset(&mdata, 0, sizeof(mdata)); 331 | 332 | parse_file(name, &codec); 333 | 334 | config.codec = &codec; 335 | 336 | compress = compress_open(card, device, COMPRESS_IN, &config); 337 | if (!compress || !is_compress_ready(compress)) { 338 | fprintf(stderr, "Unable to open Compress device %d:%d\n", 339 | card, device); 340 | fprintf(stderr, "ERR: %s\n", compress_get_error(compress)); 341 | goto FILE_EXIT; 342 | }; 343 | if (verbose) 344 | printf("%s: Opened compress device\n", __func__); 345 | size = config.fragment_size; 346 | buffer = malloc(size * config.fragments); 347 | if (!buffer) { 348 | fprintf(stderr, "Unable to allocate %d bytes\n", size); 349 | goto COMP_EXIT; 350 | } 351 | 352 | if (gapless) 353 | compress_set_gapless_metadata(compress, &mdata); 354 | 355 | /* we will write frag fragment_size and then start */ 356 | num_read = fread(buffer, 1, size * config.fragments, file); 357 | if (num_read > 0) { 358 | if (verbose) 359 | printf("%s: Doing first buffer write of %d\n", __func__, num_read); 360 | wrote = compress_write(compress, buffer, num_read); 361 | if (wrote < 0) { 362 | fprintf(stderr, "Error %d playing sample\n", wrote); 363 | fprintf(stderr, "ERR: %s\n", compress_get_error(compress)); 364 | goto BUF_EXIT; 365 | } 366 | if (wrote != num_read) { 367 | /* TODO: Buufer pointer needs to be set here */ 368 | fprintf(stderr, "We wrote %d, DSP accepted %d\n", num_read, wrote); 369 | } 370 | } 371 | printf("Playing file %s On Card %u device %u, with buffer of %lu bytes\n", 372 | name, card, device, buffer_size); 373 | printf("Format %u Channels %u, %u Hz, Bit Rate %d\n", 374 | codec.id, codec.ch_in, codec.sample_rate, codec.bit_rate); 375 | 376 | compress_start(compress); 377 | if (verbose) 378 | printf("%s: You should hear audio NOW!!!\n", __func__); 379 | 380 | do { 381 | if (file_idx != 0) { 382 | name = files[file_idx]; 383 | file = fopen(name, "rb"); 384 | if (!file) 385 | goto TRACK_EXIT; 386 | 387 | if (verbose) 388 | printf("Playing file %s On Card %u device %u, with buffer of %lu bytes\n", 389 | name, card, device, buffer_size); 390 | 391 | if (gapless) { 392 | parse_file(name, &codec); 393 | 394 | rc = compress_next_track(compress); 395 | if (rc) 396 | fprintf(stderr, "ERR: compress next track set\n"); 397 | 398 | rc = compress_set_gapless_metadata(compress, &mdata); 399 | if (rc) 400 | fprintf(stderr, "ERR: set gapless metadata\n"); 401 | 402 | rc = compress_set_codec_params(compress, &codec); 403 | if (rc) 404 | fprintf(stderr, "ERR: set next track codec params\n"); 405 | 406 | /* issue partial drain if it supports */ 407 | rc = compress_partial_drain(compress); 408 | if (rc) 409 | fprintf(stderr, "ERR: partial drain\n"); 410 | } 411 | } 412 | 413 | do { 414 | num_read = fread(buffer, 1, size, file); 415 | if (num_read > 0) { 416 | wrote = compress_write(compress, buffer, num_read); 417 | if (wrote < 0) { 418 | fprintf(stderr, "Error playing sample\n"); 419 | fprintf(stderr, "ERR: %s\n", compress_get_error(compress)); 420 | goto BUF_EXIT; 421 | } 422 | if (wrote != num_read) { 423 | /* TODO: Buffer pointer needs to be set here */ 424 | fprintf(stderr, "We wrote %d, DSP accepted %d\n", num_read, wrote); 425 | } 426 | if (verbose) { 427 | print_time(compress); 428 | printf("%s: wrote %d\n", __func__, wrote); 429 | } 430 | } 431 | } while (num_read > 0); 432 | 433 | file_idx++; 434 | if (file_idx == file_count) { 435 | /* issue drain if it supports */ 436 | compress_drain(compress); 437 | } 438 | fclose(file); 439 | } while (file_idx < file_count); 440 | 441 | if (verbose) 442 | printf("%s: exit success\n", __func__); 443 | /* issue drain if it supports */ 444 | free(buffer); 445 | compress_close(compress); 446 | return; 447 | 448 | TRACK_EXIT: 449 | if (verbose) 450 | printf("%s: exit track\n", __func__); 451 | BUF_EXIT: 452 | free(buffer); 453 | COMP_EXIT: 454 | compress_close(compress); 455 | FILE_EXIT: 456 | fclose(file); 457 | if (verbose) 458 | printf("%s: exit failure\n", __func__); 459 | exit(EXIT_FAILURE); 460 | } 461 | -------------------------------------------------------------------------------- /src/utils/Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS = cplay crecord 2 | 3 | cplay_SOURCES = cplay.c wave.c 4 | crecord_SOURCES = crecord.c wave.c 5 | 6 | cplay_CFLAGS = -I$(top_srcdir)/include 7 | crecord_CFLAGS = -I$(top_srcdir)/include 8 | 9 | 10 | cplay_LDADD = $(top_builddir)/src/lib/libtinycompress.la 11 | crecord_LDADD = $(top_builddir)/src/lib/libtinycompress.la 12 | -------------------------------------------------------------------------------- /src/utils/cplay.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is provided under a dual BSD/LGPLv2.1 license. When using or 3 | * redistributing this file, you may do so under either license. 4 | * 5 | * BSD LICENSE 6 | * 7 | * tinyplay command line player for compress audio offload in alsa 8 | * Copyright (c) 2011-2012, Intel Corporation 9 | * All rights reserved. 10 | * 11 | * Author: Vinod Koul 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that the following conditions are met: 15 | * 16 | * Redistributions of source code must retain the above copyright notice, 17 | * this list of conditions and the following disclaimer. 18 | * Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * Neither the name of Intel Corporation nor the names of its contributors 22 | * may be used to endorse or promote products derived from this software 23 | * without specific prior written permission. 24 | * 25 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 26 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 29 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 35 | * THE POSSIBILITY OF SUCH DAMAGE. 36 | * 37 | * LGPL LICENSE 38 | * 39 | * tinyplay command line player for compress audio offload in alsa 40 | * Copyright (c) 2011-2012, Intel Corporation. 41 | * 42 | * This program is free software; you can redistribute it and/or modify it 43 | * under the terms and conditions of the GNU Lesser General Public License, 44 | * version 2.1, as published by the Free Software Foundation. 45 | * 46 | * This program is distributed in the hope it will be useful, but WITHOUT 47 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 48 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 49 | * License for more details. 50 | * 51 | * You should have received a copy of the GNU Lesser General Public License 52 | * along with this program; if not, write to 53 | * the Free Software Foundation, Inc., 54 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 55 | */ 56 | 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | #define __force 72 | #define __bitwise 73 | #define __user 74 | #include "sound/compress_params.h" 75 | #include "tinycompress/tinycompress.h" 76 | #include "tinycompress/tinymp3.h" 77 | #include "tinycompress/tinywave.h" 78 | 79 | #define ID3V2_HEADER_SIZE 10 80 | #define ID3V2_FILE_IDENTIFIER_SIZE 3 81 | 82 | enum { 83 | DO_NOTHING = -1, 84 | DO_PAUSE_PUSH, 85 | DO_PAUSE_RELEASE 86 | }; 87 | 88 | static int verbose, interactive; 89 | static bool is_paused = false; 90 | static long term_c_lflag = -1, stdin_flags = -1; 91 | 92 | static const struct { 93 | const char *name; 94 | unsigned int id; 95 | } codec_ids[] = { 96 | { "PCM", SND_AUDIOCODEC_PCM }, 97 | { "MP3", SND_AUDIOCODEC_MP3 }, 98 | { "AMR", SND_AUDIOCODEC_AMR }, 99 | { "AMRWB", SND_AUDIOCODEC_AMRWB }, 100 | { "AMRWBPLUS", SND_AUDIOCODEC_AMRWBPLUS }, 101 | { "AAC", SND_AUDIOCODEC_AAC }, 102 | { "WMA", SND_AUDIOCODEC_WMA }, 103 | { "REAL", SND_AUDIOCODEC_REAL }, 104 | { "VORBIS", SND_AUDIOCODEC_VORBIS }, 105 | { "FLAC", SND_AUDIOCODEC_FLAC }, 106 | { "IEC61937", SND_AUDIOCODEC_IEC61937 }, 107 | { "G723_1", SND_AUDIOCODEC_G723_1 }, 108 | { "G729", SND_AUDIOCODEC_G729 }, 109 | /* BESPOKE isn't defined on older kernels */ 110 | #ifdef SND_AUDIOCODEC_BESPOKE 111 | { "BESPOKE", SND_AUDIOCODEC_BESPOKE }, 112 | #endif 113 | }; 114 | #define CPLAY_NUM_CODEC_IDS (sizeof(codec_ids) / sizeof(codec_ids[0])) 115 | 116 | static void init_stdin(void) 117 | { 118 | struct termios term; 119 | int ret; 120 | 121 | if (!interactive) 122 | return; 123 | 124 | ret = tcgetattr(fileno(stdin), &term); 125 | if (ret < 0) { 126 | fprintf(stderr, "Unable to get terminal attributes.\n"); 127 | exit(EXIT_FAILURE); 128 | } 129 | 130 | /* save previous terminal flags */ 131 | term_c_lflag = term.c_lflag; 132 | 133 | /* save previous stdin flags and add O_NONBLOCK*/ 134 | stdin_flags = fcntl(fileno(stdin), F_GETFL); 135 | if (stdin_flags < 0 || fcntl(fileno(stdin), F_SETFL, stdin_flags|O_NONBLOCK) < 0) 136 | fprintf(stderr, "stdin O_NONBLOCK flag setup failed\n"); 137 | 138 | /* prepare to enter noncanonical mode */ 139 | term.c_lflag &= ~ICANON; 140 | 141 | ret = tcsetattr(fileno(stdin), TCSANOW, &term); 142 | if (ret < 0) { 143 | fprintf(stderr, "Unable to set terminal attributes.\n"); 144 | exit(EXIT_FAILURE); 145 | } 146 | } 147 | 148 | static void done_stdin(void) 149 | { 150 | struct termios term; 151 | int ret; 152 | 153 | if (!interactive) 154 | return; 155 | 156 | if (term_c_lflag == -1 || stdin_flags == -1) 157 | return; 158 | 159 | ret = tcgetattr(fileno(stdin), &term); 160 | if (ret < 0) { 161 | fprintf(stderr, "Unable to get terminal attributes.\n"); 162 | exit(EXIT_FAILURE); 163 | } 164 | 165 | /* restore previous terminal attributes */ 166 | term.c_lflag = term_c_lflag; 167 | 168 | ret = tcsetattr(fileno(stdin), TCSANOW, &term); 169 | if (ret < 0) { 170 | fprintf(stderr, "Unable to set terminal attributes.\n"); 171 | exit(EXIT_FAILURE); 172 | } 173 | 174 | /* restore previous stdin attributes */ 175 | ret = fcntl(fileno(stdin), F_SETFL, stdin_flags); 176 | if (ret < 0) { 177 | fprintf(stderr, "Unable to set stdin attributes.\n"); 178 | exit(EXIT_FAILURE); 179 | } 180 | } 181 | 182 | static int do_pause(void) 183 | { 184 | unsigned char chr; 185 | 186 | if (!interactive) 187 | return DO_NOTHING; 188 | 189 | while (read(fileno(stdin), &chr, 1) == 1) { 190 | switch(chr) { 191 | case '\r': 192 | case ' ': 193 | if (is_paused) { 194 | fprintf(stderr, "\r=== Resume ===\n"); 195 | return DO_PAUSE_RELEASE; 196 | } else { 197 | fprintf(stderr, "\r=== Pause ===\n"); 198 | return DO_PAUSE_PUSH; 199 | } 200 | break; 201 | default: 202 | break; 203 | } 204 | } 205 | 206 | return DO_NOTHING; 207 | } 208 | 209 | static void usage(void) 210 | { 211 | int i; 212 | 213 | fprintf(stderr, "usage: cplay [OPTIONS] filename\n" 214 | "-c\tcard number\n" 215 | "-d\tdevice node\n" 216 | "-I\tspecify codec ID (default is mp3)\n" 217 | "-b\tbuffer size\n" 218 | "-f\tfragments\n\n" 219 | "-v\tverbose mode\n" 220 | "-i\tinteractive mode (press SPACE or ENTER for play/pause)\n" 221 | "-h\tPrints this help list\n\n" 222 | "Example:\n" 223 | "\tcplay -c 1 -d 2 test.mp3\n" 224 | "\tcplay -f 5 test.mp3\n\n" 225 | "Valid codec IDs:\n"); 226 | 227 | for (i = 0; i < CPLAY_NUM_CODEC_IDS; ++i) 228 | fprintf(stderr, "%s%c", codec_ids[i].name, 229 | ((i + 1) % 8) ? ' ' : '\n'); 230 | 231 | fprintf(stderr, "\nor the value in decimal or hex\n"); 232 | 233 | exit(EXIT_FAILURE); 234 | } 235 | 236 | void play_samples(char *name, unsigned int card, unsigned int device, 237 | unsigned long buffer_size, unsigned int frag, 238 | unsigned long codec_id); 239 | 240 | struct mp3_header { 241 | uint16_t sync; 242 | uint8_t format1; 243 | uint8_t format2; 244 | }; 245 | 246 | int find_adts_header(FILE *file, unsigned int *num_channels, unsigned int *sample_rate, unsigned int *format) 247 | { 248 | int ret; 249 | unsigned char buf[5]; 250 | 251 | ret = fread(buf, sizeof(buf), 1, file); 252 | if (ret < 0) { 253 | fprintf(stderr, "open file error: %d\n", ret); 254 | return 0; 255 | } 256 | fseek(file, 0, SEEK_SET); 257 | 258 | if ((buf[0] != 0xff) || ((buf[1] & 0xf0) != 0xf0)) 259 | return 0; 260 | /* mpeg id */ 261 | switch (buf[1]>>3 & 0x1) { 262 | case 0x0: 263 | *format = SND_AUDIOSTREAMFORMAT_MP4ADTS; 264 | break; 265 | case 0x1: 266 | *format = SND_AUDIOSTREAMFORMAT_MP2ADTS; 267 | break; 268 | default: 269 | fprintf(stderr, "can't find stream format\n"); 270 | break; 271 | } 272 | /* sample_rate */ 273 | switch (buf[2]>>2 & 0xf) { 274 | case 0x0: 275 | *sample_rate = 96000; 276 | break; 277 | case 0x1: 278 | *sample_rate = 88200; 279 | break; 280 | case 0x2: 281 | *sample_rate = 64000; 282 | break; 283 | case 0x3: 284 | *sample_rate = 48000; 285 | break; 286 | case 0x4: 287 | *sample_rate = 44100; 288 | break; 289 | case 0x5: 290 | *sample_rate = 32000; 291 | break; 292 | case 0x6: 293 | *sample_rate = 24000; 294 | break; 295 | case 0x7: 296 | *sample_rate = 22050; 297 | break; 298 | case 0x8: 299 | *sample_rate = 16000; 300 | break; 301 | case 0x9: 302 | *sample_rate = 12000; 303 | break; 304 | case 0xa: 305 | *sample_rate = 11025; 306 | break; 307 | case 0xb: 308 | *sample_rate = 8000; 309 | break; 310 | case 0xc: 311 | *sample_rate = 7350; 312 | break; 313 | default: 314 | break; 315 | } 316 | /* channel */ 317 | switch (((buf[2]&0x1) << 2) | (buf[3]>>6)) { 318 | case 1: 319 | *num_channels = 1; 320 | break; 321 | case 2: 322 | *num_channels = 2; 323 | break; 324 | case 3: 325 | *num_channels = 3; 326 | break; 327 | case 4: 328 | *num_channels = 4; 329 | break; 330 | case 5: 331 | *num_channels = 5; 332 | break; 333 | case 6: 334 | *num_channels = 6; 335 | break; 336 | case 7: 337 | *num_channels = 7; 338 | break; 339 | default: 340 | break; 341 | } 342 | return 1; 343 | } 344 | 345 | static const int aac_sample_rates[] = { 96000, 88200, 64000, 48000, 44100, 346 | 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350 347 | }; 348 | 349 | #define MAX_SR_NUM sizeof(aac_sample_rates)/sizeof(aac_sample_rates[0]) 350 | 351 | static int get_sample_rate_from_index(int sr_index) 352 | { 353 | if (sr_index >= 0 && sr_index < MAX_SR_NUM) 354 | return aac_sample_rates[sr_index]; 355 | 356 | return 0; 357 | } 358 | 359 | int find_adif_header(FILE *file, unsigned int *num_channels, unsigned int *sample_rate, unsigned int *format) 360 | { 361 | int ret; 362 | unsigned char adif_id[4]; 363 | unsigned char adif_header[20]; 364 | int bitstream_type; 365 | int sr_index; 366 | int skip_size = 0; 367 | 368 | ret = fread(adif_id, sizeof(unsigned char), 4, file); 369 | if (ret < 0) { 370 | fprintf(stderr, "read data from file err: %d\n", ret); 371 | return 0; 372 | } 373 | /* adif id */ 374 | if ((adif_id[0] != 0x41) || (adif_id[1] != 0x44) || 375 | (adif_id[2] != 0x49) || (adif_id[3] != 0x46)) 376 | return 0; 377 | 378 | fread(adif_header, sizeof(unsigned char), 20, file); 379 | 380 | /* copyright string */ 381 | if (adif_header[0] & 0x80) 382 | skip_size = 9; 383 | 384 | bitstream_type = adif_header[0 + skip_size] & 0x10; 385 | 386 | if (bitstream_type == 0) 387 | sr_index = (adif_header[7 + skip_size] & 0x78) >> 3; 388 | 389 | /* VBR */ 390 | else 391 | sr_index = ((adif_header[4 + skip_size] & 0x07) << 1) | 392 | ((adif_header[5 + skip_size] & 0x80) >> 7); 393 | 394 | /* sample rate */ 395 | *sample_rate = get_sample_rate_from_index(sr_index); 396 | 397 | /* FIXME: assume channels is 2 */ 398 | *num_channels = 2; 399 | 400 | *format = SND_AUDIOSTREAMFORMAT_ADIF; 401 | fseek(file, 0, SEEK_SET); 402 | return 1; 403 | } 404 | 405 | static int parse_aac_header(FILE *file, unsigned int *num_channels, unsigned int *sample_rate, unsigned int *format) 406 | { 407 | if (find_adts_header(file, num_channels, sample_rate, format)) 408 | return 1; 409 | else if (find_adif_header(file, num_channels, sample_rate, format)) 410 | return 1; 411 | else { 412 | fprintf(stderr, "can't find streams format\n"); 413 | return 0; 414 | } 415 | 416 | return 1; 417 | } 418 | 419 | static int parse_mp3_header(struct mp3_header *header, unsigned int *num_channels, 420 | unsigned int *sample_rate, unsigned int *bit_rate) 421 | { 422 | int ver_idx, mp3_version, layer, bit_rate_idx, sample_rate_idx, channel_idx; 423 | 424 | /* check sync bits */ 425 | if ((header->sync & MP3_SYNC) != MP3_SYNC) { 426 | fprintf(stderr, "Error: Can't find sync word\n"); 427 | return -1; 428 | } 429 | ver_idx = (header->sync >> 11) & 0x03; 430 | mp3_version = ver_idx == 0 ? MPEG25 : ((ver_idx & 0x1) ? MPEG1 : MPEG2); 431 | layer = 4 - ((header->sync >> 9) & 0x03); 432 | bit_rate_idx = ((header->format1 >> 4) & 0x0f); 433 | sample_rate_idx = ((header->format1 >> 2) & 0x03); 434 | channel_idx = ((header->format2 >> 6) & 0x03); 435 | 436 | if (sample_rate_idx == 3 || layer == 4 || bit_rate_idx == 15) { 437 | fprintf(stderr, "Error: Can't find valid header\n"); 438 | return -1; 439 | } 440 | *num_channels = (channel_idx == MONO ? 1 : 2); 441 | *sample_rate = mp3_sample_rates[mp3_version][sample_rate_idx]; 442 | *bit_rate = (mp3_bit_rates[mp3_version][layer - 1][bit_rate_idx]) * 1000; 443 | if (verbose) 444 | printf("%s: exit\n", __func__); 445 | return 0; 446 | } 447 | 448 | static int print_time(struct compress *compress) 449 | { 450 | unsigned int avail; 451 | struct timespec tstamp; 452 | 453 | if (compress_get_hpointer(compress, &avail, &tstamp) != 0) { 454 | fprintf(stderr, "Error querying timestamp\n"); 455 | fprintf(stderr, "ERR: %s\n", compress_get_error(compress)); 456 | return -1; 457 | } else 458 | fprintf(stderr, "DSP played %jd.%jd\n", (intmax_t)tstamp.tv_sec, (intmax_t)tstamp.tv_nsec*1000); 459 | return 0; 460 | } 461 | 462 | int main(int argc, char **argv) 463 | { 464 | char *file; 465 | unsigned long buffer_size = 0; 466 | int c, i; 467 | unsigned int card = 0, device = 0, frag = 0; 468 | unsigned int codec_id = SND_AUDIOCODEC_MP3; 469 | 470 | if (argc < 2) 471 | usage(); 472 | 473 | verbose = 0; 474 | while ((c = getopt(argc, argv, "hvb:f:c:d:I:i")) != -1) { 475 | switch (c) { 476 | case 'h': 477 | usage(); 478 | break; 479 | case 'b': 480 | buffer_size = strtol(optarg, NULL, 0); 481 | break; 482 | case 'f': 483 | frag = strtol(optarg, NULL, 10); 484 | break; 485 | case 'c': 486 | card = strtol(optarg, NULL, 10); 487 | break; 488 | case 'd': 489 | device = strtol(optarg, NULL, 10); 490 | break; 491 | case 'I': 492 | if (optarg[0] == '0') { 493 | codec_id = strtol(optarg, NULL, 0); 494 | } else { 495 | for (i = 0; i < CPLAY_NUM_CODEC_IDS; ++i) { 496 | if (strcmp(optarg, 497 | codec_ids[i].name) == 0) { 498 | codec_id = codec_ids[i].id; 499 | break; 500 | } 501 | } 502 | 503 | if (i == CPLAY_NUM_CODEC_IDS) { 504 | fprintf(stderr, "Unrecognised ID: %s\n", 505 | optarg); 506 | usage(); 507 | } 508 | } 509 | break; 510 | case 'v': 511 | verbose = 1; 512 | break; 513 | case 'i': 514 | fprintf(stderr, "Interactive mode: ON\n"); 515 | interactive = 1; 516 | break; 517 | default: 518 | exit(EXIT_FAILURE); 519 | } 520 | } 521 | if (optind >= argc) 522 | usage(); 523 | 524 | file = argv[optind]; 525 | 526 | play_samples(file, card, device, buffer_size, frag, codec_id); 527 | 528 | fprintf(stderr, "Finish Playing.... Close Normally\n"); 529 | exit(EXIT_SUCCESS); 530 | } 531 | 532 | #if ENABLE_PCM 533 | void get_codec_pcm(FILE *file, struct compr_config *config, 534 | struct snd_codec *codec) 535 | { 536 | size_t read; 537 | struct wave_header header; 538 | unsigned int channels, rate, format; 539 | 540 | read = fread(&header, 1, sizeof(header), file); 541 | if (read != sizeof(header)) { 542 | fprintf(stderr, "Unable to read header \n"); 543 | fclose(file); 544 | exit(EXIT_FAILURE); 545 | } 546 | 547 | if (parse_wave_header(&header, &channels, &rate, &format) == -1) { 548 | fclose(file); 549 | exit(EXIT_FAILURE); 550 | } 551 | 552 | codec->id = SND_AUDIOCODEC_PCM; 553 | codec->ch_in = channels; 554 | codec->ch_out = channels; 555 | codec->sample_rate = rate; 556 | if (!codec->sample_rate) { 557 | fprintf(stderr, "invalid sample rate %d\n", rate); 558 | fclose(file); 559 | exit(EXIT_FAILURE); 560 | } 561 | codec->bit_rate = 0; 562 | codec->rate_control = 0; 563 | codec->profile = SND_AUDIOCODEC_PCM; 564 | codec->level = 0; 565 | codec->ch_mode = 0; 566 | codec->format = format; 567 | } 568 | #endif 569 | 570 | void get_codec_aac(FILE *file, struct compr_config *config, 571 | struct snd_codec *codec) 572 | { 573 | unsigned int channels, rate, format; 574 | 575 | if (parse_aac_header(file, &channels, &rate, &format) == 0) { 576 | fclose(file); 577 | exit(EXIT_FAILURE); 578 | }; 579 | fseek(file, 0, SEEK_SET); 580 | 581 | codec->id = SND_AUDIOCODEC_AAC; 582 | codec->ch_in = channels; 583 | codec->ch_out = channels; 584 | codec->sample_rate = rate; 585 | codec->bit_rate = 0; 586 | codec->rate_control = 0; 587 | codec->profile = SND_AUDIOPROFILE_AAC; 588 | codec->level = 0; 589 | codec->ch_mode = 0; 590 | codec->format = format; 591 | } 592 | 593 | static int skip_id3v2_header(FILE *file) 594 | { 595 | char buffer[ID3V2_HEADER_SIZE + 1]; 596 | int ret, bytes_read; 597 | uint32_t header_size; 598 | 599 | /* we only need to parse ID3v2 header in order to skip 600 | * the whole ID3v2 tag found at the beginning of the file. 601 | * 602 | * ID3v2 header has the following structure(v2.3.0): 603 | * 604 | * 1) file identifier 605 | * => 3 bytes long. 606 | * => has the value of ID3 for ID3v2 tag. 607 | * 608 | * 2) version 609 | * => 2 bytes long. 610 | * 611 | * 3) flags 612 | * => 1 byte long. 613 | * 614 | * 4) size 615 | * => 4 bytes long. 616 | * => the MSB of each byte is 0 so it needs to be ignored 617 | * when trying to parse the size. 618 | * => this field doesn't include the 10 bytes which make up 619 | * the ID3v2 header so a +10 needs to be added to this 620 | * field in order to get the correct position of the end 621 | * of the ID3v2 tag. 622 | */ 623 | 624 | /* for now, we only support ID3v2 header found at the beginning 625 | * of the file so we need to move file cursor to the beginning 626 | * of the file 627 | */ 628 | ret = fseek(file, 0, SEEK_SET); 629 | if (ret < 0) 630 | return ret; 631 | 632 | buffer[ID3V2_HEADER_SIZE] = '\0'; 633 | 634 | /* read first ID3V2_HEADER_SIZE chunk */ 635 | bytes_read = fread(buffer, sizeof(char), ID3V2_HEADER_SIZE, file); 636 | 637 | /* ID3v2 header is 10 bytes long 638 | * 639 | * if we can't read the 10 bytes then there's obviously no 640 | * ID3v2 tag to skip 641 | */ 642 | if (bytes_read != ID3V2_HEADER_SIZE) 643 | return 0; 644 | 645 | /* check if we're dealing with ID3v2 */ 646 | if (strncmp(buffer, "ID3", ID3V2_FILE_IDENTIFIER_SIZE) != 0) 647 | return 0; 648 | 649 | header_size = buffer[9]; 650 | header_size |= (buffer[8] << 7); 651 | header_size |= (buffer[7] << 14); 652 | header_size |= (buffer[6] << 21); 653 | 654 | /* the header size field in ID3v2 header doesn't 655 | * include the 10 bytes of which the header is made 656 | * so we need to add them to get the correct position 657 | */ 658 | return header_size + ID3V2_HEADER_SIZE; 659 | } 660 | 661 | void get_codec_mp3(FILE *file, struct compr_config *config, 662 | struct snd_codec *codec) 663 | { 664 | size_t read; 665 | struct mp3_header header; 666 | unsigned int channels, rate, bits; 667 | int offset; 668 | 669 | offset = skip_id3v2_header(file); 670 | if (offset < 0) { 671 | fprintf(stderr, "Failed to get ID3 tag end position.\n"); 672 | fclose(file); 673 | exit(EXIT_FAILURE); 674 | } 675 | 676 | if (fseek(file, offset, SEEK_SET) < 0) { 677 | fprintf(stderr, "Unable to seek.\n"); 678 | fclose(file); 679 | exit(EXIT_FAILURE); 680 | } 681 | 682 | read = fread(&header, 1, sizeof(header), file); 683 | if (read != sizeof(header)) { 684 | fprintf(stderr, "Unable to read header \n"); 685 | fclose(file); 686 | exit(EXIT_FAILURE); 687 | } 688 | 689 | if (parse_mp3_header(&header, &channels, &rate, &bits) == -1) { 690 | fclose(file); 691 | exit(EXIT_FAILURE); 692 | } 693 | 694 | codec->id = SND_AUDIOCODEC_MP3; 695 | codec->ch_in = channels; 696 | codec->ch_out = channels; 697 | codec->sample_rate = rate; 698 | if (!codec->sample_rate) { 699 | fprintf(stderr, "invalid sample rate %d\n", rate); 700 | fclose(file); 701 | exit(EXIT_FAILURE); 702 | } 703 | codec->bit_rate = bits; 704 | codec->rate_control = 0; 705 | codec->profile = 0; 706 | codec->level = 0; 707 | codec->ch_mode = 0; 708 | codec->format = 0; 709 | 710 | /* reset file cursor to offset position 711 | * 712 | * this is done because if we leave it as is 713 | * the program will hang in a poll call waiting 714 | * for ring buffer to empty out. 715 | * 716 | * this never actually happens because of the fact 717 | * that the codec probably expects to receive the 718 | * MP3 header along with its associated MP3 data 719 | * so, if we leave the file cursor positioned at 720 | * the first MP3 data then the codec will most 721 | * likely hang because it's expecting to also get 722 | * the MP3 header 723 | */ 724 | if (fseek(file, offset, SEEK_SET) < 0) { 725 | fprintf(stderr, "Failed to set cursor to offset.\n"); 726 | fclose(file); 727 | exit(EXIT_FAILURE); 728 | } 729 | } 730 | 731 | void get_codec_iec(FILE *file, struct compr_config *config, 732 | struct snd_codec *codec) 733 | { 734 | codec->id = SND_AUDIOCODEC_IEC61937; 735 | /* FIXME: cannot get accurate ch_in, any channels may be accepted */ 736 | codec->ch_in = 2; 737 | codec->ch_out = 2; 738 | codec->sample_rate = 0; 739 | codec->bit_rate = 0; 740 | codec->rate_control = 0; 741 | codec->profile = SND_AUDIOPROFILE_IEC61937_SPDIF; 742 | codec->level = 0; 743 | codec->ch_mode = 0; 744 | codec->format = 0; 745 | } 746 | 747 | static int check_stdin(struct compress *compress) 748 | { 749 | switch(do_pause()) { 750 | case DO_PAUSE_PUSH: 751 | if (compress_pause(compress) != 0) { 752 | fprintf(stderr, "Pause ERROR\n"); 753 | return -1; 754 | } 755 | is_paused = true; 756 | break; 757 | case DO_PAUSE_RELEASE: 758 | if (compress_resume(compress) != 0) { 759 | fprintf(stderr, "Resume ERROR\n"); 760 | return -1; 761 | } 762 | is_paused = false; 763 | break; 764 | case DO_NOTHING: 765 | break; 766 | } 767 | 768 | return 0; 769 | } 770 | 771 | void play_samples(char *name, unsigned int card, unsigned int device, 772 | unsigned long buffer_size, unsigned int frag, 773 | unsigned long codec_id) 774 | { 775 | struct compr_config config; 776 | struct snd_codec codec; 777 | struct compress *compress; 778 | FILE *file; 779 | char *buffer; 780 | int size, num_read, wrote; 781 | 782 | if (verbose) 783 | printf("%s: entry\n", __func__); 784 | file = fopen(name, "rb"); 785 | if (!file) { 786 | fprintf(stderr, "Unable to open file '%s'\n", name); 787 | exit(EXIT_FAILURE); 788 | } 789 | 790 | init_stdin(); 791 | 792 | switch (codec_id) { 793 | #if ENABLE_PCM 794 | case SND_AUDIOCODEC_PCM: 795 | get_codec_pcm(file, &config, &codec); 796 | break; 797 | #endif 798 | case SND_AUDIOCODEC_AAC: 799 | get_codec_aac(file, &config, &codec); 800 | break; 801 | case SND_AUDIOCODEC_MP3: 802 | get_codec_mp3(file, &config, &codec); 803 | break; 804 | case SND_AUDIOCODEC_IEC61937: 805 | get_codec_iec(file, &config, &codec); 806 | break; 807 | default: 808 | fprintf(stderr, "codec ID %ld is not supported\n", codec_id); 809 | exit(EXIT_FAILURE); 810 | } 811 | 812 | if ((buffer_size != 0) && (frag != 0)) { 813 | config.fragment_size = buffer_size/frag; 814 | config.fragments = frag; 815 | } else { 816 | /* use driver defaults */ 817 | config.fragment_size = 0; 818 | config.fragments = 0; 819 | } 820 | config.codec = &codec; 821 | 822 | compress = compress_open(card, device, COMPRESS_IN, &config); 823 | if (!compress || !is_compress_ready(compress)) { 824 | fprintf(stderr, "Unable to open Compress device %d:%d\n", 825 | card, device); 826 | fprintf(stderr, "ERR: %s\n", compress_get_error(compress)); 827 | goto FILE_EXIT; 828 | }; 829 | if (verbose) 830 | printf("%s: Opened compress device\n", __func__); 831 | size = config.fragments * config.fragment_size; 832 | buffer = malloc(size); 833 | if (!buffer) { 834 | fprintf(stderr, "Unable to allocate %d bytes\n", size); 835 | goto COMP_EXIT; 836 | } 837 | 838 | /* we will write frag fragment_size and then start */ 839 | num_read = fread(buffer, 1, size, file); 840 | if (num_read > 0) { 841 | if (verbose) 842 | printf("%s: Doing first buffer write of %d\n", __func__, num_read); 843 | wrote = compress_write(compress, buffer, num_read); 844 | if (wrote < 0) { 845 | fprintf(stderr, "Error %d playing sample\n", wrote); 846 | fprintf(stderr, "ERR: %s\n", compress_get_error(compress)); 847 | goto BUF_EXIT; 848 | } 849 | if (wrote != num_read) { 850 | /* TODO: Buffer pointer needs to be set here */ 851 | fprintf(stderr, "We wrote %d, DSP accepted %d\n", num_read, wrote); 852 | } 853 | } 854 | printf("Playing file %s On Card %u device %u, with buffer of %d bytes\n", 855 | name, card, device, size); 856 | printf("Format %u Channels %u, %u Hz, Bit Rate %d\n", 857 | codec.id, codec.ch_in, codec.sample_rate, codec.bit_rate); 858 | 859 | compress_start(compress); 860 | if (verbose) 861 | printf("%s: You should hear audio NOW!!!\n", __func__); 862 | 863 | do { 864 | if (check_stdin(compress) != 0) 865 | goto BUF_EXIT; 866 | 867 | if (!is_paused) 868 | num_read = fread(buffer, 1, size, file); 869 | else 870 | num_read = 0; 871 | 872 | if (num_read > 0) { 873 | wrote = compress_write(compress, buffer, num_read); 874 | if (wrote < 0) { 875 | fprintf(stderr, "Error playing sample\n"); 876 | fprintf(stderr, "ERR: %s\n", compress_get_error(compress)); 877 | goto BUF_EXIT; 878 | } 879 | if (wrote != num_read) { 880 | /* TODO: Buffer pointer needs to be set here */ 881 | fprintf(stderr, "We wrote %d, DSP accepted %d\n", num_read, wrote); 882 | } 883 | if (verbose) { 884 | print_time(compress); 885 | printf("%s: wrote %d\n", __func__, wrote); 886 | } 887 | } 888 | } while (num_read > 0 || is_paused == true); 889 | 890 | if (verbose) 891 | printf("%s: exit success\n", __func__); 892 | /* issue drain if it supports */ 893 | compress_drain(compress); 894 | free(buffer); 895 | fclose(file); 896 | compress_close(compress); 897 | done_stdin(); 898 | return; 899 | BUF_EXIT: 900 | free(buffer); 901 | COMP_EXIT: 902 | compress_close(compress); 903 | done_stdin(); 904 | FILE_EXIT: 905 | fclose(file); 906 | if (verbose) 907 | printf("%s: exit failure\n", __func__); 908 | exit(EXIT_FAILURE); 909 | } 910 | 911 | -------------------------------------------------------------------------------- /src/utils/crecord.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is provided under a dual BSD/LGPLv2.1 license. When using or 3 | * redistributing this file, you may do so under either license. 4 | * 5 | * BSD LICENSE 6 | * 7 | * crecord command line recorder for compress audio record in alsa 8 | * Copyright (c) 2011-2012, Intel Corporation 9 | * Copyright (c) 2013-2014, Wolfson Microelectronic Ltd. 10 | * All rights reserved. 11 | * 12 | * Author: Vinod Koul 13 | * Author: Charles Keepax 14 | * 15 | * Redistribution and use in source and binary forms, with or without 16 | * modification, are permitted provided that the following conditions are met: 17 | * 18 | * Redistributions of source code must retain the above copyright notice, 19 | * this list of conditions and the following disclaimer. 20 | * Redistributions in binary form must reproduce the above copyright notice, 21 | * this list of conditions and the following disclaimer in the documentation 22 | * and/or other materials provided with the distribution. 23 | * Neither the name of Intel Corporation nor the names of its contributors 24 | * may be used to endorse or promote products derived from this software 25 | * without specific prior written permission. 26 | * 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 31 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 37 | * THE POSSIBILITY OF SUCH DAMAGE. 38 | * 39 | * LGPL LICENSE 40 | * 41 | * crecord command line recorder for compress audio record in alsa 42 | * Copyright (c) 2011-2012, Intel Corporation 43 | * Copyright (c) 2013-2014, Wolfson Microelectronic Ltd. 44 | * 45 | * This program is free software; you can redistribute it and/or modify it 46 | * under the terms and conditions of the GNU Lesser General Public License, 47 | * version 2.1, as published by the Free Software Foundation. 48 | * 49 | * This program is distributed in the hope it will be useful, but WITHOUT 50 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 51 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 52 | * License for more details. 53 | * 54 | * You should have received a copy of the GNU Lesser General Public License 55 | * along with this program; if not, write to 56 | * the Free Software Foundation, Inc., 57 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 58 | */ 59 | 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | #include 72 | #include 73 | #include 74 | #define __force 75 | #define __bitwise 76 | #define __user 77 | #include "sound/compress_params.h" 78 | #include "sound/compress_offload.h" 79 | #include "tinycompress/tinycompress.h" 80 | #include "tinycompress/tinywave.h" 81 | 82 | static int verbose; 83 | static int file; 84 | static FILE *finfo; 85 | static bool streamed; 86 | 87 | static const unsigned int DEFAULT_CHANNELS = 1; 88 | static const unsigned int DEFAULT_RATE = 44100; 89 | static const unsigned int DEFAULT_FORMAT = SNDRV_PCM_FORMAT_S16_LE; 90 | static const unsigned int DEFAULT_CODEC_ID = SND_AUDIOCODEC_PCM; 91 | 92 | static const struct { 93 | const char *name; 94 | unsigned int id; 95 | } codec_ids[] = { 96 | { "PCM", SND_AUDIOCODEC_PCM }, 97 | { "MP3", SND_AUDIOCODEC_MP3 }, 98 | { "AMR", SND_AUDIOCODEC_AMR }, 99 | { "AMRWB", SND_AUDIOCODEC_AMRWB }, 100 | { "AMRWBPLUS", SND_AUDIOCODEC_AMRWBPLUS }, 101 | { "AAC", SND_AUDIOCODEC_AAC }, 102 | { "WMA", SND_AUDIOCODEC_WMA }, 103 | { "REAL", SND_AUDIOCODEC_REAL }, 104 | { "VORBIS", SND_AUDIOCODEC_VORBIS }, 105 | { "FLAC", SND_AUDIOCODEC_FLAC }, 106 | { "IEC61937", SND_AUDIOCODEC_IEC61937 }, 107 | { "G723_1", SND_AUDIOCODEC_G723_1 }, 108 | { "G729", SND_AUDIOCODEC_G729 }, 109 | /* BESPOKE isn't defined on older kernels */ 110 | #ifdef SND_AUDIOCODEC_BESPOKE 111 | { "BESPOKE", SND_AUDIOCODEC_BESPOKE }, 112 | #endif 113 | }; 114 | #define CREC_NUM_CODEC_IDS (sizeof(codec_ids) / sizeof(codec_ids[0])) 115 | 116 | static const char *codec_name_from_id(unsigned int id) 117 | { 118 | static char hexname[12]; 119 | int i; 120 | 121 | for (i = 0; i < CREC_NUM_CODEC_IDS; ++i) { 122 | if (codec_ids[i].id == id) 123 | return codec_ids[i].name; 124 | } 125 | 126 | snprintf(hexname, sizeof(hexname), "0x%x", id); 127 | return hexname; /* a static is safe because we're single-threaded */ 128 | } 129 | 130 | static void usage(void) 131 | { 132 | int i; 133 | 134 | fprintf(stderr, "usage: crecord [OPTIONS] [filename.wav]\n" 135 | "-c\tcard number\n" 136 | "-d\tdevice node\n" 137 | "-b\tbuffer size\n" 138 | "-f\tfragments\n" 139 | "-v\tverbose mode\n" 140 | "-l\tlength of record in seconds\n" 141 | "-h\tPrints this help list\n\n" 142 | "-C\tSpecify the number of channels (default %u)\n" 143 | "-R\tSpecify the sample rate (default %u)\n" 144 | "-F\tSpecify the format: S16_LE, S32_LE (default S16_LE)\n" 145 | "-I\tSpecify codec ID (default %s)\n\n" 146 | "If filename.wav is not given the output is written to stdout\n" 147 | "Only PCM data can be written to a WAV file.\n\n" 148 | "Example:\n" 149 | "\tcrecord -c 1 -d 2 test.wav\n" 150 | "\tcrecord -f 5 test.wav\n" 151 | "\tcrecord -I BESPOKE >raw.bin\n\n" 152 | "Valid codec IDs:\n", 153 | DEFAULT_CHANNELS, DEFAULT_RATE, 154 | codec_name_from_id(DEFAULT_CODEC_ID)); 155 | 156 | for (i = 0; i < CREC_NUM_CODEC_IDS; ++i) 157 | fprintf(stderr, "%s%c", codec_ids[i].name, 158 | ((i + 1) % 8) ? ' ' : '\n'); 159 | 160 | fprintf(stderr, "\nor the value in decimal or hex\n"); 161 | 162 | exit(EXIT_FAILURE); 163 | } 164 | 165 | static int print_time(struct compress *compress) 166 | { 167 | unsigned int avail; 168 | struct timespec tstamp; 169 | 170 | if (compress_get_hpointer(compress, &avail, &tstamp) != 0) { 171 | fprintf(stderr, "Error querying timestamp\n"); 172 | fprintf(stderr, "ERR: %s\n", compress_get_error(compress)); 173 | return -1; 174 | } else { 175 | fprintf(finfo, "DSP recorded %jd.%jd\n", 176 | (intmax_t)tstamp.tv_sec, (intmax_t)tstamp.tv_nsec*1000); 177 | } 178 | return 0; 179 | } 180 | 181 | static int finish_record(void) 182 | { 183 | struct wave_header header; 184 | int ret; 185 | size_t nread, written; 186 | 187 | if (!file) 188 | return -ENOENT; 189 | 190 | /* can't rewind if streaming to stdout */ 191 | if (streamed) 192 | return 0; 193 | 194 | /* Get amount of data written to file */ 195 | ret = lseek(file, 0, SEEK_END); 196 | if (ret < 0) 197 | return -errno; 198 | 199 | written = ret; 200 | if (written < sizeof(header)) 201 | return -ENOENT; 202 | written -= sizeof(header); 203 | 204 | /* Sync file header from file */ 205 | ret = lseek(file, 0, SEEK_SET); 206 | if (ret < 0) 207 | return -errno; 208 | 209 | nread = read(file, &header, sizeof(header)); 210 | if (nread != sizeof(header)) 211 | return -errno; 212 | 213 | /* Update file header */ 214 | ret = lseek(file, 0, SEEK_SET); 215 | if (ret < 0) 216 | return -errno; 217 | 218 | size_wave_header(&header, written); 219 | 220 | written = write(file, &header, sizeof(header)); 221 | if (written != sizeof(header)) 222 | return -errno; 223 | 224 | return 0; 225 | } 226 | 227 | static void capture_samples(char *name, unsigned int card, unsigned int device, 228 | unsigned long buffer_size, unsigned int frag, 229 | unsigned int length, unsigned int rate, 230 | unsigned int channels, unsigned int format, 231 | unsigned int codec_id) 232 | { 233 | struct compr_config config; 234 | struct snd_codec codec; 235 | struct compress *compress; 236 | struct wave_header header; 237 | char *buffer; 238 | size_t written; 239 | int read, ret; 240 | unsigned int size, total_read = 0; 241 | unsigned int samplebits; 242 | 243 | switch (format) { 244 | case SNDRV_PCM_FORMAT_S32_LE: 245 | samplebits = 32; 246 | break; 247 | default: 248 | samplebits = 16; 249 | break; 250 | } 251 | 252 | /* Convert length from seconds to bytes */ 253 | length = length * rate * (samplebits / 8) * channels; 254 | 255 | if (verbose) 256 | fprintf(finfo, "%s: entry, reading %u bytes\n", __func__, length); 257 | if (!name) { 258 | file = STDOUT_FILENO; 259 | } else { 260 | file = open(name, O_RDWR | O_CREAT, 261 | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); 262 | if (file == -1) { 263 | fprintf(stderr, "Unable to open file '%s'\n", name); 264 | exit(EXIT_FAILURE); 265 | } 266 | } 267 | 268 | /* Write a header, will update with size once record is complete */ 269 | if (!streamed) { 270 | init_wave_header(&header, channels, rate, samplebits); 271 | written = write(file, &header, sizeof(header)); 272 | if (written != sizeof(header)) { 273 | fprintf(stderr, "Error writing output file header: %s\n", 274 | strerror(errno)); 275 | goto file_exit; 276 | } 277 | } 278 | 279 | memset(&codec, 0, sizeof(codec)); 280 | memset(&config, 0, sizeof(config)); 281 | codec.id = codec_id; 282 | codec.ch_in = channels; 283 | codec.ch_out = channels; 284 | codec.sample_rate = rate; 285 | if (!codec.sample_rate) { 286 | fprintf(stderr, "invalid sample rate %d\n", rate); 287 | goto file_exit; 288 | } 289 | codec.format = format; 290 | if ((buffer_size != 0) && (frag != 0)) { 291 | config.fragment_size = buffer_size/frag; 292 | config.fragments = frag; 293 | } 294 | config.codec = &codec; 295 | 296 | compress = compress_open(card, device, COMPRESS_OUT, &config); 297 | if (!compress || !is_compress_ready(compress)) { 298 | fprintf(stderr, "Unable to open Compress device %d:%d\n", 299 | card, device); 300 | fprintf(stderr, "ERR: %s\n", compress_get_error(compress)); 301 | goto file_exit; 302 | }; 303 | 304 | if (verbose) 305 | fprintf(finfo, "%s: Opened compress device\n", __func__); 306 | 307 | size = config.fragments * config.fragment_size; 308 | buffer = malloc(size); 309 | if (!buffer) { 310 | fprintf(stderr, "Unable to allocate %d bytes\n", size); 311 | goto comp_exit; 312 | } 313 | 314 | fprintf(finfo, "Recording file %s On Card %u device %u, with buffer of %u bytes\n", 315 | name, card, device, size); 316 | fprintf(finfo, "Codec %u Format %u Channels %u, %u Hz\n", 317 | codec.id, codec.format, codec.ch_out, rate); 318 | 319 | compress_start(compress); 320 | 321 | if (verbose) 322 | fprintf(finfo, "%s: Capturing audio NOW!!!\n", __func__); 323 | 324 | do { 325 | read = compress_read(compress, buffer, size); 326 | if (read < 0) { 327 | fprintf(stderr, "Error reading sample\n"); 328 | fprintf(stderr, "ERR: %s\n", compress_get_error(compress)); 329 | goto buf_exit; 330 | } 331 | if ((unsigned int)read != size) { 332 | fprintf(stderr, "We read %d, DSP sent %d\n", 333 | size, read); 334 | } 335 | 336 | if (read > 0) { 337 | total_read += read; 338 | 339 | written = write(file, buffer, read); 340 | if (written != (size_t)read) { 341 | fprintf(stderr, "Error writing output file: %s\n", 342 | strerror(errno)); 343 | goto buf_exit; 344 | } 345 | if (verbose) { 346 | print_time(compress); 347 | fprintf(finfo, "%s: read %d\n", __func__, read); 348 | } 349 | } 350 | } while (!length || total_read < length); 351 | 352 | ret = compress_stop(compress); 353 | if (ret < 0) { 354 | fprintf(stderr, "Error closing stream\n"); 355 | fprintf(stderr, "ERR: %s\n", compress_get_error(compress)); 356 | } 357 | 358 | ret = finish_record(); 359 | if (ret < 0) { 360 | fprintf(stderr, "Failed to finish header: %s\n", strerror(ret)); 361 | goto buf_exit; 362 | } 363 | 364 | if (verbose) 365 | fprintf(finfo, "%s: exit success\n", __func__); 366 | 367 | free(buffer); 368 | close(file); 369 | file = 0; 370 | 371 | compress_close(compress); 372 | 373 | return; 374 | buf_exit: 375 | free(buffer); 376 | comp_exit: 377 | compress_close(compress); 378 | file_exit: 379 | close(file); 380 | 381 | if (verbose) 382 | fprintf(finfo, "%s: exit failure\n", __func__); 383 | 384 | exit(EXIT_FAILURE); 385 | } 386 | 387 | static void sig_handler(int signum __attribute__ ((unused))) 388 | { 389 | finish_record(); 390 | 391 | if (file) 392 | close(file); 393 | 394 | _exit(EXIT_FAILURE); 395 | } 396 | 397 | int main(int argc, char **argv) 398 | { 399 | char *file; 400 | unsigned long buffer_size = 0; 401 | int c, i; 402 | unsigned int card = 0, device = 0, frag = 0, length = 0; 403 | unsigned int rate = DEFAULT_RATE, channels = DEFAULT_CHANNELS; 404 | unsigned int format = DEFAULT_FORMAT; 405 | unsigned int codec_id = DEFAULT_CODEC_ID; 406 | 407 | if (signal(SIGINT, sig_handler) == SIG_ERR) { 408 | fprintf(stderr, "Error registering signal handler\n"); 409 | exit(EXIT_FAILURE); 410 | } 411 | 412 | if (argc < 1) 413 | usage(); 414 | 415 | verbose = 0; 416 | while ((c = getopt(argc, argv, "hvl:R:C:F:I:b:f:c:d:")) != -1) { 417 | switch (c) { 418 | case 'h': 419 | usage(); 420 | break; 421 | case 'b': 422 | buffer_size = strtol(optarg, NULL, 0); 423 | break; 424 | case 'f': 425 | frag = strtol(optarg, NULL, 10); 426 | break; 427 | case 'c': 428 | card = strtol(optarg, NULL, 10); 429 | break; 430 | case 'd': 431 | device = strtol(optarg, NULL, 10); 432 | break; 433 | case 'v': 434 | verbose = 1; 435 | break; 436 | case 'l': 437 | length = strtol(optarg, NULL, 10); 438 | break; 439 | case 'R': 440 | rate = strtol(optarg, NULL, 10); 441 | break; 442 | case 'C': 443 | channels = strtol(optarg, NULL, 10); 444 | break; 445 | case 'F': 446 | if (strcmp(optarg, "S16_LE") == 0) { 447 | format = SNDRV_PCM_FORMAT_S16_LE; 448 | } else if (strcmp(optarg, "S32_LE") == 0) { 449 | format = SNDRV_PCM_FORMAT_S32_LE; 450 | } else { 451 | fprintf(stderr, "Unrecognised format: %s\n", 452 | optarg); 453 | usage(); 454 | } 455 | break; 456 | case 'I': 457 | if (optarg[0] == '0') { 458 | codec_id = strtol(optarg, NULL, 0); 459 | } else { 460 | for (i = 0; i < CREC_NUM_CODEC_IDS; ++i) { 461 | if (strcmp(optarg, 462 | codec_ids[i].name) == 0) { 463 | codec_id = codec_ids[i].id; 464 | break; 465 | } 466 | } 467 | 468 | if (i == CREC_NUM_CODEC_IDS) { 469 | fprintf(stderr, "Unrecognised ID: %s\n", 470 | optarg); 471 | usage(); 472 | } 473 | } 474 | break; 475 | default: 476 | exit(EXIT_FAILURE); 477 | } 478 | } 479 | 480 | if (optind >= argc) { 481 | file = NULL; 482 | finfo = fopen("/dev/null", "w"); 483 | streamed = true; 484 | } else if (codec_id == SND_AUDIOCODEC_PCM) { 485 | file = argv[optind]; 486 | finfo = stdout; 487 | streamed = false; 488 | } else { 489 | fprintf(stderr, "ERROR: Only PCM can be written to a WAV file\n"); 490 | exit(EXIT_FAILURE); 491 | } 492 | 493 | capture_samples(file, card, device, buffer_size, frag, length, 494 | rate, channels, format, codec_id); 495 | 496 | fprintf(finfo, "Finish capturing... Close Normally\n"); 497 | 498 | exit(EXIT_SUCCESS); 499 | } 500 | -------------------------------------------------------------------------------- /src/utils/wave.c: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: (LGPL-2.1-only OR BSD-3-Clause) 2 | 3 | // 4 | // WAVE helper functions 5 | // 6 | // Copyright 2021 NXP 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "tinycompress/tinywave.h" 14 | 15 | static const struct wave_header blank_wave_header = { 16 | .riff = { 17 | .chunk = { 18 | .desc = "RIFF", 19 | }, 20 | .format = "WAVE", 21 | }, 22 | .fmt = { 23 | .chunk = { 24 | .desc = "fmt ", /* Note the space is important here */ 25 | .size = sizeof(blank_wave_header.fmt) - 26 | sizeof(blank_wave_header.fmt.chunk), 27 | }, 28 | .type = 0x01, /* PCM */ 29 | }, 30 | .data = { 31 | .chunk = { 32 | .desc = "data", 33 | }, 34 | }, 35 | }; 36 | 37 | void init_wave_header(struct wave_header *header, uint16_t channels, 38 | uint32_t rate, uint16_t samplebits) 39 | { 40 | memcpy(header, &blank_wave_header, sizeof(blank_wave_header)); 41 | 42 | header->fmt.channels = channels; 43 | header->fmt.rate = rate; 44 | header->fmt.byterate = channels * rate * (samplebits / 8); 45 | header->fmt.blockalign = channels * (samplebits / 8); 46 | header->fmt.samplebits = samplebits; 47 | } 48 | 49 | void size_wave_header(struct wave_header *header, uint32_t size) 50 | { 51 | header->riff.chunk.size = sizeof(*header) - 52 | sizeof(header->riff.chunk) + size; 53 | header->data.chunk.size = size; 54 | } 55 | 56 | int parse_wave_header(struct wave_header *header, unsigned int *channels, 57 | unsigned int *rate, unsigned int *format) 58 | { 59 | if (strncmp(header->riff.chunk.desc, "RIFF", 4) != 0) { 60 | fprintf(stderr, "RIFF magic not found\n"); 61 | return -1; 62 | } 63 | 64 | if (strncmp(header->riff.format, "WAVE", 4) != 0) { 65 | fprintf(stderr, "WAVE magic not found\n"); 66 | return -1; 67 | } 68 | 69 | if (strncmp(header->fmt.chunk.desc, "fmt", 3) != 0) { 70 | fprintf(stderr, "FMT section not found"); 71 | return -1; 72 | } 73 | 74 | *channels = header->fmt.channels; 75 | *rate = header->fmt.rate; 76 | 77 | switch(header->fmt.samplebits) { 78 | case 8: 79 | *format = SNDRV_PCM_FORMAT_U8; 80 | break; 81 | case 16: 82 | *format = SNDRV_PCM_FORMAT_S16_LE; 83 | break; 84 | case 32: 85 | *format = SNDRV_PCM_FORMAT_S32_LE; 86 | break; 87 | default: 88 | fprintf(stderr, "Unsupported sample bits %d\n", 89 | header->fmt.samplebits); 90 | return -1; 91 | } 92 | 93 | return 0; 94 | } 95 | -------------------------------------------------------------------------------- /tinycompress.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: libtinycompress 7 | Description: Tinycompress library for ALSA compress audio offload 8 | URL: alsa-project.org 9 | Version: @VERSION@ 10 | Cflags: -I${includedir}/tinycompress 11 | Libs: -L${libdir} -ltinycompress 12 | --------------------------------------------------------------------------------