├── COPYING ├── Makefile.am ├── README ├── autogen.sh ├── clean.sh ├── configure.ac ├── plugins ├── Makefile.am ├── plugin-audacious.c ├── plugin-gstreamer.c ├── plugin-winamp.c ├── plugin-xmms2.c └── winamp.h └── src ├── Makefile.am ├── acmtool.c ├── decode.c ├── libacm.h └── util.c /COPYING: -------------------------------------------------------------------------------- 1 | 2 | The libacm core code is licensed under minimal BSD/ISC license. 3 | (Core code = everything except plugins.) 4 | 5 | The plugins for various players are licensed under LGPL. 6 | 7 | ------------------------------------------------------------------------- 8 | 9 | Copyright (c) 2004-2010, Marko Kreen 10 | 11 | Permission to use, copy, modify, and/or distribute this software for any 12 | purpose with or without fee is hereby granted, provided that the above 13 | copyright notice and this permission notice appear in all copies. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 18 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 21 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 | 23 | ------------------------------------------------------------------------- 24 | 25 | GNU LESSER GENERAL PUBLIC LICENSE 26 | Version 2.1, February 1999 27 | 28 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 29 | 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 30 | Everyone is permitted to copy and distribute verbatim copies 31 | of this license document, but changing it is not allowed. 32 | 33 | [This is the first released version of the Lesser GPL. It also counts 34 | as the successor of the GNU Library Public License, version 2, hence 35 | the version number 2.1.] 36 | 37 | Preamble 38 | 39 | The licenses for most software are designed to take away your 40 | freedom to share and change it. By contrast, the GNU General Public 41 | Licenses are intended to guarantee your freedom to share and change 42 | free software--to make sure the software is free for all its users. 43 | 44 | This license, the Lesser General Public License, applies to some 45 | specially designated software packages--typically libraries--of the 46 | Free Software Foundation and other authors who decide to use it. You 47 | can use it too, but we suggest you first think carefully about whether 48 | this license or the ordinary General Public License is the better 49 | strategy to use in any particular case, based on the explanations 50 | below. 51 | 52 | When we speak of free software, we are referring to freedom of use, 53 | not price. Our General Public Licenses are designed to make sure that 54 | you have the freedom to distribute copies of free software (and charge 55 | for this service if you wish); that you receive source code or can get 56 | it if you want it; that you can change the software and use pieces of 57 | it in new free programs; and that you are informed that you can do 58 | these things. 59 | 60 | To protect your rights, we need to make restrictions that forbid 61 | distributors to deny you these rights or to ask you to surrender these 62 | rights. These restrictions translate to certain responsibilities for 63 | you if you distribute copies of the library or if you modify it. 64 | 65 | For example, if you distribute copies of the library, whether gratis 66 | or for a fee, you must give the recipients all the rights that we gave 67 | you. You must make sure that they, too, receive or can get the source 68 | code. If you link other code with the library, you must provide 69 | complete object files to the recipients, so that they can relink them 70 | with the library after making changes to the library and recompiling 71 | it. And you must show them these terms so they know their rights. 72 | 73 | We protect your rights with a two-step method: (1) we copyright the 74 | library, and (2) we offer you this license, which gives you legal 75 | permission to copy, distribute and/or modify the library. 76 | 77 | To protect each distributor, we want to make it very clear that 78 | there is no warranty for the free library. Also, if the library is 79 | modified by someone else and passed on, the recipients should know 80 | that what they have is not the original version, so that the original 81 | author's reputation will not be affected by problems that might be 82 | introduced by others. 83 | 84 | Finally, software patents pose a constant threat to the existence of 85 | any free program. We wish to make sure that a company cannot 86 | effectively restrict the users of a free program by obtaining a 87 | restrictive license from a patent holder. Therefore, we insist that 88 | any patent license obtained for a version of the library must be 89 | consistent with the full freedom of use specified in this license. 90 | 91 | Most GNU software, including some libraries, is covered by the 92 | ordinary GNU General Public License. This license, the GNU Lesser 93 | General Public License, applies to certain designated libraries, and 94 | is quite different from the ordinary General Public License. We use 95 | this license for certain libraries in order to permit linking those 96 | libraries into non-free programs. 97 | 98 | When a program is linked with a library, whether statically or using 99 | a shared library, the combination of the two is legally speaking a 100 | combined work, a derivative of the original library. The ordinary 101 | General Public License therefore permits such linking only if the 102 | entire combination fits its criteria of freedom. The Lesser General 103 | Public License permits more lax criteria for linking other code with 104 | the library. 105 | 106 | We call this license the "Lesser" General Public License because it 107 | does Less to protect the user's freedom than the ordinary General 108 | Public License. It also provides other free software developers Less 109 | of an advantage over competing non-free programs. These disadvantages 110 | are the reason we use the ordinary General Public License for many 111 | libraries. However, the Lesser license provides advantages in certain 112 | special circumstances. 113 | 114 | For example, on rare occasions, there may be a special need to 115 | encourage the widest possible use of a certain library, so that it 116 | becomes a de-facto standard. To achieve this, non-free programs must 117 | be allowed to use the library. A more frequent case is that a free 118 | library does the same job as widely used non-free libraries. In this 119 | case, there is little to gain by limiting the free library to free 120 | software only, so we use the Lesser General Public License. 121 | 122 | In other cases, permission to use a particular library in non-free 123 | programs enables a greater number of people to use a large body of 124 | free software. For example, permission to use the GNU C Library in 125 | non-free programs enables many more people to use the whole GNU 126 | operating system, as well as its variant, the GNU/Linux operating 127 | system. 128 | 129 | Although the Lesser General Public License is Less protective of the 130 | users' freedom, it does ensure that the user of a program that is 131 | linked with the Library has the freedom and the wherewithal to run 132 | that program using a modified version of the Library. 133 | 134 | The precise terms and conditions for copying, distribution and 135 | modification follow. Pay close attention to the difference between a 136 | "work based on the library" and a "work that uses the library". The 137 | former contains code derived from the library, whereas the latter must 138 | be combined with the library in order to run. 139 | 140 | GNU LESSER GENERAL PUBLIC LICENSE 141 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 142 | 143 | 0. This License Agreement applies to any software library or other 144 | program which contains a notice placed by the copyright holder or 145 | other authorized party saying it may be distributed under the terms of 146 | this Lesser General Public License (also called "this License"). 147 | Each licensee is addressed as "you". 148 | 149 | A "library" means a collection of software functions and/or data 150 | prepared so as to be conveniently linked with application programs 151 | (which use some of those functions and data) to form executables. 152 | 153 | The "Library", below, refers to any such software library or work 154 | which has been distributed under these terms. A "work based on the 155 | Library" means either the Library or any derivative work under 156 | copyright law: that is to say, a work containing the Library or a 157 | portion of it, either verbatim or with modifications and/or translated 158 | straightforwardly into another language. (Hereinafter, translation is 159 | included without limitation in the term "modification".) 160 | 161 | "Source code" for a work means the preferred form of the work for 162 | making modifications to it. For a library, complete source code means 163 | all the source code for all modules it contains, plus any associated 164 | interface definition files, plus the scripts used to control 165 | compilation and installation of the library. 166 | 167 | Activities other than copying, distribution and modification are not 168 | covered by this License; they are outside its scope. The act of 169 | running a program using the Library is not restricted, and output from 170 | such a program is covered only if its contents constitute a work based 171 | on the Library (independent of the use of the Library in a tool for 172 | writing it). Whether that is true depends on what the Library does 173 | and what the program that uses the Library does. 174 | 175 | 1. You may copy and distribute verbatim copies of the Library's 176 | complete source code as you receive it, in any medium, provided that 177 | you conspicuously and appropriately publish on each copy an 178 | appropriate copyright notice and disclaimer of warranty; keep intact 179 | all the notices that refer to this License and to the absence of any 180 | warranty; and distribute a copy of this License along with the 181 | Library. 182 | 183 | You may charge a fee for the physical act of transferring a copy, 184 | and you may at your option offer warranty protection in exchange for a 185 | fee. 186 | 187 | 2. You may modify your copy or copies of the Library or any portion 188 | of it, thus forming a work based on the Library, and copy and 189 | distribute such modifications or work under the terms of Section 1 190 | above, provided that you also meet all of these conditions: 191 | 192 | a) The modified work must itself be a software library. 193 | 194 | b) You must cause the files modified to carry prominent notices 195 | stating that you changed the files and the date of any change. 196 | 197 | c) You must cause the whole of the work to be licensed at no 198 | charge to all third parties under the terms of this License. 199 | 200 | d) If a facility in the modified Library refers to a function or a 201 | table of data to be supplied by an application program that uses 202 | the facility, other than as an argument passed when the facility 203 | is invoked, then you must make a good faith effort to ensure that, 204 | in the event an application does not supply such function or 205 | table, the facility still operates, and performs whatever part of 206 | its purpose remains meaningful. 207 | 208 | (For example, a function in a library to compute square roots has 209 | a purpose that is entirely well-defined independent of the 210 | application. Therefore, Subsection 2d requires that any 211 | application-supplied function or table used by this function must 212 | be optional: if the application does not supply it, the square 213 | root function must still compute square roots.) 214 | 215 | These requirements apply to the modified work as a whole. If 216 | identifiable sections of that work are not derived from the Library, 217 | and can be reasonably considered independent and separate works in 218 | themselves, then this License, and its terms, do not apply to those 219 | sections when you distribute them as separate works. But when you 220 | distribute the same sections as part of a whole which is a work based 221 | on the Library, the distribution of the whole must be on the terms of 222 | this License, whose permissions for other licensees extend to the 223 | entire whole, and thus to each and every part regardless of who wrote 224 | it. 225 | 226 | Thus, it is not the intent of this section to claim rights or contest 227 | your rights to work written entirely by you; rather, the intent is to 228 | exercise the right to control the distribution of derivative or 229 | collective works based on the Library. 230 | 231 | In addition, mere aggregation of another work not based on the Library 232 | with the Library (or with a work based on the Library) on a volume of 233 | a storage or distribution medium does not bring the other work under 234 | the scope of this License. 235 | 236 | 3. You may opt to apply the terms of the ordinary GNU General Public 237 | License instead of this License to a given copy of the Library. To do 238 | this, you must alter all the notices that refer to this License, so 239 | that they refer to the ordinary GNU General Public License, version 2, 240 | instead of to this License. (If a newer version than version 2 of the 241 | ordinary GNU General Public License has appeared, then you can specify 242 | that version instead if you wish.) Do not make any other change in 243 | these notices. 244 | 245 | Once this change is made in a given copy, it is irreversible for 246 | that copy, so the ordinary GNU General Public License applies to all 247 | subsequent copies and derivative works made from that copy. 248 | 249 | This option is useful when you wish to copy part of the code of 250 | the Library into a program that is not a library. 251 | 252 | 4. You may copy and distribute the Library (or a portion or 253 | derivative of it, under Section 2) in object code or executable form 254 | under the terms of Sections 1 and 2 above provided that you accompany 255 | it with the complete corresponding machine-readable source code, which 256 | must be distributed under the terms of Sections 1 and 2 above on a 257 | medium customarily used for software interchange. 258 | 259 | If distribution of object code is made by offering access to copy 260 | from a designated place, then offering equivalent access to copy the 261 | source code from the same place satisfies the requirement to 262 | distribute the source code, even though third parties are not 263 | compelled to copy the source along with the object code. 264 | 265 | 5. A program that contains no derivative of any portion of the 266 | Library, but is designed to work with the Library by being compiled or 267 | linked with it, is called a "work that uses the Library". Such a 268 | work, in isolation, is not a derivative work of the Library, and 269 | therefore falls outside the scope of this License. 270 | 271 | However, linking a "work that uses the Library" with the Library 272 | creates an executable that is a derivative of the Library (because it 273 | contains portions of the Library), rather than a "work that uses the 274 | library". The executable is therefore covered by this License. 275 | Section 6 states terms for distribution of such executables. 276 | 277 | When a "work that uses the Library" uses material from a header file 278 | that is part of the Library, the object code for the work may be a 279 | derivative work of the Library even though the source code is not. 280 | Whether this is true is especially significant if the work can be 281 | linked without the Library, or if the work is itself a library. The 282 | threshold for this to be true is not precisely defined by law. 283 | 284 | If such an object file uses only numerical parameters, data 285 | structure layouts and accessors, and small macros and small inline 286 | functions (ten lines or less in length), then the use of the object 287 | file is unrestricted, regardless of whether it is legally a derivative 288 | work. (Executables containing this object code plus portions of the 289 | Library will still fall under Section 6.) 290 | 291 | Otherwise, if the work is a derivative of the Library, you may 292 | distribute the object code for the work under the terms of Section 6. 293 | Any executables containing that work also fall under Section 6, 294 | whether or not they are linked directly with the Library itself. 295 | 296 | 6. As an exception to the Sections above, you may also combine or 297 | link a "work that uses the Library" with the Library to produce a 298 | work containing portions of the Library, and distribute that work 299 | under terms of your choice, provided that the terms permit 300 | modification of the work for the customer's own use and reverse 301 | engineering for debugging such modifications. 302 | 303 | You must give prominent notice with each copy of the work that the 304 | Library is used in it and that the Library and its use are covered by 305 | this License. You must supply a copy of this License. If the work 306 | during execution displays copyright notices, you must include the 307 | copyright notice for the Library among them, as well as a reference 308 | directing the user to the copy of this License. Also, you must do one 309 | of these things: 310 | 311 | a) Accompany the work with the complete corresponding 312 | machine-readable source code for the Library including whatever 313 | changes were used in the work (which must be distributed under 314 | Sections 1 and 2 above); and, if the work is an executable linked 315 | with the Library, with the complete machine-readable "work that 316 | uses the Library", as object code and/or source code, so that the 317 | user can modify the Library and then relink to produce a modified 318 | executable containing the modified Library. (It is understood 319 | that the user who changes the contents of definitions files in the 320 | Library will not necessarily be able to recompile the application 321 | to use the modified definitions.) 322 | 323 | b) Use a suitable shared library mechanism for linking with the 324 | Library. A suitable mechanism is one that (1) uses at run time a 325 | copy of the library already present on the user's computer system, 326 | rather than copying library functions into the executable, and (2) 327 | will operate properly with a modified version of the library, if 328 | the user installs one, as long as the modified version is 329 | interface-compatible with the version that the work was made with. 330 | 331 | c) Accompany the work with a written offer, valid for at least 332 | three years, to give the same user the materials specified in 333 | Subsection 6a, above, for a charge no more than the cost of 334 | performing this distribution. 335 | 336 | d) If distribution of the work is made by offering access to copy 337 | from a designated place, offer equivalent access to copy the above 338 | specified materials from the same place. 339 | 340 | e) Verify that the user has already received a copy of these 341 | materials or that you have already sent this user a copy. 342 | 343 | For an executable, the required form of the "work that uses the 344 | Library" must include any data and utility programs needed for 345 | reproducing the executable from it. However, as a special exception, 346 | the materials to be distributed need not include anything that is 347 | normally distributed (in either source or binary form) with the major 348 | components (compiler, kernel, and so on) of the operating system on 349 | which the executable runs, unless that component itself accompanies 350 | the executable. 351 | 352 | It may happen that this requirement contradicts the license 353 | restrictions of other proprietary libraries that do not normally 354 | accompany the operating system. Such a contradiction means you cannot 355 | use both them and the Library together in an executable that you 356 | distribute. 357 | 358 | 7. You may place library facilities that are a work based on the 359 | Library side-by-side in a single library together with other library 360 | facilities not covered by this License, and distribute such a combined 361 | library, provided that the separate distribution of the work based on 362 | the Library and of the other library facilities is otherwise 363 | permitted, and provided that you do these two things: 364 | 365 | a) Accompany the combined library with a copy of the same work 366 | based on the Library, uncombined with any other library 367 | facilities. This must be distributed under the terms of the 368 | Sections above. 369 | 370 | b) Give prominent notice with the combined library of the fact 371 | that part of it is a work based on the Library, and explaining 372 | where to find the accompanying uncombined form of the same work. 373 | 374 | 8. You may not copy, modify, sublicense, link with, or distribute 375 | the Library except as expressly provided under this License. Any 376 | attempt otherwise to copy, modify, sublicense, link with, or 377 | distribute the Library is void, and will automatically terminate your 378 | rights under this License. However, parties who have received copies, 379 | or rights, from you under this License will not have their licenses 380 | terminated so long as such parties remain in full compliance. 381 | 382 | 9. You are not required to accept this License, since you have not 383 | signed it. However, nothing else grants you permission to modify or 384 | distribute the Library or its derivative works. These actions are 385 | prohibited by law if you do not accept this License. Therefore, by 386 | modifying or distributing the Library (or any work based on the 387 | Library), you indicate your acceptance of this License to do so, and 388 | all its terms and conditions for copying, distributing or modifying 389 | the Library or works based on it. 390 | 391 | 10. Each time you redistribute the Library (or any work based on the 392 | Library), the recipient automatically receives a license from the 393 | original licensor to copy, distribute, link with or modify the Library 394 | subject to these terms and conditions. You may not impose any further 395 | restrictions on the recipients' exercise of the rights granted herein. 396 | You are not responsible for enforcing compliance by third parties with 397 | this License. 398 | 399 | 11. If, as a consequence of a court judgment or allegation of patent 400 | infringement or for any other reason (not limited to patent issues), 401 | conditions are imposed on you (whether by court order, agreement or 402 | otherwise) that contradict the conditions of this License, they do not 403 | excuse you from the conditions of this License. If you cannot 404 | distribute so as to satisfy simultaneously your obligations under this 405 | License and any other pertinent obligations, then as a consequence you 406 | may not distribute the Library at all. For example, if a patent 407 | license would not permit royalty-free redistribution of the Library by 408 | all those who receive copies directly or indirectly through you, then 409 | the only way you could satisfy both it and this License would be to 410 | refrain entirely from distribution of the Library. 411 | 412 | If any portion of this section is held invalid or unenforceable under 413 | any particular circumstance, the balance of the section is intended to 414 | apply, and the section as a whole is intended to apply in other 415 | circumstances. 416 | 417 | It is not the purpose of this section to induce you to infringe any 418 | patents or other property right claims or to contest validity of any 419 | such claims; this section has the sole purpose of protecting the 420 | integrity of the free software distribution system which is 421 | implemented by public license practices. Many people have made 422 | generous contributions to the wide range of software distributed 423 | through that system in reliance on consistent application of that 424 | system; it is up to the author/donor to decide if he or she is willing 425 | to distribute software through any other system and a licensee cannot 426 | impose that choice. 427 | 428 | This section is intended to make thoroughly clear what is believed to 429 | be a consequence of the rest of this License. 430 | 431 | 12. If the distribution and/or use of the Library is restricted in 432 | certain countries either by patents or by copyrighted interfaces, the 433 | original copyright holder who places the Library under this License 434 | may add an explicit geographical distribution limitation excluding those 435 | countries, so that distribution is permitted only in or among 436 | countries not thus excluded. In such case, this License incorporates 437 | the limitation as if written in the body of this License. 438 | 439 | 13. The Free Software Foundation may publish revised and/or new 440 | versions of the Lesser General Public License from time to time. 441 | Such new versions will be similar in spirit to the present version, 442 | but may differ in detail to address new problems or concerns. 443 | 444 | Each version is given a distinguishing version number. If the Library 445 | specifies a version number of this License which applies to it and 446 | "any later version", you have the option of following the terms and 447 | conditions either of that version or of any later version published by 448 | the Free Software Foundation. If the Library does not specify a 449 | license version number, you may choose any version ever published by 450 | the Free Software Foundation. 451 | 452 | 14. If you wish to incorporate parts of the Library into other free 453 | programs whose distribution conditions are incompatible with these, 454 | write to the author to ask for permission. For software which is 455 | copyrighted by the Free Software Foundation, write to the Free 456 | Software Foundation; we sometimes make exceptions for this. Our 457 | decision will be guided by the two goals of preserving the free status 458 | of all derivatives of our free software and of promoting the sharing 459 | and reuse of software generally. 460 | 461 | NO WARRANTY 462 | 463 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 464 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 465 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 466 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 467 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 468 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 469 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 470 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 471 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 472 | 473 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 474 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 475 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 476 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 477 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 478 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 479 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 480 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 481 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 482 | DAMAGES. 483 | 484 | END OF TERMS AND CONDITIONS 485 | 486 | How to Apply These Terms to Your New Libraries 487 | 488 | If you develop a new library, and you want it to be of the greatest 489 | possible use to the public, we recommend making it free software that 490 | everyone can redistribute and change. You can do so by permitting 491 | redistribution under these terms (or, alternatively, under the terms 492 | of the ordinary General Public License). 493 | 494 | To apply these terms, attach the following notices to the library. 495 | It is safest to attach them to the start of each source file to most 496 | effectively convey the exclusion of warranty; and each file should 497 | have at least the "copyright" line and a pointer to where the full 498 | notice is found. 499 | 500 | 501 | 502 | Copyright (C) 503 | 504 | This library is free software; you can redistribute it and/or 505 | modify it under the terms of the GNU Lesser General Public 506 | License as published by the Free Software Foundation; either 507 | version 2.1 of the License, or (at your option) any later version. 508 | 509 | This library is distributed in the hope that it will be useful, 510 | but WITHOUT ANY WARRANTY; without even the implied warranty of 511 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 512 | Lesser General Public License for more details. 513 | 514 | You should have received a copy of the GNU Lesser General Public 515 | License along with this library; if not, write to the Free Software 516 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 517 | 518 | Also add information on how to contact you by electronic and paper mail. 519 | 520 | You should also get your employer (if you work as a programmer) or 521 | your school, if any, to sign a "copyright disclaimer" for the library, 522 | if necessary. Here is a sample; alter the names: 523 | 524 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 525 | library `Frob' (a library for tweaking knobs) written by James 526 | Random Hacker. 527 | 528 | , 1 April 1990 529 | Ty Coon, President of Vice 530 | 531 | That's all there is to it! 532 | 533 | 534 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | AUTOMAKE_OPTIONS = 1.6 foreign 3 | ACLOCAL_AMFLAGS = -I m4 4 | 5 | SUBDIRS = src #plugins 6 | 7 | EXTRA_DIST = autogen.sh 8 | 9 | # 10 | # html 11 | # 12 | 13 | README.html: README 14 | asciidoc -a toc $< 15 | 16 | html: README.html 17 | .PHONY: html 18 | 19 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | libacm - Library for InterPlay ACM Audio format. 2 | ================================================ 3 | 4 | Intro 5 | ----- 6 | 7 | Decoder library for InterPlay ACM audio files. 8 | Includes command line tool. 9 | 10 | The format was used for games Fallout, Fallout 2 and Baldur's Gate series. 11 | It is less efficient than the more advanced MP3, Vorbis or AAC algorithms, 12 | but it's main feature is very fast decoding speed. 13 | 14 | 15 | NEWS 16 | ---- 17 | 18 | FFmpeg 19 | ~~~~~~ 20 | 21 | * This decoder was merged into FFmpeg 3.0. Now ACM files can be played 22 | via ffmpeg commands or using libavcodec. 23 | 24 | Version 1.3 25 | ~~~~~~~~~~~ 26 | 27 | * acmtool: Fix crash with libao 1.x 28 | * plugins: drop, they do not work with any modern player. 29 | 30 | Version 1.2 31 | ~~~~~~~~~~~ 32 | 33 | * decoder: Fix error handling in load_bits() (Brian "Moses" Hall) 34 | * acmtool: Show time instead number of samples. 35 | 36 | * plugin-xmms2: New plugin for XMMS2 37 | * plugin-audacious: Support Audacious up to 2.4 38 | * plugin-gstreamer: Fix End-Of-Stream handling 39 | * plugin-xmms: Drop it. XMMS is obsolete. 40 | 41 | Version 1.1 42 | ~~~~~~~~~~~ 43 | 44 | * acmtool: play files with libao: acmtool -p FILE 45 | * decoder: support WAVC files, which are ACM files with additional header. 46 | * decoder: take mono ACM as stereo, as there are lot of badly tagged files. 47 | Trust channel count on WAVC files, those seem to be correct, 48 | and WAVC is mostly used for samples. 49 | * decoder: avoid divide-by-zero in bitrate calculation. 50 | * plugin-audacious: Keep up with API changes in Audacious 2.x branch. 51 | It seems to be still compatible with 1.x, but I have not tested. 52 | 53 | Version 1.0 54 | ~~~~~~~~~~~ 55 | 56 | * decoder: excpect EOF in more places - otherwise libacm 57 | complained about "unexpected eof". 58 | * decoder: on reading assume there is additional single zero byte 59 | at the end of all files. Otherwise last block on some files was 60 | skipped because last few bits could not be read. 61 | 62 | Thanks to above 2 changes libacm now decodes all Fallout 1/2 63 | files in full length and without complaining. 64 | 65 | * License plugins under LGPL. 66 | * New separate plugin for Audacious 1.5+ 67 | * Remove plugin for Beep Media Player 68 | * Rewrite plugin-gstreamer.c for GStreamer "Hell On Earth" 0.10 69 | * acmtool -d -o - writes to stdout. 70 | 71 | Version 0.9.2 72 | ~~~~~~~~~~~~~ 73 | 74 | * License core libacm code under minimal BSD/ISC license. 75 | Plugins continue to be under GPL. 76 | * Fix acmtool crash. (ahalda@cs.mcgill.ca) 77 | 78 | Version 0.9.1 (unreleased) 79 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 80 | 81 | * Use autoconf/automake for build system 82 | * Plugin for GStreamer 0.8. (alsasink seems to be buggy for 22050 Hz 83 | audio. If you hear any cracks, try to use osssink.) 84 | * Small cleanups. 85 | 86 | Version 0.9 87 | ~~~~~~~~~~~ 88 | 89 | * First public release. 90 | * Includes: decoder, acmtool, plugins for XMMS, BEEP and Winamp 91 | 92 | 93 | Installation 94 | ------------ 95 | 96 | ./configure 97 | make 98 | make install 99 | 100 | Command line tools usage 101 | ------------------------ 102 | 103 | $ acmtool -h 104 | acmtool - libacm version 1.0 105 | Decode: acmtool -d [-q][-m|-s] [-r|-n] -o outfile infile 106 | acmtool -d [-q][-m|-s] [-r|-n] infile [infile ...] 107 | Other: acmtool -i ACMFILE [ACMFILE ...] 108 | acmtool -M|-S ACMFILE [ACMFILE ...] 109 | Commands: 110 | -d decode audio into WAV files 111 | -p play audio 112 | -i show info about ACM files 113 | -M modify ACM header to have 1 channel 114 | -S modify ACM header to have 2 channels 115 | Switches: 116 | -m force mono wav 117 | -s force stereo wav 118 | -r raw output - no wav header 119 | -q be quiet 120 | -n no output - for benchmarking 121 | -o FN output to file, can be used if single source file 122 | 123 | The mono/stereo options are necessary because for some ACM files 124 | the number of channels in header in wrong. Usually those are 125 | game samples, which contain mono audio but are tagger as stereo. 126 | 127 | 128 | Credits 129 | ------- 130 | 131 | All the hard work of reverse engineering the format was done 132 | by ABel from TeamX. I simply re-implemented it in C. 133 | 134 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | unset LANG LC_ALL LC_COLLATE 4 | export LANG LC_ALL LC_COLLATE 5 | 6 | AUTOCONF=${AUTOCONF:-autoconf} \ 7 | AUTOMAKE=${AUTOMAKE:-automake} \ 8 | ACLOCAL=${ACLOCAL:-aclocal} \ 9 | autoreconf -i -f 10 | 11 | echo "Now run ./configure" 12 | 13 | -------------------------------------------------------------------------------- /clean.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | echo "Deleting all autogenerated stuff..." 4 | 5 | rm -f *.o *.so config.* Makefile configure 6 | rm -f Makefile.in aclocal.m4 confdefs.h ltconfig ltmain.sh aclocal* 7 | rm -f install-sh mkinstalldirs missing depcomp 8 | rm -f m4/Makefile m4/Makefile.in 9 | rm -rf .libs .deps src/.libs src/.deps 10 | rm -f src/*.o src/*.[ls][ao] *.[ls][ao] 11 | rm -f src/Makefile.in src/Makefile libtool 12 | rm -rf autom4te.cache stamp-* compile libacm-* 13 | 14 | rm -rf src/.libs src/.deps src/*.lo src/*.la src/Makefile src/Makefile.in 15 | rm -rf plugins/.libs plugins/.deps plugins/*.lo plugins/*.la 16 | rm -f plugins/Makefile plugins/Makefile.in src/acmtool 17 | 18 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | dnl Process this file with autoconf to produce a configure script. 2 | AC_PREREQ([2.71]) 3 | 4 | AC_INIT([libacm],[1.3],[markokr@gmail.com]) 5 | AC_CONFIG_SRCDIR([src/libacm.h]) 6 | AM_INIT_AUTOMAKE 7 | AC_CONFIG_HEADERS([config.h]) 8 | AC_CONFIG_MACRO_DIRS([m4]) 9 | 10 | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) 11 | 12 | dnl Checks for programs. 13 | AC_PROG_AWK 14 | AC_PROG_CC 15 | AC_PROG_CPP 16 | AC_PROG_INSTALL 17 | AC_PROG_LN_S 18 | LT_INIT 19 | 20 | AC_DISABLE_STATIC 21 | AM_PROG_CC_C_O 22 | 23 | dnl Additional gcc tuning 24 | if test x"$GCC" = xyes; then 25 | AC_MSG_CHECKING([for working warning switches]) 26 | good_CFLAGS="$CFLAGS" 27 | warnings="" 28 | flags="-Wall -Wextra" 29 | # turn off noise from Wextra 30 | flags="$flags -Wno-unused-parameter -Wno-missing-field-initializers" 31 | # Wextra does not turn those on? 32 | flags="$flags -Wmissing-prototypes -Wpointer-arith -Wendif-labels" 33 | flags="$flags -Wdeclaration-after-statement -Wold-style-definition" 34 | flags="$flags -Wstrict-prototypes -Wundef -Wformat -Wnonnull -Wstrict-overflow" 35 | for f in $flags; do 36 | CFLAGS="$good_CFLAGS $warnings $f" 37 | AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void foo(void){}]])], [warnings="$warnings $f"]) 38 | done 39 | CFLAGS="$good_CFLAGS $warnings" 40 | AC_MSG_RESULT([$warnings]) 41 | fi 42 | 43 | dnl Checks for typedefs, structures, and compiler characteristics. 44 | AC_C_CONST 45 | AC_TYPE_SIZE_T 46 | 47 | dnl Checks for library functions. 48 | AC_CHECK_INCLUDES_DEFAULT 49 | 50 | dnl Plugin configuration 51 | PKG_PROG_PKG_CONFIG 52 | 53 | dnl Check for libao 54 | have_ao=no 55 | PKG_CHECK_MODULES([AO], [ao], [have_ao=yes], 56 | [AC_MSG_WARN([*** libao-dev not found, audio output disabled ***])]) 57 | 58 | if test "$have_ao" = "yes"; then 59 | AC_SUBST([AO_CFLAGS]) 60 | AC_SUBST([AO_LIBS]) 61 | AC_DEFINE([HAVE_AO], 1, [Define 1 if libao is usable]) 62 | fi 63 | 64 | AM_CONDITIONAL(USE_LIBAO, test "$have_ao" = "yes") 65 | 66 | AC_CONFIG_FILES([Makefile src/Makefile]) 67 | AC_OUTPUT 68 | 69 | 70 | echo "" 71 | echo "Audio output: $have_ao" 72 | echo "" 73 | -------------------------------------------------------------------------------- /plugins/Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | EXTRA_DIST = plugin-xmms2.c plugin-audacious.c plugin-gstreamer.c plugin-winamp.c winamp.h 3 | 4 | acm_cpp = -I$(top_srcdir)/src 5 | acm_lib = $(top_builddir)/src/libacm.la 6 | 7 | modflags = -module -avoid-version -no-undefined 8 | 9 | if MAKE_XMMS2_PLUGIN 10 | xmms2_plugindir = $(XMMS2_DEST) 11 | xmms2_plugin_LTLIBRARIES = libxmms_acm.la 12 | endif 13 | 14 | if MAKE_AUDACIOUS_PLUGIN 15 | audacious_plugindir = $(AUDACIOUS_DEST) 16 | audacious_plugin_LTLIBRARIES = audacious-acm.la 17 | endif 18 | 19 | if MAKE_GST10_PLUGIN 20 | gst10_plugindir = $(GST10_DEST) 21 | gst10_plugin_LTLIBRARIES = acmdec-gst10.la 22 | endif 23 | 24 | libxmms_acm_la_SOURCES = plugin-xmms2.c 25 | libxmms_acm_la_CPPFLAGS = $(XMMS2_CFLAGS) $(acm_cpp) 26 | libxmms_acm_la_LIBADD = $(acm_lib) 27 | libxmms_acm_la_LDFLAGS = $(XMMS2_LIBS) $(modflags) -export-symbols-regex XMMS_PLUGIN_DESC 28 | 29 | audacious_acm_la_SOURCES = plugin-audacious.c 30 | audacious_acm_la_CPPFLAGS = $(AUDACIOUS_CFLAGS) $(acm_cpp) 31 | audacious_acm_la_LIBADD = $(acm_lib) 32 | audacious_acm_la_LDFLAGS = $(AUDACIOUS_LIBS) $(modflags) -export-symbols-regex get_plugin_info 33 | 34 | acmdec_gst10_la_SOURCES = plugin-gstreamer.c 35 | acmdec_gst10_la_CPPFLAGS = $(GST10_CFLAGS) $(acm_cpp) 36 | acmdec_gst10_la_LIBADD = $(acm_lib) -lgstbase-0.10 37 | acmdec_gst10_la_LDFLAGS = $(GST10_LIBS) $(modflags) -export-symbols-regex gst_plugin_desc 38 | 39 | -------------------------------------------------------------------------------- /plugins/plugin-audacious.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libacm plugin for Audacious 3 | * 4 | * Copyright (C) 2004-2011 Marko Kreen 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include "config.h" 23 | #endif 24 | 25 | #define GDK_DISABLE_DEPRECATED 26 | #define GTK_DISABLE_DEPRECATED 27 | 28 | #include 29 | 30 | #include "libacm.h" 31 | 32 | 33 | static int acmx_seek_to = -1; 34 | 35 | static int acmx_open_vfs(ACMStream **acm_p, const gchar *url); 36 | 37 | /* 38 | * useful stuff 39 | */ 40 | 41 | static gchar *get_title(const gchar * filename) 42 | { 43 | const gchar *p = strrchr(filename, '/'); 44 | p = (p != NULL) ? (p + 1) : filename; 45 | return g_strdup(p); 46 | } 47 | 48 | 49 | /* 50 | * module functions 51 | */ 52 | 53 | static gint acmx_is_our_file(const gchar * filename) 54 | { 55 | ACMStream *acm; 56 | 57 | if (acmx_open_vfs(&acm, filename) < 0) 58 | return FALSE; 59 | acm_close(acm); 60 | return TRUE; 61 | } 62 | 63 | static Tuple *acmx_get_song_tuple(const gchar * filename) 64 | { 65 | ACMStream *acm; 66 | const ACMInfo *info; 67 | int err; 68 | Tuple *tup = NULL; 69 | char buf[512]; 70 | gchar *title, *ext; 71 | 72 | ext = strrchr(filename, '.'); 73 | if (!ext || strcasecmp(ext, ".acm") != 0) 74 | return NULL; 75 | 76 | if ((err = acmx_open_vfs(&acm, filename)) < 0) 77 | return NULL; 78 | 79 | tup = tuple_new_from_filename(filename); 80 | 81 | title = get_title(filename); 82 | tuple_associate_string(tup, FIELD_TITLE, NULL, title); 83 | g_free(title); 84 | 85 | info = acm_info(acm); 86 | snprintf(buf, sizeof(buf), "acm-level=%d acm-subblocks=%d", 87 | info->acm_level, info->acm_rows); 88 | tuple_associate_string(tup, FIELD_COMMENT, NULL, buf); 89 | 90 | tuple_associate_int(tup, FIELD_LENGTH, NULL, acm_time_total(acm)); 91 | tuple_associate_int(tup, FIELD_BITRATE, NULL, acm_bitrate(acm) / 1024); 92 | tuple_associate_string(tup, FIELD_CODEC, NULL, "InterPlay ACM"); 93 | tuple_associate_string(tup, FIELD_MIMETYPE, NULL, "application/acm"); 94 | tuple_associate_string(tup, FIELD_QUALITY, NULL, "lossy"); 95 | 96 | acm_close(acm); 97 | return tup; 98 | } 99 | 100 | static void acmx_pause(InputPlayback *pback, gshort p) 101 | { 102 | pback->output->pause(p); 103 | } 104 | 105 | static void acmx_seek(InputPlayback *pback, gint secs) 106 | { 107 | acmx_seek_to = secs * 1000; 108 | while (acmx_seek_to >= 0) 109 | g_usleep(20000); 110 | } 111 | 112 | 113 | static void read_and_play(ACMStream *acm, InputPlayback *pback, gchar *buf, int block_len) 114 | { 115 | int got_bytes; 116 | 117 | got_bytes = acm_read_loop(acm, buf, block_len, 0, 2, 1); 118 | if (got_bytes > 0) { 119 | pback->pass_audio(pback, FMT_S16_LE, acm_channels(acm), got_bytes, buf, &pback->playing); 120 | } else { 121 | /* flush buffer */ 122 | while (pback->output->buffer_playing()) { 123 | if (!pback->playing) 124 | break; 125 | g_usleep(10000); 126 | } 127 | pback->eof = TRUE; 128 | } 129 | } 130 | 131 | static void try_seeking(ACMStream *acm, InputPlayback *pback) 132 | { 133 | int pos = acm_seek_time(acm, acmx_seek_to); 134 | 135 | if (pos >= 0) { 136 | pback->output->flush(acm_time_tell(acm)); 137 | pback->eof = 0; 138 | } 139 | acmx_seek_to = -1; 140 | } 141 | 142 | #define BLK_SAMPLES 512 143 | 144 | static void play_file(InputPlayback *pback) 145 | { 146 | gchar *filename = pback->filename; 147 | gint res; 148 | ACMStream *acm; 149 | int err, block_len; 150 | gchar *buf; 151 | 152 | if ((err = acmx_open_vfs(&acm, filename)) < 0) 153 | return; 154 | 155 | pback->set_params(pback, NULL, 0, acm_bitrate(acm), acm_rate(acm), acm_channels(acm)); 156 | 157 | res = pback->output->open_audio(FMT_S16_LE, acm_rate(acm), acm_channels(acm)); 158 | if (res == 0) { 159 | pback->error = TRUE; 160 | acm_close(acm); 161 | return; 162 | } 163 | 164 | /* 165 | * main loop 166 | */ 167 | block_len = BLK_SAMPLES * acm_channels(acm) * ACM_WORD; 168 | buf = g_malloc0(block_len); 169 | while (pback->playing) { 170 | if (acmx_seek_to >= 0) 171 | try_seeking(acm, pback); 172 | 173 | if (!pback->eof) { 174 | read_and_play(acm, pback, buf, block_len); 175 | } else if (pback->output->buffer_playing()) { 176 | g_usleep(10000); 177 | } else { 178 | break; 179 | } 180 | } 181 | 182 | if (!pback->error) { 183 | /* flush buffer */ 184 | while (pback->eof && pback->output->buffer_playing()) { 185 | g_usleep(10000); 186 | } 187 | } 188 | 189 | g_free(buf); 190 | acm_close(acm); 191 | pback->output->close_audio(); 192 | pback->playing = 0; 193 | } 194 | 195 | static void acmx_play_file(InputPlayback *pback) 196 | { 197 | pback->playing = 1; 198 | pback->eof = 0; 199 | pback->error = FALSE; 200 | 201 | pback->set_pb_ready(pback); 202 | 203 | play_file(pback); 204 | } 205 | 206 | static void acmx_stop(InputPlayback *pback) 207 | { 208 | pback->playing = 0; 209 | } 210 | 211 | /* 212 | * Plugin info. 213 | */ 214 | 215 | static const gchar * const acmx_fmts[] = { "acm", NULL }; 216 | 217 | static InputPlugin acmx_plugin = { 218 | .description = "InterPlay ACM Audio Plugin", 219 | 220 | .is_our_file = acmx_is_our_file, 221 | .play_file = acmx_play_file, 222 | .stop = acmx_stop, 223 | .pause = acmx_pause, 224 | .seek = acmx_seek, 225 | 226 | .get_song_tuple = acmx_get_song_tuple, /* aud 1.1.0 */ 227 | .vfs_extensions = acmx_fmts, /* aud 1.3.0 */ 228 | }; 229 | 230 | static InputPlugin *acmx_plugin_list[] = { &acmx_plugin, NULL }; 231 | SIMPLE_INPUT_PLUGIN(libacm, acmx_plugin_list); 232 | 233 | 234 | /* 235 | * vfs i/o funcs. 236 | */ 237 | 238 | static int acmx_vfs_read(void *dst, int size, int n, void *arg) 239 | { 240 | VFSFile *f = arg; 241 | return vfs_fread(dst, size, n, f); 242 | } 243 | 244 | static int acmx_vfs_seek(void *arg, int offset, int whence) 245 | { 246 | VFSFile *f = arg; 247 | return vfs_fseek(f, offset, whence); 248 | } 249 | 250 | static int acmx_vfs_close(void *arg) 251 | { 252 | VFSFile *f = arg; 253 | vfs_fclose(f); 254 | return 0; 255 | } 256 | 257 | static int acmx_vfs_get_length(void *arg) 258 | { 259 | VFSFile *f = arg; 260 | return vfs_fsize(f); 261 | } 262 | 263 | static const acm_io_callbacks acmx_vfs_cb = { 264 | acmx_vfs_read, 265 | acmx_vfs_seek, 266 | acmx_vfs_close, 267 | acmx_vfs_get_length 268 | }; 269 | 270 | static int acmx_open_vfs(ACMStream **acm_p, const gchar *url) 271 | { 272 | VFSFile *stream; 273 | int res; 274 | 275 | stream = vfs_fopen(url, "r"); 276 | if (stream == NULL) 277 | return ACM_ERR_OPEN; 278 | 279 | res = acm_open_decoder(acm_p, stream, acmx_vfs_cb, 0); 280 | if (res < 0) 281 | vfs_fclose(stream); 282 | return res; 283 | } 284 | 285 | -------------------------------------------------------------------------------- /plugins/plugin-gstreamer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libacm plugin for GStreamer 3 | * 4 | * Copyright (C) 2004-2008 Marko Kreen 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include "config.h" 23 | #endif 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | 33 | #include "libacm.h" 34 | 35 | /* make sure we know byte order */ 36 | #if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN) 37 | #error BYTE_ORDER must be defined. 38 | #endif 39 | #if BYTE_ORDER == LITTLE_ENDIAN 40 | #define ACM_NATIVE_BE 0 41 | #else 42 | #define ACM_NATIVE_BE 1 43 | #endif 44 | 45 | /* default request length */ 46 | #define REQLEN (4 * 1024) 47 | 48 | typedef unsigned long long usec_t; 49 | #define USEC 1000000 50 | 51 | #define ACMDEC_SEEK_WAIT (USEC / 5) 52 | 53 | #define FN(f) GST_DEBUG_FUNCPTR(f) 54 | 55 | GST_DEBUG_CATEGORY_STATIC (acmdec_debug); 56 | #define GST_CAT_DEFAULT acmdec_debug 57 | 58 | /* 59 | * AcmDec struct 60 | */ 61 | typedef struct AcmDec { 62 | GstElement parent; 63 | 64 | GstPad *srcpad, *sinkpad; 65 | ACMStream *ctx; 66 | 67 | int fileofs; 68 | gboolean discont; 69 | 70 | int seek_to_pcm; 71 | usec_t last_seek_event_time; 72 | } AcmDec; 73 | 74 | typedef struct AcmDecClass { 75 | GstElementClass parent_class; 76 | } AcmDecClass; 77 | 78 | /* 79 | * define element class 80 | */ 81 | 82 | #define TYPE_ACMDEC (acmdec_get_type()) 83 | #define ACMDEC(o) G_TYPE_CHECK_INSTANCE_CAST((o),TYPE_ACMDEC,AcmDec) 84 | #define ACMDEC_CLASS(k) G_TYPE_CHECK_CLASS_CAST((k),TYPE_ACMDEC,AcmDecClass) 85 | #define IS_ACMDEC(o) G_TYPE_CHECK_INSTANCE_TYPE((o),TYPE_ACMDEC) 86 | #define IS_ACMDEC_CLASS(c) G_TYPE_CHECK_CLASS_TYPE((c),TYPE_ACMDEC) 87 | 88 | GST_BOILERPLATE (AcmDec, acmdec, GstElement, GST_TYPE_ELEMENT); 89 | 90 | static GstElementDetails acmdec_details = GST_ELEMENT_DETAILS( 91 | "acmdec", 92 | "Codec/Decoder/Audio", 93 | "InterPlay ACM Audio decoder", 94 | "Marko Kreen " 95 | ); 96 | 97 | /* 98 | * define plugin 99 | */ 100 | 101 | static void acmdec_detect_file(GstTypeFind *find, gpointer junk); 102 | 103 | static gboolean acmdec_plugin_init(GstPlugin *plugin) 104 | { 105 | static char *ext_list[] = {"acm", NULL}; 106 | GstCaps *caps = gst_caps_new_simple("audio/x-acm", NULL); 107 | 108 | if (!gst_element_register(plugin, "acmdec", GST_RANK_PRIMARY, TYPE_ACMDEC)) 109 | return FALSE; 110 | 111 | if (!gst_type_find_register(plugin, "type_acm", GST_RANK_PRIMARY, 112 | acmdec_detect_file, ext_list, caps, NULL, NULL)) 113 | return FALSE; 114 | 115 | GST_DEBUG_CATEGORY_INIT (acmdec_debug, "acmdec", 0, "ACM decoder"); 116 | return TRUE; 117 | } 118 | 119 | GST_PLUGIN_DEFINE( 120 | GST_VERSION_MAJOR, GST_VERSION_MINOR, 121 | "acmdec", 122 | "InterPlay ACM Audio Format", 123 | acmdec_plugin_init, 124 | LIBACM_VERSION, 125 | "LGPL", 126 | "libacm", 127 | "http://libacm.berlios.de/" 128 | ); 129 | 130 | /* 131 | * pad format details 132 | */ 133 | 134 | #define BASE_CAPS \ 135 | "audio/x-raw-int, " \ 136 | "endianness = (int) BYTE_ORDER, " \ 137 | "width = (int) 16, " \ 138 | "depth = (int) 16, " \ 139 | "signed = (bool) TRUE" 140 | 141 | static GstStaticPadTemplate acmdec_src_template = 142 | GST_STATIC_PAD_TEMPLATE( 143 | "src", GST_PAD_SRC, GST_PAD_ALWAYS, 144 | GST_STATIC_CAPS(BASE_CAPS ", " 145 | "rate = (int) [ 4000, 48000 ], " 146 | "channels = (int) [ 1, 2 ]") 147 | ); 148 | 149 | static GstStaticPadTemplate acmdec_sink_template = 150 | GST_STATIC_PAD_TEMPLATE ( 151 | "sink", GST_PAD_SINK, GST_PAD_ALWAYS, 152 | GST_STATIC_CAPS ("audio/x-acm")); 153 | 154 | 155 | /* 156 | * File type handler. 157 | */ 158 | 159 | static void acmdec_detect_file(GstTypeFind *find, gpointer junk) 160 | { 161 | guint8 *buf; 162 | static const guint8 acm_id[] = { 0x97, 0x28, 0x03 }; 163 | 164 | buf = gst_type_find_peek(find, 0, 3); 165 | if (!buf || memcmp(buf, acm_id, 3)) 166 | return; 167 | 168 | gst_type_find_suggest(find, GST_TYPE_FIND_MAXIMUM, 169 | gst_caps_new_simple ("audio/x-acm", NULL)); 170 | } 171 | 172 | /* 173 | * Fire reader with gst_pad_pull_range() 174 | */ 175 | 176 | static int acmdec_pull_read(void *dst, int size, int n, void *arg) 177 | { 178 | unsigned int got, need_bytes = size * n; 179 | AcmDec *acm = arg; 180 | GstBuffer *buf = NULL; 181 | GstFlowReturn flow; 182 | void *data; 183 | 184 | flow = gst_pad_pull_range(acm->sinkpad, acm->fileofs, need_bytes, &buf); 185 | if (flow != GST_FLOW_OK) { 186 | if (flow == GST_FLOW_UNEXPECTED) 187 | return 0; 188 | GST_ERROR_OBJECT(acm, "random flow from source: %d", flow); 189 | return -1; 190 | } 191 | 192 | data = GST_BUFFER_DATA(buf); 193 | got = GST_BUFFER_SIZE(buf); 194 | 195 | memcpy(dst, data, got); 196 | acm->fileofs += got; 197 | 198 | return got; 199 | } 200 | 201 | static int acmdec_pull_seek(void *arg, int ofs, int whence) 202 | { 203 | AcmDec *acm = arg; 204 | switch (whence) { 205 | case SEEK_SET: 206 | acm->fileofs = ofs; 207 | break; 208 | case SEEK_CUR: 209 | acm->fileofs += ofs; 210 | break; 211 | case SEEK_END: 212 | /* unsupported */ 213 | return -1; 214 | } 215 | return acm->fileofs; 216 | } 217 | 218 | 219 | static int acmdec_io_get_size(void *arg) 220 | { 221 | GstFormat fmt = GST_FORMAT_BYTES; 222 | gint64 len; 223 | AcmDec *acm = arg; 224 | GstPad *peer; 225 | gboolean ok; 226 | 227 | peer = gst_pad_get_peer(acm->sinkpad); 228 | if (!peer) 229 | return -1; 230 | ok = gst_pad_query_duration (peer, &fmt, &len); 231 | gst_object_unref (peer); 232 | 233 | if (!ok || fmt != GST_FORMAT_BYTES || len <= 0) 234 | return -1; 235 | return len; 236 | } 237 | 238 | static usec_t get_real_time(void) 239 | { 240 | struct timeval tv; 241 | gettimeofday(&tv, NULL); 242 | return USEC * (usec_t)tv.tv_sec + tv.tv_usec; 243 | } 244 | 245 | /* 246 | * Info sending. 247 | */ 248 | 249 | static void acmdec_post_tags(AcmDec *acm) 250 | { 251 | GstTagList *tags; 252 | GstMessage *msg; 253 | GST_DEBUG_OBJECT(acm, "sending tags"); 254 | tags = gst_tag_list_new(); 255 | gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, 256 | GST_TAG_AUDIO_CODEC, "ACM", NULL); 257 | gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, 258 | GST_TAG_BITRATE, acm_bitrate(acm->ctx), NULL); 259 | msg = gst_message_new_tag (GST_OBJECT (acm), tags); 260 | if (!gst_element_post_message (GST_ELEMENT (acm), msg)) 261 | GST_ERROR_OBJECT(acm, "failed to send tags"); 262 | } 263 | 264 | static void acmdec_send_segment(AcmDec *acm, gint64 pcmpos) 265 | { 266 | GstEvent *ev; 267 | guint64 start, stop, curpos; 268 | 269 | curpos = GST_SECOND * pcmpos / acm_rate(acm->ctx); 270 | stop = GST_SECOND * acm_time_total(acm->ctx) / 1000; 271 | start = curpos; 272 | 273 | GST_DEBUG_OBJECT(acm, "sending segment"); 274 | 275 | ev = gst_event_new_new_segment(FALSE, 1.0, GST_FORMAT_TIME, start, stop, curpos); 276 | if (!gst_pad_push_event(acm->srcpad, ev)) 277 | GST_DEBUG_OBJECT(acm, "sending segment failed"); 278 | else 279 | GST_DEBUG_OBJECT(acm, "sending segment done"); 280 | } 281 | 282 | static gboolean acmdec_convert(AcmDec *acm, GstFormat src_format, gint64 src_value, 283 | GstFormat *dest_format, gint64 *dest_value) 284 | { 285 | gboolean res = TRUE; 286 | guint64 pcmval; 287 | 288 | if (src_format == *dest_format || src_value <= 0) { 289 | *dest_value = src_value; 290 | return TRUE; 291 | } 292 | 293 | 294 | if (!acm->ctx) { 295 | GST_ERROR_OBJECT(acm, "no stream open"); 296 | return FALSE; 297 | } 298 | 299 | switch (src_format) { 300 | case GST_FORMAT_TIME: 301 | pcmval = src_value * acm_rate(acm->ctx) / GST_SECOND; 302 | break; 303 | case GST_FORMAT_BYTES: 304 | pcmval = src_value / (2 * acm_channels(acm->ctx)); 305 | break; 306 | case GST_FORMAT_DEFAULT: 307 | pcmval = src_value; 308 | break; 309 | default: 310 | goto no_format; 311 | } 312 | 313 | switch (*dest_format) { 314 | case GST_FORMAT_BYTES: 315 | *dest_value = pcmval * 2 * acm_channels(acm->ctx); 316 | break; 317 | case GST_FORMAT_DEFAULT: 318 | *dest_value = pcmval; 319 | break; 320 | case GST_FORMAT_TIME: 321 | *dest_value = GST_SECOND * pcmval / acm_rate(acm->ctx); 322 | break; 323 | default: 324 | goto no_format; 325 | } 326 | 327 | done: 328 | return res; 329 | 330 | no_format: 331 | GST_ERROR_OBJECT(acm, "formats unsupported"); 332 | res = FALSE; 333 | goto done; 334 | } 335 | 336 | /* 337 | * Object init 338 | */ 339 | 340 | static gboolean acmdec_init_decoder(AcmDec *acm) 341 | { 342 | static const acm_io_callbacks pull_cb = { 343 | .read_func = acmdec_pull_read, 344 | .seek_func = acmdec_pull_seek, 345 | .get_length_func = acmdec_io_get_size, 346 | }; 347 | 348 | int res; 349 | GstCaps *caps; 350 | 351 | GST_DEBUG_OBJECT(acm, "init decoder"); 352 | res = acm_open_decoder(&acm->ctx, acm, pull_cb, 0); 353 | if (res < 0) { 354 | GST_DEBUG_OBJECT(acm, "decoder init failed: %s", acm_strerror(res)); 355 | return FALSE; 356 | } 357 | GST_DEBUG_OBJECT(acm, "size=%d samples=%d", acm->ctx->data_len, acm->ctx->total_values); 358 | 359 | caps = gst_caps_from_string(BASE_CAPS); 360 | gst_caps_set_simple(caps, 361 | "channels", G_TYPE_INT, acm_channels(acm->ctx), 362 | "rate", G_TYPE_INT, acm_rate(acm->ctx), NULL); 363 | gst_pad_use_fixed_caps(acm->srcpad); 364 | if (!gst_pad_set_caps(acm->srcpad, caps)) { 365 | gst_caps_unref(caps); 366 | GST_ERROR_OBJECT(acm, "failed to set caps"); 367 | return FALSE; 368 | } 369 | gst_caps_unref(caps); 370 | 371 | acmdec_post_tags(acm); 372 | 373 | return TRUE; 374 | } 375 | 376 | static void acmdec_reset(AcmDec *acm) 377 | { 378 | GST_DEBUG_OBJECT(acm, "do reset"); 379 | if (acm->ctx) { 380 | acm_close(acm->ctx); 381 | acm->ctx = NULL; 382 | } 383 | 384 | acm->fileofs = 0; 385 | acm->discont = TRUE; 386 | acm->seek_to_pcm = -1; 387 | acm->last_seek_event_time = 0; 388 | } 389 | 390 | static void acmdec_dispose(GObject *obj) 391 | { 392 | AcmDec *acm = ACMDEC(obj); 393 | 394 | acmdec_reset(acm); 395 | 396 | G_OBJECT_CLASS (parent_class)->dispose (obj); 397 | } 398 | 399 | 400 | /* 401 | * srcpad methods 402 | */ 403 | 404 | static const GstQueryType * 405 | acmdec_src_query_types (GstPad *pad) 406 | { 407 | static const GstQueryType speex_dec_src_query_types[] = { 408 | GST_QUERY_POSITION, GST_QUERY_DURATION, GST_QUERY_CONVERT, 0 }; 409 | return speex_dec_src_query_types; 410 | } 411 | 412 | 413 | static gboolean 414 | acmdec_src_query (GstPad *pad, GstQuery *query) 415 | { 416 | AcmDec *acm = ACMDEC(gst_pad_get_parent(pad)); /* incref */ 417 | gboolean res = FALSE; 418 | gint64 pcmval, val, dest_val; 419 | GstFormat fmt = GST_FORMAT_TIME, dest_fmt = 0; 420 | 421 | //GST_DEBUG_OBJECT(acm, "src_query: %s", GST_QUERY_TYPE_NAME (query)); 422 | 423 | switch (GST_QUERY_TYPE (query)) { 424 | case GST_QUERY_POSITION: 425 | gst_query_parse_position (query, &fmt, NULL); 426 | if (acm->seek_to_pcm >= 0) 427 | pcmval = acm->seek_to_pcm; 428 | else 429 | pcmval = acm_pcm_tell(acm->ctx); 430 | res = acmdec_convert(acm, GST_FORMAT_DEFAULT, pcmval, &fmt, &val); 431 | if (res) 432 | gst_query_set_position(query, fmt, val); 433 | break; 434 | case GST_QUERY_DURATION: 435 | gst_query_parse_duration (query, &fmt, NULL); 436 | pcmval = acm_pcm_total(acm->ctx); 437 | res = acmdec_convert(acm, GST_FORMAT_DEFAULT, pcmval, &fmt, &val); 438 | if (res) 439 | gst_query_set_duration(query, fmt, val); 440 | break; 441 | case GST_QUERY_CONVERT: 442 | gst_query_parse_convert (query, &fmt, &val, &dest_fmt, NULL); 443 | res = acmdec_convert(acm, fmt, val, &dest_fmt, &dest_val); 444 | if (res) 445 | gst_query_set_convert(query, fmt, val, dest_fmt, dest_val); 446 | break; 447 | default: 448 | res = gst_pad_query_default (pad, query); 449 | break; 450 | } 451 | 452 | gst_object_unref (acm); 453 | return res; 454 | } 455 | 456 | static gboolean handle_seek(AcmDec *acm, GstEvent *event) 457 | { 458 | GstFormat format, tformat; 459 | gdouble rate; 460 | GstSeekFlags flags; 461 | GstSeekType cur_type, stop_type; 462 | gint64 cur, stop, pcmpos; 463 | 464 | gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, 465 | &stop_type, &stop); 466 | GST_DEBUG_OBJECT(acm, "do_seek: rate=%0.1f format=%d seek_flags=%d" 467 | " curtype=%d stoptype=%d curpos=%lld stop_pos=%lld", 468 | rate, format, flags, cur_type, stop_type, (long long)cur, (long long)stop); 469 | if (format == GST_FORMAT_TIME) { 470 | GST_DEBUG_OBJECT(acm, "fmt as time=%" GST_TIME_FORMAT, GST_TIME_ARGS(cur)); 471 | } 472 | 473 | if (flags & GST_SEEK_FLAG_SEGMENT) { 474 | GST_WARNING_OBJECT(acm, "GST_SEEK_FLAG_SEGMENT not supported"); 475 | return FALSE; 476 | } 477 | if (!(flags & GST_SEEK_FLAG_FLUSH)) { 478 | GST_WARNING_OBJECT(acm, "!GST_SEEK_FLAG_FLUSH not supported"); 479 | return FALSE; 480 | } 481 | if (stop_type) { 482 | GST_WARNING_OBJECT(acm, "seek with stop_pos not supported"); 483 | return FALSE; 484 | } 485 | if (cur_type != GST_SEEK_TYPE_SET) { 486 | GST_WARNING_OBJECT(acm, "seek with cur_type = CUR/END not supported"); 487 | return FALSE; 488 | } 489 | 490 | tformat = GST_FORMAT_DEFAULT; 491 | if (!acmdec_convert (acm, format, cur, &tformat, &pcmpos)) 492 | return FALSE; 493 | 494 | if (pcmpos != acm_rate(acm->ctx) * cur / GST_SECOND) { 495 | GST_ERROR_OBJECT(acm, "do_seek: bad conversion"); 496 | } 497 | 498 | GST_DEBUG_OBJECT(acm, "do_seek: newpos=%llu curpos=%d", 499 | pcmpos, (int)acm_pcm_tell(acm->ctx)); 500 | 501 | /* 502 | * Set seek pos. Lock as touched from play thread too. 503 | */ 504 | 505 | GST_OBJECT_LOCK(acm); 506 | acm->seek_to_pcm = pcmpos; 507 | acm->last_seek_event_time = get_real_time(); 508 | GST_OBJECT_UNLOCK(acm); 509 | 510 | return TRUE; 511 | } 512 | 513 | static gboolean acmdec_src_event(GstPad *pad, GstEvent *event) 514 | { 515 | AcmDec *acm = ACMDEC(gst_pad_get_parent(pad)); /* incref */ 516 | gboolean res = FALSE; 517 | 518 | 519 | switch (GST_EVENT_TYPE (event)) { 520 | case GST_EVENT_SEEK: 521 | res = handle_seek(acm, event); 522 | gst_event_unref(event); 523 | break; 524 | default: 525 | GST_DEBUG_OBJECT (acm, "src received %s event", GST_EVENT_TYPE_NAME (event)); 526 | case GST_EVENT_QOS: 527 | case GST_EVENT_NAVIGATION: 528 | res = gst_pad_event_default (pad, event); 529 | break; 530 | } 531 | gst_object_unref(acm); 532 | return res; 533 | } 534 | 535 | /* 536 | * new block of data. 537 | */ 538 | 539 | static gboolean acmdec_src_check_get_range (GstPad *pad) 540 | { 541 | GST_DEBUG_OBJECT(GST_PAD_PARENT(pad), "check_get_range"); 542 | return TRUE; 543 | } 544 | 545 | // GST_FLOW_UNEXPECTED -> EOS 546 | static GstFlowReturn acmdec_src_get_range(GstPad *srcpad, guint64 offset, guint size, GstBuffer **buf) 547 | { 548 | AcmDec *acm = ACMDEC(GST_PAD_PARENT(srcpad)); 549 | unsigned int req_pos; 550 | int got, frame; 551 | gint64 pcmpos, pcmlen; 552 | GstFlowReturn flow = GST_FLOW_ERROR; 553 | 554 | *buf = NULL; 555 | 556 | //GST_DEBUG_OBJECT(acm, "get_range"); 557 | 558 | if (!acm->ctx) { 559 | if (!acmdec_init_decoder(acm)) { 560 | GST_ERROR_OBJECT(acm, "cannot initialize stream"); 561 | goto error; 562 | } 563 | } 564 | 565 | frame = ACM_WORD * acm_channels(acm->ctx); 566 | if (offset % frame || size % frame) { 567 | GST_ERROR_OBJECT(acm, "request not multiple of frame (%d)", frame); 568 | goto error; 569 | } 570 | 571 | req_pos = offset / frame; 572 | if (acm_pcm_tell(acm->ctx) != req_pos) { 573 | GST_INFO_OBJECT(acm, "seeking: cur=%d, new=%d", acm_pcm_tell(acm->ctx), req_pos); 574 | if (acm_seek_pcm(acm->ctx, req_pos) < 0) { 575 | GST_ERROR_OBJECT(acm, "seek failed"); 576 | goto error; 577 | } 578 | if (acm_pcm_tell(acm->ctx) != req_pos) { 579 | GST_ERROR_OBJECT(acm, "seek failed to reach right pos"); 580 | goto error; 581 | } 582 | } 583 | 584 | flow = gst_pad_alloc_buffer_and_set_caps(srcpad, GST_CLOCK_TIME_NONE, 585 | size, GST_PAD_CAPS(srcpad), buf); 586 | if (flow != GST_FLOW_OK) 587 | goto error; 588 | 589 | pcmpos = acm_pcm_tell(acm->ctx); 590 | got = acm_read_loop(acm->ctx, GST_BUFFER_DATA(*buf), size, ACM_NATIVE_BE, 2, 1); 591 | if (got < 0) { 592 | GST_ERROR_OBJECT(acm, "acm_read_loop: %s", acm_strerror(got)); 593 | /* tag it still as EOS */ 594 | flow = GST_FLOW_UNEXPECTED; 595 | goto error; 596 | } else if (got == 0) { 597 | /* EOS */ 598 | flow = GST_FLOW_UNEXPECTED; 599 | goto error; 600 | } 601 | pcmlen = got / frame; 602 | GST_BUFFER_SIZE (*buf) = got; 603 | GST_BUFFER_TIMESTAMP (*buf) = GST_SECOND * pcmpos / acm_rate(acm->ctx); 604 | GST_BUFFER_DURATION (*buf) = GST_SECOND * pcmlen / acm_rate(acm->ctx); 605 | 606 | return GST_FLOW_OK; 607 | 608 | error: 609 | if (*buf) { 610 | gst_buffer_unref(*buf); 611 | *buf = NULL; 612 | } 613 | return flow; 614 | } 615 | 616 | /* 617 | * Sinkpad methods. If downstream is push-based, 618 | * launch separate thread for pull->push handling. 619 | */ 620 | 621 | static void do_real_seek(AcmDec *acm) 622 | { 623 | int seek_to_pcm; 624 | usec_t seek_time; 625 | 626 | GST_DEBUG_OBJECT(acm, "do_real_seek"); 627 | 628 | GST_OBJECT_LOCK(acm); 629 | seek_to_pcm = acm->seek_to_pcm; 630 | seek_time = acm->last_seek_event_time; 631 | GST_OBJECT_UNLOCK(acm); 632 | 633 | if (seek_to_pcm < 0) 634 | return; 635 | if (seek_time + ACMDEC_SEEK_WAIT > get_real_time()) 636 | return; 637 | 638 | gst_pad_push_event (acm->srcpad, gst_event_new_flush_start ()); 639 | 640 | if (acm_seek_pcm(acm->ctx, seek_to_pcm) < 0) { 641 | GST_ERROR_OBJECT(acm, "acm_seek_pcm failed"); 642 | } 643 | 644 | GST_DEBUG_OBJECT(acm, "reached seek pos at %d", (int)acm_pcm_tell(acm->ctx)); 645 | GST_OBJECT_LOCK(acm); 646 | acm->seek_to_pcm = -1; 647 | acm->discont = TRUE; 648 | GST_OBJECT_UNLOCK(acm); 649 | 650 | gst_pad_push_event(acm->srcpad, gst_event_new_flush_stop()); 651 | } 652 | 653 | static void acmdec_sink_loop(void *arg) 654 | { 655 | GstPad *sinkpad = (GstPad *)arg; 656 | AcmDec *acm = ACMDEC(GST_PAD_PARENT(sinkpad)); 657 | GstFlowReturn flow; 658 | gint64 size, offset, pcmpos; 659 | GstBuffer *buf = NULL; 660 | int frame; 661 | 662 | if (!acm->ctx) { 663 | if (!acmdec_init_decoder(acm)) { 664 | GST_ERROR_OBJECT(acm, "cannot initialize stream"); 665 | goto pause; 666 | } 667 | } 668 | 669 | if (acm->seek_to_pcm >= 0) 670 | do_real_seek(acm); 671 | 672 | frame = ACM_WORD * acm_channels(acm->ctx); 673 | pcmpos = acm_pcm_tell(acm->ctx); 674 | offset = pcmpos * frame; 675 | size = acm->ctx->block_len * frame; 676 | 677 | flow = acmdec_src_get_range(acm->srcpad, offset, size, &buf); 678 | if (flow == GST_FLOW_UNEXPECTED) 679 | goto eos_and_pause; 680 | else if (flow != GST_FLOW_OK) { 681 | GST_DEBUG_OBJECT (acm, "get_range failed: %s", gst_flow_get_name (flow)); 682 | goto pause; 683 | } 684 | 685 | if (acm->discont) { 686 | acmdec_send_segment(acm, pcmpos); 687 | 688 | buf = gst_buffer_make_metadata_writable(buf); 689 | GST_BUFFER_FLAG_SET(buf, GST_BUFFER_FLAG_DISCONT); 690 | acm->discont = FALSE; 691 | } 692 | 693 | flow = gst_pad_push(acm->srcpad, buf); 694 | if (flow != GST_FLOW_OK) { 695 | GST_DEBUG_OBJECT(acm, "gst_pad_push failed: %s", gst_flow_get_name(flow)); 696 | goto pause; 697 | } 698 | return; 699 | 700 | eos_and_pause: 701 | GST_DEBUG_OBJECT(acm, "EOS"); 702 | gst_pad_push_event(acm->srcpad, gst_event_new_eos()); 703 | pause: 704 | GST_DEBUG_OBJECT(acm, "pausing task"); 705 | gst_pad_pause_task(acm->sinkpad); 706 | } 707 | 708 | 709 | static gboolean acmdec_sink_activate_pull (GstPad *sinkpad, gboolean active) 710 | { 711 | GST_DEBUG_OBJECT(GST_PAD_PARENT(sinkpad), "activate_pull: %d", active); 712 | if (active) { 713 | return gst_pad_start_task(sinkpad, acmdec_sink_loop, sinkpad); 714 | } else { 715 | return gst_pad_stop_task(sinkpad); 716 | } 717 | } 718 | 719 | static gboolean acmdec_sink_activate (GstPad *sinkpad) 720 | { 721 | GST_DEBUG_OBJECT(GST_PAD_PARENT(sinkpad), "sink_activate"); 722 | if (gst_pad_check_pull_range(sinkpad)) 723 | return gst_pad_activate_pull(sinkpad, TRUE); 724 | return FALSE; 725 | } 726 | 727 | 728 | static gboolean acmdec_sink_activate_push(GstPad *pad, gboolean active) 729 | { 730 | GST_DEBUG_OBJECT(GST_PAD_PARENT(pad), "push active = %d", active); 731 | return FALSE; 732 | } 733 | 734 | /* 735 | * change state 736 | */ 737 | 738 | static GstStateChangeReturn acmdec_change_state (GstElement *elem, GstStateChange transition) 739 | { 740 | GstStateChangeReturn ret; 741 | AcmDec *acm = ACMDEC(elem); 742 | 743 | GST_DEBUG_OBJECT(acm, "change_state: from=%s to=%s", 744 | gst_element_state_get_name( 745 | GST_STATE_TRANSITION_CURRENT(transition)), 746 | gst_element_state_get_name( 747 | GST_STATE_TRANSITION_NEXT(transition))); 748 | 749 | ret = parent_class->change_state(elem, transition); 750 | if (ret != GST_STATE_CHANGE_SUCCESS) { 751 | GST_DEBUG_OBJECT(acm, "parent change_state: %s", 752 | gst_element_state_change_return_get_name(ret)); 753 | return ret; 754 | } 755 | 756 | if (transition == GST_STATE_CHANGE_PAUSED_TO_READY) 757 | acmdec_reset(acm); 758 | 759 | return ret; 760 | } 761 | 762 | /* 763 | * GST initalization. 764 | */ 765 | 766 | static void acmdec_init(AcmDec *acm, AcmDecClass *klass) 767 | { 768 | GstPad *src, *sink; 769 | 770 | GST_DEBUG_OBJECT(acm, "acmdec_init"); 771 | 772 | acmdec_reset(acm); 773 | 774 | sink = gst_pad_new_from_static_template (&acmdec_sink_template, "sink"); 775 | gst_pad_set_activate_function (sink, FN(acmdec_sink_activate)); 776 | gst_pad_set_activatepull_function (sink, FN(acmdec_sink_activate_pull)); 777 | gst_pad_set_activatepush_function (sink, FN(acmdec_sink_activate_push)); 778 | gst_element_add_pad (GST_ELEMENT (acm), sink); 779 | acm->sinkpad = sink; 780 | 781 | src = gst_pad_new_from_static_template (&acmdec_src_template, "src"); 782 | gst_pad_use_fixed_caps (src); 783 | gst_pad_set_event_function (src, FN(acmdec_src_event)); 784 | gst_pad_set_query_type_function (src, FN(acmdec_src_query_types)); 785 | gst_pad_set_query_function (src, FN(acmdec_src_query)); 786 | /* do those get actually used? */ 787 | gst_pad_set_checkgetrange_function (src, FN(acmdec_src_check_get_range)); 788 | gst_pad_set_getrange_function (src, FN(acmdec_src_get_range)); 789 | gst_element_add_pad (GST_ELEMENT (acm), src); 790 | 791 | acm->srcpad = src; 792 | } 793 | 794 | static void acmdec_class_init(AcmDecClass *acm_class) 795 | { 796 | GstElementClass *elem_class = GST_ELEMENT_CLASS(acm_class); 797 | GObjectClass *gobj_class = G_OBJECT_CLASS(acm_class); 798 | 799 | elem_class->change_state = FN(acmdec_change_state); 800 | gobj_class->dispose = FN(acmdec_dispose); 801 | } 802 | 803 | static void acmdec_base_init(gpointer klass) 804 | { 805 | GstElementClass *element_class = GST_ELEMENT_CLASS(klass); 806 | 807 | gst_element_class_add_pad_template(element_class, 808 | gst_static_pad_template_get(&acmdec_src_template)); 809 | gst_element_class_add_pad_template(element_class, 810 | gst_static_pad_template_get(&acmdec_sink_template)); 811 | 812 | gst_element_class_set_details(element_class, &acmdec_details); 813 | } 814 | 815 | -------------------------------------------------------------------------------- /plugins/plugin-winamp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libacm plugin for Winamp 3 | * 4 | * Copyright (C) 2004-2008 Marko Kreen 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include "winamp.h" 25 | #include "libacm.h" 26 | 27 | /* 576 samples seems preferred by winamp */ 28 | #define SBLOCK 576 29 | 30 | /* eof msg to winamp */ 31 | #define WM_AMP_EOF (WM_USER + 2) 32 | 33 | typedef struct { 34 | ACMStream *acm; 35 | int paused; 36 | int seek_to; 37 | char *filename; 38 | } ACMInput; 39 | 40 | static ACMInput in; 41 | static In_Module *plugin; 42 | 43 | static int dec_quit = 0; 44 | static HANDLE dec_thread = INVALID_HANDLE_VALUE; 45 | 46 | /* 47 | * module functions 48 | */ 49 | static void init() 50 | { 51 | } 52 | 53 | static void quit() 54 | { 55 | } 56 | 57 | static void config(HWND hwndParent) 58 | { 59 | MessageBox(hwndParent, "No configuration.", 60 | "InterPlay ACM Audio Decoder", MB_OK); 61 | } 62 | 63 | static int is_our_file(char *fn) 64 | { 65 | /* used for detecting URL streams.. unused here. */ 66 | return 0; 67 | } 68 | 69 | static void get_song_info(char *filename, char *title, int *length_in_ms) 70 | { 71 | char *fn, *p; 72 | ACMStream *acm; 73 | 74 | if (filename && *filename) { 75 | if (acm_open_file(&acm, filename, 0) < 0) 76 | return; 77 | fn = filename; 78 | } else { 79 | acm = in.acm; 80 | fn = in.filename; 81 | } 82 | if ((p = strrchr(fn, '\\')) != NULL) 83 | strcpy(title, p + 1); 84 | else 85 | strcpy(title, fn); 86 | 87 | *length_in_ms = acm_time_total(acm); 88 | 89 | if (filename && *filename) 90 | acm_close(acm); 91 | } 92 | 93 | static void pause() 94 | { 95 | in.paused = 1; 96 | plugin->outMod->Pause(1); 97 | } 98 | 99 | static void unpause() 100 | { 101 | in.paused = 0; 102 | plugin->outMod->Pause(0); 103 | } 104 | 105 | static int ispaused() 106 | { 107 | return in.paused; 108 | } 109 | 110 | static int getlength() 111 | { 112 | return acm_time_total(in.acm); 113 | } 114 | 115 | static int get_time() 116 | { 117 | int d; 118 | if (in.seek_to >= 0) 119 | return in.seek_to; 120 | d = plugin->outMod->GetWrittenTime() - plugin->outMod->GetOutputTime(); 121 | return acm_time_tell(in.acm) - d; 122 | /* return plugin->outMod->GetOutputTime(); */ 123 | } 124 | 125 | static void setoutputtime(int ms) 126 | { 127 | in.seek_to = ms; 128 | } 129 | 130 | static void setvolume(int vol) 131 | { 132 | plugin->outMod->SetVolume(vol); 133 | } 134 | 135 | static void setpan(int pan) 136 | { 137 | plugin->outMod->SetPan(pan); 138 | } 139 | 140 | static int read_and_play(char *buf) { 141 | int pos_ms, blen, need_len, res, snum; 142 | 143 | /* check if buffer available */ 144 | blen = need_len = SBLOCK * acm_channels(in.acm) * ACM_WORD; 145 | if (plugin->dsp_isactive()) 146 | need_len *= 2; 147 | if (plugin->outMod->CanWrite() < need_len) { 148 | Sleep(20); 149 | return 0; 150 | } 151 | 152 | /* load samples */ 153 | res = acm_read_loop(in.acm, buf, blen, 0,2,1); 154 | if (res <= 0) 155 | return 1; 156 | snum = res / (acm_channels(in.acm) * ACM_WORD); 157 | 158 | /* vis seems ignored when dsp is on */ 159 | pos_ms = plugin->outMod->GetWrittenTime(); 160 | plugin->SAAddPCMData(buf, acm_channels(in.acm), ACM_WORD*8, pos_ms); 161 | plugin->VSAAddPCMData(buf, acm_channels(in.acm), ACM_WORD*8, pos_ms); 162 | 163 | /* apply effects if needed */ 164 | if (plugin->dsp_isactive()) { 165 | snum = plugin->dsp_dosamples((short*)buf, snum, ACM_WORD*8, 166 | acm_channels(in.acm), acm_rate(in.acm)); 167 | } 168 | 169 | /* output */ 170 | plugin->outMod->Write((char*)buf, res); 171 | return 0; 172 | } 173 | 174 | static int try_seeking() 175 | { 176 | int eof = 1, pos; 177 | pos = acm_seek_time(in.acm, in.seek_to); 178 | if (pos >= 0) { 179 | plugin->outMod->Flush(acm_time_tell(in.acm)); 180 | eof = in.paused = 0; 181 | } 182 | in.seek_to = -1; 183 | return eof; 184 | } 185 | 186 | static DWORD WINAPI __stdcall decode_thread(void *arg) 187 | { 188 | int eof = 0; 189 | char *buf; 190 | 191 | buf = malloc(SBLOCK * acm_channels(in.acm) * ACM_WORD); 192 | 193 | while (!dec_quit) { 194 | if (in.seek_to >= 0) 195 | eof = try_seeking(); 196 | 197 | if (!eof) { 198 | eof = read_and_play(buf); 199 | continue; 200 | } 201 | 202 | /* eof, wait until buffer empty before notifying */ 203 | plugin->outMod->CanWrite(); 204 | if (!plugin->outMod->IsPlaying()) { 205 | PostMessage(plugin->hMainWindow, WM_AMP_EOF, 0, 0); 206 | free(buf); 207 | return 0; 208 | } 209 | Sleep(10); 210 | } 211 | free(buf); 212 | return 0; 213 | } 214 | 215 | static int play(char *fn) 216 | { 217 | ACMStream *acm; 218 | DWORD thread_id; 219 | int err, latency; 220 | 221 | if ((err = acm_open_file(&acm, fn, 0)) < 0) 222 | return 1; 223 | 224 | latency = plugin->outMod->Open(acm_rate(acm), acm_channels(acm), 225 | ACM_WORD*8, -1,-1); 226 | if (latency < 0) { 227 | /* error opening device */ 228 | acm_close(acm); 229 | return 1; 230 | } 231 | in.acm = acm; 232 | in.filename = strdup(fn); 233 | in.paused = 0; 234 | in.seek_to = -1; 235 | 236 | plugin->SetInfo(acm_bitrate(acm) / 1000, 237 | acm_rate(acm) / 1000, acm_channels(acm), 1); 238 | 239 | /* initialize vis stuff */ 240 | plugin->SAVSAInit(latency, acm_rate(acm)); 241 | plugin->VSASetInfo(acm_rate(acm), acm_channels(acm)); 242 | 243 | /* set the output plug-ins default volume */ 244 | plugin->outMod->SetVolume(-666); 245 | 246 | dec_quit = 0; 247 | dec_thread = CreateThread(NULL, 0, decode_thread, NULL, 0, &thread_id); 248 | 249 | return 0; 250 | } 251 | 252 | static void stop() 253 | { 254 | if (dec_thread != INVALID_HANDLE_VALUE) { 255 | dec_quit = 1; 256 | if (WaitForSingleObject(dec_thread,INFINITE) == WAIT_TIMEOUT) { 257 | MessageBox(plugin->hMainWindow, 258 | "error asking thread to die!\n", 259 | "error killing decode thread", 0); 260 | TerminateThread(dec_thread, 0); 261 | } 262 | CloseHandle(dec_thread); 263 | dec_thread = INVALID_HANDLE_VALUE; 264 | } 265 | acm_close(in.acm); 266 | free(in.filename); 267 | 268 | plugin->outMod->Close(); 269 | plugin->SAVSADeInit(); 270 | } 271 | 272 | static int file_info_box(char *fn, HWND hwnd) 273 | { 274 | char buf[1024]; 275 | ACMStream *acm; 276 | int err, kbps, secs; 277 | const ACMInfo *inf; 278 | 279 | err = acm_open_file(&acm, fn, 0); 280 | if (err < 0) 281 | return 1; 282 | 283 | kbps = acm_bitrate(acm) / 1000; 284 | secs = acm_time_total(acm) / 1000; 285 | inf = acm_info(acm); 286 | 287 | sprintf(buf, "%s\n\n" 288 | "Length: %d:%02d\n" 289 | "Samples: %d\n" 290 | "Samplerate: %d Hz\n" 291 | "Channels: %d\n" 292 | "Avg. bitrate: %d kbps\n\n" 293 | "ACM subblock len=%d\n" 294 | "ACM num subblocks=%d\n" 295 | "ACM block=%d\n", 296 | fn, secs/60, secs % 60, 297 | acm_pcm_total(acm), inf->rate, 298 | inf->channels, kbps, 299 | inf->acm_cols, inf->acm_rows, 300 | inf->acm_cols * inf->acm_rows); 301 | acm_close(acm); 302 | 303 | MessageBox(hwnd, buf, "InterPlay ACM Audio file", MB_OK); 304 | return 0; 305 | } 306 | 307 | static void about(HWND hwndParent) 308 | { 309 | MessageBox(hwndParent, 310 | "InterPlay ACM Audio Decoder - " 311 | "libacm " LIBACM_VERSION "\n\n" 312 | "Homepage: http://libacm.berlios.de/\n" 313 | "\n", 314 | "About", MB_OK); 315 | } 316 | 317 | static In_Module _plugin = 318 | { 319 | IN_VER, 320 | "InterPlay ACM Audio Decoder (libacm " LIBACM_VERSION ")", 321 | 0, /* hMainWindow */ 322 | 0, /* hDllInstance */ 323 | "ACM\0ACM Audio File (*.ACM)\0", 324 | 1, /* is_seekable */ 325 | 1, /* uses output */ 326 | config, 327 | about, 328 | init, 329 | quit, 330 | get_song_info, 331 | file_info_box, 332 | is_our_file, 333 | play, 334 | pause, 335 | unpause, 336 | ispaused, 337 | stop, 338 | getlength, 339 | get_time, 340 | setoutputtime, 341 | setvolume, 342 | setpan, 343 | 344 | 0,0,0,0,0,0,0,0,0, /* vis stuff */ 345 | 0, 0, /* dsp */ 346 | NULL, /* eq_set */ 347 | NULL, /* setinfo */ 348 | 0 /* out_mod */ 349 | }; 350 | 351 | __declspec(dllexport) In_Module *winampGetInModule2() 352 | { 353 | plugin = &_plugin; 354 | return plugin; 355 | } 356 | 357 | -------------------------------------------------------------------------------- /plugins/plugin-xmms2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libacm plugin for XMMS2 3 | * 4 | * Copyright (C) 2011 Marko Kreen 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include "config.h" 23 | #endif 24 | 25 | #include "xmms/xmms_xformplugin.h" 26 | #include "xmms/xmms_log.h" 27 | #include "xmms/xmms_medialib.h" 28 | 29 | #include 30 | #include 31 | 32 | #include "libacm.h" 33 | 34 | struct XmmsACM { 35 | ACMStream *acm; 36 | xmms_error_t *error; 37 | }; 38 | 39 | /* 40 | * callbacks for libacm 41 | */ 42 | 43 | static int acmio_filesize(void *ctx) 44 | { 45 | xmms_xform_t *xform = ctx; 46 | int fsize = 0; 47 | 48 | if (!xmms_xform_metadata_get_int (xform, 49 | XMMS_MEDIALIB_ENTRY_PROPERTY_SIZE, 50 | &fsize)) 51 | fsize = -1; 52 | return fsize; 53 | } 54 | 55 | static int acmio_read(void *ptr, int size, int n, void *ctx) 56 | { 57 | xmms_xform_t *xform = ctx; 58 | struct XmmsACM *priv; 59 | int res; 60 | 61 | g_return_val_if_fail (xform, -1); 62 | 63 | priv = xmms_xform_private_data_get (xform); 64 | g_return_val_if_fail (priv, -1); 65 | 66 | res = xmms_xform_read (xform, ptr, n*size, priv->error); 67 | return res; 68 | } 69 | 70 | static int acmio_seek(void *ctx, int offset, int whence) 71 | { 72 | xmms_xform_t *xform = ctx; 73 | struct XmmsACM *priv; 74 | int res; 75 | xmms_xform_seek_mode_t xwhence; 76 | 77 | g_return_val_if_fail (xform, -1); 78 | 79 | priv = xmms_xform_private_data_get (xform); 80 | g_return_val_if_fail (priv, -1); 81 | 82 | switch (whence) { 83 | case SEEK_SET: xwhence = XMMS_XFORM_SEEK_SET; break; 84 | case SEEK_CUR: xwhence = XMMS_XFORM_SEEK_CUR; break; 85 | case SEEK_END: xwhence = XMMS_XFORM_SEEK_END; break; 86 | default: 87 | return -1; 88 | } 89 | 90 | res = xmms_xform_seek (xform, offset, xwhence, priv->error); 91 | return res; 92 | } 93 | 94 | static const acm_io_callbacks acmio = { 95 | .read_func = acmio_read, 96 | .seek_func = acmio_seek, 97 | .close_func = NULL, 98 | .get_length_func = acmio_filesize, 99 | }; 100 | 101 | /* 102 | * callbacks for XMMS2 103 | */ 104 | 105 | static gboolean 106 | xmms_acm_init (xmms_xform_t *xform) 107 | { 108 | int err; 109 | struct XmmsACM *priv; 110 | xmms_error_t errbuf; 111 | 112 | g_return_val_if_fail (xform, FALSE); 113 | 114 | priv = g_new0 (struct XmmsACM, 1); 115 | g_return_val_if_fail (priv, FALSE); 116 | xmms_xform_private_data_set (xform, priv); 117 | 118 | /* open acm */ 119 | memset (&errbuf, 0, sizeof(errbuf)); 120 | priv->error = &errbuf; 121 | err = acm_open_decoder (&priv->acm, xform, acmio, 0); 122 | if (err != 0) { 123 | xmms_log_error("acm_open_decoder failed"); 124 | return FALSE; 125 | } 126 | 127 | /* set metainfo */ 128 | xmms_xform_metadata_set_int (xform, 129 | XMMS_MEDIALIB_ENTRY_PROPERTY_DURATION, 130 | acm_time_total (priv->acm)); 131 | 132 | xmms_xform_metadata_set_int (xform, 133 | XMMS_MEDIALIB_ENTRY_PROPERTY_BITRATE, 134 | acm_bitrate (priv->acm)); 135 | 136 | /* set output format */ 137 | xmms_xform_outdata_type_add (xform, 138 | XMMS_STREAM_TYPE_MIMETYPE, 139 | "audio/pcm", 140 | XMMS_STREAM_TYPE_FMT_FORMAT, 141 | XMMS_SAMPLE_FORMAT_S16, 142 | XMMS_STREAM_TYPE_FMT_CHANNELS, 143 | acm_channels (priv->acm), 144 | XMMS_STREAM_TYPE_FMT_SAMPLERATE, 145 | acm_rate (priv->acm), 146 | XMMS_STREAM_TYPE_END); 147 | 148 | return TRUE; 149 | } 150 | 151 | static void 152 | xmms_acm_destroy (xmms_xform_t *xform) 153 | { 154 | struct XmmsACM *priv; 155 | if (xform) { 156 | priv = xmms_xform_private_data_get (xform); 157 | if (priv) { 158 | acm_close (priv->acm); 159 | g_free (priv); 160 | } 161 | } 162 | } 163 | 164 | static gint 165 | xmms_acm_read (xmms_xform_t *xform, 166 | xmms_sample_t *buf, 167 | gint len, 168 | xmms_error_t *error) 169 | { 170 | struct XmmsACM *priv; 171 | 172 | g_return_val_if_fail (xform, -1); 173 | 174 | priv = xmms_xform_private_data_get (xform); 175 | g_return_val_if_fail (priv, -1); 176 | 177 | priv->error = error; 178 | return acm_read_loop (priv->acm, buf, len, 0, 2, 1); 179 | } 180 | 181 | static gint64 182 | xmms_acm_seek (xmms_xform_t *xform, 183 | gint64 samples, 184 | xmms_xform_seek_mode_t whence, 185 | xmms_error_t *error) 186 | { 187 | struct XmmsACM *priv; 188 | gint64 pos; 189 | 190 | g_return_val_if_fail (xform, -1); 191 | 192 | priv = xmms_xform_private_data_get (xform); 193 | g_return_val_if_fail (priv, -1); 194 | 195 | /* calc new pos */ 196 | switch (whence) { 197 | case XMMS_XFORM_SEEK_CUR: 198 | pos = acm_pcm_tell (priv->acm) + samples; 199 | break; 200 | case XMMS_XFORM_SEEK_END: 201 | pos = acm_pcm_total (priv->acm) + samples; 202 | break; 203 | case XMMS_XFORM_SEEK_SET: 204 | pos = samples; 205 | break; 206 | default: 207 | return -1; 208 | } 209 | 210 | /* sanitize */ 211 | if (pos < 0) 212 | pos = 0; 213 | else if (pos > acm_pcm_total (priv->acm)) 214 | pos = acm_pcm_total (priv->acm); 215 | 216 | /* actual seek */ 217 | priv->error = error; 218 | return acm_seek_pcm (priv->acm, pos); 219 | } 220 | 221 | static gboolean 222 | xmms_acm_plugin_setup (xmms_xform_plugin_t *plugin) 223 | { 224 | xmms_xform_methods_t methods; 225 | 226 | XMMS_XFORM_METHODS_INIT (methods); 227 | methods.init = xmms_acm_init; 228 | methods.destroy = xmms_acm_destroy; 229 | methods.read = xmms_acm_read; 230 | methods.seek = xmms_acm_seek; 231 | 232 | xmms_xform_plugin_methods_set (plugin, &methods); 233 | 234 | xmms_xform_plugin_indata_add (plugin, 235 | XMMS_STREAM_TYPE_MIMETYPE, 236 | "audio/x-acm", 237 | NULL); 238 | 239 | xmms_magic_add ("acm header", "audio/x-acm", 240 | "0 lelong 0x01032897", 241 | NULL); 242 | 243 | xmms_magic_add ("wavc header", "audio/x-acm", 244 | "0 string WAVC", 245 | NULL); 246 | 247 | xmms_magic_extension_add ("audio/x-acm", "*.acm"); 248 | 249 | return TRUE; 250 | } 251 | 252 | /* 253 | * plugin info 254 | */ 255 | 256 | XMMS_XFORM_PLUGIN ("acm", 257 | "Interplay ACM Decoder", 258 | LIBACM_VERSION, 259 | "libacm plugin for XMMS2", 260 | xmms_acm_plugin_setup); 261 | 262 | -------------------------------------------------------------------------------- /plugins/winamp.h: -------------------------------------------------------------------------------- 1 | #define OUT_VER 0x10 2 | 3 | typedef struct 4 | { 5 | int version; /* module version (OUT_VER) */ 6 | char *description; /* description of module, with version string */ 7 | int id; /* module id. each input module gets its own. non-nullsoft modules should */ 8 | /* be >= 65536. */ 9 | 10 | HWND hMainWindow; /* winamp's main window (filled in by winamp) */ 11 | HINSTANCE hDllInstance; /* DLL instance handle (filled in by winamp) */ 12 | 13 | void (*Config)(HWND hwndParent); /* configuration dialog */ 14 | void (*About)(HWND hwndParent); /* about dialog */ 15 | 16 | void (*Init)(); /* called when loaded */ 17 | void (*Quit)(); /* called when unloaded */ 18 | 19 | int (*Open)(int samplerate, int numchannels, int bitspersamp, int bufferlenms, int prebufferms); 20 | /* returns >=0 on success, <0 on failure */ 21 | /* NOTENOTENOTE: bufferlenms and prebufferms are ignored in most if not all output plug-ins. */ 22 | /* ... so don't expect the max latency returned to be what you asked for. */ 23 | /* returns max latency in ms (0 for diskwriters, etc) */ 24 | /* bufferlenms and prebufferms must be in ms. 0 to use defaults. */ 25 | /* prebufferms must be <= bufferlenms */ 26 | 27 | void (*Close)(); /* close the ol' output device. */ 28 | 29 | int (*Write)(char *buf, int len); 30 | /* 0 on success. Len == bytes to write (<= 8192 always). buf is straight audio data. */ 31 | /* 1 returns not able to write (yet). Non-blocking, always. */ 32 | 33 | int (*CanWrite)(); /* returns number of bytes possible to write at a given time. */ 34 | /* Never will decrease unless you call Write (or Close, heh) */ 35 | 36 | int (*IsPlaying)(); /* non0 if output is still going or if data in buffers waiting to be */ 37 | /* written (i.e. closing while IsPlaying() returns 1 would truncate the song */ 38 | 39 | int (*Pause)(int pause); /* returns previous pause state */ 40 | 41 | void (*SetVolume)(int volume); /* volume is 0-255 */ 42 | void (*SetPan)(int pan); /* pan is -128 to 128 */ 43 | 44 | void (*Flush)(int t); /* flushes buffers and restarts output at time t (in ms) */ 45 | /* (used for seeking) */ 46 | 47 | int (*GetOutputTime)(); /* returns played time in MS */ 48 | int (*GetWrittenTime)(); /* returns time written in MS (used for synching up vis stuff) */ 49 | 50 | } Out_Module; 51 | 52 | 53 | /* note: exported symbol is now winampGetInModule2. */ 54 | 55 | #define IN_VER 0x100 56 | 57 | typedef struct 58 | { 59 | int version; /* module type (IN_VER) */ 60 | char *description; /* description of module, with version string */ 61 | 62 | HWND hMainWindow; /* winamp's main window (filled in by winamp) */ 63 | HINSTANCE hDllInstance; /* DLL instance handle (Also filled in by winamp) */ 64 | 65 | char *FileExtensions; /* "mp3\0Layer 3 MPEG\0mp2\0Layer 2 MPEG\0mpg\0Layer 1 MPEG\0" */ 66 | /* May be altered from Config, so the user can select what they want */ 67 | 68 | int is_seekable; /* is this stream seekable? */ 69 | int UsesOutputPlug; /* does this plug-in use the output plug-ins? (musn't ever change, ever :) */ 70 | 71 | void (*Config)(HWND hwndParent); /* configuration dialog */ 72 | void (*About)(HWND hwndParent); /* about dialog */ 73 | 74 | void (*Init)(); /* called at program init */ 75 | void (*Quit)(); /* called at program quit */ 76 | 77 | void (*GetFileInfo)(char *file, char *title, int *length_in_ms); /* if file == NULL, current playing is used */ 78 | int (*InfoBox)(char *file, HWND hwndParent); 79 | 80 | int (*IsOurFile)(char *fn); /* called before extension checks, to allow detection of mms://, etc */ 81 | /* playback stuff */ 82 | int (*Play)(char *fn); /* return zero on success, -1 on file-not-found, some other value on other (stopping winamp) error */ 83 | void (*Pause)(); /* pause stream */ 84 | void (*UnPause)(); /* unpause stream */ 85 | int (*IsPaused)(); /* ispaused? return 1 if paused, 0 if not */ 86 | void (*Stop)(); /* stop (unload) stream */ 87 | 88 | /* time stuff */ 89 | int (*GetLength)(); /* get length in ms */ 90 | int (*GetOutputTime)(); /* returns current output time in ms. (usually returns outMod->GetOutputTime() */ 91 | void (*SetOutputTime)(int time_in_ms); /* seeks to point in stream (in ms). Usually you signal yoru thread to seek, which seeks and calls outMod->Flush().. */ 92 | 93 | /* volume stuff */ 94 | void (*SetVolume)(int volume); /* from 0 to 255.. usually just call outMod->SetVolume */ 95 | void (*SetPan)(int pan); /* from -127 to 127.. usually just call outMod->SetPan */ 96 | 97 | /* in-window builtin vis stuff */ 98 | 99 | void (*SAVSAInit)(int maxlatency_in_ms, int srate); /* call once in Play(). maxlatency_in_ms should be the value returned from outMod->Open() */ 100 | /* call after opening audio device with max latency in ms and samplerate */ 101 | void (*SAVSADeInit)(); /* call in Stop() */ 102 | 103 | 104 | /* simple vis supplying mode */ 105 | void (*SAAddPCMData)(void *PCMData, int nch, int bps, int timestamp); 106 | /* sets the spec data directly from PCM data */ 107 | /* quick and easy way to get vis working :) */ 108 | /* needs at least 576 samples :) */ 109 | 110 | /* advanced vis supplying mode, only use if you're cool. Use SAAddPCMData for most stuff. */ 111 | int (*SAGetMode)(); /* gets csa (the current type (4=ws,2=osc,1=spec)) */ 112 | /* use when calling SAAdd() */ 113 | void (*SAAdd)(void *data, int timestamp, int csa); /* sets the spec data, filled in by winamp */ 114 | 115 | 116 | /* vis stuff (plug-in) */ 117 | /* simple vis supplying mode */ 118 | void (*VSAAddPCMData)(void *PCMData, int nch, int bps, int timestamp); /* sets the vis data directly from PCM data */ 119 | /* quick and easy way to get vis working :) */ 120 | /* needs at least 576 samples :) */ 121 | 122 | /* advanced vis supplying mode, only use if you're cool. Use VSAAddPCMData for most stuff. */ 123 | int (*VSAGetMode)(int *specNch, int *waveNch); /* use to figure out what to give to VSAAdd */ 124 | void (*VSAAdd)(void *data, int timestamp); /* filled in by winamp, called by plug-in */ 125 | 126 | 127 | /* call this in Play() to tell the vis plug-ins the current output params. */ 128 | void (*VSASetInfo)(int nch, int srate); 129 | 130 | 131 | /* dsp plug-in processing: */ 132 | /* (filled in by winamp, called by input plug) */ 133 | 134 | /* returns 1 if active (which means that the number of samples returned by dsp_dosamples */ 135 | /* could be greater than went in.. Use it to estimate if you'll have enough room in the */ 136 | /* output buffer */ 137 | int (*dsp_isactive)(); 138 | 139 | /* returns number of samples to output. This can be as much as twice numsamples. */ 140 | /* be sure to allocate enough buffer for samples, then. */ 141 | int (*dsp_dosamples)(short int *samples, int numsamples, int bps, int nch, int srate); 142 | 143 | 144 | /* eq stuff */ 145 | void (*EQSet)(int on, char data[10], int preamp); /* 0-64 each, 31 is +0, 0 is +12, 63 is -12. Do nothing to ignore. */ 146 | 147 | /* info setting (filled in by winamp) */ 148 | void (*SetInfo)(int bitrate, int srate, int stereo, int synched); /* if -1, changes ignored? :) */ 149 | 150 | Out_Module *outMod; /* filled in by winamp, optionally used :) */ 151 | } In_Module; 152 | 153 | 154 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | bin_PROGRAMS = acmtool 3 | noinst_LTLIBRARIES = libacm.la 4 | 5 | noinst_HEADERS = libacm.h 6 | 7 | libacm_la_SOURCES = decode.c util.c 8 | 9 | acmtool_SOURCES = acmtool.c 10 | 11 | if USE_LIBAO 12 | acmtool_CFLAGS = $(AO_CFLAGS) 13 | acmtool_LDADD = libacm.la $(AO_LIBS) 14 | else 15 | acmtool_LDADD = libacm.la 16 | endif 17 | 18 | -------------------------------------------------------------------------------- /src/acmtool.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Command line tool for ACM manipulating. 3 | * 4 | * Copyright (c) 2004-2010, Marko Kreen 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifdef HAVE_CONFIG_H 20 | #include "config.h" 21 | #endif 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "libacm.h" 31 | 32 | static const char * version = "acmtool - libacm version " LIBACM_VERSION; 33 | 34 | static int cf_raw = 0; 35 | static int cf_force_chans = 0; 36 | static int cf_no_output = 0; 37 | static int cf_quiet = 0; 38 | 39 | static void show_header(const char *fn, ACMStream *acm) 40 | { 41 | int kbps; 42 | const ACMInfo *inf; 43 | unsigned m, s, tmp; 44 | if (cf_quiet) 45 | return; 46 | inf = acm_info(acm); 47 | kbps = acm_bitrate(acm) / 1000; 48 | tmp = acm_time_total(acm) / 1000; 49 | s = tmp % 60; 50 | m = tmp / 60; 51 | printf("%s: Length:%2d:%02d Chans:%d(%d) Freq:%d A:%d/%d kbps:%d\n", 52 | fn, m, s, acm_channels(acm), acm->info.acm_channels, 53 | acm_rate(acm), inf->acm_level, inf->acm_rows, kbps); 54 | } 55 | 56 | #ifdef HAVE_AO 57 | 58 | /* 59 | * Audio playback with libao 60 | */ 61 | 62 | #include 63 | 64 | static ao_device *dev = NULL; 65 | static ao_sample_format old_fmt; 66 | 67 | static ao_device *open_audio(ao_sample_format *fmt) 68 | { 69 | if (dev && memcmp(fmt, &old_fmt, sizeof(old_fmt)) != 0) { 70 | ao_close(dev); 71 | dev = NULL; 72 | } 73 | if (dev == NULL) { 74 | int id = ao_default_driver_id(); 75 | if (id < 0) { 76 | fprintf(stderr, "failed to find audio driver\n"); 77 | exit(1); 78 | } 79 | dev = ao_open_live(id, fmt, NULL); 80 | old_fmt = *fmt; 81 | } 82 | if (dev == NULL) { 83 | fprintf(stderr, "failed to open audio device\n"); 84 | exit(1); 85 | } 86 | return dev; 87 | } 88 | 89 | static void close_audio(void) 90 | { 91 | if (dev) 92 | ao_close(dev); 93 | dev = NULL; 94 | } 95 | 96 | static void play_file(const char *fn) 97 | { 98 | ACMStream *acm; 99 | int err, res, buflen; 100 | ao_sample_format fmt; 101 | ao_device *dev; 102 | char *buf; 103 | unsigned int total_bytes, bytes_done = 0; 104 | 105 | err = acm_open_file(&acm, fn, cf_force_chans); 106 | if (err < 0) { 107 | fprintf(stderr, "%s: %s\n", fn, acm_strerror(err)); 108 | return; 109 | } 110 | show_header(fn, acm); 111 | 112 | memset(&fmt, 0, sizeof fmt); 113 | fmt.bits = 16; 114 | fmt.rate = acm_rate(acm); 115 | fmt.channels = acm_channels(acm); 116 | fmt.byte_format = AO_FMT_LITTLE; 117 | 118 | dev = open_audio(&fmt); 119 | 120 | buflen = 4*1024; 121 | buf = malloc(buflen); 122 | 123 | total_bytes = acm_pcm_total(acm) * acm_channels(acm) * ACM_WORD; 124 | while (bytes_done < total_bytes) { 125 | res = acm_read_loop(acm, buf, buflen/ACM_WORD, 0,2,1); 126 | if (res == 0) 127 | break; 128 | if (res > 0) { 129 | bytes_done += res; 130 | res = ao_play(dev, buf, res); 131 | } else { 132 | fprintf(stderr, "%s: %s\n", fn, acm_strerror(res)); 133 | break; 134 | } 135 | } 136 | 137 | memset(buf, 0, buflen); 138 | if (bytes_done < total_bytes) 139 | fprintf(stderr, "%s: adding filler_samples: %d\n", 140 | fn, total_bytes - bytes_done); 141 | while (bytes_done < total_bytes) { 142 | int bs; 143 | if (bytes_done + buflen > total_bytes) { 144 | bs = total_bytes - bytes_done; 145 | } else { 146 | bs = buflen; 147 | } 148 | res = ao_play(dev, buf, bs); 149 | if (res != bs) 150 | break; 151 | bytes_done += res; 152 | } 153 | 154 | acm_close(acm); 155 | free(buf); 156 | } 157 | 158 | #endif /* HAVE_AO */ 159 | 160 | /* 161 | * WAV writing 162 | */ 163 | 164 | static char * makefn(const char *fn, const char *ext) 165 | { 166 | char *dstfn, *p; 167 | dstfn = malloc(strlen(fn) + strlen(ext) + 2); 168 | strcpy(dstfn, fn); 169 | p = strrchr(dstfn, '.'); 170 | if (p != NULL) 171 | *p = 0; 172 | strcat(dstfn, ext); 173 | return dstfn; 174 | } 175 | 176 | #define put_word(p, val) do { \ 177 | *p++ = val & 0xFF; \ 178 | *p++ = (val >> 8) & 0xFF; \ 179 | } while (0) 180 | 181 | #define put_dword(p, val) do { \ 182 | *p++ = val & 0xFF; \ 183 | *p++ = (val >> 8) & 0xFF; \ 184 | *p++ = (val >> 16) & 0xFF; \ 185 | *p++ = (val >> 24) & 0xFF; \ 186 | } while (0) 187 | 188 | #define put_data(p, data, len) do { \ 189 | memcpy(p, data, len); \ 190 | p += len; \ 191 | } while (0) 192 | 193 | static int write_wav_header(FILE *f, ACMStream *acm) 194 | { 195 | unsigned char hdr[50], *p = hdr; 196 | int res; 197 | unsigned datalen = acm_pcm_total(acm) * ACM_WORD * acm_channels(acm); 198 | 199 | int code = 1; 200 | unsigned n_channels = acm_channels(acm); 201 | unsigned srate = acm_rate(acm); 202 | unsigned avg_bps = srate * n_channels * ACM_WORD; 203 | unsigned significant_bits = ACM_WORD * 8; 204 | unsigned block_align = significant_bits * n_channels / 8; 205 | unsigned hdrlen = 16; 206 | unsigned wavlen = 4 + 8 + hdrlen + 8 + datalen; 207 | 208 | memset(hdr, 0, sizeof(hdr)); 209 | 210 | put_data(p, "RIFF", 4); 211 | put_dword(p, wavlen); 212 | put_data(p, "WAVEfmt ", 8); 213 | put_dword(p, hdrlen); 214 | put_word(p, code); 215 | put_word(p, n_channels); 216 | put_dword(p, srate); 217 | put_dword(p, avg_bps); 218 | put_word(p, block_align); 219 | put_word(p, significant_bits); 220 | 221 | put_data(p, "data", 4); 222 | put_dword(p, datalen); 223 | 224 | res = fwrite(hdr, 1, p - hdr, f); 225 | if (res != p - hdr) 226 | return -1; 227 | else 228 | return 0; 229 | } 230 | 231 | static void decode_file(const char *fn, const char *fn2) 232 | { 233 | ACMStream *acm; 234 | char *buf; 235 | int res, res2, buflen, err; 236 | FILE *fo = NULL; 237 | int bytes_done = 0, total_bytes; 238 | 239 | err = acm_open_file(&acm, fn, cf_force_chans); 240 | if (err < 0) { 241 | fprintf(stderr, "%s: %s\n", fn, acm_strerror(err)); 242 | return; 243 | } 244 | 245 | if (!cf_no_output) { 246 | if (!strcmp(fn2, "-")) { 247 | fo = stdout; 248 | cf_quiet = 1; 249 | } else { 250 | fo = fopen(fn2, "wb"); 251 | } 252 | if (fo == NULL) { 253 | perror(fn2); 254 | acm_close(acm); 255 | return; 256 | } 257 | } 258 | 259 | show_header(fn, acm); 260 | 261 | if ((!cf_raw) && (!cf_no_output)) { 262 | if ((err = write_wav_header(fo, acm)) < 0) { 263 | perror(fn2); 264 | fclose(fo); 265 | acm_close(acm); 266 | return; 267 | } 268 | } 269 | buflen = 16*1024; 270 | buf = malloc(buflen); 271 | 272 | total_bytes = acm_pcm_total(acm) * acm_channels(acm) * ACM_WORD; 273 | 274 | while (bytes_done < total_bytes) { 275 | res = acm_read_loop(acm, buf, buflen/2, 0,2,1); 276 | if (res == 0) 277 | break; 278 | if (res > 0) { 279 | if (!cf_no_output) { 280 | res2 = fwrite(buf, 1, res, fo); 281 | if (res2 != res) { 282 | fprintf(stderr, "%s: write error\n", fn2); 283 | break; 284 | } 285 | } 286 | bytes_done += res; 287 | } else { 288 | fprintf(stderr, "%s: %s\n", fn, acm_strerror(res)); 289 | break; 290 | } 291 | } 292 | 293 | memset(buf, 0, buflen); 294 | if (bytes_done < total_bytes) 295 | fprintf(stderr, "%s: adding filler_samples: %d\n", 296 | fn, total_bytes - bytes_done); 297 | while (bytes_done < total_bytes) { 298 | int bs; 299 | if (bytes_done + buflen > total_bytes) { 300 | bs = total_bytes - bytes_done; 301 | } else { 302 | bs = buflen; 303 | } 304 | if (!cf_no_output) { 305 | res2 = fwrite(buf, 1, bs, fo); 306 | if (res2 != bs) 307 | break; 308 | } 309 | bytes_done += bs; 310 | } 311 | 312 | acm_close(acm); 313 | if (!cf_no_output) 314 | fclose(fo); 315 | free(buf); 316 | } 317 | 318 | /* 319 | * Modify header 320 | */ 321 | 322 | static void set_channels(const char *fn, int n_chan) 323 | { 324 | FILE *f; 325 | static const unsigned char acm_id[] = { 0x97, 0x28, 0x03, 0x01 }; 326 | unsigned char hdr[14]; 327 | int oldnum, res; 328 | 329 | if ((f = fopen(fn, "rb+")) == NULL) { 330 | perror(fn); 331 | return; 332 | } 333 | res = fread(hdr, 1, 14, f); 334 | if (res != 14) { 335 | fprintf(stderr, "%s: cannot read header\n", fn); 336 | return; 337 | } 338 | 339 | if (memcmp(hdr, acm_id, 4)) { 340 | fprintf(stderr, "%s: not an ACM file\n", fn); 341 | return; 342 | } 343 | 344 | oldnum = (hdr[9] << 8) + hdr[8]; 345 | if (oldnum != 1 && oldnum != 2) { 346 | fprintf(stderr, "%s: suspicios number of channels: %d\n", 347 | fn, oldnum); 348 | return; 349 | } 350 | 351 | if (fseek(f, 0, SEEK_SET)) { 352 | perror(fn); 353 | return; 354 | } 355 | 356 | hdr[8] = n_chan; 357 | res = fwrite(hdr, 1, 14, f); 358 | if (res != 14) { 359 | perror(fn); 360 | } 361 | fclose(f); 362 | } 363 | 364 | /* 365 | * Just show info 366 | */ 367 | 368 | static void show_info(const char *fn) 369 | { 370 | int err; 371 | ACMStream *acm; 372 | 373 | err = acm_open_file(&acm, fn, cf_force_chans); 374 | if (err < 0) { 375 | printf("%s: %s\n", fn, acm_strerror(err)); 376 | return; 377 | } 378 | 379 | show_header(fn, acm); 380 | acm_close(acm); 381 | } 382 | 383 | static void usage(int err) 384 | { 385 | printf("%s\n", version); 386 | printf("Play: acmtool -p [-q][-m|-s] acmfile [acmfile ...]\n"); 387 | printf("Decode: acmtool -d [-q][-m|-s] [-r|-n] -o wavfile acmfile\n"); 388 | printf(" acmtool -d [-q][-m|-s] [-r|-n] acmfile [acmfile ...]\n"); 389 | printf("Other: acmtool -i acmfile [acmfile ...]\n"); 390 | printf(" acmtool -M|-S acmfile [acmfile ...]\n"); 391 | printf("Commands:\n"); 392 | printf(" -p play file(s)\n"); 393 | printf(" -d decode audio into WAV files\n"); 394 | printf(" -i show info about ACM files\n"); 395 | printf(" -M modify ACM header to have 1 channel\n"); 396 | printf(" -S modify ACM header to have 2 channels\n"); 397 | printf("Switches:\n"); 398 | printf(" -m force mono\n"); 399 | printf(" -s force stereo (default)\n"); 400 | printf(" -r raw output - no wav header\n"); 401 | printf(" -q be quiet\n"); 402 | printf(" -n no output - for benchmarking\n"); 403 | printf(" -o FN output to file, can be used if single source file\n"); 404 | exit(err); 405 | } 406 | 407 | int main(int argc, char *argv[]) 408 | { 409 | int c, i; 410 | char *fn, *fn2 = NULL; 411 | int cmd_decode = 0; 412 | int cmd_chg_channels = 0; 413 | int cmd_info = 0, cmd_play = 0; 414 | int cf_set_chans = 0; 415 | 416 | while ((c = getopt(argc, argv, "pdiMSqhrmsnvo:")) != -1) { 417 | switch (c) { 418 | case 'h': 419 | usage(0); 420 | break; 421 | case 'd': 422 | cmd_decode = 1; 423 | break; 424 | case 'i': 425 | cmd_info = 1; 426 | break; 427 | case 'p': 428 | cmd_play = 1; 429 | break; 430 | case 'M': 431 | cmd_chg_channels = 1; 432 | cf_set_chans = 1; 433 | break; 434 | case 'S': 435 | cmd_chg_channels = 1; 436 | cf_set_chans = 2; 437 | break; 438 | case 'q': 439 | cf_quiet = 1; 440 | break; 441 | case 'm': 442 | cf_force_chans = 1; 443 | break; 444 | case 's': 445 | cf_force_chans = 2; 446 | break; 447 | case 'r': 448 | cf_raw = 1; 449 | break; 450 | case 'n': 451 | cf_no_output = 1; 452 | break; 453 | case 'o': 454 | fn2 = optarg; 455 | break; 456 | case 'v': 457 | printf("%s\n", version); 458 | exit(0); 459 | default: 460 | fprintf(stderr, "bad arg: -%c\n", c); 461 | usage(1); 462 | } 463 | } 464 | i = cmd_chg_channels + cmd_info + cmd_decode + cmd_play; 465 | if (i < 1 || i > 1) { 466 | fprintf(stderr, "only one command at a time please\n"); 467 | usage(1); 468 | } 469 | 470 | /* play file */ 471 | if (cmd_play) { 472 | #ifdef HAVE_AO 473 | ao_initialize(); 474 | for (i = optind; i < argc; i++) 475 | play_file(argv[i]); 476 | close_audio(); 477 | ao_shutdown(); 478 | return 0; 479 | #else 480 | fprintf(stderr, "For audio output, please compile with libao.\n"); 481 | return 1; 482 | #endif 483 | } 484 | 485 | /* show info */ 486 | if (cmd_info) { 487 | for (i = optind; i < argc; i++) 488 | show_info(argv[i]); 489 | return 0; 490 | } 491 | 492 | /* channel changing */ 493 | if (cmd_chg_channels) { 494 | for (i = optind; i < argc; i++) 495 | set_channels(argv[i], cf_set_chans); 496 | return 0; 497 | } 498 | 499 | /* regular converting */ 500 | if (optind == argc) 501 | usage(1); 502 | if (fn2) { 503 | if (optind + 1 != argc) 504 | usage(1); 505 | fn = argv[optind]; 506 | decode_file(fn, fn2); 507 | } else { 508 | while (optind < argc) { 509 | fn = argv[optind++]; 510 | fn2 = makefn(fn, cf_raw ? ".raw" : ".wav"); 511 | decode_file(fn, fn2); 512 | free(fn2); 513 | } 514 | } 515 | return 0; 516 | } 517 | 518 | -------------------------------------------------------------------------------- /src/decode.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ACM decoder. 3 | * 4 | * Copyright (c) 2004-2010, Marko Kreen 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifdef HAVE_CONFIG_H 20 | #include "config.h" 21 | #endif 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include "libacm.h" 28 | 29 | #define ACM_BUFLEN (64*1024) 30 | 31 | #define ACM_EXPECTED_EOF -99 32 | 33 | typedef int (*filler_t)(ACMStream *acm, unsigned ind, unsigned col); 34 | 35 | /************************************** 36 | * Stream processing 37 | **************************************/ 38 | 39 | /* NB: bits <= 31! Thus less checks in code. */ 40 | 41 | static int load_buf(ACMStream *acm) 42 | { 43 | int res = 0; 44 | 45 | if (acm->file_eof) 46 | return 0; 47 | 48 | acm->buf_start_ofs += acm->buf_size; 49 | 50 | if (acm->io.read_func != NULL) 51 | res = acm->io.read_func(acm->buf, 1, acm->buf_max, 52 | acm->io_arg); 53 | 54 | if (res < 0) 55 | return ACM_ERR_READ_ERR; 56 | 57 | if (res == 0) { 58 | acm->file_eof = 1; 59 | /* add single zero byte */ 60 | acm->buf[0] = 0; 61 | acm->buf_size = 1; 62 | } else { 63 | acm->buf_size = res; 64 | } 65 | acm->buf_pos = 0; 66 | return 0; 67 | } 68 | 69 | static int load_bits(ACMStream *acm) 70 | { 71 | int err; 72 | unsigned data, got; 73 | unsigned char *p = acm->buf + acm->buf_pos; 74 | switch (acm->buf_size - acm->buf_pos) { 75 | default: 76 | data = 0; 77 | got = 0; 78 | break; 79 | case 1: 80 | data = p[0]; 81 | got = 8; 82 | break; 83 | case 2: 84 | data = p[0] + (p[1] << 8); 85 | got = 16; 86 | break; 87 | case 3: 88 | data = p[0] + (p[1] << 8) + (p[2] << 16); 89 | got = 24; 90 | break; 91 | } 92 | 93 | if ((err = load_buf(acm)) < 0) 94 | return err; 95 | 96 | while (got < 32) { 97 | if (acm->buf_size - acm->buf_pos == 0) 98 | break; 99 | data |= acm->buf[acm->buf_pos] << got; 100 | got += 8; 101 | acm->buf_pos++; 102 | } 103 | acm->bit_data = data; 104 | acm->bit_avail = got; 105 | return 0; 106 | } 107 | 108 | static int get_bits_reload(ACMStream *acm, unsigned bits) 109 | { 110 | int got, err; 111 | unsigned data, b_data, b_avail; 112 | 113 | data = acm->bit_data; 114 | got = acm->bit_avail; 115 | bits -= got; 116 | 117 | if (acm->buf_size - acm->buf_pos >= 4) { 118 | unsigned char *p = acm->buf + acm->buf_pos; 119 | acm->buf_pos += 4; 120 | b_data = p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24); 121 | b_avail = 32; 122 | } else { 123 | if ((err = load_bits(acm)) < 0) 124 | return err; 125 | if (acm->bit_avail < bits) 126 | return ACM_ERR_UNEXPECTED_EOF; 127 | b_data = acm->bit_data; 128 | b_avail = acm->bit_avail; 129 | } 130 | 131 | data |= (b_data & ((1 << bits) - 1)) << got; 132 | acm->bit_data = b_data >> bits; 133 | acm->bit_avail = b_avail - bits; 134 | return data; 135 | } 136 | 137 | #define GET_BITS_NOERR(tmpval, acm, bits) do { \ 138 | if (acm->bit_avail >= bits) { \ 139 | tmpval = acm->bit_data & ((1 << bits) - 1); \ 140 | acm->bit_data >>= bits; \ 141 | acm->bit_avail -= bits; \ 142 | } else \ 143 | tmpval = get_bits_reload(acm, bits); \ 144 | } while (0) 145 | 146 | #define GET_BITS(res, acm, bits) do { \ 147 | int tmpval; \ 148 | GET_BITS_NOERR(tmpval, acm, bits); \ 149 | if (tmpval < 0) \ 150 | return tmpval; \ 151 | res = tmpval; \ 152 | } while (0) 153 | 154 | #define GET_BITS_EXPECT_EOF(res, acm, bits) do { \ 155 | int tmpval; \ 156 | GET_BITS_NOERR(tmpval, acm, bits); \ 157 | if (tmpval < 0) { \ 158 | if (tmpval == ACM_ERR_UNEXPECTED_EOF) \ 159 | return ACM_EXPECTED_EOF; \ 160 | return tmpval; \ 161 | } \ 162 | res = tmpval; \ 163 | } while (0) 164 | 165 | /************************************************* 166 | * Table filling 167 | *************************************************/ 168 | static const int map_1bit[] = { -1, +1 }; 169 | static const int map_2bit_near[] = { -2, -1, +1, +2 }; 170 | static const int map_2bit_far[] = { -3, -2, +2, +3 }; 171 | static const int map_3bit[] = { -4, -3, -2, -1, +1, +2, +3, +4 }; 172 | 173 | /* IOW: (r * acm->subblock_len) + c */ 174 | #define set_pos(acm, r, c, idx) do { \ 175 | unsigned _pos = ((r) << acm->info.acm_level) + (c); \ 176 | acm->block[_pos] = acm->midbuf[idx]; \ 177 | } while (0) 178 | 179 | /************ Fillers **********/ 180 | 181 | static int f_zero(ACMStream *acm, unsigned ind, unsigned col) 182 | { 183 | unsigned i; 184 | for (i = 0; i < acm->info.acm_rows; i++) 185 | set_pos(acm, i, col, 0); 186 | 187 | return 1; 188 | } 189 | 190 | static int f_bad(ACMStream *acm, unsigned ind, unsigned col) 191 | { 192 | /* corrupt block? */ 193 | return ACM_ERR_CORRUPT; 194 | } 195 | 196 | static int f_linear(ACMStream *acm, unsigned ind, unsigned col) 197 | { 198 | unsigned int i; 199 | int b, middle = 1 << (ind - 1); 200 | 201 | for (i = 0; i < acm->info.acm_rows; i++) { 202 | GET_BITS(b, acm, ind); 203 | set_pos(acm, i, col, b - middle); 204 | } 205 | return 1; 206 | } 207 | 208 | static int f_k13(ACMStream *acm, unsigned ind, unsigned col) 209 | { 210 | unsigned i, b; 211 | for (i = 0; i < acm->info.acm_rows; i++) { 212 | GET_BITS(b, acm, 1); 213 | if (b == 0) { 214 | /* 0 */ 215 | set_pos(acm, i++, col, 0); 216 | if (i >= acm->info.acm_rows) 217 | break; 218 | set_pos(acm, i, col, 0); 219 | continue; 220 | } 221 | GET_BITS(b, acm, 1); 222 | if (b == 0) { 223 | /* 1, 0 */ 224 | set_pos(acm, i, col, 0); 225 | continue; 226 | } 227 | /* 1, 1, ? */ 228 | GET_BITS(b, acm, 1); 229 | set_pos(acm, i, col, map_1bit[b]); 230 | } 231 | return 1; 232 | } 233 | 234 | static int f_k12(ACMStream *acm, unsigned ind, unsigned col) 235 | { 236 | unsigned i, b; 237 | for (i = 0; i < acm->info.acm_rows; i++) { 238 | GET_BITS(b, acm, 1); 239 | if (b == 0) { 240 | /* 0 */ 241 | set_pos(acm, i, col, 0); 242 | continue; 243 | } 244 | 245 | /* 1, ? */ 246 | GET_BITS(b, acm, 1); 247 | set_pos(acm, i, col, map_1bit[b]); 248 | } 249 | return 1; 250 | } 251 | 252 | static int f_k24(ACMStream *acm, unsigned ind, unsigned col) 253 | { 254 | unsigned i, b; 255 | for (i = 0; i < acm->info.acm_rows; i++) { 256 | GET_BITS(b, acm, 1); 257 | if (b == 0) { 258 | /* 0 */ 259 | set_pos(acm, i++, col, 0); 260 | if (i >= acm->info.acm_rows) break; 261 | set_pos(acm, i, col, 0); 262 | continue; 263 | } 264 | 265 | GET_BITS(b, acm, 1); 266 | if (b == 0) { 267 | /* 1, 0 */ 268 | set_pos(acm, i, col, 0); 269 | continue; 270 | } 271 | 272 | /* 1, 1, ?, ? */ 273 | GET_BITS(b, acm, 2); 274 | set_pos(acm, i, col, map_2bit_near[b]); 275 | } 276 | return 1; 277 | } 278 | 279 | static int f_k23(ACMStream *acm, unsigned ind, unsigned col) 280 | { 281 | unsigned i, b; 282 | for (i = 0; i < acm->info.acm_rows; i++) { 283 | GET_BITS(b, acm, 1); 284 | if (b == 0) { 285 | /* 0 */ 286 | set_pos(acm, i, col, 0); 287 | continue; 288 | } 289 | 290 | /* 1, ?, ? */ 291 | GET_BITS(b, acm, 2); 292 | set_pos(acm, i, col, map_2bit_near[b]); 293 | } 294 | return 1; 295 | } 296 | 297 | static int f_k35(ACMStream *acm, unsigned ind, unsigned col) 298 | { 299 | unsigned i, b; 300 | for (i = 0; i < acm->info.acm_rows; i++) { 301 | GET_BITS(b, acm, 1); 302 | if (b == 0) { 303 | /* 0 */ 304 | set_pos(acm, i++, col, 0); 305 | if (i >= acm->info.acm_rows) 306 | break; 307 | set_pos(acm, i, col, 0); 308 | continue; 309 | } 310 | 311 | GET_BITS(b, acm, 1); 312 | if (b == 0) { 313 | /* 1, 0 */ 314 | set_pos(acm, i, col, 0); 315 | continue; 316 | } 317 | 318 | GET_BITS(b, acm, 1); 319 | if (b == 0) { 320 | /* 1, 1, 0, ? */ 321 | GET_BITS(b, acm, 1); 322 | set_pos(acm, i, col, map_1bit[b]); 323 | continue; 324 | } 325 | 326 | /* 1, 1, 1, ?, ? */ 327 | GET_BITS(b, acm, 2); 328 | set_pos(acm, i, col, map_2bit_far[b]); 329 | } 330 | return 1; 331 | } 332 | 333 | static int f_k34(ACMStream *acm, unsigned ind, unsigned col) 334 | { 335 | unsigned i, b; 336 | for (i = 0; i < acm->info.acm_rows; i++) { 337 | GET_BITS(b, acm, 1); 338 | if (b == 0) { 339 | /* 0 */ 340 | set_pos(acm, i, col, 0); 341 | continue; 342 | } 343 | 344 | GET_BITS(b, acm, 1); 345 | if (b == 0) { 346 | /* 1, 0, ? */ 347 | GET_BITS(b, acm, 1); 348 | set_pos(acm, i, col, map_1bit[b]); 349 | continue; 350 | } 351 | 352 | /* 1, 1, ?, ? */ 353 | GET_BITS(b, acm, 2); 354 | set_pos(acm, i, col, map_2bit_far[b]); 355 | } 356 | return 1; 357 | } 358 | 359 | static int f_k45(ACMStream *acm, unsigned ind, unsigned col) 360 | { 361 | unsigned i, b; 362 | for (i = 0; i < acm->info.acm_rows; i++) { 363 | GET_BITS(b, acm, 1); 364 | if (b == 0) { 365 | /* 0 */ 366 | set_pos(acm, i, col, 0); i++; 367 | if (i >= acm->info.acm_rows) 368 | break; 369 | set_pos(acm, i, col, 0); 370 | continue; 371 | } 372 | 373 | GET_BITS(b, acm, 1); 374 | if (b == 0) { 375 | /* 1, 0 */ 376 | set_pos(acm, i, col, 0); 377 | continue; 378 | } 379 | 380 | /* 1, 1, ?, ?, ? */ 381 | GET_BITS(b, acm, 3); 382 | set_pos(acm, i, col, map_3bit[b]); 383 | } 384 | return 1; 385 | } 386 | 387 | static int f_k44(ACMStream *acm, unsigned ind, unsigned col) 388 | { 389 | unsigned i, b; 390 | for (i = 0; i < acm->info.acm_rows; i++) { 391 | GET_BITS(b, acm, 1); 392 | if (b == 0) { 393 | /* 0 */ 394 | set_pos(acm, i, col, 0); 395 | continue; 396 | } 397 | 398 | /* 1, ?, ?, ? */ 399 | GET_BITS(b, acm, 3); 400 | set_pos(acm, i, col, map_3bit[b]); 401 | } 402 | return 1; 403 | } 404 | 405 | static int f_t15(ACMStream *acm, unsigned ind, unsigned col) 406 | { 407 | unsigned i, b; 408 | int n1, n2, n3, tmp; 409 | for (i = 0; i < acm->info.acm_rows; i++) { 410 | /* b = (x1) + (x2 * 3) + (x3 * 9) */ 411 | GET_BITS(b, acm, 5); 412 | if (b >= 3 * 3 * 3) 413 | return ACM_ERR_CORRUPT; 414 | 415 | n1 = b % 3 - 1; 416 | tmp = b / 3; 417 | n2 = tmp % 3 - 1; 418 | n3 = tmp / 3 - 1; 419 | 420 | set_pos(acm, i++, col, n1); 421 | if (i >= acm->info.acm_rows) 422 | break; 423 | set_pos(acm, i++, col, n2); 424 | if (i >= acm->info.acm_rows) 425 | break; 426 | set_pos(acm, i, col, n3); 427 | } 428 | return 1; 429 | } 430 | 431 | static int f_t27(ACMStream *acm, unsigned ind, unsigned col) 432 | { 433 | unsigned i, b; 434 | int n1, n2, n3, tmp; 435 | for (i = 0; i < acm->info.acm_rows; i++) { 436 | /* b = (x1) + (x2 * 5) + (x3 * 25) */ 437 | GET_BITS(b, acm, 7); 438 | if (b >= 5 * 5 * 5) 439 | return ACM_ERR_CORRUPT; 440 | 441 | n1 = b % 5 - 2; 442 | tmp = b / 5; 443 | n2 = tmp % 5 - 2; 444 | n3 = tmp / 5 - 2; 445 | 446 | set_pos(acm, i++, col, n1); 447 | if (i >= acm->info.acm_rows) 448 | break; 449 | set_pos(acm, i++, col, n2); 450 | if (i >= acm->info.acm_rows) 451 | break; 452 | set_pos(acm, i, col, n3); 453 | } 454 | return 1; 455 | } 456 | 457 | static int f_t37(ACMStream *acm, unsigned ind, unsigned col) 458 | { 459 | unsigned i, b; 460 | int n1, n2; 461 | for (i = 0; i < acm->info.acm_rows; i++) { 462 | /* b = (x1) + (x2 * 11) */ 463 | GET_BITS(b, acm, 7); 464 | if (b >= 11 * 11) 465 | return ACM_ERR_CORRUPT; 466 | 467 | n1 = b % 11 - 5; 468 | n2 = b / 11 - 5; 469 | 470 | set_pos(acm, i++, col, n1); 471 | if (i >= acm->info.acm_rows) 472 | break; 473 | set_pos(acm, i, col, n2); 474 | } 475 | return 1; 476 | } 477 | 478 | /****************/ 479 | 480 | static const filler_t filler_list[] = { 481 | f_zero, f_bad, f_bad, f_linear, /* 0..3 */ 482 | f_linear, f_linear, f_linear, f_linear, /* 4..7 */ 483 | f_linear, f_linear, f_linear, f_linear, /* 8..11 */ 484 | f_linear, f_linear, f_linear, f_linear, /* 12..15 */ 485 | f_linear, f_k13, f_k12, f_t15, /* 16..19 */ 486 | f_k24, f_k23, f_t27, f_k35, /* 20..23 */ 487 | f_k34, f_bad, f_k45, f_k44, /* 24..27 */ 488 | f_bad, f_t37, f_bad, f_bad /* 28..31 */ 489 | }; 490 | 491 | static int fill_block(ACMStream *acm) 492 | { 493 | unsigned i, ind; 494 | int err; 495 | for (i = 0; i < acm->info.acm_cols; i++) { 496 | GET_BITS_EXPECT_EOF(ind, acm, 5); 497 | err = filler_list[ind](acm, ind, i); 498 | if (err < 0) 499 | return err; 500 | } 501 | return 1; 502 | } 503 | 504 | /********************************************** 505 | * Decompress code 506 | **********************************************/ 507 | 508 | static void juggle(int *wrap_p, int *block_p, unsigned sub_len, unsigned sub_count) 509 | { 510 | unsigned int i, j; 511 | int *p; 512 | unsigned int r0, r1, r2, r3; 513 | for (i = 0; i < sub_len; i++) { 514 | p = block_p; 515 | r0 = wrap_p[0]; 516 | r1 = wrap_p[1]; 517 | for (j = 0; j < sub_count/2; j++) { 518 | r2 = *p; *p = r1*2 + (r0 + r2); p += sub_len; 519 | r3 = *p; *p = r2*2 - (r1 + r3); p += sub_len; 520 | r0 = r2; r1 = r3; 521 | } 522 | *wrap_p++ = r0; 523 | *wrap_p++ = r1; 524 | block_p++; 525 | } 526 | } 527 | 528 | static void juggle_block(ACMStream *acm) 529 | { 530 | unsigned sub_count, sub_len, todo_count, step_subcount, i; 531 | int *wrap_p, *block_p, *p; 532 | 533 | /* juggle only if subblock_len > 1 */ 534 | if (acm->info.acm_level == 0) 535 | return; 536 | 537 | /* 2048 / subblock_len */ 538 | if (acm->info.acm_level > 9) 539 | step_subcount = 1; 540 | else 541 | step_subcount = (2048 >> acm->info.acm_level) - 2; 542 | 543 | /* Apply juggle() (rows)x(cols) 544 | * from (step_subcount * 2) x (subblock_len/2) 545 | * to (step_subcount * subblock_len) x (1) 546 | */ 547 | todo_count = acm->info.acm_rows; 548 | block_p = acm->block; 549 | while (1) { 550 | wrap_p = acm->wrapbuf; 551 | sub_count = step_subcount; 552 | if (sub_count > todo_count) 553 | sub_count = todo_count; 554 | 555 | sub_len = acm->info.acm_cols / 2; 556 | sub_count *= 2; 557 | 558 | juggle(wrap_p, block_p, sub_len, sub_count); 559 | wrap_p += sub_len*2; 560 | 561 | for (i = 0, p = block_p; i < sub_count; i++) { 562 | p[0]++; 563 | p += sub_len; 564 | } 565 | 566 | while (sub_len > 1) { 567 | sub_len /= 2; 568 | sub_count *= 2; 569 | juggle(wrap_p, block_p, sub_len, sub_count); 570 | wrap_p += sub_len*2; 571 | } 572 | if (todo_count <= step_subcount) 573 | break; 574 | todo_count -= step_subcount; 575 | block_p += step_subcount << acm->info.acm_level; 576 | } 577 | } 578 | 579 | /***************************************************************/ 580 | static int decode_block(ACMStream *acm) 581 | { 582 | int pwr, count, val, i, x, err; 583 | 584 | acm->block_ready = 0; 585 | acm->block_pos = 0; 586 | 587 | /* read header */ 588 | GET_BITS_EXPECT_EOF(pwr, acm, 4); 589 | GET_BITS_EXPECT_EOF(val, acm, 16); 590 | 591 | /* generate tables */ 592 | count = 1 << pwr; 593 | for (i = 0, x = 0; i < count; i++) { 594 | acm->midbuf[i] = x; 595 | x += val; 596 | } 597 | for (i = 1, x = -val; i <= count; i++) { 598 | acm->midbuf[-i] = (unsigned)x; 599 | x -= val; 600 | } 601 | 602 | /* to_check? */ 603 | if ((err = fill_block(acm)) <= 0) 604 | return err; 605 | 606 | juggle_block(acm); 607 | 608 | acm->block_ready = 1; 609 | 610 | return 1; 611 | } 612 | 613 | /****************************** 614 | * Output formats 615 | ******************************/ 616 | 617 | static unsigned char *out_s16le(int *src, unsigned char *dst, unsigned n, unsigned shift) 618 | { 619 | while (n--) { 620 | int val = *src++ >> shift; 621 | *dst++ = val & 0xFF; 622 | *dst++ = (val >> 8) & 0xFF; 623 | } 624 | return dst; 625 | } 626 | 627 | static unsigned char *out_s16be(int *src, unsigned char *dst, unsigned n, unsigned shift) 628 | { 629 | while (n--) { 630 | int val = *src++ >> shift; 631 | *dst++ = (val >> 8) & 0xFF; 632 | *dst++ = val & 0xFF; 633 | } 634 | return dst; 635 | } 636 | 637 | static unsigned char *out_u16le(int *src, unsigned char *dst, unsigned n, unsigned shift) 638 | { 639 | while (n--) { 640 | int val = (*src++ >> shift) + 0x8000; 641 | *dst++ = val & 0xFF; 642 | *dst++ = (val >> 8) & 0xFF; 643 | } 644 | return dst; 645 | } 646 | 647 | static unsigned char *out_u16be(int *src, unsigned char *dst, unsigned n, unsigned shift) 648 | { 649 | while (n--) { 650 | int val = (*src++ >> shift) + 0x8000; 651 | *dst++ = (val >> 8) & 0xFF; 652 | *dst++ = val & 0xFF; 653 | } 654 | return dst; 655 | } 656 | 657 | static int output_values(int *src, unsigned char *dst, int n, 658 | int acm_level, int bigendianp, int wordlen, int sgned) 659 | { 660 | unsigned char *res = NULL; 661 | if (wordlen == 2) { 662 | if (bigendianp == 0) { 663 | if (sgned) 664 | res = out_s16le(src, dst, n, acm_level); 665 | else 666 | res = out_u16le(src, dst, n, acm_level); 667 | } else { 668 | if (sgned) 669 | res = out_s16be(src, dst, n, acm_level); 670 | else 671 | res = out_u16be(src, dst, n, acm_level); 672 | } 673 | } 674 | if (res != NULL) 675 | return res - dst; 676 | return ACM_ERR_BADFMT; 677 | } 678 | 679 | /* 680 | * WAVC (compressed WAV) files are ACM files with additional header. 681 | * 682 | * 'WAVC' + 'V1.00' + uncompr(4b) + compr(4b) + 12b 683 | */ 684 | 685 | #define WAVC_ID 0x564157 /* 'WAV' */ 686 | 687 | static int read_wavc_header(ACMStream *acm) 688 | { 689 | static const unsigned short expect[12] = { 690 | /* 'V1.0', raw_size, acm_size */ 691 | 0x3156, 0x302E, 0,0, 0,0, 692 | /* hdrlen?, chans?, bits?, hz */ 693 | 28,0, 1, 16, 22050, 0 694 | }; 695 | unsigned short i, buf[12]; 696 | 697 | for (i = 0; i < 12; i++) 698 | GET_BITS(buf[i], acm, 16); 699 | if (memcmp(buf, expect, 4) != 0) 700 | return -1; 701 | /* full comparision is too strict */ 702 | if (0 && memcmp(buf + 6, expect + 6, 12) != 0) 703 | return -1; 704 | /* just make sure the magic 28 is there */ 705 | if (expect[6] != buf[6]) 706 | return -1; 707 | 708 | acm->wavc_file = 1; 709 | return 0; 710 | } 711 | 712 | static int read_header(ACMStream *acm) 713 | { 714 | unsigned int tmp; 715 | 716 | /* read header */ 717 | 718 | GET_BITS(tmp, acm, 24); 719 | if (tmp == WAVC_ID) { 720 | GET_BITS(tmp, acm, 8); 721 | if (tmp != 'C') 722 | return ACM_ERR_NOT_ACM; 723 | if (read_wavc_header(acm) < 0) 724 | return ACM_ERR_NOT_ACM; 725 | GET_BITS(tmp, acm, 24); 726 | } 727 | if (tmp != ACM_ID) 728 | return ACM_ERR_NOT_ACM; 729 | acm->info.acm_id = tmp; 730 | 731 | GET_BITS(acm->info.acm_version, acm, 8); 732 | if (acm->info.acm_version != 1) 733 | return ACM_ERR_NOT_ACM; 734 | GET_BITS(acm->total_values, acm, 16); 735 | GET_BITS(tmp, acm, 16); 736 | acm->total_values += tmp << 16; 737 | if (acm->total_values == 0) 738 | return ACM_ERR_NOT_ACM; 739 | GET_BITS(acm->info.channels, acm, 16); 740 | if (acm->info.channels < 1 || acm->info.channels > 2) 741 | return ACM_ERR_NOT_ACM; 742 | acm->info.acm_channels = acm->info.channels; 743 | GET_BITS(acm->info.rate, acm, 16); 744 | if (acm->info.rate < 4096) 745 | return ACM_ERR_NOT_ACM; 746 | 747 | GET_BITS(acm->info.acm_level, acm, 4); 748 | GET_BITS(acm->info.acm_rows, acm, 12); 749 | if (!acm->info.acm_rows) 750 | return ACM_ERR_NOT_ACM; 751 | return 0; 752 | } 753 | 754 | /*********************************************** 755 | * Public functions 756 | ***********************************************/ 757 | 758 | int acm_open_decoder(ACMStream **res, void *arg, acm_io_callbacks io_cb, int force_chans) 759 | { 760 | int err = ACM_ERR_OTHER; 761 | ACMStream *acm; 762 | 763 | acm = malloc(sizeof(*acm)); 764 | if (!acm) 765 | return err; 766 | memset(acm, 0, sizeof(*acm)); 767 | 768 | acm->io_arg = arg; 769 | acm->io = io_cb; 770 | 771 | if (acm->io.get_length_func) { 772 | acm->data_len = acm->io.get_length_func(acm->io_arg); 773 | } else { 774 | acm->data_len = 0; 775 | } 776 | 777 | acm->buf_max = ACM_BUFLEN; 778 | acm->buf = malloc(acm->buf_max); 779 | if (!acm->buf) 780 | goto err_out; 781 | 782 | /* read header data */ 783 | err = ACM_ERR_NOT_ACM; 784 | if (read_header(acm) < 0) 785 | goto err_out; 786 | 787 | /* 788 | * Overwrite channel info if requested, if force_chans == 0 789 | * use channel count from the header. 790 | * For force_chans == -1, assume that "plain" ACM files are always stereo 791 | * (there are many plain ACM files in the wild that are really stereo 792 | * even though the header specifies 1 channel), but still trust the 793 | * header of WAVC ACM files, as those seem to be correct. 794 | */ 795 | if (force_chans > 0) 796 | acm->info.channels = force_chans; 797 | else if (force_chans == -1 && !acm->wavc_file && acm->info.channels < 2) 798 | acm->info.channels = 2; 799 | /* else if force_chans == 0, trust the file's header */ 800 | 801 | /* calculate blocks */ 802 | acm->info.acm_cols = 1 << acm->info.acm_level; 803 | acm->wrapbuf_len = 2 * acm->info.acm_cols - 2; 804 | acm->block_len = acm->info.acm_rows * acm->info.acm_cols; 805 | 806 | /* allocate */ 807 | acm->block = malloc(acm->block_len * sizeof(int)); 808 | acm->wrapbuf = malloc(acm->wrapbuf_len * sizeof(int)); 809 | acm->ampbuf = malloc(0x10000 * sizeof(int)); 810 | acm->midbuf = acm->ampbuf + 0x8000; 811 | 812 | memset(acm->wrapbuf, 0, acm->wrapbuf_len * sizeof(int)); 813 | 814 | *res = acm; 815 | return ACM_OK; 816 | 817 | err_out: 818 | /* disable callbacks */ 819 | memset(&acm->io, 0, sizeof(acm->io)); 820 | acm->io_arg = NULL; 821 | 822 | acm_close(acm); 823 | return err; 824 | } 825 | 826 | int acm_read(ACMStream *acm, void *dst, unsigned numbytes, 827 | int bigendianp, int wordlen, int sgned) 828 | { 829 | int avail, gotbytes = 0, err; 830 | int *src, numwords; 831 | 832 | if (wordlen == 2) 833 | numwords = numbytes / 2; 834 | else 835 | return ACM_ERR_BADFMT; 836 | 837 | if (acm->stream_pos >= acm->total_values) 838 | return 0; 839 | 840 | if (!acm->block_ready) { 841 | err = decode_block(acm); 842 | if (err == ACM_EXPECTED_EOF) 843 | return 0; 844 | if (err < 0) 845 | return err; 846 | } 847 | 848 | /* check how many words can be read */ 849 | avail = acm->block_len - acm->block_pos; 850 | if (avail < numwords) 851 | numwords = avail; 852 | 853 | if (acm->stream_pos + numwords > acm->total_values) 854 | numwords = acm->total_values - acm->stream_pos; 855 | 856 | if (acm->info.channels > 1) 857 | numwords -= numwords % acm->info.channels; 858 | 859 | /* convert, but if dst == NULL, simulate */ 860 | if (dst != NULL) { 861 | src = acm->block + acm->block_pos; 862 | gotbytes = output_values(src, dst, numwords, 863 | acm->info.acm_level, 864 | bigendianp, wordlen, sgned); 865 | } else 866 | gotbytes = numwords * wordlen; 867 | 868 | if (gotbytes >= 0) { 869 | acm->stream_pos += numwords; 870 | acm->block_pos += numwords; 871 | if (acm->block_pos == acm->block_len) 872 | acm->block_ready = 0; 873 | } 874 | 875 | return gotbytes; 876 | } 877 | 878 | void acm_close(ACMStream *acm) 879 | { 880 | if (acm == NULL) 881 | return; 882 | if (acm->io.close_func) 883 | acm->io.close_func(acm->io_arg); 884 | if (acm->buf) 885 | free(acm->buf); 886 | if (acm->block) 887 | free(acm->block); 888 | if (acm->wrapbuf) 889 | free(acm->wrapbuf); 890 | if (acm->ampbuf) 891 | free(acm->ampbuf); 892 | free(acm); 893 | } 894 | 895 | -------------------------------------------------------------------------------- /src/libacm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * libacm - Interplay ACM audio decoder. 3 | * 4 | * Copyright (c) 2004-2010, Marko Kreen 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __LIBACM_H 20 | #define __LIBACM_H 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | #define LIBACM_VERSION "1.3" 27 | 28 | #define ACM_ID 0x032897 29 | #define ACM_WORD 2 30 | 31 | #define ACM_OK 0 32 | #define ACM_ERR_OTHER -1 33 | #define ACM_ERR_OPEN -2 34 | #define ACM_ERR_NOT_ACM -3 35 | #define ACM_ERR_READ_ERR -4 36 | #define ACM_ERR_BADFMT -5 37 | #define ACM_ERR_CORRUPT -6 38 | #define ACM_ERR_UNEXPECTED_EOF -7 39 | #define ACM_ERR_NOT_SEEKABLE -8 40 | 41 | typedef struct ACMInfo { 42 | unsigned channels; /* number of sound channels (1: mono, 2: stereo */ 43 | unsigned rate; /* samplerate */ 44 | unsigned acm_id; 45 | unsigned acm_version; 46 | unsigned acm_channels; /* channels from header (often wrong) */ 47 | unsigned acm_level; 48 | unsigned acm_cols; /* 1 << acm_level */ 49 | unsigned acm_rows; 50 | } ACMInfo; 51 | 52 | typedef struct { 53 | /* 54 | * Read up to "n" items with "size" bytes each into the buf at "ptr", 55 | * kinda like fread(). 56 | * "datasrc" is the "io_arg" that was passed to acm_open_decoder() 57 | * 58 | * return the number of items read (not numbytes, but numbytes/size) 59 | * or 0 on EOF (end of file) 60 | * or a negative value on error 61 | */ 62 | int (*read_func)(void *ptr, int size, int n, void *datasrc); 63 | /* optional, must support seeking into start*/ 64 | int (*seek_func)(void *datasrc, int offset, int whence); 65 | /* optional, called on acm_close */ 66 | int (*close_func)(void *datasrc); 67 | /* returns size in bytes*/ 68 | int (*get_length_func)(void *datasrc); 69 | } acm_io_callbacks; 70 | 71 | struct ACMStream { 72 | ACMInfo info; 73 | unsigned total_values; /* number of sound samples in the ACM file */ 74 | 75 | /* acm data stream */ 76 | void *io_arg; 77 | acm_io_callbacks io; 78 | unsigned data_len; 79 | 80 | /* acm stream buffer */ 81 | unsigned char *buf; 82 | unsigned buf_max, buf_size, buf_pos, bit_avail; 83 | unsigned bit_data; 84 | unsigned buf_start_ofs; 85 | 86 | /* block lengths (in samples) */ 87 | unsigned block_len; 88 | unsigned wrapbuf_len; 89 | /* buffers */ 90 | int *block; 91 | int *wrapbuf; 92 | int *ampbuf; 93 | int *midbuf; /* pointer into ampbuf */ 94 | /* result */ 95 | unsigned block_ready:1; 96 | unsigned file_eof:1; 97 | unsigned wavc_file:1; 98 | unsigned stream_pos; /* in words. absolute */ 99 | unsigned block_pos; /* in words, relative */ 100 | }; 101 | typedef struct ACMStream ACMStream; 102 | 103 | /* decode.c */ 104 | 105 | /* 106 | * Open ACMStream from acm_io_callbacks. 107 | * - res: if opening the decoder succeeds, the new ACMStream will be assigned to *res 108 | * - io_arg: will be passed to the acm_io_callbacks as "datasrc" argument 109 | * - io: your custom callbacks to read the data 110 | * - force_chans >= 1: force the file to be decoded and played with that 111 | * many channels, regardless of number of channels in the file header 112 | * - force_chans == 0: assume the ACM file's header contains the 113 | * correct amount of channels (this is not always the case, stereo 114 | * files tagged as mono exist in some games!) 115 | * - force_chans == -1: quirk mode: for plain ACM files stereo 116 | * is assumed, for WAVC files the header's value is used 117 | * 118 | * returns ACM_OK if opening was successful, otherwise an ACM_ERR_* code 119 | */ 120 | int acm_open_decoder(ACMStream **res, void *io_arg, acm_io_callbacks io, int force_chans); 121 | 122 | /* 123 | * Read up to "nbytes" bytes of audio samples from ACMStream "acm" into buffer "buf". 124 | * "bigendianp", "wordlen" and "sgned" specify the format you want the returned samples 125 | * to have, for example 0, 2, 1 for "little endian, 16bit (2byte), signed" (s16le). 126 | * - bigendianp: 0 for samples in little endian byteorder, 1 for big endian 127 | * - wordlen: only 2 for 16bit (2byte) samples is currently supported 128 | * - sgned: 1 for signed samples, 0 for unsigned samples 129 | * 130 | * returns the amount of bytes have been successfully read into buf 131 | * or 0 on EOF (nothing left to read in file) 132 | * or a value < 0 (ACM_ERR_*) on error 133 | */ 134 | int acm_read(ACMStream *acm, void *buf, unsigned nbytes, 135 | int bigendianp, int wordlen, int sgned); 136 | void acm_close(ACMStream *acm); 137 | 138 | /* util.c */ 139 | 140 | /* 141 | * Open ACMStream from file. 142 | * - acm: if opening the decoder succeeds, the new ACMStream will be assigned to *acm 143 | * - filename: filename (incl. path) to the ACM file to be opened 144 | * - force_chans >= 1: force the file to be decoded and played with that 145 | * many channels, regardless of number of channels in the file header 146 | * - force_chans == 0: assume the ACM file's header contains the 147 | * correct amount of channels (this is not always the case, stereo 148 | * files tagged as mono exist in some games!) 149 | * - force_chans == -1: quirk mode: for plain ACM files stereo 150 | * is assumed, for WAVC files the header's value is used 151 | * 152 | * returns ACM_OK if opening was successful, otherwise an ACM_ERR_* code 153 | */ 154 | int acm_open_file(ACMStream **acm, const char *filename, int force_chans); 155 | const ACMInfo *acm_info(ACMStream *acm); 156 | int acm_seekable(ACMStream *acm); 157 | unsigned acm_bitrate(ACMStream *acm); 158 | unsigned acm_rate(ACMStream *acm); 159 | unsigned acm_channels(ACMStream *acm); 160 | unsigned acm_raw_total(ACMStream *acm); 161 | unsigned acm_raw_tell(ACMStream *acm); 162 | unsigned acm_pcm_total(ACMStream *acm); 163 | unsigned acm_pcm_tell(ACMStream *acm); 164 | unsigned acm_time_total(ACMStream *acm); 165 | unsigned acm_time_tell(ACMStream *acm); 166 | int acm_read_loop(ACMStream *acm, void *dst, unsigned len, 167 | int bigendianp, int wordlen, int sgned); 168 | int acm_seek_pcm(ACMStream *acm, unsigned pcm_pos); 169 | int acm_seek_time(ACMStream *acm, unsigned pos_ms); 170 | const char *acm_strerror(int err); 171 | 172 | #ifdef __cplusplus 173 | } // extern "C" 174 | #endif 175 | 176 | #endif 177 | 178 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Utility functions for libacm. 3 | * 4 | * Copyright (c) 2004-2010, Marko Kreen 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifdef HAVE_CONFIG_H 20 | #include "config.h" 21 | #endif 22 | 23 | #include 24 | #include 25 | 26 | #include "libacm.h" 27 | 28 | #define WAVC_HEADER_LEN 28 29 | #define ACM_HEADER_LEN 14 30 | 31 | /* 32 | * error strings 33 | */ 34 | static const char *_errlist[] = { 35 | "No error", 36 | "ACM error", 37 | "Cannot open file", 38 | "Not an ACM file", 39 | "Read error", 40 | "Bad format", 41 | "Corrupt file", 42 | "Unexcpected EOF", 43 | "Stream not seekable" 44 | }; 45 | 46 | const char *acm_strerror(int err) 47 | { 48 | int nerr = sizeof(_errlist) / sizeof(char *); 49 | if ((-err) < 0 || (-err) >= nerr) 50 | return "Unknown error"; 51 | return _errlist[-err]; 52 | } 53 | 54 | /* 55 | * File IO using stdio 56 | */ 57 | 58 | static int _read_file(void *ptr, int size, int n, void *arg) 59 | { 60 | FILE *f = (FILE *)arg; 61 | return fread(ptr, size, n, f); 62 | } 63 | 64 | static int _close_file(void *arg) 65 | { 66 | FILE *f = (FILE *)arg; 67 | return fclose(f); 68 | } 69 | 70 | static int _seek_file(void *arg, int offset, int whence) 71 | { 72 | FILE *f = (FILE *)arg; 73 | return fseek(f, offset, whence); 74 | } 75 | 76 | static int _get_length_file(void *arg) 77 | { 78 | FILE *f = (FILE *)arg; 79 | int res, pos, len = -1; 80 | 81 | pos = ftell(f); 82 | if (pos < 0) 83 | return -1; 84 | 85 | res = fseek(f, 0, SEEK_END); 86 | if (res >= 0) { 87 | len = ftell(f); 88 | fseek(f, pos, SEEK_SET); 89 | } 90 | return len; 91 | } 92 | 93 | int acm_open_file(ACMStream **res, const char *filename, int force_chans) 94 | { 95 | int err; 96 | FILE *f; 97 | acm_io_callbacks io; 98 | ACMStream *acm; 99 | 100 | if ((f = fopen(filename, "rb")) == NULL) 101 | return ACM_ERR_OPEN; 102 | 103 | memset(&io, 0, sizeof(io)); 104 | io.read_func = _read_file; 105 | io.seek_func = _seek_file; 106 | io.close_func = _close_file; 107 | io.get_length_func = _get_length_file; 108 | 109 | if ((err = acm_open_decoder(&acm, f, io, force_chans)) < 0) { 110 | fclose(f); 111 | return err; 112 | } 113 | *res = acm; 114 | return 0; 115 | } 116 | 117 | /* 118 | * utility functions 119 | */ 120 | 121 | static unsigned pcm2time(ACMStream *acm, unsigned long long pcm) 122 | { 123 | return pcm * 1000 / acm->info.rate; 124 | /* return ((10 * pcm) / acm->info.rate) * 100; */ 125 | } 126 | 127 | static unsigned time2pcm(ACMStream *acm, unsigned long long time_ms) 128 | { 129 | return time_ms * acm->info.rate / 1000; 130 | /* return (time_ms / 100) * (acm->info.rate / 10); */ 131 | } 132 | 133 | /* 134 | * info functions 135 | */ 136 | 137 | const ACMInfo *acm_info(ACMStream *acm) 138 | { 139 | return &acm->info; 140 | } 141 | 142 | unsigned acm_rate(ACMStream *acm) 143 | { 144 | return acm->info.rate; 145 | } 146 | 147 | unsigned acm_channels(ACMStream *acm) 148 | { 149 | return acm->info.channels; 150 | } 151 | 152 | int acm_seekable(ACMStream *acm) 153 | { 154 | return acm->data_len > 0; 155 | } 156 | 157 | unsigned acm_bitrate(ACMStream *acm) 158 | { 159 | unsigned long long bits, time, bitrate = 0; 160 | 161 | if (acm_raw_total(acm) == 0) 162 | return 13000; 163 | 164 | time = acm_time_total(acm); 165 | if (time > 0) { 166 | bits = 8 * acm_raw_total(acm); 167 | bitrate = 1000 * bits / time; 168 | } 169 | return bitrate; 170 | } 171 | 172 | unsigned acm_pcm_tell(ACMStream *acm) 173 | { 174 | return acm->stream_pos / acm->info.channels; 175 | } 176 | 177 | unsigned acm_pcm_total(ACMStream *acm) 178 | { 179 | return acm->total_values / acm->info.channels; 180 | } 181 | 182 | unsigned acm_time_tell(ACMStream *acm) 183 | { 184 | return pcm2time(acm, acm_pcm_tell(acm)); 185 | } 186 | 187 | unsigned acm_time_total(ACMStream *acm) 188 | { 189 | return pcm2time(acm, acm_pcm_total(acm)); 190 | } 191 | 192 | unsigned acm_raw_tell(ACMStream *acm) 193 | { 194 | return acm->buf_start_ofs + acm->buf_pos; 195 | } 196 | 197 | unsigned acm_raw_total(ACMStream *acm) 198 | { 199 | return acm->data_len; 200 | } 201 | 202 | /* 203 | * seeking 204 | */ 205 | 206 | int acm_seek_time(ACMStream *acm, unsigned time_ms) 207 | { 208 | int res = acm_seek_pcm(acm, time2pcm(acm, time_ms)); 209 | if (res <= 0) 210 | return res; 211 | return pcm2time(acm, res); 212 | } 213 | 214 | int acm_seek_pcm(ACMStream *acm, unsigned pcm_pos) 215 | { 216 | unsigned word_pos = pcm_pos * acm->info.channels; 217 | unsigned start_ofs; 218 | 219 | if (word_pos < acm->stream_pos) { 220 | if (acm->io.seek_func == NULL) 221 | return ACM_ERR_NOT_SEEKABLE; 222 | 223 | start_ofs = ACM_HEADER_LEN; 224 | if (acm->wavc_file) 225 | start_ofs += WAVC_HEADER_LEN; 226 | 227 | if (acm->io.seek_func(acm->io_arg, start_ofs, SEEK_SET) < 0) 228 | return ACM_ERR_NOT_SEEKABLE; 229 | 230 | acm->file_eof = 0; 231 | acm->buf_pos = 0; 232 | acm->buf_size = 0; 233 | acm->bit_avail = 0; 234 | acm->bit_data = 0; 235 | 236 | acm->stream_pos = 0; 237 | acm->block_pos = 0; 238 | acm->block_ready = 0; 239 | acm->buf_start_ofs = ACM_HEADER_LEN; 240 | 241 | memset(acm->wrapbuf, 0, acm->wrapbuf_len * sizeof(int)); 242 | } 243 | while (acm->stream_pos < word_pos) { 244 | int step = 2048, res; 245 | if (acm->stream_pos + step > word_pos) 246 | step = word_pos - acm->stream_pos; 247 | 248 | res = acm_read(acm, NULL, step*2, 0,2,1); 249 | if (res < 1) 250 | break; 251 | } 252 | return acm->stream_pos / acm->info.channels; 253 | } 254 | 255 | /* 256 | * read loop - full block reading 257 | */ 258 | int acm_read_loop(ACMStream *acm, void *dst, unsigned bytes, 259 | int bigendianp, int wordlen, int sgned) 260 | { 261 | unsigned char *dstp = dst; 262 | int res, got = 0; 263 | while (bytes > 0) { 264 | res = acm_read(acm, dstp, bytes, bigendianp, wordlen, sgned); 265 | if (res > 0) { 266 | if (dstp) 267 | dstp += res; 268 | got += res; 269 | bytes -= res; 270 | } else { 271 | if (res < 0 && got == 0) 272 | return res; 273 | break; 274 | } 275 | } 276 | return got; 277 | } 278 | 279 | --------------------------------------------------------------------------------