From cf94899f4f4d8535074db6421245b973d2fcde8c Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Mon, 19 Sep 2016 05:58:51 +0300 Subject: [PATCH] add mikmod source --- libs/mikmod/COPYING.LESSER | 502 +++++ libs/mikmod/COPYING.LIB | 481 +++++ libs/mikmod/GNUmakefile | 19 + libs/mikmod/README | 407 ++++ libs/mikmod/README.dos | 110 + libs/mikmod/config.h | 85 + libs/mikmod/depackers/mmcmp.c | 433 ++++ libs/mikmod/depackers/pp20.c | 208 ++ libs/mikmod/depackers/s404.c | 393 ++++ libs/mikmod/depackers/xpk.c | 427 ++++ libs/mikmod/drivers/dos/dosdma.c | 214 ++ libs/mikmod/drivers/dos/dosdma.h | 190 ++ libs/mikmod/drivers/dos/dosgus.c | 1907 ++++++++++++++++++ libs/mikmod/drivers/dos/dosgus.h | 514 +++++ libs/mikmod/drivers/dos/dosirq.c | 323 +++ libs/mikmod/drivers/dos/dosirq.h | 122 ++ libs/mikmod/drivers/dos/dossb.c | 575 ++++++ libs/mikmod/drivers/dos/dossb.h | 341 ++++ libs/mikmod/drivers/dos/doswss.c | 577 ++++++ libs/mikmod/drivers/dos/doswss.h | 217 ++ libs/mikmod/drivers/dos/libgus.h | 402 ++++ libs/mikmod/drivers/drv_oss.c | 417 ++++ libs/mikmod/drivers/drv_sb.c | 235 +++ libs/mikmod/drivers/drv_sdl.c | 205 ++ libs/mikmod/drivers/drv_ultra.c | 1020 ++++++++++ libs/mikmod/drivers/drv_wss.c | 217 ++ libs/mikmod/include/mikmod.h | 879 ++++++++ libs/mikmod/include/mikmod_ctype.h | 84 + libs/mikmod/include/mikmod_internals.h | 866 ++++++++ libs/mikmod/loaders/load_it.c | 1020 ++++++++++ libs/mikmod/loaders/load_mod.c | 514 +++++ libs/mikmod/loaders/load_s3m.c | 476 +++++ libs/mikmod/loaders/load_xm.c | 840 ++++++++ libs/mikmod/mmio/mmalloc.c | 142 ++ libs/mikmod/mmio/mmerror.c | 315 +++ libs/mikmod/mmio/mmio.c | 537 +++++ libs/mikmod/playercode/mdreg.c | 162 ++ libs/mikmod/playercode/mdriver.c | 968 +++++++++ libs/mikmod/playercode/mdulaw.c | 2119 ++++++++++++++++++++ libs/mikmod/playercode/mloader.c | 670 +++++++ libs/mikmod/playercode/mlreg.c | 67 + libs/mikmod/playercode/mlutil.c | 330 +++ libs/mikmod/playercode/mplayer.c | 3429 ++++++++++++++++++++++++++++++++ libs/mikmod/playercode/munitrk.c | 303 +++ libs/mikmod/playercode/mwav.c | 388 ++++ libs/mikmod/playercode/npertab.c | 48 + libs/mikmod/playercode/sloader.c | 522 +++++ libs/mikmod/playercode/virtch.c | 1351 +++++++++++++ libs/mikmod/playercode/virtch2.c | 1368 +++++++++++++ libs/mikmod/playercode/virtch_common.c | 467 +++++ libs/mikmod/posix/memcmp.c | 22 + libs/mikmod/posix/strcasecmp.c | 23 + libs/mikmod/posix/strstr.c | 19 + 53 files changed, 28470 insertions(+) create mode 100644 libs/mikmod/COPYING.LESSER create mode 100644 libs/mikmod/COPYING.LIB create mode 100644 libs/mikmod/GNUmakefile create mode 100644 libs/mikmod/README create mode 100644 libs/mikmod/README.dos create mode 100644 libs/mikmod/config.h create mode 100644 libs/mikmod/depackers/mmcmp.c create mode 100644 libs/mikmod/depackers/pp20.c create mode 100644 libs/mikmod/depackers/s404.c create mode 100644 libs/mikmod/depackers/xpk.c create mode 100644 libs/mikmod/drivers/dos/dosdma.c create mode 100644 libs/mikmod/drivers/dos/dosdma.h create mode 100644 libs/mikmod/drivers/dos/dosgus.c create mode 100644 libs/mikmod/drivers/dos/dosgus.h create mode 100644 libs/mikmod/drivers/dos/dosirq.c create mode 100644 libs/mikmod/drivers/dos/dosirq.h create mode 100644 libs/mikmod/drivers/dos/dossb.c create mode 100644 libs/mikmod/drivers/dos/dossb.h create mode 100644 libs/mikmod/drivers/dos/doswss.c create mode 100644 libs/mikmod/drivers/dos/doswss.h create mode 100644 libs/mikmod/drivers/dos/libgus.h create mode 100644 libs/mikmod/drivers/drv_oss.c create mode 100644 libs/mikmod/drivers/drv_sb.c create mode 100644 libs/mikmod/drivers/drv_sdl.c create mode 100644 libs/mikmod/drivers/drv_ultra.c create mode 100644 libs/mikmod/drivers/drv_wss.c create mode 100644 libs/mikmod/include/mikmod.h create mode 100644 libs/mikmod/include/mikmod_ctype.h create mode 100644 libs/mikmod/include/mikmod_internals.h create mode 100644 libs/mikmod/loaders/load_it.c create mode 100644 libs/mikmod/loaders/load_mod.c create mode 100644 libs/mikmod/loaders/load_s3m.c create mode 100644 libs/mikmod/loaders/load_xm.c create mode 100644 libs/mikmod/mmio/mmalloc.c create mode 100644 libs/mikmod/mmio/mmerror.c create mode 100644 libs/mikmod/mmio/mmio.c create mode 100644 libs/mikmod/playercode/mdreg.c create mode 100644 libs/mikmod/playercode/mdriver.c create mode 100644 libs/mikmod/playercode/mdulaw.c create mode 100644 libs/mikmod/playercode/mloader.c create mode 100644 libs/mikmod/playercode/mlreg.c create mode 100644 libs/mikmod/playercode/mlutil.c create mode 100644 libs/mikmod/playercode/mplayer.c create mode 100644 libs/mikmod/playercode/munitrk.c create mode 100644 libs/mikmod/playercode/mwav.c create mode 100644 libs/mikmod/playercode/npertab.c create mode 100644 libs/mikmod/playercode/sloader.c create mode 100644 libs/mikmod/playercode/virtch.c create mode 100644 libs/mikmod/playercode/virtch2.c create mode 100644 libs/mikmod/playercode/virtch_common.c create mode 100644 libs/mikmod/posix/memcmp.c create mode 100644 libs/mikmod/posix/strcasecmp.c create mode 100644 libs/mikmod/posix/strstr.c diff --git a/libs/mikmod/COPYING.LESSER b/libs/mikmod/COPYING.LESSER new file mode 100644 index 0000000..4362b49 --- /dev/null +++ b/libs/mikmod/COPYING.LESSER @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/libs/mikmod/COPYING.LIB b/libs/mikmod/COPYING.LIB new file mode 100644 index 0000000..5bc8fb2 --- /dev/null +++ b/libs/mikmod/COPYING.LIB @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/libs/mikmod/GNUmakefile b/libs/mikmod/GNUmakefile new file mode 100644 index 0000000..54ad1c3 --- /dev/null +++ b/libs/mikmod/GNUmakefile @@ -0,0 +1,19 @@ +src = $(wildcard drivers/*.c) \ + $(wildcard loaders/*.c) \ + $(wildcard mmio/*.c) \ + $(wildcard deackers/*.c) \ + $(wildcard posix/*.c) \ + $(wildcard playercode/*.c) +obj = $(src:.c=.o) +alib = libmikmod.a + +def = -DHAVE_CONFIG_H -DMIKMOD_BUILD +inc = -I. -Iinclude +CFLAGS = -pedantic -Wall -g $(def) $(inc) `pkg-config --cflags sdl` + +$(alib): $(obj) + $(AR) rcs $@ $(obj) + +.PHONY: clean +clean: + rm -f $(obj) $(bin) diff --git a/libs/mikmod/README b/libs/mikmod/README new file mode 100644 index 0000000..4318cfb --- /dev/null +++ b/libs/mikmod/README @@ -0,0 +1,407 @@ + Hello folks! + + +This is libmikmod, version 3.3.10, a portable sound library for Unix +and other systems. Check out the file 'NEWS' for more history information. + + +>> BUILDING LIBMIKMOD +--------------------- + +- If you're building libmikmod for Windows, refer to the 'README' file + located in the 'win32' subdirectory. + +- If you're building libmikmod for Mac OS X, refer to the 'README' file + located in the 'macosx' subdirectory. + +- If you're building libmikmod for Android, refer to the 'README' file + located in the 'android' subdirectory. + +- If you're building libmikmod for DOS, refer to the 'README' file + located in the 'dos' subdirectory. + +- If you're building libmikmod for OS/2, refer to the 'README' file + located in the 'os2' subdirectory. + +- If you're building libmikmod for AmigaOS, or its variants like MorphOS + or AROS, refer to the 'README' located in the 'amiga' subdirectory. + +- If you're building libmikmod for MacOS, refer to the 'README' file + located in the 'macintosh' subdirectory. + +- If you're building libmikmod for dreamcast, gp32 or psp platforms, + refer to the corresponding 'README' files located under the 'dc', + 'gp32' or 'psp' subdirectory. + +- If you're building libmikmod under any other system which is not a + Unix flavour, then be warned that your platform is probably not + supported and that libmikmod will probably not build out of the box. + Drop us a note and we'll see what we can do for this situation. + +So you're on a good old Unix workstation, aren't you ? + +You'll need an ANSI C compiler to build libmikmod. If your system does +not come with an ANSI C compiler, you might try the GNU C compiler, gcc. +If you're building on a 32 bit architecture, your compiler must provide +a 64 bit integer type (usually 'long long'). + +To prevent clobbering the sources, I recommend building libmikmod in an +alternate directory, for example 'build': + + mkdir build + cd build + +In this directory, run libmikmod's configure script: + + ../configure + +The configure script will attempt to guess correct values for various +system-dependent variables used during the build process, and will +create appropriate Makefiles for proper compilation. + +If you're not familiar with configure scripts and their standard +options, you can find more general information about them in the file +INSTALL. + +The default behaviour of the configure script is to create both a static +and a shared library, with as many drivers as possible, which are +dynamically loaded whenever possible. However, it can be given several +options to tweak your configuration of libmikmod: + +The --enable-af, --enable-alsa, --enable-esd, --enable-oss, +--enable-sam9407 and --enable-ultra options will compile respectively +the Digital AudioFile, Advanced Linux Sound Architecture (ALSA), +Enlightened Sound Daemon, Open Sound System (OSS), sam9407 and Linux +Ultrasound drivers. + +Since the configure script will search for the appropriate include files +and libraries, to compile as much drivers as possible, these options are +mostly useful in their negative form: + ../configure --disable-esd +will configure libmikmod without the Enlightened Sound Daemon driver, +even if all the necessary files for compiling are present on the +system. + +The --enable-dl option enables the dynamic load of the alsa, esd and +ultra drivers at runtime, if your systems support it. This option is +enabled by default if the build system supports it, so it is more useful +in its negative form: + ../configure --disable-dl +will configure libmikmod without the dynamic loading facility. + +The --enable-threads option enables the creation of a thread-safe +libmikmod library, if your system provides POSIX threads. This option is +enabled by default, so it is more useful in its negative form: + ../configure --disable-threads +will configure for a non thread-safe version of libmikmod. + +The --enable-shared and --enable-static options control whether a static +library, a shared library or both should be built. + +The --enable-debug option creates a debug version of libmikmod. + +After you've successfully run configure, simply run + + make + +to get all things build. Then, run + + make install + +to have the library installed. Depending on where you choose to install +it (using the --prefix= option to configure), you may need root +privileges for this operation. + + +>> DRIVER PARAMETERS +-------------------- + +Until a good place to put this information is found, here is the list of +parameters recognized by the drivers, as well as the driver alias list. +When specifying multiple parameters, use a comma (,) to separate the +different parameters, for example: somevalue=1,someothervalue=2 + +- AudioFile (alias "audiofile") + machine= same syntax as the AUDIOFILE environment variable. + +- AIX ("AIX") [AIX only] + buffer= logarithmic size of the buffer, in the range 12-19. Default + is 15. + +- DART ("dart") [OS/2 only] + buffer= logarithmic fragment size, in the range 12-16. Default is + computed to a bit more than 1/4" of playback. + count= fragment count, in the range 2-8. Default is 2. + device= waveaudio device number, in the range 0-8. Default is 0 (use + default waveaudio device). + +- DirectX ("ds") [Win32 only] + buffer= logarithmic size of the buffer, in the range 12-19. Default + is 16. + globalfocus + always play music, even if the application has not the + focus. Required for full-screen applications. + +- EsounD ("esd") [Unix only] + machine= same syntax as the ESPEAKER environment variable. + +- HP ("hp") [HP-UX only] + buffer= logarithmic size of the buffer, in the range 12-19. Default + is 15. + headphone redirects the output to the headphone port. + +- MacOS ("mac") [MacOS only] + buffer= logarithmic size of the buffer, in the range 10-16. Default + is 12. + +- OS/2 MMPM ("os2") [OS/2 only] + buffer= logarithmic size of the buffer, in the range 12-16. Default + is computed to a bit more than 1/4" of playback. + device= waveaudio device number, in the range 0-8. Default is 0 (use + default waveaudio device). + +- OSS ("oss") [Unix only] + card= card number. Default is the card whose driver was loaded + first. + buffer= logarithmic fragment size, in the range 7-17. Default is 14. + Replaces the MM_FRAGSIZE environment variable, which is now + deprecated. + count= fragment count, in the range 2-255. Default is 16. + Replaces the MM_NUMFRAGS environment variable, which is now + deprecated. + +- Piped output ("pipe") [Unix only] + pipe= Pipe command (mandatory). + +- SGI audio library ("sgi") [IRIX only] + fragsize= buffer size for libmikmod internal use. + Replaces the MM_SGI_FRAGSIZE environment variable, which is + now deprecated. + bufsize= buffer size for the audio library. + Replaces the MM_SGI_BUFSIZE environment variable, which is + now deprecated. + +- Disk writers in raw and wav formats ("raw", "wav" and "aiff") + file= Output file name. Default is music.raw for the raw driver + and music.wav for the wav driver. + +- OpenBSD sndio ("sndio") [OpenBSD only] + buffer= logarithmic fragment size, in the range 7-17. Default is 12. + +- Sun/Solaris/NetBSD/OpenBSD audio ("audio") + [SunOS, Solaris, NetBSD, OpenBSD only] + buffer= logarithmic fragment size, in the range 7-17. Default is 12. + headphone on SunOS/Solaris only, redirects the output to the headphone + port. + speaker on SunOS/Solaris only, redirects the output to the speaker. + +- Linux sam9407-based soundcards ("sam9407") [Linux only] + card= card number. Default is first card. + +- NoSound ("nosound"), Standard output ("stdout"), Ultrasound ("ultra"), + Windows Multimedia ("winmm"), Windows XAudio2 ("xaudio2"), + Amiga AHI ("ahi"), Linux ALSA ("alsa") + These drivers have no options. + + +>> ALSA DRIVER SPECIFIC INFORMATION (Linux specific) +----------------------------------- + +The Advanced Linux Sound Architecture (ALSA) project aims to provide +better sound facilities than the current OSS drivers. You can find +more information on ALSA, including a HOWTO, on the web: + http://www.alsa-project.org + +This version of libmikmod ALSA driver works with ALSA versions 1.0.x. +ALSA versions 0.9.x and earlier are not supported any more. + + +>> ENLIGHTENED SOUND DAEMON SPECIFIC INFORMATION (Unix specific) +------------------------------------------------ + +The Enlightened Sound Daemon (EsounD) development has long been stopped, +but libmikmod still supports it. libmikmod should work with any esound +version starting from 0.2.18, although the latest 0.2.41 is recommended: + http://ftp.gnome.org/pub/gnome/sources/esound/0.2/ + +You can find more information on EsounD on the web: + http://www.tux.org/~ricdude/EsounD.html + +If the esd daemon dies, libmikmod will try to reconnect every 5 seconds +and every new module, if a module ends. So, you can safely restart esd +and wait 5 seconds, and voila! Sound is back... + +If you run esd and a libmikmod application on the same machine, +everything should work fine. However, if there is a real network +connection, synchronization problems can occur. + +If sound clicks or gets chopped, then you've likely got a +synchronization problem. Pausing the player for a second should cause +the problem to disappear. If there's still problems, perhaps your +network is not fast enough. Lowering the playback rate will hopefully +solve the problem. + +Also, the performance of the esd is really abominable if the esd +playback frequency can't be divided by the libmikmod playback rate. For +example, runinng a libmikmod application at 42000 Hz with esd at 44100 +Hz will sound horrible, and take a lot of CPU time due to resampling. + + +>> SGI DRIVER SPECIFIC INFORMATION (IRIX specific) +---------------------------------- + +The SGI audio driver was written by Stephan Kanthak in 1996 and its +author grants to distribute it with the libmikmod package under the same +restrictions as the library. + +If you encounter any problems concerning crackles or short stops while +playing, feel free to experiment with the values of the fragsize and +bufsize options of the driver. The default values are 20000 for fragsize +and 40000 for bufsize. Increasing bufsize might result in nonstop sound +on slow machines, but increases latency of interactive applications. The +value of fragsize should be set to about half of bufsize in most cases +and needs to be increased only if you own a very slow SGI. + +Common problems + +- libmikmod does not compile on my SGI? + First check out whether you have the SGI audio library (libaudio) or + not. If the audio library is missing you should upgrade to IRIX 5.3 or + newer and you will obtain the media development package automatically + with it. If you have the audio library installed, check out if it is + in the linker path. + + Also, the audio API has been extended in recent IRIX releases (6.4 and + later). The older API used by libmikmod is supposed to be still supported, + please drop me a note if it is not on your IRIX release. + +- Sound is _very_ noisy? + Change sample size to 16 bits. + +- Sound crackles or stops temporarily? + Try to increase the value of the fragsize driver option (default value + is 20000). Switch to mono mode if necessary. + +- libmikmod applications only react very slowly? + This is a typical effect on SGI machines because the audio library + sets up an internal buffer that seems to be quite large on many + installations. Try to decrease the bufsize driver option (default + value is 40000). + +How to contact the driver author: +Stephan Kanthak +Please cc: me (miod), just in case. + + +>> SUNOS, SOLARIS, NETBSD AND OPENBSD DRIVER SPECIFIC INFORMATION +----------------------------------------------------------------- + +The above mentioned systems use the same interface to the audio device. +The libmikmod driver for this interface is the Sun driver. It was coded +by Valtteri Vuorikoski and updated to libmikmod 3 by Tor +Norbye , and has been modified to work under NetBSD +and OpenBSD by Miodrag Vallat. + +This driver works with old sound hardware using 8 KHz mono ulaw, and +with modern hardware using pcm mono or stereo at any frequency. If your +settings aren't supported by the audio device, sound initialization will +fail. Refer to the audio(7) man page under SunOS/Solaris and the +audio(4) man page under NetBSD/OpenBSD for more details on your audio +hardware and its capabilities. + +On Sun workstations, you might be interested in passing the "headphone" +option to the driver to force output on the headphones, since plugging +the headphones is not enough. + +If you run NetBSD or OpenBSD, the driver does not support the headphone +and speaker parameters, but you can achieve the same effect with +audioctl(1), for example: + audioctl -w play.port=1 +will select the speaker, while a value of 2 would have selected the +headphone. + +If sound is jerky, you can pass the "buffer=xx" option to the driver to +increase its internal buffer size. The default value (when this option +is not used) is 12; the slower your machine, the greater this value has +to be, in the range 7-17. + +If you can't get libmikmod to work with your hardware, you can use its +raw disk writer driver, in 8 bit mono 8 kHz, and send the music.raw file +to /dev/audio with sox, using the following command line: + sox -t raw -c 1 -r 8000 -u -b music.raw -t raw -U -r 8000 \ + -c 1 -b /dev/audio +(or use the piped output driver with this command line) + +Or if you played in 16 bit stereo, you can convert the file to a .au +file: + audioconvert -o music.au -f sun \ + -i rate=44.1k,channels=stereo,encoding=linear16 music.raw +and play the file: + audioplay -p headphone -v 10 music.au + + +>> SAM9407 DRIVER SPECIFIC INFORMATION (Linux specific) +-------------------------------------- + +The SAM9407 driver provides an OSS-compatible driver for the soundcards +based on the sam9407 audio chip (MaxiSound 64 and Terratec EWS, among +others), and provides advanced features such as hardware module +playback. + +You can find more information on this driver on the web: + http://www.anime.net/~sam9407 + +The version of the libmikmod sam9407 driver coincides with the latest sam9407 +driver release available when this version of libmikmod was released; for the +3.1.10 release, this is sam9407 driver v1.0.0. + + +>> THANKS +--------- + +I would like to thank everyone who contributed to libmikmod. Their names +are in the AUTHORS file for the significative contributions, but some +other names can be found in the NEWS file. Thanks a lot! Keeping +libmikmod alive wouldn't be much fun without you. + + +>> LICENSE +---------- + +The libmikmod sound library is covered by the GNU Library General Public +License as published by the Free Software Fundation (you'll find it in +the file COPYING.LIB); either version 2 of the licence, or (at your +option) any later version. + +The GNU Lesser General Public License, version 2.1, in file +COPYING.LESSER, can be considered as a later version of the LGPL, and is +strongly recommended for people who will embed libmikmod in their +application as a shared library. + +Parts of the library (in playercode/mdulaw.c) are derived from the files +libst.h and raw.c from an old version of the sox (SOund eXchange) +package written by Lance Norskog and Jef Poskanzer. The following +copyright notice applies to these parts: + + Copyright (C) 1989 by Jef Poskanzer. + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, provided + that the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. This software is provided "as is" without express or + implied warranty. + + +>> CONTACT AND DOWNLOAD INFO +---------------------------- + +libmikmod home page is located at SourceForge: + + http://mikmod.sourceforge.net/ + http://sourceforge.net/projects/mikmod/ + +There's a mailing list (mikmod-public) for discussing the development +of MikMod (new features, bugs, ideas...) Look for more information on +the web site. + diff --git a/libs/mikmod/README.dos b/libs/mikmod/README.dos new file mode 100644 index 0000000..b6d104a --- /dev/null +++ b/libs/mikmod/README.dos @@ -0,0 +1,110 @@ + Hello folks ! + + +This is libmikmod, version 3.3.10, a portable sound library for DOS. +Comments & feedback are welcome. + + +>> BUILDING LIBMIKMOD +--------------------- + +- If you're not building libmikmod for DOS, then you're lost in the sources. + Go up one directory, and read the main README file. + +This port has been designed to work only with DJGPP compiler. However, it +should not be too complex to make it compile with any other compiler. If you +manage to make libmikmod compile and work with another compiler, we'd like to +hear from you. You'll likely have to write an appropiate makefile, or build +things manually ... + +If you have all proper tools installed, just type + + make -f Makefile.dj + +To make your library ready to use, copy the files 'libmikmod.a' and +'include/mikmod.h' to your '%DJGPP%/lib' and '%DJGPP%/include' directories +respectively. + +HTML documentation of the library, for programmers, is located in the doc +directory. + + +>> SUPPORTED SOUNDCARDS +----------------------- + +Currently three brands of sound cards are supported under DOS: + +- Gravis Ultrasound and compatibles. Not tested with Interwave cards. If + somebody has one and can debug the code under Interwave, please drop us a note + (see email at bottom). + +- SoundBlaster and compatibles. There is only one driver that supports all + flavours of SoundBlasters: + + + SB original (8-bit mono, 22KHz) + + SB 2.0 (DMA autoinit mode -> less clicks, 44KHz 8-bit mono). + + SB Pro (8-bit 22KHz stereo, 8-bit 44KHz mono) + + SB 16+ (8- and 16-bit stereo, up to 44Khz both) + + Note that SB16 should cover AWE32/64 as well... + +- Windows Sound System and compatibles. Most today SoundBlaster clones + (including the CS4236B card used for testing) emulates (in hardware) WSS. + That's because Creative Labs copyrighted the SB16 interface and thus + clonemakers cannot do similar hardware without violating their rights, so + most of them only emulate SB Pro (see above). + + WSS supports up to 44KHz 16-bit stereo. The following playback rates (in both + 8- and 16-bit) are supported: 5510, 6620, 8000, 9600, 11025, 16000, 18900, + 22050, 27420, 32000, 33075, 37800, 44100 and 48000 Hz. + + +>> THANKS +--------- + +We would like to thank everyone who contributed to libmikmod. Their names +are in the AUTHORS file for the significative contributions, but some other +names can be found in the NEWS file. Thanks a lot ! Keeping libmikmod alive +wouldn't be much fun without you. + + +>> LICENSE +---------- + +The libmikmod sound library is covered by the GNU Library General Public +License as published by the Free Software Fundation (you'll find it in the +file COPYING.LIB) ; either version 2 of the licence, or (at your option) +any later version. + +The GNU Lesser General Public License, version 2.1, in file COPYING.LESSER, can +be considered as a later version of the LGPL, and is strongly recommended for +people who will embed libmikmod in their application as a shared library. + + +>> CONTACT AND DOWNLOAD INFO +---------------------------- + +libmikmod home page is located at SourceForge: + + http://mikmod.sourceforge.net/ + http://sourceforge.net/projects/mikmod/ + +There's a mailing list (mikmod-public) for discussing the development +of MikMod (new features, bugs, ideas...) Look for more information on +the web site. + +Things related to the DOS port should also be forwarded to the DOS +``portmaster'', Andrew Zabolotny, at: bit@eltech.ru + + +>> LAST NOTES +------------- + +We hope you'll enjoy using this version of libmikmod as well as we enjoyed +debugging and improving it. + +-- Miodrag ("Miod") Vallat, 10/19/1999 + miodrag@mikmod.darkorb.net + + Andrew Zabolotny + bit@eltech.ru diff --git a/libs/mikmod/config.h b/libs/mikmod/config.h new file mode 100644 index 0000000..92683f5 --- /dev/null +++ b/libs/mikmod/config.h @@ -0,0 +1,85 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +#define HAVE_LIMITS_H 1 +#define HAVE_MEMCMP 1 +#define HAVE_STDLIB_H 1 +#define HAVE_STRINGS_H 1 +#define HAVE_STRSTR 1 + +#ifdef DOS +#define DRV_SB 1 +#define DRV_ULTRA 1 +#define DRV_WSS 1 + +#define HAVE_UNISTD_H 1 +#define HAVE_FCNTL_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_SYS_IOCTL_H 1 +#endif + +#if defined(__unix__) || defined(__APPLE__) +#define DRV_SDL 1 + +#define HAVE_FCNTL_H 1 +#define HAVE_INTTYPES_H 1 +#define HAVE_SYS_IOCTL_H 1 +#define HAVE_SNPRINTF 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_SYS_WAIT_H 1 +#define HAVE_UNISTD_H 1 +#define HAVE_POSIX_MEMALIGN 1 +#define HAVE_PTHREAD 1 +#define HAVE_SETENV 1 +#define HAVE_SRANDOM 1 + +#define MIKMOD_UNIX 1 +#endif + + +#if defined(WIN32) +#define HAVE_WINDOWS_H 1 +#define HAVE_MALLOC_H 1 +#endif + +#undef MIKMOD_DEBUG + +/* disable the high quality mixer (build only with the standart mixer) */ +#undef NO_HQMIXER + +/* Name of package */ +#undef PACKAGE +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT +/* Define to the full name of this package. */ +#undef PACKAGE_NAME +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME +/* Define to the home page for this package. */ +#undef PACKAGE_URL +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Version number of package */ +#undef VERSION + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +# undef WORDS_BIGENDIAN +# endif +#endif + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif diff --git a/libs/mikmod/depackers/mmcmp.c b/libs/mikmod/depackers/mmcmp.c new file mode 100644 index 0000000..2a83540 --- /dev/null +++ b/libs/mikmod/depackers/mmcmp.c @@ -0,0 +1,433 @@ +/* MikMod sound library (c) 2003-2015 Raphael Assenat and others - + * see AUTHORS file for a complete list. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +/* MMCMP ("ziRCONia") decompression support + * + * Based on the public domain version from the libmodplug library by + * Olivier Lapicque (sezero's fork of libmodplug + * at github: http://github.com/sezero/libmodplug/tree/sezero ) + * + * Rewritten for libmikmod by O. Sezer + * with some extra ideas from the libxmp version by Claudio Matsuoka. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*#define MMCMP_DEBUG*/ + +typedef struct MMCMPFILEHDR +{ + UBYTE id[8]; /* string 'ziRCONia' */ + UWORD hdrsize; /* sizeof MMCMPHEADER */ +} MMCMPFILEHDR; /* 10 bytes */ + +typedef struct MMCMPHEADER +{ + UWORD version; + UWORD nblocks; + ULONG filesize; + ULONG blktable; + UBYTE glb_comp; + UBYTE fmt_comp; +} MMCMPHEADER; /* 14 bytes */ + +typedef struct MMCMPBLOCK +{ + ULONG unpk_size; + ULONG pk_size; + ULONG xor_chk; + UWORD sub_blk; + UWORD flags; + UWORD tt_entries; + UWORD num_bits; +} MMCMPBLOCK; /* 20 bytes */ + +typedef struct MMCMPSUBBLOCK +{ + ULONG unpk_pos; + ULONG unpk_size; +} MMCMPSUBBLOCK; /* 8 bytes */ + +#define MMCMP_COMP 0x0001 +#define MMCMP_DELTA 0x0002 +#define MMCMP_16BIT 0x0004 +#define MMCMP_STEREO 0x0100 +#define MMCMP_ABS16 0x0200 +#define MMCMP_ENDIAN 0x0400 + + +typedef struct MMCMPBITBUFFER +{ + ULONG bitcount; + ULONG bitbuffer; + const UBYTE *start; + const UBYTE *end; +} MMCMPBITBUFFER; + +static ULONG MMCMP_GetBits(MMCMPBITBUFFER* b, ULONG nBits) +{ + ULONG d; + if (!nBits) return 0; + while (b->bitcount < 24) + { + b->bitbuffer |= ((b->start < b->end) ? *b->start++ : 0) << b->bitcount; + b->bitcount += 8; + } + d = b->bitbuffer & ((1 << nBits) - 1); + b->bitbuffer >>= nBits; + b->bitcount -= nBits; + return d; +} + +static const ULONG MMCMP8BitCommands[8] = +{ + 0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF8 +}; + +static const ULONG MMCMP8BitFetch[8] = +{ + 3, 3, 3, 3, 2, 1, 0, 0 +}; + +static const ULONG MMCMP16BitCommands[16] = +{ + 0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF0, + 0x1F0, 0x3F0, 0x7F0, 0xFF0, 0x1FF0, 0x3FF0, 0x7FF0, 0xFFF0 +}; + +static const ULONG MMCMP16BitFetch[16] = +{ + 4, 4, 4, 4, 3, 2, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + + +BOOL MMCMP_Unpack(MREADER* reader, void** out, long* outlen) +{ + ULONG srclen, destlen; + UBYTE *destbuf, *destptr; + MMCMPHEADER mmh; + ULONG *pblk_table; + MMCMPSUBBLOCK *subblocks; + ULONG i, blockidx, numsubs; + UBYTE *buf; ULONG bufsize; + + _mm_fseek(reader,0,SEEK_END); + srclen = _mm_ftell(reader); + if (srclen < 256) return 0; + + _mm_rewind(reader); + if (_mm_read_I_ULONG(reader) != 0x4352697A) /* 'ziRC' */ + return 0; + if (_mm_read_I_ULONG(reader) != 0x61694e4f) /* 'ONia' */ + return 0; + if (_mm_read_I_UWORD(reader) != 14) /* header size */ + return 0; + + mmh.version = _mm_read_I_UWORD(reader); + mmh.nblocks = _mm_read_I_UWORD(reader); + mmh.filesize = _mm_read_I_ULONG(reader); + mmh.blktable = _mm_read_I_ULONG(reader); + mmh.glb_comp = _mm_read_UBYTE(reader); + mmh.fmt_comp = _mm_read_UBYTE(reader); + + if ((!mmh.nblocks) || (mmh.filesize < 16) || (mmh.filesize > 0x8000000) || + (mmh.blktable >= srclen) || (mmh.blktable + 4*mmh.nblocks > srclen)) { + return 0; + } + destlen = mmh.filesize; + numsubs = 32; + bufsize = 65536; + + destbuf = (UBYTE*)MikMod_malloc(destlen); + buf = (UBYTE*)MikMod_malloc(bufsize); + pblk_table = (ULONG*)MikMod_malloc(mmh.nblocks*4); + subblocks = (MMCMPSUBBLOCK*)MikMod_malloc(numsubs*sizeof(MMCMPSUBBLOCK)); + if (!destbuf || !buf || !pblk_table || !subblocks) + goto err; + + _mm_fseek(reader,mmh.blktable,SEEK_SET); + for (blockidx = 0; blockidx < mmh.nblocks; blockidx++) { + pblk_table[blockidx] = _mm_read_I_ULONG(reader); + } + + for (blockidx = 0; blockidx < mmh.nblocks; blockidx++) + { + ULONG srcpos = pblk_table[blockidx]; + MMCMPBLOCK block; + + if (srcpos + 20 >= srclen) goto err; + + _mm_fseek(reader,srcpos,SEEK_SET); + block.unpk_size = _mm_read_I_ULONG(reader); + block.pk_size = _mm_read_I_ULONG(reader); + block.xor_chk = _mm_read_I_ULONG(reader); + block.sub_blk = _mm_read_I_UWORD(reader); + block.flags = _mm_read_I_UWORD(reader); + block.tt_entries = _mm_read_I_UWORD(reader); + block.num_bits = _mm_read_I_UWORD(reader); + + if (!block.unpk_size || !block.pk_size || !block.sub_blk) + goto err; + if (block.pk_size <= block.tt_entries) + goto err; + if (block.flags & MMCMP_COMP) { + if (block.flags & MMCMP_16BIT) { + if (block.num_bits >= 16) + goto err; + } + else { + if (block.num_bits >= 8) + goto err; + } + } + + srcpos += 20 + block.sub_blk*8; + if (srcpos >= srclen) goto err; + + if (numsubs < block.sub_blk) { + numsubs = (block.sub_blk + 31) & ~31; + MikMod_free(subblocks); + subblocks = (MMCMPSUBBLOCK*)MikMod_malloc(numsubs*sizeof(MMCMPSUBBLOCK)); + if (!subblocks) goto err; + } + for (i = 0; i < block.sub_blk; i++) { + subblocks[i].unpk_pos = _mm_read_I_ULONG(reader); + subblocks[i].unpk_size = _mm_read_I_ULONG(reader); + if (subblocks[i].unpk_pos >= destlen) goto err; + if (subblocks[i].unpk_size > destlen) goto err; + if (subblocks[i].unpk_size > destlen - subblocks[i].unpk_pos) goto err; + } + +#ifdef MMCMP_DEBUG + fprintf(stderr, "block %u: flags=%04X sub_blocks=%u", + blockidx, (unsigned)block.flags, (unsigned)block.sub_blk); + fprintf(stderr, " pksize=%u unpksize=%u", block.pk_size, block.unpk_size); + fprintf(stderr, " tt_entries=%u num_bits=%u\n", block.tt_entries, block.num_bits); +#endif + if (!(block.flags & MMCMP_COMP)) + { /* Data is not packed */ + _mm_fseek(reader,srcpos,SEEK_SET); + destptr = destbuf + subblocks[0].unpk_pos; + i = 0; + + while (1) { +#ifdef MMCMP_DEBUG + fprintf(stderr, " Unpacked sub-block %u: offset %u, size=%u\n", + i, subblocks[i].unpk_pos, subblocks[i].unpk_size); +#endif + _mm_read_UBYTES(destptr,subblocks[i].unpk_size,reader); + destptr += subblocks[i].unpk_size; + if (++i == block.sub_blk) break; + } + } + else if (block.flags & MMCMP_16BIT) + { /* Data is 16-bit packed */ + MMCMPBITBUFFER bb; + ULONG size; + ULONG pos = 0; + ULONG numbits = block.num_bits; + ULONG oldval = 0; + +#ifdef MMCMP_DEBUG + fprintf(stderr, " 16-bit block: pos=%u size=%u ", + subblocks[0].unpk_pos, subblocks[0].unpk_size); + if (block.flags & MMCMP_DELTA) fprintf(stderr, "DELTA "); + if (block.flags & MMCMP_ABS16) fprintf(stderr, "ABS16 "); + fprintf(stderr, "\n"); +#endif + size = block.pk_size - block.tt_entries; + if (bufsize < size) { + while (bufsize < size) bufsize += 65536; + MikMod_free(buf); + if (!(buf = (UBYTE*)MikMod_malloc(bufsize))) + goto err; + } + + bb.bitcount = 0; + bb.bitbuffer = 0; + bb.start = buf; + bb.end = buf + size; + + _mm_fseek(reader,srcpos+block.tt_entries,SEEK_SET); + _mm_read_UBYTES(buf,size,reader); + destptr = destbuf + subblocks[0].unpk_pos; + size = subblocks[0].unpk_size; + i = 0; + + while (1) + { + ULONG newval = 0x10000; + ULONG d = MMCMP_GetBits(&bb, numbits+1); + + if (d >= MMCMP16BitCommands[numbits]) + { + ULONG nFetch = MMCMP16BitFetch[numbits]; + ULONG newbits = MMCMP_GetBits(&bb, nFetch) + ((d - MMCMP16BitCommands[numbits]) << nFetch); + if (newbits != numbits) + { + numbits = newbits & 0x0F; + } else + { + if ((d = MMCMP_GetBits(&bb, 4)) == 0x0F) + { + if (MMCMP_GetBits(&bb, 1)) break; + newval = 0xFFFF; + } else + { + newval = 0xFFF0 + d; + } + } + } else + { + newval = d; + } + if (newval < 0x10000) + { + newval = (newval & 1) ? (ULONG)(-(SLONG)((newval+1) >> 1)) : (ULONG)(newval >> 1); + if (block.flags & MMCMP_DELTA) + { + newval += oldval; + oldval = newval; + } else + if (!(block.flags & MMCMP_ABS16)) + { + newval ^= 0x8000; + } + destptr[pos++] = (UBYTE) (((UWORD)newval) & 0xff); + destptr[pos++] = (UBYTE) (((UWORD)newval) >> 8); + } + if (pos >= size) + { + if (++i == block.sub_blk) break; + size = subblocks[i].unpk_size; + destptr = destbuf + subblocks[i].unpk_pos; + pos = 0; + } + } + } + else + { /* Data is 8-bit packed */ + MMCMPBITBUFFER bb; + ULONG size; + ULONG pos = 0; + ULONG numbits = block.num_bits; + ULONG oldval = 0; + UBYTE ptable[0x100]; + + size = block.pk_size - block.tt_entries; + if (bufsize < size) { + while (bufsize < size) bufsize += 65536; + MikMod_free(buf); + if (!(buf = (UBYTE*)MikMod_malloc(bufsize))) + goto err; + } + + bb.bitcount = 0; + bb.bitbuffer = 0; + bb.start = buf; + bb.end = buf + size; + + _mm_read_UBYTES(ptable,0x100,reader); + _mm_fseek(reader,srcpos+block.tt_entries,SEEK_SET); + _mm_read_UBYTES(buf,size,reader); + destptr = destbuf + subblocks[0].unpk_pos; + size = subblocks[0].unpk_size; + i = 0; + + while (1) + { + ULONG newval = 0x100; + ULONG d = MMCMP_GetBits(&bb, numbits+1); + + if (d >= MMCMP8BitCommands[numbits]) + { + ULONG nFetch = MMCMP8BitFetch[numbits]; + ULONG newbits = MMCMP_GetBits(&bb, nFetch) + ((d - MMCMP8BitCommands[numbits]) << nFetch); + if (newbits != numbits) + { + numbits = newbits & 0x07; + } else + { + if ((d = MMCMP_GetBits(&bb, 3)) == 7) + { + if (MMCMP_GetBits(&bb, 1)) break; + newval = 0xFF; + } else + { + newval = 0xF8 + d; + } + } + } else + { + newval = d; + } + if (newval < 0x100) + { + int n = ptable[newval]; + if (block.flags & MMCMP_DELTA) + { + n += oldval; + oldval = n; + } + destptr[pos++] = (UBYTE)n; + } + if (pos >= size) + { + if (++i == block.sub_blk) break; + size = subblocks[i].unpk_size; + destptr = destbuf + subblocks[i].unpk_pos; + pos = 0; + } + } + } + } + + MikMod_free(buf); + MikMod_free(pblk_table); + MikMod_free(subblocks); + *out = destbuf; + *outlen = destlen; + return 1; + + err: + MikMod_free(buf); + MikMod_free(pblk_table); + MikMod_free(subblocks); + MikMod_free(destbuf); + return 0; +} diff --git a/libs/mikmod/depackers/pp20.c b/libs/mikmod/depackers/pp20.c new file mode 100644 index 0000000..e5bd323 --- /dev/null +++ b/libs/mikmod/depackers/pp20.c @@ -0,0 +1,208 @@ +/* MikMod sound library (c) 2003-2015 Raphael Assenat and others - + * see AUTHORS file for a complete list. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +/* amiga Powerpack PP20 decompression support + * + * Code from Heikki Orsila's amigadepack 0.02 + * based on code by Stuart Caie + * This software is in the Public Domain + * + * Modified for xmp by Claudio Matsuoka, 08/2007 + * - merged mld's checks from the old depack sources. Original credits: + * - corrupt file and data detection + * (thanks to Don Adan and Dirk Stoecker for help and infos) + * - implemeted "efficiency" checks + * - further detection based on code by Georg Hoermann + * + * Modified for xmp by Claudio Matsuoka, 05/2013 + * - decryption code removed + * + * Modified for libmikmod by O. Sezer, Apr. 2015, with a few extra bits + * from the libmodplug library by Olivier Lapicque. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +#define PP_READ_BITS(nbits, var) do { \ + bit_cnt = (nbits); \ + while (bits_left < bit_cnt) { \ + if (buf_src < src) return 0; /* out of source bits */ \ + bit_buffer |= (*--buf_src << bits_left); \ + bits_left += 8; \ + } \ + (var) = 0; \ + bits_left -= bit_cnt; \ + while (bit_cnt--) { \ + (var) = ((var) << 1) | (bit_buffer & 1); \ + bit_buffer >>= 1; \ + } \ +} while(0) + +#define PP_BYTE_OUT(byte) do { \ + if (out <= dest) return 0; /* output overflow */ \ + *--out = (byte); \ + written++; \ +} while (0) + +static BOOL ppDecrunch(const UBYTE *src, UBYTE *dest, + const UBYTE *offset_lens, + ULONG src_len, ULONG dest_len, + UBYTE skip_bits) +{ + ULONG bit_buffer, x, todo, offbits, offset, written; + const UBYTE *buf_src; + UBYTE *out, *dest_end, bits_left, bit_cnt; + + /* set up input and output pointers */ + buf_src = src + src_len; + out = dest_end = dest + dest_len; + + written = 0; + bit_buffer = 0; + bits_left = 0; + + /* skip the first few bits */ + PP_READ_BITS(skip_bits, x); + + /* while there are input bits left */ + while (written < dest_len) { + PP_READ_BITS(1, x); + if (x == 0) { + /* 1bit==0: literal, then match. 1bit==1: just match */ + todo = 1; do { PP_READ_BITS(2, x); todo += x; } while (x == 3); + while (todo--) { PP_READ_BITS(8, x); PP_BYTE_OUT(x); } + + /* should we end decoding on a literal, break out of the main loop */ + if (written == dest_len) break; + } + + /* match: read 2 bits for initial offset bitlength / match length */ + PP_READ_BITS(2, x); + offbits = offset_lens[x]; + todo = x+2; + if (x == 3) { + PP_READ_BITS(1, x); + if (x==0) offbits = 7; + PP_READ_BITS(offbits, offset); + do { PP_READ_BITS(3, x); todo += x; } while (x == 7); + } + else { + PP_READ_BITS(offbits, offset); + } + if ((out + offset) >= dest_end) return 0; /* match overflow */ + while (todo--) { x = out[offset]; PP_BYTE_OUT(x); } + } + + /* all output bytes written without error */ + return 1; + /* return (src == buf_src) ? 1 : 0; */ +} + +BOOL PP20_Unpack(MREADER* reader, void** out, long* outlen) +{ + ULONG srclen, destlen; + UBYTE *destbuf, *srcbuf; + UBYTE tmp[4], skip; + BOOL ret; + + /* PP FORMAT: + * 1 longword identifier 'PP20' or 'PX20' + * [1 word checksum (if 'PX20') $ssss] + * 1 longword efficiency $eeeeeeee + * X longwords crunched file $cccccccc,$cccccccc,... + * 1 longword decrunch info 'decrlen' << 8 | '8 bits other info' + */ + + _mm_fseek(reader,0,SEEK_END); + srclen = _mm_ftell(reader); + if (srclen < 256) return 0; + /* file length should be a multiple of 4 */ + if (srclen & 3) return 0; + + _mm_rewind(reader); + if (_mm_read_I_ULONG(reader) != 0x30325050) /* 'PP20' */ + return 0; + + _mm_fseek(reader,srclen-4,SEEK_SET); + _mm_read_UBYTES(tmp,4,reader); + destlen = tmp[0] << 16; + destlen |= tmp[1] << 8; + destlen |= tmp[2]; + skip = tmp[3]; + + _mm_fseek(reader,4,SEEK_SET); + _mm_read_UBYTES(tmp,4,reader); + + /* original pp20 only support efficiency + * from 9 9 9 9 up to 9 10 12 13, afaik, + * but the xfd detection code says this... + * + * move.l 4(a0),d0 + * cmp.b #9,d0 + * blo.b .Exit + * and.l #$f0f0f0f0,d0 + * bne.s .Exit + */ + if ((tmp[0] < 9) || (tmp[0] & 0xf0)) return 0; + if ((tmp[1] < 9) || (tmp[1] & 0xf0)) return 0; + if ((tmp[2] < 9) || (tmp[2] & 0xf0)) return 0; + if ((tmp[3] < 9) || (tmp[3] & 0xf0)) return 0; + + if ((destlen < 512) || (destlen > 0x400000) || (destlen > 16*srclen)) + return 0; + if ((destbuf = (UBYTE*)MikMod_malloc(destlen)) == NULL) + return 0; + + srclen -= 12; + if ((srcbuf = (UBYTE*)MikMod_malloc(srclen)) == NULL) { + MikMod_free(destbuf); + return 0; + } + _mm_read_UBYTES(srcbuf,srclen,reader); + + ret = ppDecrunch(srcbuf, destbuf, tmp, srclen, destlen, skip); + MikMod_free(srcbuf); + + if (!ret) { + MikMod_free(destbuf); + } + else { + *out = destbuf; + *outlen = destlen; + } + return ret; +} diff --git a/libs/mikmod/depackers/s404.c b/libs/mikmod/depackers/s404.c new file mode 100644 index 0000000..7ca41cd --- /dev/null +++ b/libs/mikmod/depackers/s404.c @@ -0,0 +1,393 @@ +/* MikMod sound library (c) 2003-2015 Raphael Assenat and others - + * see AUTHORS file for a complete list. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +/* + * StoneCracker S404 algorithm data decompression routine + * (c) 2006 Jouni 'Mr.Spiv' Korhonen. The code is in public domain. + * + * modified for xmp by Claudio Matsuoka, Jan 2010 + * modified for libmikmod by O. Sezer -- May 2015 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include +/*#include */ + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + + +/*#define STC_DEBUG*/ + +static UWORD readmem16b(UBYTE *m) +{ + ULONG a, b; + a = m[0]; + b = m[1]; + return (a << 8) | b; +} + +struct bitstream { + /* bit buffer for rolling data bit by bit from the compressed file */ + ULONG word; + + /* bits left in the bit buffer */ + int left; + + /* compressed data source */ + UWORD *src; + UBYTE *orgsrc; +}; + +static int initGetb(struct bitstream *bs, UBYTE *src, ULONG src_length) +{ + int eff; + + bs->src = (UWORD *) (src + src_length); + bs->orgsrc = src; + + bs->left = readmem16b((UBYTE *)bs->src); /* bit counter */ +#ifdef STC_DEBUG + if (bs->left & (~0xf)) + fprintf(stderr, "Worked around an ancient stc bug\n"); +#endif + /* mask off any corrupt bits */ + bs->left &= 0x000f; + bs->src--; + + /* get the first 16-bits of the compressed stream */ + bs->word = readmem16b((UBYTE *)bs->src); + bs->src--; + + eff = readmem16b((UBYTE *)bs->src); /* efficiency */ + bs->src--; + + return eff; +} + +/* get nbits from the compressed stream */ +static int getb(struct bitstream *bs, int nbits) +{ + bs->word &= 0x0000ffff; + + /* If not enough bits in the bit buffer, get more */ + if (bs->left < nbits) { + bs->word <<= bs->left; + /*assert((bs->word & 0x0000ffffU) == 0);*/ + + /* Check that we don't go out of bounds */ + if (bs->orgsrc > (UBYTE *)bs->src) { + return -1; + } + + bs->word |= readmem16b((UBYTE *)bs->src); + bs->src--; + + nbits -= bs->left; + bs->left = 16; /* 16 unused (and some used) bits left in the word */ + } + + /* Shift nbits off the word and return them */ + bs->left -= nbits; + bs->word <<= nbits; + return bs->word >> 16; +} + +static int decompressS404(UBYTE *src, UBYTE *orgdst, + SLONG dst_length, SLONG src_length) +{ + UWORD w; + SLONG eff; + SLONG n; + UBYTE *dst; + SLONG oLen = dst_length; + struct bitstream bs; + int x; + + dst = orgdst + oLen; + + eff = initGetb(&bs, src, src_length); + + while (oLen > 0) { + x = getb(&bs, 9); + /* Sanity check */ + if (x < 0) { + return -1; + } + + w = x; + + if (w < 0x100) { + if (orgdst >= dst) { + return -1; + } + *--dst = w; + oLen--; + } else if (w == 0x13e || w == 0x13f) { + w <<= 4; + x = getb(&bs, 4); + /* Sanity check */ + if (x < 0) { + return -1; + } + w |= x; + + n = (w & 0x1f) + 14; + oLen -= n; + while (n-- > 0) { + x = getb(&bs, 8); + /* Sanity check */ + if (x < 0) { + return -1; + } + w = x; + + if (orgdst >= dst) { + return -1; + } + + *--dst = w; + } + } else { + if (w >= 0x180) { + /* copy 2-3 */ + n = w & 0x40 ? 3 : 2; + + if (w & 0x20) { + /* dist 545 -> */ + w = (w & 0x1f) << (eff - 5); + x = getb(&bs, eff - 5); + /* Sanity check */ + if (x < 0) { + return -1; + } + w |= x; + w += 544; + } else if (w & 0x30) { + /* dist 1 -> 32 */ + w = (w & 0x0f) << 1; + x = getb(&bs, 1); + /* Sanity check */ + if (x < 0) { + return -1; + } + w |= x; + } else { + /* dist 33 -> 544 */ + w = (w & 0x0f) << 5; + x = getb(&bs, 5); + /* Sanity check */ + if (x < 0) { + return -1; + } + w |= x; + w += 32; + } + } else if (w >= 0x140) { + /* copy 4-7 */ + n = ((w & 0x30) >> 4) + 4; + + if (w & 0x08) { + /* dist 545 -> */ + w = (w & 0x07) << (eff - 3); + x = getb(&bs, eff - 3); + /* Sanity check */ + if (x < 0) { + return -1; + } + w |= x; + w += 544; + } else if (w & 0x0c) { + /* dist 1 -> 32 */ + w = (w & 0x03) << 3; + x = getb(&bs, 3); + /* Sanity check */ + if (x < 0) { + return -1; + } + w |= x; + } else { + /* dist 33 -> 544 */ + w = (w & 0x03) << 7; + x = getb(&bs, 7); + /* Sanity check */ + if (x < 0) { + return -1; + } + w |= x; + w += 32; + } + } else if (w >= 0x120) { + /* copy 8-22 */ + n = ((w & 0x1e) >> 1) + 8; + + if (w & 0x01) { + /* dist 545 -> */ + x = getb(&bs, eff); + /* Sanity check */ + if (x < 0) { + return -1; + } + w = x; + w += 544; + } else { + x = getb(&bs, 6); + /* Sanity check */ + if (x < 0) { + return -1; + } + w = x; + + if (w & 0x20) { + /* dist 1 -> 32 */ + w &= 0x1f; + } else { + /* dist 33 -> 544 */ + w <<= 4; + x = getb(&bs, 4); + /* Sanity check */ + if (x < 0) { + return -1; + } + w |= x; + w += 32; + } + } + } else { + w = (w & 0x1f) << 3; + x = getb(&bs, 3); + /* Sanity check */ + if (x < 0) { + return -1; + } + w |= x; + n = 23; + + while (w == 0xff) { + n += w; + x = getb(&bs, 8); + /* Sanity check */ + if (x < 0) { + return -1; + } + w = x; + } + n += w; + + x = getb(&bs, 7); + w = x; + + if (w & 0x40) { + /* dist 545 -> */ + w = (w & 0x3f) << (eff - 6); + x = getb(&bs, eff - 6); + /* Sanity check */ + if (x < 0) { + return -1; + } + w |= x; + w += 544; + } else if (w & 0x20) { + /* dist 1 -> 32 */ + w &= 0x1f; + } else { + /* dist 33 -> 544; */ + w <<= 4; + x = getb(&bs, 4); + /* Sanity check */ + if (x < 0) { + return -1; + } + w |= x; + w += 32; + } + } + + oLen -= n; + + while (n-- > 0) { + dst--; + if (dst < orgdst || (dst + w + 1) >= (orgdst + dst_length)) + return -1; + *dst = dst[w + 1]; + } + } + } + + return 0; +} + +BOOL S404_Unpack(MREADER *reader, void **out, long *outlen) +{ + SLONG iLen, sLen, oLen, pLen; + UBYTE *src, *dst = NULL; + int err; + + _mm_fseek(reader,0,SEEK_END); + iLen = _mm_ftell(reader); + if (iLen <= 16) return 0; + + _mm_rewind(reader); + if (_mm_read_M_ULONG(reader) != 0x53343034) /* S404 */ + return 0; + + sLen = _mm_read_M_SLONG(reader); /* Security length */ + oLen = _mm_read_M_SLONG(reader); /* Depacked length */ + pLen = _mm_read_M_SLONG(reader); /* Packed length */ +#ifdef STC_DEBUG + fprintf(stderr,"S404: iLen= %d, sLen= %d, pLen= %d, oLen= %d\n", + iLen, sLen, pLen, oLen); +#endif + if (sLen < 0 || oLen <= 0 || pLen <= 0) return 0; + if (pLen + 16 >= iLen) return 0; /* sanity check */ + + if (!(src = (UBYTE*) MikMod_malloc(iLen - 16))) + return 0; + if (!(dst = (UBYTE*) MikMod_malloc(oLen))) { + MikMod_free(src); + return 0; + } + + _mm_read_UBYTES(src, iLen - 16, reader); + err = decompressS404(src, dst, oLen, pLen); + MikMod_free(src); + + if (!err) { + *out = dst; + *outlen = oLen; + return 1; + } + + MikMod_free(dst); + return 0; +} diff --git a/libs/mikmod/depackers/xpk.c b/libs/mikmod/depackers/xpk.c new file mode 100644 index 0000000..5bd7eea --- /dev/null +++ b/libs/mikmod/depackers/xpk.c @@ -0,0 +1,427 @@ +/* MikMod sound library (c) 2003-2015 Raphael Assenat and others - + * see AUTHORS file for a complete list. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +/* XPK-SQSH depacker + * Algorithm from the portable decruncher by Bert Jahn (24.12.97) + * Checksum added by Sipos Attila + * Rewritten for libxmp by Claudio Matsuoka + * Rewritten for libmikmod by O. Sezer + * + * Copyright (C) 2013 Claudio Matsuoka + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +struct io { + UBYTE *src; + UBYTE *dest; + int offs; +}; + +static const UBYTE ctable[]={ + 2, 3, 4, 5, 6, 7, 8, 0, + 3, 2, 4, 5, 6, 7, 8, 0, + 4, 3, 5, 2, 6, 7, 8, 0, + 5, 4, 6, 2, 3, 7, 8, 0, + 6, 5, 7, 2, 3, 4, 8, 0, + 7, 6, 8, 2, 3, 4, 5, 0, + 8, 7, 6, 2, 3, 4, 5, 0 +}; + +static UWORD readmem16b(UBYTE *m) +{ + ULONG a, b; + + a = m[0]; + b = m[1]; + + return (a << 8) | b; +} + +static ULONG readmem24b(UBYTE *m) +{ + ULONG a, b, c; + + a = m[0]; + b = m[1]; + c = m[2]; + + return (a << 16) | (b << 8) | c; +} + +static UWORD xchecksum(ULONG * ptr, ULONG count) +{ + register ULONG sum = 0; + + while (count-- > 0) { + sum ^= *ptr++; + } + + return (UWORD) (sum ^ (sum >> 16)); +} + +static int get_bits(struct io *io, int count) +{ + int r = readmem24b(io->src + (io->offs >> 3)); + + r <<= io->offs % 8; + r &= 0xffffff; + r >>= 24 - count; + io->offs += count; + + return r; +} + +static int get_bits_final(struct io *io, int count) +{ + int r = readmem24b(io->src + (io->offs >> 3)); + + r <<= (io->offs % 8) + 8; + r >>= 32 - count; + io->offs += count; + + return r; +} + +static int copy_data(struct io *io, int d1, int *data, UBYTE *dest_start, UBYTE *dest_end) +{ + UBYTE *copy_src; + int dest_offset, count, copy_len; + + if (get_bits(io, 1) == 0) { + copy_len = get_bits(io, 1) + 2; + } else if (get_bits(io, 1) == 0) { + copy_len = get_bits(io, 1) + 4; + } else if (get_bits(io, 1) == 0) { + copy_len = get_bits(io, 1) + 6; + } else if (get_bits(io, 1) == 0) { + copy_len = get_bits(io, 3) + 8; + } else { + copy_len = get_bits(io, 5) + 16; + } + + if (get_bits(io, 1) == 0) { + if (get_bits(io, 1) == 0) { + count = 8; + dest_offset = 0; + } else { + count = 14; + dest_offset = -0x1100; + } + } else { + count = 12; + dest_offset = -0x100; + } + + copy_len -= 3; + + if (copy_len >= 0) { + if (copy_len != 0) { + d1--; + } + d1--; + if (d1 < 0) { + d1 = 0; + } + } + + copy_len += 2; + + copy_src = io->dest + dest_offset - get_bits(io, count) - 1; + + /* Sanity check */ + if (copy_src < dest_start || copy_src + copy_len >= dest_end) { + return -1; + } + + do { + //printf("dest=%p src=%p end=%p\n", io->dest, copy_src, dest_end); + *io->dest++ = *copy_src++; + } while (copy_len--); + + *data = *(--copy_src); + + return d1; +} + +static int unsqsh_block(struct io *io, UBYTE *dest_start, UBYTE *dest_end) +{ + int d1, d2, data, unpack_len, count, old_count; + + d1 = d2 = data = old_count = 0; + io->offs = 0; + + data = *(io->src++); + *(io->dest++) = data; + + do { + if (d1 < 8) { + if (get_bits(io, 1)) { + d1 = copy_data(io, d1, &data, dest_start, dest_end); + if (d1 < 0) + return -1; + d2 -= d2 >> 3; + continue; + } + unpack_len = 0; + count = 8; + } else { + if (get_bits(io, 1)) { + count = 8; + if (count == old_count) { + if (d2 >= 20) { + unpack_len = 1; + d2 += 8; + } else { + unpack_len = 0; + } + } else { + count = old_count; + unpack_len = 4; + d2 += 8; + } + } else { + if (get_bits(io, 1) == 0) { + d1 = copy_data(io, d1, &data, dest_start, dest_end); + if (d1 < 0) + return -1; + d2 -= d2 >> 3; + continue; + } + + if (get_bits(io, 1) == 0) { + count = 2; + } else { + if (get_bits(io, 1)) { + io->offs--; + count = get_bits(io, 3); + } else { + count = 3; + } + } + + count = ctable[8 * old_count + count - 17]; + if (count != 8) { + unpack_len = 4; + d2 += 8; + } else { + if (d2 >= 20) { + unpack_len = 1; + d2 += 8; + } else { + unpack_len = 0; + } + } + } + } + + do { + data -= get_bits_final(io, count); + *io->dest++ = data; + } while (unpack_len--); + + if (d1 != 31) { + d1++; + } + + old_count = count; + + d2 -= d2 >> 3; + + } while (io->dest < dest_end); + + return 0; +} + +static int unsqsh(UBYTE *src, SLONG srclen, UBYTE *dest, SLONG destlen) +{ + SLONG len = destlen; + SLONG decrunched = 0; + UBYTE type; + SLONG packed_size, unpacked_size; + ULONG sum, lchk; + UBYTE *c, *dest_start, *dest_end; + UBYTE bc[3]; + struct io io; + + io.src = src; + io.dest = dest; + + dest_start = io.dest; + + c = src + 20; + + while (len) { + /* Sanity check */ + if (c >= src + srclen) { + return -1; + } + + type = *c++; + c++; /* hchk */ + + sum = *(UWORD *)c; + c += 2; /* checksum */ + + packed_size = readmem16b(c); /* packed */ + c += 2; + + unpacked_size = readmem16b(c); /* unpacked */ + c += 2; + + /* Sanity check */ + if (packed_size <= 0 || unpacked_size <= 0) { + return -1; + } + if (c + packed_size + 3 > src + srclen) { + return -1; + } + + io.src = c + 2; + memcpy(bc, c + packed_size, 3); + memset(c + packed_size, 0, 3); + lchk = xchecksum((ULONG *) (c), (packed_size + 3) >> 2); + memcpy(c + packed_size, bc, 3); + + if (lchk != sum) { + return decrunched; + } + + if (type == 0) { + /* verbatim block */ + memcpy(io.dest, c, packed_size); + io.dest += packed_size; + c += packed_size; + len -= packed_size; + decrunched += packed_size; + continue; + } + + if (type != 1) { + /* unknown type */ + return decrunched; + } + + len -= unpacked_size; + decrunched += unpacked_size; + + /* Sanity check */ + if (decrunched > destlen) { + return -1; + } + + packed_size = (packed_size + 3) & 0xfffc; + c += packed_size; + dest_end = io.dest + unpacked_size; + + if (unsqsh_block(&io, dest_start, dest_end) < 0) { + return -1; + } + + io.dest = dest_end; + } + + return decrunched; + +} + +BOOL XPK_Unpack(MREADER *reader, void **out, long *outlen) +{ + UBYTE *src, *dest; + SLONG inlen, srclen, destlen; + + _mm_fseek(reader,0,SEEK_END); + inlen = _mm_ftell(reader); + if (inlen <= 8 || inlen > 0x100000) + return 0; + + _mm_rewind(reader); + if (_mm_read_M_ULONG(reader) != 0x58504b46) /* XPKF */ + return 0; + srclen = _mm_read_M_SLONG(reader); + if (srclen <= 8 || srclen > 0x100000 || srclen > inlen-8) + return 0; + if (_mm_read_M_ULONG(reader) != 0x53515348) /* SQSH */ + return 0; + destlen = _mm_read_M_SLONG(reader); + if (destlen < 0 || destlen > 0x200000) + return 0; + + if ((src = (UBYTE*) MikMod_malloc(srclen + 3)) == NULL) + return 0; + if ((dest = (UBYTE*) MikMod_malloc(destlen + 100)) == NULL) { + MikMod_free(src); + return 0; + } + + if (!_mm_read_UBYTES(src, srclen - 8, reader)) + goto err; + + if (unsqsh(src, srclen, dest, destlen) != destlen) + goto err; + + *out = dest; + *outlen = destlen; + + MikMod_free(src); + + return 1; + + err: + MikMod_free(dest); + MikMod_free(src); + return 0; +} + diff --git a/libs/mikmod/drivers/dos/dosdma.c b/libs/mikmod/drivers/dos/dosdma.c new file mode 100644 index 0000000..e232c30 --- /dev/null +++ b/libs/mikmod/drivers/dos/dosdma.c @@ -0,0 +1,214 @@ +/* + Implementation of DMA routines on DOS + Copyright (C) 1999 by Andrew Zabolotny, + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "dosdma.h" + +#include /* includes sys/version.h (djgpp >= 2.02) */ +#include +#include +#include +#include +#include "mikmod.h" /* for MikMod_malloc() & co */ + +/* BUG WARNING: there is an error in DJGPP libraries <= 2.01: + * src/libc/dpmi/api/d0102.s loads the selector and allocsize + * arguments in the wrong order. DJGPP >= 2.02 have it fixed. */ +#if (!defined(__DJGPP_MINOR__) || (__DJGPP_MINOR__+0) < 2) +#warning __dpmi_resize_dos_memory() from DJGPP <= 2.01 is broken! +#endif + +__dma_regs dma[8] = { +/* *INDENT-OFF* */ + {DMA_ADDR_0, DMA_PAGE_0, DMA_SIZE_0, + DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG}, + {DMA_ADDR_1, DMA_PAGE_1, DMA_SIZE_1, + DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG}, + + {DMA_ADDR_2, DMA_PAGE_2, DMA_SIZE_2, + DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG}, + {DMA_ADDR_3, DMA_PAGE_3, DMA_SIZE_3, + DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG}, + + {DMA_ADDR_4, 0, DMA_SIZE_4, + DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG}, + {DMA_ADDR_5, DMA_PAGE_5, DMA_SIZE_5, + DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG}, + + {DMA_ADDR_6, DMA_PAGE_6, DMA_SIZE_6, + DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG}, + {DMA_ADDR_7, DMA_PAGE_7, DMA_SIZE_7, + DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG} +/* *INDENT-ON* */ +}; + +static int __initialized = 0; +static int __buffer_count = 0; +static __dpmi_meminfo __locked_data; + +int dma_initialize() +{ + if (!__djgpp_nearptr_enable()) + return 0; + + /* Trick: Avoid re-setting DS selector limit on each memory allocation + call */ + __djgpp_selector_limit = 0xffffffff; + + __locked_data.address = __djgpp_base_address + (unsigned long)&dma; + __locked_data.size = sizeof(dma); + if (__dpmi_lock_linear_region(&__locked_data)) + return 0; + + return (__initialized = 1); +} + +void dma_finalize() +{ + if (!__initialized) + return; + __dpmi_unlock_linear_region(&__locked_data); + __djgpp_nearptr_disable(); +} + +dma_buffer *dma_allocate(unsigned int channel, unsigned int size) +{ + int parsize = (size + 15) >> 4; /* size in paragraphs */ + int par = 0; /* Real-mode paragraph */ + int selector = 0; /* Protected-mode selector */ + int mask = channel <= 3 ? 0xfff : 0x1fff; /* Alignment mask in para. */ + int allocsize = parsize; /* Allocated size in paragraphs */ + int count; /* Try count */ + int bound = 0; /* Nearest bound address */ + int maxsize; /* Maximal possible block size */ + dma_buffer *buffer = NULL; + __dpmi_meminfo buff_info, struct_info; + + if (!dma_initialize()) + return NULL; + + /* Loop until we'll get a properly aligned memory block */ + for (count = 8; count; count--) { + int resize = (selector != 0); + + /* Try first to resize (possibly previously) allocated block */ + if (resize) { + maxsize = (bound + parsize) - par; + if (maxsize > parsize * 2) + maxsize = parsize * 2; + if (maxsize == allocsize) + resize = 0; + else { + allocsize = maxsize; + if (__dpmi_resize_dos_memory(selector, allocsize, &maxsize) != + 0) resize = 0; + } + } + + if (!resize) { + if (selector) + __dpmi_free_dos_memory(selector), selector = 0; + par = __dpmi_allocate_dos_memory(allocsize, &selector); + } + + if ((par == 0) || (par == -1)) + goto exit; + + /* If memory block contains a properly aligned portion, quit loop */ + bound = (par + mask + 1) & ~mask; + if (par + parsize <= bound) + break; + if (bound + parsize <= par + allocsize) { + par = bound; + break; + } + } + if (!count) { + __dpmi_free_dos_memory(selector); + goto exit; + } + + buffer = (dma_buffer *) MikMod_malloc(sizeof(dma_buffer)); + buffer->linear = (unsigned char *)(__djgpp_conventional_base + bound * 16); + buffer->physical = bound * 16; + buffer->size = parsize * 16; + buffer->selector = selector; + buffer->channel = channel; + + buff_info.address = buffer->physical; + buff_info.size = buffer->size; + /* + Don't pay attention to return code since under plain DOS it often + returns error (at least under HIMEM/CWSDPMI and EMM386/DPMI) + */ + __dpmi_lock_linear_region(&buff_info); + + /* Lock the DMA buffer control structure as well */ + struct_info.address = __djgpp_base_address + (unsigned long)buffer; + struct_info.size = sizeof(dma_buffer); + if (__dpmi_lock_linear_region(&struct_info)) { + __dpmi_unlock_linear_region(&buff_info); + __dpmi_free_dos_memory(selector); + MikMod_free(buffer); + buffer = NULL; + goto exit; + } + + exit: + if (buffer) + __buffer_count++; + else if (--__buffer_count == 0) + dma_finalize(); + return buffer; +} + +void dma_free(dma_buffer * buffer) +{ + __dpmi_meminfo buff_info; + + if (!buffer) + return; + + buff_info.address = buffer->physical; + buff_info.size = buffer->size; + __dpmi_unlock_linear_region(&buff_info); + + __dpmi_free_dos_memory(buffer->selector); + MikMod_free(buffer); + + if (--__buffer_count == 0) + dma_finalize(); +} + +void dma_start(dma_buffer * buffer, unsigned long count, unsigned char mode) +{ + /* Disable interrupts */ + int old_ints = disable(); + dma_disable(buffer->channel); + dma_set_mode(buffer->channel, mode); + dma_clear_ff(buffer->channel); + dma_set_addr(buffer->channel, buffer->physical); + dma_clear_ff(buffer->channel); + dma_set_count(buffer->channel, count); + dma_enable(buffer->channel); + /* Re-enable interrupts */ + if (old_ints) + enable(); +} + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/dos/dosdma.h b/libs/mikmod/drivers/dos/dosdma.h new file mode 100644 index 0000000..882c9ac --- /dev/null +++ b/libs/mikmod/drivers/dos/dosdma.h @@ -0,0 +1,190 @@ +/* + Interface for DMA routines on DOS + Copyright (C) 1999 by Andrew Zabolotny, + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __DOSDMA_H__ +#define __DOSDMA_H__ + +#include + +#define DMA1_BASE 0x00 /* 8 bit slave DMA, channels 0..3 */ +#define DMA2_BASE 0xC0 /* 16 bit master DMA, ch 4(=slave input)..7 */ + +#define DMA1_CMD_REG 0x08 /* command register (w) */ +#define DMA1_STAT_REG 0x08 /* status register (r) */ +#define DMA1_REQ_REG 0x09 /* request register (w) */ +#define DMA1_MASK_REG 0x0A /* single-channel mask (w) */ +#define DMA1_MODE_REG 0x0B /* mode register (w) */ +#define DMA1_CLEAR_FF_REG 0x0C /* clear pointer flip-flop (w) */ +#define DMA1_TEMP_REG 0x0D /* Temporary Register (r) */ +#define DMA1_RESET_REG 0x0D /* Master Clear (w) */ +#define DMA1_CLR_MASK_REG 0x0E /* Clear Mask */ +#define DMA1_MASK_ALL_REG 0x0F /* all-channels mask (w) */ + +#define DMA2_CMD_REG 0xD0 /* command register (w) */ +#define DMA2_STAT_REG 0xD0 /* status register (r) */ +#define DMA2_REQ_REG 0xD2 /* request register (w) */ +#define DMA2_MASK_REG 0xD4 /* single-channel mask (w) */ +#define DMA2_MODE_REG 0xD6 /* mode register (w) */ +#define DMA2_CLEAR_FF_REG 0xD8 /* clear pointer flip-flop (w) */ +#define DMA2_TEMP_REG 0xDA /* Temporary Register (r) */ +#define DMA2_RESET_REG 0xDA /* Master Clear (w) */ +#define DMA2_CLR_MASK_REG 0xDC /* Clear Mask */ +#define DMA2_MASK_ALL_REG 0xDE /* all-channels mask (w) */ + +#define DMA_ADDR_0 0x00 /* DMA address registers */ +#define DMA_ADDR_1 0x02 +#define DMA_ADDR_2 0x04 +#define DMA_ADDR_3 0x06 +#define DMA_ADDR_4 0xC0 +#define DMA_ADDR_5 0xC4 +#define DMA_ADDR_6 0xC8 +#define DMA_ADDR_7 0xCC + +#define DMA_SIZE_0 0x01 /* DMA transfer size registers */ +#define DMA_SIZE_1 0x03 +#define DMA_SIZE_2 0x05 +#define DMA_SIZE_3 0x07 +#define DMA_SIZE_4 0xC2 +#define DMA_SIZE_5 0xC6 +#define DMA_SIZE_6 0xCA +#define DMA_SIZE_7 0xCE + +#define DMA_PAGE_0 0x87 /* DMA page registers */ +#define DMA_PAGE_1 0x83 +#define DMA_PAGE_2 0x81 +#define DMA_PAGE_3 0x82 +#define DMA_PAGE_5 0x8B +#define DMA_PAGE_6 0x89 +#define DMA_PAGE_7 0x8A + +#define DMA_MODE_AUTOINIT 0x10 /* Auto-init mode bit */ +#define DMA_MODE_READ 0x44 /* I/O to memory, no autoinit, increment, single mode */ +#define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */ +#define DMA_MODE_CASCADE 0xC0 /* pass thru DREQ->HRQ, DACK<-HLDA only */ + +/* Indexable specific DMA registers */ +typedef struct __dma_regs_s { + unsigned char addr; /* DMA transfer address register */ + unsigned char page; /* DMA page register */ + unsigned char size; /* DMA transfer size register */ + unsigned char mask; /* DMA mask/unmask register */ + unsigned char flip; /* DMA flip-flop reset register */ + unsigned char mode; /* DMA mode register */ +} __dma_regs; + +extern __dma_regs dma[8]; + +/* Enable a specific DMA channel */ +static inline void dma_enable(unsigned int channel) +{ + outportb(dma[channel].mask, channel & 3); +} + +/* Disable a specific DMA channel */ +static inline void dma_disable(unsigned int channel) +{ + outportb(dma[channel].mask, (channel & 3) | 0x04); +} + +/* Clear the 'DMA Flip Flop' flag */ +static inline void dma_clear_ff(unsigned int channel) +{ + outportb(dma[channel].flip, 0); +} + +/* Set mode for a specific DMA channel */ +static inline void dma_set_mode(unsigned int channel, char mode) +{ + outportb(dma[channel].mode, mode | (channel & 3)); +} + +/* Set DMA page register */ +static inline void dma_set_page(unsigned int channel, char page) +{ + if (channel > 3) + page &= 0xfe; + outportb(dma[channel].page, page); +} + +/* + Set transfer address & page bits for specific DMA channel. + Assumes dma flipflop is clear. +*/ +static inline void dma_set_addr(unsigned int channel, unsigned int address) +{ + unsigned char dma_reg = dma[channel].addr; + dma_set_page(channel, address >> 16); + if (channel <= 3) { + outportb(dma_reg, (address) & 0xff); + outportb(dma_reg, (address >> 8) & 0xff); + } else { + outportb(dma_reg, (address >> 1) & 0xff); + outportb(dma_reg, (address >> 9) & 0xff); + } +} + +/* + Set transfer size for a specific DMA channel. + Assumes dma flip-flop is clear. +*/ +static inline void dma_set_count(unsigned int channel, unsigned int count) +{ + unsigned char dma_reg = dma[channel].size; + count--; /* number of DMA transfers is bigger by one */ + if (channel > 3) + count >>= 1; + outportb(dma_reg, (count) & 0xff); + outportb(dma_reg, (count >> 8) & 0xff); +} + +/* + Query the number of bytes left to transfer. + Assumes DMA flip-flop is clear. +*/ +static inline int dma_get_count(unsigned int channel) +{ + unsigned char dma_reg = dma[channel].size; + + /* using short to get 16-bit wrap around */ + unsigned short count; + count = inportb(dma_reg); + count |= inportb(dma_reg) << 8; + count++; + return (channel <= 3) ? count : (count << 1); +} + +typedef struct dma_buffer_s { + unsigned char *linear; /* Linear address */ + unsigned long physical; /* Physical address */ + unsigned long size; /* Buffer size */ + unsigned short selector; /* The selector assigned to this memory */ + unsigned char channel; /* The DMA channel */ +} dma_buffer; + +/* Allocate a block of memory suitable for using as a DMA buffer */ +extern dma_buffer *dma_allocate(unsigned int channel, unsigned int size); +/* Deallocate a DMA buffer */ +extern void dma_free(dma_buffer * buffer); +/* Start DMA transfer to or from given buffer */ +extern void dma_start(dma_buffer * buffer, unsigned long count, + unsigned char mode); + +#endif /* __DOSDMA_H__ */ + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/dos/dosgus.c b/libs/mikmod/drivers/dos/dosgus.c new file mode 100644 index 0000000..637b526 --- /dev/null +++ b/libs/mikmod/drivers/dos/dosgus.c @@ -0,0 +1,1907 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + Driver for GUS cards under DOS + Written by Andrew Zabolotny + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef DRV_ULTRA + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dosgus.h" +#include "mikmod.h" /* for MikMod_malloc() & co */ + +/********************************************* Private variables/routines *****/ + +/* The Gravis Ultrasound state/info */ +__gus_state gus; + +/* Try to avoid holes in DRAM less than this size */ +#define DRAM_HOLE_THRESHOLD 8192 +/* If hole is larger than that, create a free block describing it */ +#define DRAM_SPLIT_THRESHOLD 64 +/* The size of DMA buffer used for RAM->DRAM transfers */ +#define GF1_DMA_BUFFER_SIZE 8192 + +/* Debug macro: useful to change screen locations when some event occurs */ +#ifdef MIKMOD_DEBUG +# define DEBUG_PRINT(x) printf x; +# define DEBUG_OFS(addr, attr) \ + { \ + unsigned short x; \ + _dosmemgetw (0xb8780 + addr*2, 1, &x); \ + if ((x >> 8) != attr) x = '0'; \ + x = ((x + 1) & 0xff) | (attr << 8); \ + _dosmemputw (&x, 1, 0xb8780 + addr*2); \ + } +#else +# define DEBUG_PRINT(x) +# define DEBUG_OFS(addr, attr) +#endif + +static unsigned short __gus_volume_table[512] = { + 0x0000, 0x7000, 0x7ff0, 0x8800, 0x8ff0, 0x9400, 0x9800, 0x9c00, + 0x9ff0, 0xa200, 0xa400, 0xa600, 0xa800, 0xaa00, 0xac00, 0xae00, + 0xaff0, 0xb100, 0xb200, 0xb300, 0xb400, 0xb500, 0xb600, 0xb700, + 0xb800, 0xb900, 0xba00, 0xbb00, 0xbc00, 0xbd00, 0xbe00, 0xbf00, + 0xbff0, 0xc080, 0xc100, 0xc180, 0xc200, 0xc280, 0xc300, 0xc380, + 0xc400, 0xc480, 0xc500, 0xc580, 0xc600, 0xc680, 0xc700, 0xc780, + 0xc800, 0xc880, 0xc900, 0xc980, 0xca00, 0xca80, 0xcb00, 0xcb80, + 0xcc00, 0xcc80, 0xcd00, 0xcd80, 0xce00, 0xce80, 0xcf00, 0xcf80, + 0xcff0, 0xd040, 0xd080, 0xd0c0, 0xd100, 0xd140, 0xd180, 0xd1c0, + 0xd200, 0xd240, 0xd280, 0xd2c0, 0xd300, 0xd340, 0xd380, 0xd3c0, + 0xd400, 0xd440, 0xd480, 0xd4c0, 0xd500, 0xd540, 0xd580, 0xd5c0, + 0xd600, 0xd640, 0xd680, 0xd6c0, 0xd700, 0xd740, 0xd780, 0xd7c0, + 0xd800, 0xd840, 0xd880, 0xd8c0, 0xd900, 0xd940, 0xd980, 0xd9c0, + 0xda00, 0xda40, 0xda80, 0xdac0, 0xdb00, 0xdb40, 0xdb80, 0xdbc0, + 0xdc00, 0xdc40, 0xdc80, 0xdcc0, 0xdd00, 0xdd40, 0xdd80, 0xddc0, + 0xde00, 0xde40, 0xde80, 0xdec0, 0xdf00, 0xdf40, 0xdf80, 0xdfc0, + 0xdff0, 0xe020, 0xe040, 0xe060, 0xe080, 0xe0a0, 0xe0c0, 0xe0e0, + 0xe100, 0xe120, 0xe140, 0xe160, 0xe180, 0xe1a0, 0xe1c0, 0xe1e0, + 0xe200, 0xe220, 0xe240, 0xe260, 0xe280, 0xe2a0, 0xe2c0, 0xe2e0, + 0xe300, 0xe320, 0xe340, 0xe360, 0xe380, 0xe3a0, 0xe3c0, 0xe3e0, + 0xe400, 0xe420, 0xe440, 0xe460, 0xe480, 0xe4a0, 0xe4c0, 0xe4e0, + 0xe500, 0xe520, 0xe540, 0xe560, 0xe580, 0xe5a0, 0xe5c0, 0xe5e0, + 0xe600, 0xe620, 0xe640, 0xe660, 0xe680, 0xe6a0, 0xe6c0, 0xe6e0, + 0xe700, 0xe720, 0xe740, 0xe760, 0xe780, 0xe7a0, 0xe7c0, 0xe7e0, + 0xe800, 0xe820, 0xe840, 0xe860, 0xe880, 0xe8a0, 0xe8c0, 0xe8e0, + 0xe900, 0xe920, 0xe940, 0xe960, 0xe980, 0xe9a0, 0xe9c0, 0xe9e0, + 0xea00, 0xea20, 0xea40, 0xea60, 0xea80, 0xeaa0, 0xeac0, 0xeae0, + 0xeb00, 0xeb20, 0xeb40, 0xeb60, 0xeb80, 0xeba0, 0xebc0, 0xebe0, + 0xec00, 0xec20, 0xec40, 0xec60, 0xec80, 0xeca0, 0xecc0, 0xece0, + 0xed00, 0xed20, 0xed40, 0xed60, 0xed80, 0xeda0, 0xedc0, 0xede0, + 0xee00, 0xee20, 0xee40, 0xee60, 0xee80, 0xeea0, 0xeec0, 0xeee0, + 0xef00, 0xef20, 0xef40, 0xef60, 0xef80, 0xefa0, 0xefc0, 0xefe0, + 0xeff0, 0xf010, 0xf020, 0xf030, 0xf040, 0xf050, 0xf060, 0xf070, + 0xf080, 0xf090, 0xf0a0, 0xf0b0, 0xf0c0, 0xf0d0, 0xf0e0, 0xf0f0, + 0xf100, 0xf110, 0xf120, 0xf130, 0xf140, 0xf150, 0xf160, 0xf170, + 0xf180, 0xf190, 0xf1a0, 0xf1b0, 0xf1c0, 0xf1d0, 0xf1e0, 0xf1f0, + 0xf200, 0xf210, 0xf220, 0xf230, 0xf240, 0xf250, 0xf260, 0xf270, + 0xf280, 0xf290, 0xf2a0, 0xf2b0, 0xf2c0, 0xf2d0, 0xf2e0, 0xf2f0, + 0xf300, 0xf310, 0xf320, 0xf330, 0xf340, 0xf350, 0xf360, 0xf370, + 0xf380, 0xf390, 0xf3a0, 0xf3b0, 0xf3c0, 0xf3d0, 0xf3e0, 0xf3f0, + 0xf400, 0xf410, 0xf420, 0xf430, 0xf440, 0xf450, 0xf460, 0xf470, + 0xf480, 0xf490, 0xf4a0, 0xf4b0, 0xf4c0, 0xf4d0, 0xf4e0, 0xf4f0, + 0xf500, 0xf510, 0xf520, 0xf530, 0xf540, 0xf550, 0xf560, 0xf570, + 0xf580, 0xf590, 0xf5a0, 0xf5b0, 0xf5c0, 0xf5d0, 0xf5e0, 0xf5f0, + 0xf600, 0xf610, 0xf620, 0xf630, 0xf640, 0xf650, 0xf660, 0xf670, + 0xf680, 0xf690, 0xf6a0, 0xf6b0, 0xf6c0, 0xf6d0, 0xf6e0, 0xf6f0, + 0xf700, 0xf710, 0xf720, 0xf730, 0xf740, 0xf750, 0xf760, 0xf770, + 0xf780, 0xf790, 0xf7a0, 0xf7b0, 0xf7c0, 0xf7d0, 0xf7e0, 0xf7f0, + 0xf800, 0xf810, 0xf820, 0xf830, 0xf840, 0xf850, 0xf860, 0xf870, + 0xf880, 0xf890, 0xf8a0, 0xf8b0, 0xf8c0, 0xf8d0, 0xf8e0, 0xf8f0, + 0xf900, 0xf910, 0xf920, 0xf930, 0xf940, 0xf950, 0xf960, 0xf970, + 0xf980, 0xf990, 0xf9a0, 0xf9b0, 0xf9c0, 0xf9d0, 0xf9e0, 0xf9f0, + 0xfa00, 0xfa10, 0xfa20, 0xfa30, 0xfa40, 0xfa50, 0xfa60, 0xfa70, + 0xfa80, 0xfa90, 0xfaa0, 0xfab0, 0xfac0, 0xfad0, 0xfae0, 0xfaf0, + 0xfb00, 0xfb10, 0xfb20, 0xfb30, 0xfb40, 0xfb50, 0xfb60, 0xfb70, + 0xfb80, 0xfb90, 0xfba0, 0xfbb0, 0xfbc0, 0xfbd0, 0xfbe0, 0xfbf0, + 0xfc00, 0xfc10, 0xfc20, 0xfc30, 0xfc40, 0xfc50, 0xfc60, 0xfc70, + 0xfc80, 0xfc90, 0xfca0, 0xfcb0, 0xfcc0, 0xfcd0, 0xfce0, 0xfcf0, + 0xfd00, 0xfd10, 0xfd20, 0xfd30, 0xfd40, 0xfd50, 0xfd60, 0xfd70, + 0xfd80, 0xfd90, 0xfda0, 0xfdb0, 0xfdc0, 0xfdd0, 0xfde0, 0xfdf0, + 0xfe00, 0xfe10, 0xfe20, 0xfe30, 0xfe40, 0xfe50, 0xfe60, 0xfe70, + 0xfe80, 0xfe90, 0xfea0, 0xfeb0, 0xfec0, 0xfed0, 0xfee0, 0xfef0, + 0xff00, 0xff10, 0xff20, 0xff30, 0xff40, 0xff50, 0xff60, 0xff70, + 0xff80, 0xff90, 0xffa0, 0xffb0, 0xffc0, 0xffd0, 0xffe0, 0xfff0 +}; + +/* Wait a bit for GUS before doing something + * Mark function as volatile: don't allow it to be inlined. + * It *should* be slow, no need to make it work faster :-) + */ +#if !defined(__GNUC__) || (__GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ == 0) +# define _func_noinline volatile /* match original code */ +# define _func_noclone +#else +/* avoid warnings from newer gcc: + * "function definition has qualified void return type" and + * function return types not compatible due to 'volatile' */ +# define _func_noinline __attribute__((__noinline__)) +# if (__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 5) +# define _func_noclone +# else +# define _func_noclone __attribute__((__noclone__)) +# endif +#endif +_func_noinline +_func_noclone + void __gus_delay() +{ + inportb(GF1_MIX_CTRL); + inportb(GF1_MIX_CTRL); + inportb(GF1_MIX_CTRL); + inportb(GF1_MIX_CTRL); + inportb(GF1_MIX_CTRL); + inportb(GF1_MIX_CTRL); + inportb(GF1_MIX_CTRL); + inportb(GF1_MIX_CTRL); +} + +static void __gus_stop_controller(unsigned char gf1reg) +{ + register unsigned char value = __gus_inregb(gf1reg); + __gus_outregb(gf1reg, (value | GF1VC_STOPPED | GF1VC_STOP) & + ~(GF1VC_IRQ_PENDING | GF1VC_IRQ)); +} + +/* Returns 1 if volume is already at given position */ +static boolean __gus_volume_ramp_to(unsigned short volume, + unsigned char rate, + unsigned char vol_ctrl) +{ + int svol = __gus_inregw(GF1R_VOLUME) & 0xfff0; + int evol = volume; + + /* First of all, disable volume ramp */ + __gus_stop_controller(GF1R_VOLUME_CONTROL); + + /* If voice is stopped, set the volume to zero and return */ + if (__gus_inregb(GF1R_VOICE_CONTROL) & GF1VC_STOPPED) { + __gus_outregw(GF1R_VOLUME, 0); + return 1; + } + + /* Avoid clicks when volume ramp goes too high or too low */ + if (svol < 0x0400) + svol = 0x0400; + if (svol > 0xfc00) + svol = 0xfc00; + if (evol < 0x0400) + evol = 0x0400; + if (evol > 0xfc00) + evol = 0xfc00; + + /* Adjust start/end positions */ + if (svol > evol) { + unsigned short tmp = evol; + evol = svol; + svol = tmp; + vol_ctrl |= GF1VL_BACKWARD; + } + + /* If we already are (near) the target volume, quit */ + if (evol - svol < 0x1000) { + __gus_outregw(GF1R_VOLUME, volume); + return 1; + } + + __gus_outregb(GF1R_VOLUME_START, svol >> 8); + __gus_outregb(GF1R_VOLUME_END, evol >> 8); + __gus_outregb(GF1R_VOLUME_RATE, rate); + __gus_outregb_slow(GF1R_VOLUME_CONTROL, vol_ctrl); + return 0; +} + +static inline void __gus_stop_voice() +{ + __gus_stop_controller(GF1R_VOICE_CONTROL); + __gus_outregb_slow(GF1R_VOICE_CONTROL, GF1VC_STOPPED | GF1VC_STOP); +} + +/* The GUS IRQ handler */ +static void gf1_irq() +{ + unsigned char irq_source; /* The contents of GF1_IRQ_STATUS register */ + boolean timer_cb = 0; /* Call timer callback function */ + + DEBUG_OFS(0, 0xCE) + gus.eow_ignore = 0; + while ((irq_source = inportb(GF1_IRQ_STATUS))) { + DEBUG_OFS(1, 0xCE) + + if (irq_source & GF1M_IRQ_DMA_COMPLETE) { + DEBUG_OFS(4, 0x9F) + /* reset the IRQ pending bit */ + __gus_inregb(GF1R_DMA_CONTROL); + gus.dma_active = 0; + + if (gus.dma_callback) + gus.dma_callback(); + } + + if (irq_source & (GF1M_IRQ_WAVETABLE | GF1M_IRQ_ENVELOPE)) { + unsigned char vcirq; + unsigned int done_mask = 0; + + /* IRQ bits are inverse (i.e. 0 = IRQ pending) */ + while ((vcirq = __gus_inregb(GF1R_IRQ_SOURCE) ^ + (GF1IRQ_WAVE | GF1IRQ_VOLUME)) & + (GF1IRQ_WAVE | GF1IRQ_VOLUME)) { + unsigned long voice = (vcirq & 0x1f); + unsigned char voice_ctl, volume_ctl; + unsigned int voice_mask = (1 << voice); + + /* Don't handle more than one IRQ from same voice */ + if (done_mask & voice_mask) + continue; + + done_mask |= voice_mask; + + /* Read voice/volume selection registers */ + __gus_select_voice(voice); + voice_ctl = __gus_inregb(GF1R_VOICE_CONTROL); + volume_ctl = __gus_inregb(GF1R_VOLUME_CONTROL); + + if ((vcirq & GF1IRQ_WAVE) && (gus.wt_callback) + && !(gus.eow_ignore & voice_mask)) { + DEBUG_OFS(5, 0xAF) + gus.wt_callback(voice, voice_ctl, volume_ctl); + } + + if ((vcirq & GF1IRQ_VOLUME) && (gus.vl_callback)) { + DEBUG_OFS(6, 0xAF) + gus.vl_callback(voice, voice_ctl, volume_ctl); + } + } + } + + /* Reset timers that sent this IRQ */ + if (irq_source & (GF1M_IRQ_TIMER1 | GF1M_IRQ_TIMER2)) { + unsigned char timer_ctl = gus.timer_ctl_reg; + + if (irq_source & GF1M_IRQ_TIMER1) + timer_ctl &= ~GF1M_TIMER1; + + if (irq_source & GF1M_IRQ_TIMER2) + timer_ctl &= ~GF1M_TIMER2; + + __gus_outregb_slow(GF1R_TIMER_CONTROL, timer_ctl); + __gus_outregb_slow(GF1R_TIMER_CONTROL, gus.timer_ctl_reg); + } + + if (irq_source & GF1M_IRQ_TIMER1) + if (--gus.t1_countdown == 0) { + gus.t1_countdown = gus.t1_multiple; + gus.t1_ticks++; + + DEBUG_OFS(2, 0xCF) + + if (gus.t1_callback) { + timer_cb = 1; + gus.t1_callback(); + } + } + + if (irq_source & GF1M_IRQ_TIMER2) + if (--gus.t2_countdown == 0) { + gus.t2_countdown = gus.t2_multiple; + gus.t2_ticks++; + + DEBUG_OFS(3, 0xCF) + + if (gus.t2_callback) + gus.t2_callback(); + } +#if 0 + /* The following are not used and implemented yet */ + if (irq_source & (GF1M_IRQ_MIDI_TX | GF1M_IRQ_MIDI_RX)) { + } +#endif + } + + irq_ack(gus.gf1_irq); + + if (timer_cb && gus.timer_callback) + gus.timer_callback(); +} + +static void gf1_irq_end() +{ +} + +static boolean __gus_detect() +{ + /* A relatively relaxed autodetection; + We don't count on DRAM: GUS PnP could not have it + (although its anyway bad for us) + */ + __gus_select_voice(0); + __gus_stop_voice(); + __gus_outregw(GF1R_FREQUENCY, 0x1234); + __gus_outregw(GF1R_VOLUME, 0x5670); + return ((__gus_inregw(GF1R_FREQUENCY) & 0xfffe) == 0x1234) + && ((__gus_inregw(GF1R_VOLUME) & 0xfff0) == 0x5670); +} + +static void __gus_reset(boolean reset_io_dma) +{ + static unsigned char irqctl[16] = { 0, 0, 1, 3, 0, 2, 0, 4, 0, 0, 0, 5, 6, 0, 0, 7 }; + static unsigned char dmactl[8] = { 0, 1, 0, 2, 0, 3, 4, 5 }; + unsigned char irqtmp, dmatmp; + + /* Disable interrupts while resetting to avoid spurious IRQs */ + int i, timer, old_ints = disable(); + + /* Stop the timer so that GUS IRQ won't clobber registers */ + timer = (gus.timer_ctl_reg & GF1M_TIMER1); + if (timer) + gus_timer_stop(); + + gus.dma_active = 0; + + __gus_outregb(GF1R_RESET, 0); + for (i = 0; i < 10; i++) + __gus_delay(); + __gus_outregb(GF1R_RESET, GF1M_MASTER_RESET); + for (i = 0; i < 10; i++) + __gus_delay(); + + outportb(GF1_MIDI_CTRL, GF1M_MIDI_RESET); + for (i = 0; i < 10; i++) + __gus_delay(); + outportb(GF1_MIDI_CTRL, 0); + + /* Reset all IRQ sources */ + __gus_outregb(GF1R_DMA_CONTROL, 0); + __gus_outregb(GF1R_TIMER_CONTROL, 0); + __gus_outregb(GF1R_SAMPLE_CONTROL, 0); + + /* Reset all voices */ + gus_reset(gus.voices, gus.dynmask); + + /* Flush any pending IRQs */ + inportb(GF1_IRQ_STATUS); + __gus_inregb(GF1R_DMA_CONTROL); + __gus_inregb(GF1R_SAMPLE_CONTROL); + __gus_inregb(GF1R_IRQ_SOURCE); + + if (reset_io_dma) { + /* Now set up the GUS card to required IRQs and DMAs */ + if (gus.irq[0] == gus.irq[1]) + irqtmp = irqctl[gus.irq[0]] | GF1M_IRQ_EQUAL; + else + irqtmp = irqctl[gus.irq[0]] | (irqctl[gus.irq[1]] << 3); + + if (gus.dma[0] == gus.dma[1]) + dmatmp = dmactl[gus.dma[0]] | GF1M_DMA_EQUAL; + else + dmatmp = dmactl[gus.dma[0]] | (dmactl[gus.dma[1]] << 3); + + /* Reset IRQs if possible */ + gus.mixer = + GF1M_MIXER_NO_LINE_IN | GF1M_MIXER_NO_OUTPUT | GF1M_MIXER_GF1_IRQ; + if (gus.version >= GUS_CARD_VERSION_CLASSIC1) { + outportb(GF1_REG_CTRL, 0x05); + outportb(GF1_MIX_CTRL, gus.mixer); + outportb(GF1_IRQ_CTRL, 0x00); /* Reset IRQs */ + outportb(GF1_REG_CTRL, 0x00); + } + + /* Set up DMA channels: NEVER disable MIXER_GF1_IRQ in the future */ + outportb(GF1_MIX_CTRL, gus.mixer); + outportb(GF1_IRQ_CTRL, dmatmp); + + /* Set up IRQ channels */ + outportb(GF1_MIX_CTRL, gus.mixer | GF1M_CONTROL_SELECT); + outportb(GF1_IRQ_CTRL, irqtmp); + } + + __gus_outregb(GF1R_RESET, GF1M_MASTER_RESET | GF1M_OUTPUT_ENABLE | GF1M_MASTER_IRQ); + __gus_delay(); + + /* Flush IRQs again */ + inportb(GF1_IRQ_STATUS); + __gus_inregb(GF1R_DMA_CONTROL); + __gus_inregb(GF1R_SAMPLE_CONTROL); + __gus_inregb(GF1R_IRQ_SOURCE); + + _irq_ack(gus.irq[0]); + _irq_ack(gus.irq[1]); + + if (timer) + gus_timer_continue(); + + if (old_ints) + enable(); + + /* Enable output */ + __gus_mixer_output(1); +} + +/* Transfer a block of data from GUS DRAM to main RAM through port I/O */ +static void __gus_transfer_io_in(unsigned long address, unsigned char *source, + unsigned long size) +{ + while (size) { + register unsigned int size64k; + + size64k = 0x10000 - (address & 0xffff); + if (size64k > size) + size64k = size; + size -= size64k; + + __gus_outregb(GF1R_DRAM_HIGH, address >> 16); + while (size64k--) { + __gus_outregw(GF1R_DRAM_LOW, address++); + *source++ = inportb(GF1_DRAM); + } + } +} + +/* Transfer a block of data into GUS DRAM through port I/O */ +static void __gus_transfer_io(unsigned long address, unsigned char *source, + unsigned long size, int flags) +{ + while (size) { + register unsigned int size64k; + + size64k = 0x10000 - (address & 0xffff); + if (size64k > size) + size64k = size; + size -= size64k; + + __gus_outregb(GF1R_DRAM_HIGH, address >> 16); + if (flags & GUS_WAVE_INVERT) + if (flags & GUS_WAVE_16BIT) + while (size64k-- && size64k--) { + __gus_outregw(GF1R_DRAM_LOW, address++); + outportb(GF1_DRAM, *source++); + __gus_outregw(GF1R_DRAM_LOW, address++); + outportb(GF1_DRAM, (*source++) ^ 0x80); + } else + while (size64k--) { + __gus_outregw(GF1R_DRAM_LOW, address++); + outportb(GF1_DRAM, (*source++) ^ 0x80); + } else + while (size64k--) { + __gus_outregw(GF1R_DRAM_LOW, address++); + outportb(GF1_DRAM, *source++); + } + } +} + +/* Wait for DMA transfer to finish between 8-9 1/18sec timer ticks */ +static int __gus_wait_dma() +{ + unsigned long timer; + _farsetsel(_dos_ds); + timer = _farnspeekl(0x46c); + while (gus.dma_active) + if (_farnspeekl(0x46c) - timer > 8) { + /* Force DMA abort since something went wrong */ + __gus_reset(0); + return -1; + } + + return 0; +} + +/* Transfer a block of data into GUS DRAM through DMA controller */ +static void __gus_transfer_dma(unsigned long address, unsigned char *source, + unsigned long size, int flags) +{ + unsigned char dma_control; + unsigned long bytes_left; + unsigned long cur_size; + unsigned long dest_addr; + + if ((gus.dma[0] > 3) || (flags & GUS_WAVE_16BIT)) + size = (size + 1) & ~1; + + bytes_left = size; + while (bytes_left) { + __gus_wait_dma(); + + cur_size = gus.dma_buff->size; + if (cur_size > bytes_left) + cur_size = bytes_left; + bytes_left -= cur_size; + dest_addr = address; + + if (gus.dma_buff->linear != source) + memmove(gus.dma_buff->linear, source, cur_size); + source += cur_size; + address += cur_size; + + /* Disable GUS -> DMA tie */ + __gus_outregb(GF1R_DMA_CONTROL, 0); + __gus_delay(); + + /* Set up the DMA */ + dma_start(gus.dma_buff, cur_size, DMA_MODE_WRITE); + gus.dma_active = 1; + + /* Reset the DMA IRQ pending bit if set */ + __gus_inregb(GF1R_DMA_CONTROL); + + /* The 16-bit DMA channels needs a slightly different approach */ + dma_control = GF1M_DMAR_ENABLE | GF1M_DMAR_IRQ_ENABLE | gus.dma_rate; + if (gus.dma[0] > 3) { + dest_addr = __gus_convert_addr16(dest_addr); + dma_control |= GF1M_DMAR_CHAN16; + } + + __gus_outregw(GF1R_DMA_ADDRESS, dest_addr >> 4); + + if (flags & GUS_WAVE_16BIT) + dma_control |= GF1M_DMAR_DATA16; + if (flags & GUS_WAVE_INVERT) + dma_control |= GF1M_DMAR_TOGGLE_SIGN; + + /* Tell GUS to start transfer */ + __gus_outregb(GF1R_DMA_CONTROL, dma_control); + } +} + +static void __gus_detect_version() +{ + unsigned char tmp; + + switch (gus.version = inportb(GF1_REVISION)) { + case 5: + gus.version = GUS_CARD_VERSION_CLASSIC_ICS; + gus.ics = 1; + gus.ics_flipped = 1; + break; + case 6: + case 7: + case 8: + case 9: + gus.version = GUS_CARD_VERSION_CLASSIC_ICS; + gus.ics = 1; + break; + case 10: + gus.version = GUS_CARD_VERSION_MAX; + gus.codec = 1; + break; + case 11: + gus.version = GUS_CARD_VERSION_MAX1; + gus.codec = 1; + break; + case 0x30: + gus.version = GUS_CARD_VERSION_ACE; + break; + case 0x50: + gus.version = GUS_CARD_VERSION_EXTREME; + break; + case 0xff: + /* Pre-3.7 board */ + outportb(GF1_REG_CTRL, 0x20); + tmp = inportb(GF1_REG_CTRL); + if ((tmp != 0xff) && (tmp & 0x06)) + gus.version = GUS_CARD_VERSION_CLASSIC1; + else + gus.version = GUS_CARD_VERSION_CLASSIC; + break; + default: + /* Hmm... unknown revision. Assume a safe Classic model */ +#ifdef MIKMOD_DEBUG + fprintf(stderr, "libgus: Unknown board revision (%02x)\n", + gus.version); +#endif + gus.version = GUS_CARD_VERSION_CLASSIC; + break; + } +} + +static void __gus_detect_transfer() +{ + unsigned char *outbuff, *inbuff; + unsigned int i, j, seed = 0x13243546; + __gus_transfer_func func; + +#define TRANSFER_SIZE 0x4000 + + outbuff = (unsigned char *) MikMod_malloc(TRANSFER_SIZE); + inbuff = (unsigned char *) MikMod_malloc(TRANSFER_SIZE); + + /* Suppose we have an malfunctioning GUS */ + gus.transfer = NULL; + + for (i = (gus.dma_buff ? 0 : 4); i <= 4; i++) { + switch (i) { + case 0: + gus.dma_rate = GF1M_DMAR_RATE0; + func = __gus_transfer_dma; + break; + case 1: + gus.dma_rate = GF1M_DMAR_RATE1; + func = __gus_transfer_dma; + break; + case 2: + gus.dma_rate = GF1M_DMAR_RATE2; + func = __gus_transfer_dma; + break; + case 3: + gus.dma_rate = GF1M_DMAR_RATE3; + func = __gus_transfer_dma; + break; + case 4: + func = __gus_transfer_io; + break; + } + + /* Fill data array each time with pseudo-random values */ + for (j = 0; j < TRANSFER_SIZE; j++) + outbuff[j] = seed, seed = + ((seed + 358979323) ^ (seed >> 16)) * 314159265; + + /* Transfer the random array to GUS */ + /* Poke a security fence around dest block */ + __gus_poke(0x100 - 1, 0xAA); + __gus_poke(0x100 - 2, 0x55); + __gus_poke(0x100 + TRANSFER_SIZE + 0, 0xAA); + __gus_poke(0x100 + TRANSFER_SIZE + 1, 0x55); + + func(0x100, outbuff, TRANSFER_SIZE, 0); + + if (__gus_wait_dma() == 0) { + /* Check if the security fence was not damaged */ + if ((__gus_peek(0x100 - 1) != 0xAA) + || (__gus_peek(0x100 - 2) != 0x55) + || (__gus_peek(0x100 + TRANSFER_SIZE + 0) != 0xAA) + || (__gus_peek(0x100 + TRANSFER_SIZE + 1) != 0x55)) + continue; + + /* Now check if GUS DRAM really data that we expects to be transferred */ + __gus_transfer_io_in(0x100, inbuff, TRANSFER_SIZE); + if (memcmp(outbuff, inbuff, TRANSFER_SIZE) == 0) { + gus.transfer = func; + break; + } + } + } + +#undef TRANSFER_SIZE + + MikMod_free(inbuff); + MikMod_free(outbuff); +} + +static void __gus_detect_memory() +{ + unsigned int size; + for (size = 0; size < 1024; size += 256) { + __gus_poke(size * 1024, 0xaa); + if (__gus_peek(size * 1024) != 0xaa) + break; + __gus_poke(size * 1024, 0x55); + if (__gus_peek(size * 1024) != 0x55) + break; + } + gus.ram = size; +} + +static void __gus_init() +{ + char *gusenv = getenv("ULTRASND"); + + memset((void *)&gus, 0, sizeof(gus)); + gus.cmd_voice = -1; + + if (!gusenv) + return; + + sscanf(gusenv, "%x,%d,%d,%d,%d", &gus.port, &gus.dma[0], &gus.dma[1], + &gus.irq[0], &gus.irq[1]); + + /* A relaxed sanity check */ + if ((gus.port < 0x100) || (gus.port > 0x1000) + || (gus.irq[0] < 2) || (gus.irq[0] > 15) + || (gus.irq[1] < 2) || (gus.irq[1] > 15) + || (gus.dma[0] < 0) || (gus.dma[0] > 7) + || (gus.dma[1] < 0) || (gus.dma[1] > 7)) + return; + + gus.voices = 32; + gus.timer_ctl = GF1M_MASK_TIMER1 | GF1M_MASK_TIMER2; + + /* Detect if the card is really there */ + if (__gus_detect() == 0) + return; + + /* Detect the version of Gravis Ultrasound */ + __gus_detect_version(); + + /* Reset the card */ + __gus_reset(1); + + /* Detect the amount of on-board memory */ + __gus_detect_memory(); + + gus.ok = 1; +} + +static void __gus_kick(gus_wave_t * wave, unsigned int wave_offset) +{ + unsigned char vc; + + vc = GF1VC_IRQ; + if (wave->format & GUS_WAVE_16BIT) + vc |= GF1VC_DATA16; + if (wave->format & GUS_WAVE_BACKWARD) + vc |= GF1VC_BACKWARD; + if (wave->format & GUS_WAVE_LOOP) { + vc |= GF1VC_LOOP_ENABLE; + if (wave->format & GUS_WAVE_BIDIR) + vc |= GF1VC_BI_LOOP; + } + __gus_set_loop_start(vc, (wave->begin.memory << 4) + wave->loop_start); + if (wave->format & GUS_WAVE_LOOP) + __gus_set_loop_end(vc, (wave->begin.memory << 4) + wave->loop_end); + else + __gus_set_loop_end(vc, (wave->begin.memory + wave->size) << 4); + __gus_set_current(vc, (wave->begin.memory << 4) + wave_offset + 100); + __gus_outregb_slow(GF1R_VOICE_CONTROL, vc); +} + +/* Timer 1 callback function (updates voices) */ +static void __gus_timer_update() +{ + gus_wave_t *wave; + unsigned long wave_offset; + unsigned char *src, *top; + unsigned int vmask = (1 << gus.cur_voice); + + if (!gus.cmd_pool_ready) + return; + + __gus_select_voice(gus.cur_voice); + wave_offset = 0; + src = gus.cmd_pool; + top = gus.cmd_pool + gus.cmd_pool_top; + +#define GET_B *src +#define GET_W *((unsigned short *)src) +#define GET_L *((unsigned long *)src) + + while (src < top) { + __gus_delay(); + switch (GET_B++) { + case PCMD_VOICE: + __gus_select_voice(gus.cur_voice = GET_B++); + vmask = (1 << gus.cur_voice); + break; + case PCMD_FREQ: + /* __gus_outregw(GF1R_FREQUENCY, GET_W++);*/ + __gus_outregw(GF1R_FREQUENCY, *(unsigned short *)src); + src += 2; + break; + case PCMD_PAN: + __gus_outregb(GF1R_BALANCE, GET_B++); + break; + case PCMD_VOLUME: + __gus_volume_ramp_to(gus.cur_vol[gus.cur_voice] = + /* GET_W++, GUS_VOLCHANGE_RAMP, GF1VL_IRQ);*/ + *(unsigned short *)src, GUS_VOLCHANGE_RAMP, GF1VL_IRQ); + src += 2; + break; + case PCMD_VOLUME_PREPARE: + /* gus.cur_vol[gus.cur_voice] = GET_W++;*/ + gus.cur_vol[gus.cur_voice] = *(unsigned short *)src; + src += 2; + break; + case PCMD_OFFSET: + /* wave_offset = GET_L++;*/ + wave_offset = *(unsigned long *)src; + src += 4; + break; + case PCMD_START: + /* wave = (gus_wave_t *) GET_L++;*/ + wave = (gus_wave_t *) *(unsigned long *)src; + src += 4; + gus.cur_wave[gus.cur_voice] = wave; + gus.kick_offs[gus.cur_voice] = wave_offset; + if (__gus_volume_ramp_to(0, GUS_VOLCHANGE_RAMP, GF1VL_IRQ)) { + __gus_kick(wave, wave_offset); + __gus_volume_ramp_to(gus.cur_vol[gus.cur_voice], + GUS_VOLCHANGE_RAMP, GF1VL_IRQ); + } else + gus.voice_kick[gus.cur_voice] = 1; + wave_offset = 0; + gus.eow_ignore |= vmask; + break; + case PCMD_STOP: + /* If volume is close to nothing, abort immediately instead of + ramping */ + gus.cur_vol[gus.cur_voice] = 0; + gus.cur_wave[gus.cur_voice] = NULL; + if (__gus_volume_ramp_to(0, GUS_VOLCHANGE_RAMP, GF1VL_IRQ)) + __gus_stop_voice(); + break; + case PCMD_STOP_LOOP: + __gus_outregb_slow(GF1R_VOICE_CONTROL, + (__gus_inregb(GF1R_VOICE_CONTROL) | GF1VC_IRQ) + & ~GF1VC_LOOP_ENABLE); + __gus_outregb_slow(GF1R_VOLUME_CONTROL, + __gus_inregb(GF1R_VOLUME_CONTROL) & + ~GF1VL_ROLLOVER); + break; + default: + /* Alarm! Break out immediately */ + src = top; + break; + } + } + +#undef GET_B +#undef GET_W +#undef GET_L + + gus.cmd_pool_ready = 0; + gus.cmd_pool_top = 0; +} + +static void __gus_wavetable_update(unsigned int voice, unsigned int voice_ctl, + unsigned int volume_ctl) +{ + gus_wave_t *wave = gus.cur_wave[voice]; + + if (!wave || !(wave->format & GUS_WAVE_LOOP)) { + __gus_stop_voice(); + gus.cur_wave[voice] = NULL; + gus.cur_vol[voice] = 0; + if (__gus_volume_ramp_to(0, GUS_VOLCHANGE_RAMP, GF1VL_IRQ)) + __gus_stop_voice(); + } +} + +static void __gus_volume_update(unsigned int voice, unsigned int voice_ctl, + unsigned int volume_ctl) +{ + __gus_volume_ramp_to(gus.cur_vol[voice], GUS_VOLCHANGE_RAMP, GF1VL_IRQ); + if (!gus.cur_wave[voice]) + __gus_stop_voice(); + else if (gus.voice_kick[voice]) + __gus_kick(gus.cur_wave[voice], gus.kick_offs[voice]); + gus.voice_kick[voice] = 0; +} + +/***************************************************** GUS memory manager *****/ + +/* Mark all GUS memory as available */ +static void __gus_mem_clear() +{ + __gus_mcb *cur = gus.mcb; + + while (cur) { + __gus_mcb *next = cur->next; + if (cur != gus.mcb) + MikMod_free(cur); + cur = next; + } + + if (!gus.mcb) + gus.mcb = (__gus_mcb *) MikMod_malloc(sizeof(__gus_mcb)); + + gus.mcb->next = gus.mcb->prev = NULL; + gus.mcb->addr = 0; + gus.mcb->size = gus.ram * 1024; + gus.mcb->free = 1; +} + +/* Return amount of free memory */ +static unsigned int __gus_mem_get_free() +{ + __gus_mcb *cur = gus.mcb; + unsigned int size = 0; + + if (!gus.open) + return gus.ram * 1024; + + while (cur) { + if (cur->free) + size += cur->size; + cur = cur->next; + } + + return size; +} + +/* Return largest size for a 8-bit sample */ +static unsigned int __gus_mem_get_free_8() +{ + __gus_mcb *cur = gus.mcb; + unsigned int size = 0; + + if (!gus.open) + return 0; + + while (cur) { + if (cur->free && (cur->size > size)) + size = cur->size; + cur = cur->next; + } + + return size; +} + +/* Return largest size for a 16-bit sample */ +static unsigned int __gus_mem_get_free_16() +{ + __gus_mcb *cur = gus.mcb; + unsigned int size = 0; + + if (!gus.open) + return 0; + + while (cur) { + if (cur->free) { + unsigned int size16 = cur->size; + unsigned int tmp; + /* 16-bit samples cannot cross 256K boundaries */ + tmp = 0x40000 - (cur->addr & 0x3ffff); + if (size16 > tmp) + size16 = tmp; + /* 16-bit samples should be aligned on a 32-byte boundary */ + size16 -= (32 - cur->addr) & 0x1f; + + if (size16 > size) + size = size16; + + /* Now try vice versa: skip a portion of aligned memory */ + size16 = + (cur->addr + cur->size) - ((cur->addr + 0x3ffff) & ~0x3ffff); + if ((size16 < 0x7fffffff) && (size16 > size)) + size = size16; + } + cur = cur->next; + } + + return size; +} + +/* Allocate a segment of GUS DRAM for a sample with given bits per sample. + * The algorithm tries to find the smallest free block that fits requested + * size; but if found free block is larger by some (large) delta than + * requested block size, the largest possible block is preffered. + */ +static unsigned int __gus_mem_alloc(unsigned int size, int bits16) +{ + __gus_mcb *cur = gus.mcb; + __gus_mcb *best_max = NULL, *best_min = NULL; + unsigned int best_max_delta = 0, best_min_delta = 0xffffffff; + unsigned int best_max_prefix = 0, best_min_prefix = 0; + unsigned int memaddr, memsize; + + if (!gus.open || !size || (bits16 && size > 0x40000)) + return -1; + + /* Round block size up to nearest acceptable DMA bound */ + if (bits16) + size = (size + 0x1f) & ~0x1f; + else + size = (size + 0x0f) & ~0x0f; + + while (cur) { + if (cur->free) { + unsigned char fits = 0; + + memsize = cur->size; + memaddr = cur->addr; + + if (bits16) { + /* 16-bit samples cannot cross 256K boundaries */ + unsigned int tmp = 256 * 1024 - (memaddr & 0x3ffff); + if (memsize > tmp) + memsize = tmp; + /* 16-bit samples should be aligned on a 32-byte boundary */ + memsize -= (32 - memaddr) & 0x1f; + memaddr = (memaddr + 0x1f) & ~0x1f; + } + + /* If block fits, analyze it */ + if (size <= memsize) + fits = 1; + /* Look if we still can complete the request by creating a free + block */ + else if (size <= cur->size) { + /* Align start address to next 256k boundary */ + unsigned int endaddr = cur->addr + cur->size; + memaddr = (cur->addr + 0x3ffff) & ~0x3ffff; + /* Can we split current block by inserting a free block at the + beginning? */ + if ((memaddr < endaddr) && (memaddr + size <= endaddr)) + fits = 1; + } + + if (fits) { + unsigned int size_delta = cur->size - size; + unsigned int size_prefix = memaddr - cur->addr; + if (size_delta < best_min_delta) + best_min = cur, best_min_delta = + size_delta, best_min_prefix = size_prefix; + if (size_delta > best_max_delta) + best_max = cur, best_max_delta = + size_delta, best_max_prefix = size_prefix; + } + } + + cur = cur->next; + } + + if (!best_min) + return -1; + + /* If minimal block that fits is too large, use largest block that fits */ + /* But if using the maximal block is going to create a small hole, forget + it */ + if ((best_max_prefix == 0) + || (best_max_prefix >= DRAM_HOLE_THRESHOLD) + || (best_min_prefix != 0)) + if ( + ((best_min_delta < DRAM_HOLE_THRESHOLD) && + (best_max_delta >= DRAM_HOLE_THRESHOLD)) || + ((best_min_prefix > 0) && (best_min_prefix < DRAM_HOLE_THRESHOLD) + && ((best_max_prefix == 0) || + (best_max_prefix > best_min_prefix))) || + ((best_min_prefix != 0) && (best_max_prefix == 0))) { + best_min = best_max; + best_min_delta = best_max_delta; + best_min_prefix = best_max_prefix; + } + + /* Compute the DRAM address to return */ + memaddr = best_min->addr + best_min_prefix; + if (bits16) + memaddr = (memaddr + 0x1f) & ~0x1f; + else + memaddr = (memaddr + 0x0f) & ~0x0f; + + /* If we have a considerable hole at the beginning of sample, + create a free node describing the hole */ + if (memaddr - best_min->addr >= DRAM_SPLIT_THRESHOLD) { + __gus_mcb *newmcb = (__gus_mcb *) MikMod_malloc(sizeof(__gus_mcb)); + newmcb->prev = best_min->prev; + newmcb->next = best_min; + newmcb->addr = best_min->addr; + newmcb->size = memaddr - best_min->addr; + newmcb->free = 1; + best_min->addr = memaddr; + best_min->size -= newmcb->size; + best_min->prev = newmcb; + if (newmcb->prev) + newmcb->prev->next = newmcb; + } + + /* Compute the size of hole at the end of block */ + memsize = (best_min->addr + best_min->size) - (memaddr + size); + + /* Split the block if the block is larger than requested amount */ + if (memsize > DRAM_SPLIT_THRESHOLD) { + /* The next node cannot be free since free blocks are always glued + together */ + __gus_mcb *newmcb = (__gus_mcb *) MikMod_malloc(sizeof(__gus_mcb)); + best_min->size -= memsize; + newmcb->prev = best_min; + newmcb->next = best_min->next; + newmcb->addr = best_min->addr + best_min->size; + newmcb->size = memsize; + newmcb->free = 1; + if (best_min->next) + best_min->next->prev = newmcb; + best_min->next = newmcb; + } + best_min->free = 0; + + return memaddr; +} + +static void __gus_mem_free(unsigned int addr) +{ + __gus_mcb *cur = gus.mcb; + while (cur) { + if (!cur->free && (cur->addr <= addr) && + (cur->addr + cur->size > addr)) { + cur->free = 1; + + /* If next block is free as well, link them together */ + if (cur->next && cur->next->free) { + __gus_mcb *next = cur->next; + cur->size += next->size; + cur->next = next->next; + if (next->next) + next->next->prev = cur; + MikMod_free(next); + } + + /* If previous block is free, link current block with it */ + if (cur->prev && cur->prev->free) { + cur->prev->size += cur->size; + cur->prev->next = cur->next; + if (cur->next) + cur->next->prev = cur->prev; + MikMod_free(cur); + } + return; + } + cur = cur->next; + } +} + +static void __gus_mem_pack() +{ +} + +#ifdef MIKMOD_DEBUG + +/* Debug dump of GUS DRAM heap */ +void __gus_mem_dump() +{ + __gus_mcb *cur = gus.mcb; + fprintf(stderr, "/-- Offset --+-- Prev --+-- Size --+-- Free --\\\n"); + while (cur) { + fprintf(stderr, "| %08X | %08X | %6d | %s |\n", + cur->addr, cur->prev ? cur->prev->addr : -1, cur->size, + cur->free ? "yes" : " no"); + cur = cur->next; + } + fprintf(stderr, "\\------------+----------+----------+----------/\n"); +} + +#endif + +/************************************************** Middle-level routines *****/ + +static int __gus_instrument_free(gus_instrument_t * instrument) +{ + gus_instrument_t **cur_instr; + gus_layer_t *cur_layer; + gus_wave_t *cur_wave, *wave_head; + + /* Remove the instrument from the list of registered instruments */ + cur_instr = (gus_instrument_t **) & gus.instr; + while (*cur_instr) { + if (*cur_instr == instrument) { + *cur_instr = instrument->next; + goto instr_loaded; + } + cur_instr = &(*cur_instr)->next; + } + return -1; + +instr_loaded: + wave_head = NULL; + for (cur_layer = instrument->info.layer; cur_layer; + cur_layer = cur_layer->next) + /* Free all waves */ + for (cur_wave = cur_layer->wave; cur_wave; cur_wave = cur_wave->next) { + if (!wave_head) + wave_head = cur_wave; + if (cur_wave->begin.memory != (unsigned int)-1) + __gus_mem_free(cur_wave->begin.memory); + } + if (wave_head) + MikMod_free(wave_head); + + MikMod_free(instrument->info.layer); + if (instrument->name) + MikMod_free(instrument->name); + MikMod_free(instrument); + return 0; +} + +static gus_instrument_t *__gus_instrument_get(int program) +{ + gus_instrument_t *cur_instr = (gus_instrument_t *) gus.instr; + while (cur_instr) { + if (cur_instr->number.instrument == program) + return cur_instr; + cur_instr = cur_instr->next; + } + return NULL; +} + +static gus_instrument_t *__gus_instrument_copy(gus_instrument_t * instrument) +{ + gus_instrument_t **cur_instr, *instr; + gus_layer_t *cur_layer, *dest_layer; + gus_wave_t *cur_wave, *dest_wave; + unsigned int waves, layers; + + if (!instrument || !instrument->info.layer || !gus.open) + return NULL; + + if (__gus_instrument_get(instrument->number.instrument)) + return NULL; + + instr = (gus_instrument_t *) MikMod_malloc(sizeof(gus_instrument_t)); + *instr = *instrument; + + if (instrument->name) + instr->name = MikMod_strdup(instrument->name); + + /* Make a copy of all layers at once */ + for (layers = 0, cur_layer = instrument->info.layer; cur_layer; layers++) + cur_layer = cur_layer->next; + + if (!(dest_layer = instr->info.layer = (gus_layer_t *) MikMod_malloc(sizeof(gus_layer_t) * layers))) { + if (instr->name) + MikMod_free(instr->name); + MikMod_free(instr); + return NULL; + } + for (waves = 0, cur_layer = instrument->info.layer; cur_layer; + cur_layer = cur_layer->next) { + *dest_layer = *cur_layer; + dest_layer->wave = NULL; + /* Count the total number of waves */ + for (cur_wave = cur_layer->wave; cur_wave; cur_wave = cur_wave->next) + waves++; + if (cur_layer->next) + dest_layer->next = dest_layer + 1; + else + dest_layer->next = NULL; + dest_layer++; + } + + /* Allocate memory for waves */ + if (!(dest_wave = (gus_wave_t *) MikMod_malloc(sizeof(gus_wave_t) * waves))) { + MikMod_free(instr->info.layer); + if (instr->name) + MikMod_free(instr->name); + MikMod_free(instr); + return NULL; + } + for (cur_layer = instrument->info.layer, dest_layer = instr->info.layer; + cur_layer; cur_layer = cur_layer->next, dest_layer = dest_layer->next) + /* Copy all waves */ + for (cur_wave = cur_layer->wave; cur_wave; cur_wave = cur_wave->next) { + if (!dest_layer->wave) + dest_layer->wave = dest_wave; + + *dest_wave = *cur_wave; + /* Mark DRAM address as unallocated */ + dest_wave->begin.memory = -1; + + if (cur_wave->next) + dest_wave->next = (dest_wave + 1); + else + dest_wave->next = NULL; + dest_wave++; + } + + /* Insert the instrument into list of registered instruments */ + cur_instr = (gus_instrument_t **) & gus.instr; + while (*cur_instr) + cur_instr = &(*cur_instr)->next; + *cur_instr = instr; + + return instr; +} + +static void __gus_instruments_clear() +{ + gus_instrument_t *next_instr, *cur_instr = (gus_instrument_t *) gus.instr; + while (cur_instr) { + next_instr = cur_instr->next; + __gus_instrument_free(cur_instr); + cur_instr = next_instr; + } +} + +/******************************************************* libGUS interface *****/ + +/* return value: number of GUS cards installed in system */ +int gus_cards() +{ + if (!gus.ok) + __gus_init(); + return gus.ok ? 1 : 0; +} + +int gus_info(gus_info_t * info, int reread) +{ + if (!gus.ok) + __gus_init(); + if (!gus.ok) + return -1; + + strcpy((char *)info->id, "gus0"); + info->flags = (gus.ram ? GUS_STRU_INFO_F_PCM : 0); + info->version = gus.version; + info->port = gus.port; + info->irq = gus.irq[0]; + info->dma1 = gus.dma[0]; + info->dma2 = gus.dma[1]; + + info->mixing_freq = gus.freq; + + info->memory_size = gus.ram * 1024; + info->memory_free = __gus_mem_get_free(); + info->memory_block_8 = __gus_mem_get_free_8(); + info->memory_block_16 = __gus_mem_get_free_16(); + return 0; +} + +int gus_open(int card, size_t queue_buffer_size, int non_block) +{ + __dpmi_meminfo struct_info, pool_info; + + if (!gus.ok) + __gus_init(); + + if (!gus.ok || gus.open || card != 0) + return -1; + + /* Now lock the gus structure in memory */ + struct_info.address = __djgpp_base_address + (unsigned long)&gus; + struct_info.size = sizeof(gus); + if (__dpmi_lock_linear_region(&struct_info)) + return -1; + + /* And hook the GF1 interrupt */ + __irq_stack_count = 4; + gus.gf1_irq = + irq_hook(gus.irq[0], gf1_irq, (long)gf1_irq_end - (long)gf1_irq); + __irq_stack_count = 1; + if (!gus.gf1_irq) { + __dpmi_unlock_linear_region(&struct_info); + return -1; + } + + /* Enable the interrupt */ + irq_enable(gus.gf1_irq); + if (gus.irq[0] > 7) + _irq_enable(2); + + /* Allocate a DMA buffer: if we fail, we just use I/O so don't fail */ + if ((gus.transfer == NULL) || (gus.transfer == __gus_transfer_dma)) + gus.dma_buff = dma_allocate(gus.dma[0], GF1_DMA_BUFFER_SIZE); + else + gus.dma_buff = NULL; + + /* Detect the best available RAM -> DRAM transfer function */ + if (!gus.transfer) { + __gus_detect_transfer(); + if (gus.transfer != __gus_transfer_dma || !gus.transfer) + dma_free(gus.dma_buff), gus.dma_buff = NULL; + + /* If no transfer function worked, fail */ + if (!gus.transfer) { + if (gus.dma_buff) { + dma_free(gus.dma_buff); + gus.dma_buff = NULL; + } + __dpmi_unlock_linear_region(&struct_info); + irq_unhook(gus.gf1_irq); + gus.gf1_irq = NULL; + return -1; + } + } + + /* Allocate and lock command pool buffer */ + if (queue_buffer_size < 64) + queue_buffer_size = 64; + if (queue_buffer_size > 16384) + queue_buffer_size = 16384; + gus.cmd_pool = (unsigned char *) MikMod_malloc(queue_buffer_size); + pool_info.address = __djgpp_base_address + (unsigned long)&gus.cmd_pool; + pool_info.size = sizeof(queue_buffer_size); + if (__dpmi_lock_linear_region(&pool_info)) { + if (gus.dma_buff) { + dma_free(gus.dma_buff); + gus.dma_buff = NULL; + } + __dpmi_unlock_linear_region(&struct_info); + irq_unhook(gus.gf1_irq); + gus.gf1_irq = NULL; + return -1; + } + + gus.open++; + + __gus_mem_clear(); + gus.t1_callback = __gus_timer_update; + gus.wt_callback = __gus_wavetable_update; + gus.vl_callback = __gus_volume_update; + gus_do_tempo(60); /* Default is 60 Hz */ + + return 0; +} + +int gus_close(int card) +{ + __dpmi_meminfo struct_info; + + if (!gus.open || card != 0) + return -1; + + /* First reset the card: disable any operation it can currently perform */ + __gus_reset(0); + + gus.open--; + + /* Stop the timer */ + gus_timer_stop(); + + /* Free DMA buffer if used */ + if (gus.dma_buff) { + dma_free(gus.dma_buff); + gus.dma_buff = NULL; + } + + /* And unhook the GF1 interrupt */ + irq_unhook(gus.gf1_irq); + gus.gf1_irq = NULL; + + /* Unlock the gus structure */ + struct_info.address = __djgpp_base_address + (unsigned long)&gus; + struct_info.size = sizeof(gus); + __dpmi_unlock_linear_region(&struct_info); + + __gus_mem_clear(); + __gus_instruments_clear(); + + return 0; +} + +int gus_select(int card) +{ + if (!gus.open || (card != 0)) + return -1; + + return 0; +} + +/* return value: same as gus_reset function + note: this command doesn't change number of active voices and doesn't do + hardware reset */ +int gus_reset_engine_only() +{ + gus.timer_base = 100; + return 0; +} + +int gus_reset(int voices, unsigned int channel_voices) +{ + static unsigned short freq_table[32 - 14 + 1] = { + 44100, 41160, 38587, 36317, 34300, 32494, 30870, 29400, 28063, 26843, + 25725, 24696, 23746, 22866, 22050, 21289, 20580, 19916, 19293 + }; + int voice; + int timer; + + /* No support for dynamically allocated voices for now */ + gus.dynmask = channel_voices; + + if (voices < 14) + voices = 14; + if (voices > 32) + voices = 32; + + /* Stop the timer so that GUS IRQ won't clobber registers */ + timer = (gus.timer_ctl_reg & GF1M_TIMER1); + if (timer) + gus_timer_stop(); + + /* Stop all voices */ + for (voice = 0; voice < 32; voice++) { + __gus_select_voice(voice); + __gus_stop_voice(); + gus.cur_wave[voice] = NULL; + gus.cur_vol[voice] = 0; + + __gus_delay(); + + /* Reset voice parameters to reasonable values */ + __gus_set_current(0, 0); + __gus_set_loop_start(0, 0); + __gus_set_loop_end(0, 0); + __gus_outregw(GF1R_VOLUME, 0); + __gus_outregb(GF1R_VOLUME_RATE, 0); + __gus_outregb(GF1R_VOLUME_START, 0); + __gus_outregb(GF1R_VOLUME_END, 0); + __gus_outregb(GF1R_BALANCE, 0x7); + } + + voice = (__gus_inregb(GF1R_VOICES) & 0x1f) + 1; + + if (voice != voices) { + int reset = __gus_inregb(GF1R_RESET); + __gus_outregb(GF1R_RESET, reset & ~GF1M_OUTPUT_ENABLE); + __gus_delay(); + __gus_outregb(GF1R_VOICES, 0xc0 | (voices - 1)); + __gus_delay(); + __gus_outregb(GF1R_RESET, reset); + } + + /* Compute the discretization frequence */ + gus.voices = voices; + if (gus.interwave) + gus.freq = 44100; + else + gus.freq = freq_table[voices - 14]; + + gus_reset_engine_only(); + + if (timer) + gus_timer_continue(); + + return gus.voices; +} + +int gus_do_flush() +{ + DEBUG_PRINT(("gus_do_flush: top = %d\n", gus.cmd_pool_top)) + gus.cmd_pool_ready = 1; + return 0; +} + +/* set new tempo */ +void gus_do_tempo(unsigned int tempo) +{ + DEBUG_PRINT(("gus_do_tempo (%d)\n", tempo)) + gus_timer_tempo(tempo); + gus_timer_start(); +} + +/* set voice frequency in Hz */ +void gus_do_voice_frequency(unsigned char voice, unsigned int freq) +{ + DEBUG_PRINT(("gus_do_voice_frequency (%d, %d)\n", voice, freq)) + __pool_select_voice(voice); + __pool_command_w(PCMD_FREQ, + (((freq << 9) + (gus.freq >> 1)) / gus.freq) << 1); +} + +/* set voice pan (0-16384) (full left - full right) */ +void gus_do_voice_pan(unsigned char voice, unsigned short pan) +{ + DEBUG_PRINT(("gus_do_voice_pan (%d, %d)\n", voice, pan)) + pan >>= 10; + if (pan > 15) + pan = 15; + __pool_select_voice(voice); + __pool_command_b(PCMD_PAN, pan); +} + +/* set voice volume level 0-16384 (linear) */ +void gus_do_voice_volume(unsigned char voice, unsigned short vol) +{ + DEBUG_PRINT(("gus_do_voice_volume (%d, %d)\n", voice, vol)) + if (vol > 0x3fff) + vol = 0x3fff; + __pool_select_voice(voice); + __pool_command_w(PCMD_VOLUME, __gus_volume_table[vol >> 5]); +} + +/* start voice + * voice : voice # + * program : program # or ~0 = current + * freq : frequency in Hz + * volume : volume level (0-16384) or ~0 = current + * pan : pan level (0-16384) or ~0 = current + */ +void gus_do_voice_start(unsigned char voice, unsigned int program, + unsigned int freq, unsigned short volume, + unsigned short pan) +{ + gus_do_voice_start_position(voice, program, freq, volume, pan, 0); +} + +/* start voice + * voice : voice # + * program : program # or ~0 = current + * freq : frequency in Hz + * volume : volume level (0-16384) or ~0 = current + * pan : pan level (0-16384) or ~0 = current + * position : offset to wave in bytes * 16 (lowest 4 bits - fraction) + */ +void gus_do_voice_start_position(unsigned char voice, unsigned int program, + unsigned int freq, unsigned short volume, + unsigned short pan, unsigned int position) +{ + gus_instrument_t *instrument; + gus_wave_t *wave; + + DEBUG_PRINT( + ("gus_do_voice_start_position (%d, %d, pos: %d)\n", voice, + program, position)) + + instrument = __gus_instrument_get(program); + + if (!instrument + || !instrument->info.layer + || !instrument->info.layer->wave + || instrument->flags == GUS_INSTR_F_NOT_FOUND + || instrument->flags == GUS_INSTR_F_NOT_LOADED) return; + + gus_do_voice_frequency(voice, freq); + gus_do_voice_pan(voice, pan); + + /* We have to set volume different way, to avoid unneeded work in handler */ + if (volume > 0x3fff) + volume = 0x3fff; + __pool_command_w(PCMD_VOLUME_PREPARE, __gus_volume_table[volume >> 5]); + + switch (instrument->mode) { + case GUS_INSTR_SIMPLE: + wave = instrument->info.layer->wave; + if (position) + __pool_command_l(PCMD_OFFSET, position); + __pool_command_l(PCMD_START, (unsigned long)wave); + break; + } +} + +/* stop voice + * mode = 0 : stop voice now + * mode = 1 : disable wave loop and finish it + */ +void gus_do_voice_stop(unsigned char voice, unsigned char mode) +{ + __pool_select_voice(voice); + if (mode) + __pool_command(PCMD_STOP_LOOP); + else + __pool_command(PCMD_STOP); +} + +/* wait x ticks - this command is block separator + all commands between blocks are interpreted in the begining of one tick */ +void gus_do_wait(unsigned int ticks) +{ + DEBUG_PRINT(("gus_do_wait (%d)\n", ticks)) + + ticks += gus.t1_ticks; + while ((int)(ticks - gus.t1_ticks) > 0); +} + +int gus_get_voice_status(int voice) +{ + __gus_select_voice(voice); + return __gus_inregb(GF1R_VOICE_CONTROL) & GF1VC_STOPPED ? 0 : 1; +} + +/* return value: file handle (descriptor) for /dev/gus */ +int gus_get_handle() +{ + /* Return stdout handle so that select() will "work" with it */ + return 0; +} + +/* return value: zero if instrument was successfully allocated */ +int gus_memory_alloc(gus_instrument_t * instrument) +{ + gus_instrument_t *instr = __gus_instrument_copy(instrument); + gus_layer_t *cur_layer; + gus_wave_t *cur_wave; + + DEBUG_PRINT(("gus_memory_alloc (%d)\n", instrument->number.instrument)) + + if (!instr) + return -1; + + for (cur_layer = instr->info.layer; cur_layer; + cur_layer = cur_layer->next) for (cur_wave = cur_layer->wave; + cur_wave; + cur_wave = cur_wave->next) { + if (cur_layer->mode == GUS_INSTR_SIMPLE) { + cur_wave->begin.memory = __gus_mem_alloc(cur_wave->size, + cur_wave->format & + GUS_WAVE_16BIT); + if (cur_wave->begin.memory == (unsigned int)-1) { + __gus_instrument_free(instr); + return -1; + } + gus.transfer(cur_wave->begin.memory, cur_wave->begin.ptr, + cur_wave->size, cur_wave->format); + } else if (cur_layer->mode == GUS_INSTR_PATCH) + /* not supported yet */ ; + } + + return 0; +} + +/* return value: zero if instrument was successfully removed */ +int gus_memory_free(gus_instrument_t * instrument) +{ + gus_instrument_t *cur_instr = gus.instr; + + DEBUG_PRINT(("gus_memory_free (%d)\n", instrument->number.instrument)) + + for (; cur_instr; cur_instr = cur_instr->next) + if (cur_instr->number.instrument == instrument->number.instrument) + return __gus_instrument_free(cur_instr); + + return -1; +} + +/* return value: unused gus memory in bytes */ +int gus_memory_free_size() +{ + return __gus_mem_get_free(); +} + +/* return value: zero if success */ +int gus_memory_pack() +{ + __gus_mem_pack(); + return 0; +} + +/* return value: gus memory size in bytes */ +int gus_memory_size() +{ + return gus.ram * 1024; +} + +/* return value: current largest free block for 8-bit or 16-bit wave */ +int gus_memory_free_block(int w_16bit) +{ + return w_16bit ? __gus_mem_get_free_16() : __gus_mem_get_free_8(); +} + +/* input value: see to GUS_DOWNLOAD_MODE_XXXX constants (gus.h) + return value: zero if samples & instruments was successfully removed from + GF1 memory manager */ +int gus_memory_reset(int mode) +{ + __gus_mem_clear(); + __gus_instruments_clear(); + return 0; +} + +/* return value: zero if command queue was successfully flushed */ +int gus_queue_flush() +{ + return 0; +} + +/* input value: echo buffer size in items (if 0 - erase echo buffer) */ +int gus_queue_read_set_size(int items) +{ + return 0; +} + +/* input value: write queue size in items (each item have 8 bytes) */ +int gus_queue_write_set_size(int items) +{ + return 0; +} + +/* return value: zero if successfull */ +int gus_timer_start() +{ + gus.timer_ctl_reg |= GF1M_TIMER1; + __gus_outregb_slow(GF1R_TIMER_CONTROL, gus.timer_ctl_reg); + + gus.timer_ctl = gus.timer_ctl & ~GF1M_MASK_TIMER1; + outportb(GF1_TIMER_CTRL, 0x04); + outportb(GF1_TIMER_DATA, gus.timer_ctl | GF1M_START_TIMER1); + return 0; +} + +/* return value: zero if timer was stoped */ +int gus_timer_stop() +{ + gus.timer_ctl_reg &= ~GF1M_TIMER1; + __gus_outregb_slow(GF1R_TIMER_CONTROL, gus.timer_ctl_reg); + + gus.timer_ctl = gus.timer_ctl | GF1M_MASK_TIMER1; + outportb(GF1_TIMER_CTRL, 0x04); + outportb(GF1_TIMER_DATA, gus.timer_ctl); + return 0; +} + +/* return value: zero if setup was success */ +int gus_timer_tempo(int ticks) +{ + unsigned int counter; + + /* Limit ticks per second to 1..1000 range */ + if (ticks < 1) + ticks = 1; + if (ticks > 1000) + ticks = 1000; + + /* GF1 timer1 period is 80 usecs, 12500 times per second */ + counter = 1250000 / (ticks * gus.timer_base); + gus.t1_multiple = 1; + while (counter > 255) { + counter >>= 1; + gus.t1_multiple <<= 1; + } + gus.t1_countdown = gus.t1_multiple; + __gus_outregb(GF1R_TIMER1, 256 - counter); + return 0; +} + +/* return value: zero if timer will be continue */ +int gus_timer_continue() +{ + return gus_timer_start(); +} + +/* return value: zero if setup was success (default timebase = 100) */ +int gus_timer_base(int base) +{ + gus.timer_base = base; + return 0; +} + +void gus_timer_callback(void (*timer_callback) ()) +{ + gus.timer_callback = timer_callback; +} + +void gus_convert_delta(unsigned int type, unsigned char *dest, + unsigned char *src, size_t size) +{ + if (!(type & GUS_WAVE_DELTA)) + return; + + /* This doesn't depend much on wave signedness, since addition/subtraction + do not depend on operand signedness */ + if (type & GUS_WAVE_16BIT) { + unsigned short delta = type & GUS_WAVE_UNSIGNED ? 0x8000 : 0; + while (size--) { + delta = *(unsigned short *)dest = *(unsigned short *)src + delta; + src += sizeof(unsigned short); + dest += sizeof(unsigned short); + } + } else { + unsigned char delta = type & GUS_WAVE_UNSIGNED ? 0x80 : 0; + while (size--) { + delta = *(unsigned char *)dest = *(unsigned char *)src + delta; + src++; + dest++; + } + } +} + +int gus_dma_usage (int use) +{ + if (gus.dma_buff) + return -1; + gus.transfer = __gus_transfer_io; + return 0; +} + +#endif /* DRV_ULTRA */ + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/dos/dosgus.h b/libs/mikmod/drivers/dos/dosgus.h new file mode 100644 index 0000000..ae3488a --- /dev/null +++ b/libs/mikmod/drivers/dos/dosgus.h @@ -0,0 +1,514 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + libGUS-alike definitions for DOS + +==============================================================================*/ + +#ifndef __DOSGUS_H__ +#define __DOSGUS_H__ + +#include +#include "dosdma.h" +#include "dosirq.h" +#include "libgus.h" + +/* Private header file for a libGUS-alike library for DOS */ + +#define JOYSTICK_TIMER (gus.port+0x201) /* 201 */ +#define JOYSTICK_DATA (gus.port+0x201) /* 201 */ + +#define GF1_MIDI_CTRL (gus.port+0x100) /* 3X0 */ +#define GF1_MIDI_DATA (gus.port+0x101) /* 3X1 */ + +#define GF1_VOICESEL (gus.port+0x102) /* 3X2 */ +#define GF1_REGSEL (gus.port+0x103) /* 3X3 */ +#define GF1_DATA (gus.port+0x104) /* 3X4 */ +#define GF1_DATA_LOW (gus.port+0x104) /* 3X4 */ +#define GF1_DATA_HIGH (gus.port+0x105) /* 3X5 */ +#define GF1_IRQ_STATUS (gus.port+0x006) /* 2X6 */ +#define GF1_DRAM (gus.port+0x107) /* 3X7 */ + +#define GF1_MIX_CTRL (gus.port+0x000) /* 2X0 */ +#define GF1_TIMER_CTRL (gus.port+0x008) /* 2X8 */ +#define GF1_TIMER_DATA (gus.port+0x009) /* 2X9 */ +#define GF1_IRQ_CTRL (gus.port+0x00B) /* 2XB */ +#define GF1_REG_CTRL (gus.port+0x00F) /* 2XF */ + +#define GF1_REVISION (gus.port+0x506) /* 7X6 */ + +/* The GF1 hardware clock rate */ +#define CLOCK_RATE 9878400L + +/* GF1 voice-independent registers */ +#define GF1R_DMA_CONTROL 0x41 +#define GF1R_DMA_ADDRESS 0x42 +#define GF1R_DRAM_LOW 0x43 +#define GF1R_DRAM_HIGH 0x44 + +#define GF1R_TIMER_CONTROL 0x45 +#define GF1R_TIMER1 0x46 +#define GF1R_TIMER2 0x47 + +#define GF1R_SAMPLE_RATE 0x48 +#define GF1R_SAMPLE_CONTROL 0x49 + +#define GF1R_JOYSTICK 0x4B +#define GF1R_RESET 0x4C + +/* GF1 voice-specific registers */ +#define GF1R_VOICE_CONTROL 0x00 +#define GF1R_FREQUENCY 0x01 +#define GF1R_START_HIGH 0x02 +#define GF1R_START_LOW 0x03 +#define GF1R_END_HIGH 0x04 +#define GF1R_END_LOW 0x05 +#define GF1R_VOLUME_RATE 0x06 +#define GF1R_VOLUME_START 0x07 +#define GF1R_VOLUME_END 0x08 +#define GF1R_VOLUME 0x09 +#define GF1R_ACC_HIGH 0x0a +#define GF1R_ACC_LOW 0x0b +#define GF1R_BALANCE 0x0c +#define GF1R_VOLUME_CONTROL 0x0d +#define GF1R_VOICES 0x0e +#define GF1R_IRQ_SOURCE 0x0f + +/* Add this to above registers for reading */ +#define GF1R_READ_MASK 0x80 + +/* MIDI */ +#define GF1M_MIDI_RESET 0x03 +#define GF1M_MIDI_ENABLE_XMIT 0x20 +#define GF1M_MIDI_ENABLE_RCV 0x80 + +#define GF1M_MIDI_RCV_FULL 0x01 +#define GF1M_MIDI_XMIT_EMPTY 0x02 +#define GF1M_MIDI_FRAME_ERR 0x10 +#define GF1M_MIDI_OVERRUN 0x20 +#define GF1M_MIDI_IRQ_PEND 0x80 + +/* Joystick */ +#define GF1M_JOY_POSITION 0x0f +#define GF1M_JOY_BUTTONS 0xf0 + +/* GF1_IRQ_STATUS (port 2X6) */ +#define GF1M_IRQ_MIDI_TX 0x01 /* pending MIDI xmit IRQ */ +#define GF1M_IRQ_MIDI_RX 0x02 /* pending MIDI recv IRQ */ +#define GF1M_IRQ_TIMER1 0x04 /* general purpose timer */ +#define GF1M_IRQ_TIMER2 0x08 /* general purpose timer */ +#define GF1M_IRQ_WAVETABLE 0x20 /* pending wavetable IRQ */ +#define GF1M_IRQ_ENVELOPE 0x40 /* pending volume envelope IRQ */ +#define GF1M_IRQ_DMA_COMPLETE 0x80 /* pending dma transfer complete IRQ */ + +/* GF1_MIX_CTRL (port 2X0) */ +#define GF1M_MIXER_NO_LINE_IN 0x01 /* 0: enable */ +#define GF1M_MIXER_NO_OUTPUT 0x02 /* 0: enable */ +#define GF1M_MIXER_MIC_IN 0x04 /* 1: enable */ +#define GF1M_MIXER_GF1_IRQ 0x08 /* 1: enable */ +#define GF1M_GF1_COMBINED_IRQ 0x10 /* 1: IRQ1 == IRQ2 */ +#define GF1M_MIDI_LOOPBACK 0x20 /* 1: enable loop back */ +#define GF1M_CONTROL_SELECT 0x40 /* 0: DMA latches; 1: IRQ latches */ + +/* Timer data register (2X9) */ +#define GF1M_START_TIMER1 0x01 +#define GF1M_START_TIMER2 0x02 +#define GF1M_MASK_TIMER1 0x20 +#define GF1M_MASK_TIMER2 0x40 +#define GF1M_TIMER_CLRIRQ 0x80 + +/* IRQ/DMA control register (2XB) */ +#define GF1M_IRQ_EQUAL 0x40 +#define GF1M_DMA_EQUAL 0x40 + +/* (0x41) DMA control register bits */ +#define GF1M_DMAR_ENABLE 0x01 /* 1: go */ +#define GF1M_DMAR_READ 0x02 /* 1: read (->RAM), 0: write (->DRAM) */ +#define GF1M_DMAR_CHAN16 0x04 /* 1: 16 bit, 0: 8 bit DMA channel */ +#define GF1M_DMAR_RATE 0x18 /* 00: fast, 11: slow */ +#define GF1M_DMAR_IRQ_ENABLE 0x20 /* 1: enable */ +#define GF1M_DMAR_IRQ_PENDING 0x40 /* R: DMA irq pending */ +#define GF1M_DMAR_DATA16 0x40 /* W: 0: 8 bits; 1: 16 bits per sample */ +#define GF1M_DMAR_TOGGLE_SIGN 0x80 /* W: 1: invert high bit */ + +/* DMA transfer rate divisors */ +#define GF1M_DMAR_RATE0 0x00 /* Fastest DMA xfer (~650khz) */ +#define GF1M_DMAR_RATE1 0x08 /* fastest / 2 */ +#define GF1M_DMAR_RATE2 0x10 /* fastest / 4 */ +#define GF1M_DMAR_RATE3 0x18 /* Slowest DMA xfer (fastest / 8) */ + +/* (0x45) Timer Control */ +#define GF1M_TIMER1 0x04 /* Enable timer 1 IRQ */ +#define GF1M_TIMER2 0x08 /* Enable timer 2 IRQ */ + +/* (0x49) Sampling (ADC) control register */ +#define GF1M_DMAW_ENABLE 0x01 /* 1: Start sampling */ +#define GF1M_DMAW_MODE 0x02 /* 0: mono, 1: stereo */ +#define GF1M_DMAW_CHAN16 0x04 /* 0: 8 bit, 1: 16 bit */ +#define GF1M_DMAW_IRQ_ENABLE 0x20 /* 1: enable IRQ */ +#define GF1M_DMAW_IRQ_PENDING 0x40 /* 1: irq pending */ +#define GF1M_DMAW_TOGGLE_SIGN 0x80 /* 1: invert sign bit */ + +/* (0x4C) GF1 reset register */ +#define GF1M_MASTER_RESET 0x01 /* 0: hold in reset */ +#define GF1M_OUTPUT_ENABLE 0x02 /* 1: enable output */ +#define GF1M_MASTER_IRQ 0x04 /* 1: master IRQ enable */ + +/* (0x0,0x80) Voice control register - GF1R_VOICE_CONTROL */ +#define GF1VC_STOPPED 0x01 /* 1: voice has stopped */ +#define GF1VC_STOP 0x02 /* 1: stop voice */ +#define GF1VC_DATA16 0x04 /* 0: 8 bit, 1: 16 bit */ +#define GF1VC_LOOP_ENABLE 0x08 /* 1: enable */ +#define GF1VC_BI_LOOP 0x10 /* 1: bi directional looping */ +#define GF1VC_IRQ 0x20 /* 1: enable voice's wave irq */ +#define GF1VC_BACKWARD 0x40 /* 0: increasing, 1: decreasing */ +#define GF1VC_IRQ_PENDING 0x80 /* 1: wavetable irq pending */ + +/* (0x01,0x81) Frequency control */ +/* Bit 0 - Unused */ +/* Bits 1-9 - Fractional portion */ +/* Bits 10-15 - Integer portion */ + +/* (0x02,0x82) Accumulator start address - GF1R_START_HIGH */ +/* Bits 0-11 - HIGH 12 bits of address */ +/* Bits 12-15 - Unused */ + +/* (0x03,0x83) Accumulator start address - GF1R_START_LOW */ +/* Bits 0-4 - Unused */ +/* Bits 5-8 - Fractional portion */ +/* Bits 9-15 - Low 7 bits of integer portion */ + +/* (0x04,0x84) Accumulator end address - GF1R_END_HIGH */ +/* Bits 0-11 - HIGH 12 bits of address */ +/* Bits 12-15 - Unused */ + +/* (0x05,0x85) Accumulator end address - GF1R_END_LOW */ +/* Bits 0-4 - Unused */ +/* Bits 5-8 - Fractional portion */ +/* Bits 9-15 - Low 7 bits of integer portion */ + +/* (0x06,0x86) Volume Envelope control register - GF1R_VOLUME_RATE */ +#define GF1VL_RATE_MANTISSA 0x3f +#define GF1VL_RATE_RANGE 0xC0 + +/* (0x07,0x87) Volume envelope start - GF1R_VOLUME_START */ +#define GF1VL_START_MANT 0x0F +#define GF1VL_START_EXP 0xF0 + +/* (0x08,0x88) Volume envelope end - GF1R_VOLUME_END */ +#define GF1VL_END_MANT 0x0F +#define GF1VL_END_EXP 0xF0 + +/* (0x09,0x89) Current volume register - GF1R_VOLUME */ +/* Bits 0-3 - Unused */ +/* Bits 4-11 - Mantissa of current volume */ +/* Bits 10-15 - Exponent of current volume */ + +/* (0x0A,0x8A) Accumulator value (high) */ +/* Bits 0-12 - HIGH 12 bits of current position (a19-a7) */ + +/* (0x0B,0x8B) Accumulator value (low) */ +/* Bits 0-8 - Fractional portion */ +/* Bits 9-15 - Integer portion of low adress (a6-a0) */ + +/* (0x0C,0x8C) Pan (balance) position */ +/* Bits 0-3 - Balance position 0=full left, 0x0f=full right */ + +/* (0x0D,0x8D) Volume control register - GF1R_VOLUME_CONTROL */ +#define GF1VL_STOPPED 0x01 /* volume has stopped */ +#define GF1VL_STOP 0x02 /* stop volume */ +#define GF1VL_ROLLOVER 0x04 /* Roll PAST end & gen IRQ */ +#define GF1VL_LOOP_ENABLE 0x08 /* 1: enable */ +#define GF1VL_BI_LOOP 0x10 /* 1: bi directional looping */ +#define GF1VL_IRQ 0x20 /* 1: enable voice's volume irq */ +#define GF1VL_BACKWARD 0x40 /* 0: increasing, 1: decreasing */ +#define GF1VL_IRQ_PENDING 0x80 /* 1: wavetable irq pending */ + +/* (0x0E,0x8E) Number of active voices */ +/* Bits 0-5 - Number of active voices - 1 */ + +/* (0x0F,0x8F) Sources of IRQs */ +/* Bits 0-4 - interrupting voice number */ +/* Bit 5 - Always a 1 */ +#define GF1IRQ_VOLUME 0x40 /* individual voice irq bit */ +#define GF1IRQ_WAVE 0x80 /* individual waveform irq bit */ + +/* Commands are pooled and executed ON TIMER (1st timer) interrupt. + * Currently there is a limit on the number of commands that you can + * issue between gus_do_flush (...); this should not be an issue however + * because each voice has a limited (little) set of parameters that + * you can change (freq, vol, pan... what else?) + * + * The pool is a pseudo-CPU code that gets executed once per timer interrupt. + */ + +/* Below are definitions for commands placed in GUS command pool */ +#define PCMD_NOP 0x00 /* Traditionally ... */ +#define PCMD_VOICE 0x01 /* +B: select voice */ +#define PCMD_START 0x02 /* +L: start voice */ +#define PCMD_STOP 0x03 /* stop voice */ +#define PCMD_FREQ 0x04 /* +W: set frequence */ +#define PCMD_VOLUME 0x05 /* +W: set volume */ +#define PCMD_VOLUME_PREPARE 0x06 /* +W: prepare to set volume on (soon to follow) kick */ +#define PCMD_PAN 0x07 /* +B: set panning */ +#define PCMD_OFFSET 0x08 /* +L: set DRAM offset */ +#define PCMD_STOP_LOOP 0x09 /* stop looping */ + +#define GUS_VOLCHANGE_RAMP 0x20 /* Volume change ramp speed */ + +/* Definition for the boolean type */ +typedef unsigned char boolean; +/* Prototype for functions that do block transfers to GUS DRAM: + flags can contain any of the following bits: + GUS_WAVE_16BIT - sample is 16-bit + GUS_WAVE_UNSIGNED - do not invert sign bit while downloading + */ +typedef void (*__gus_transfer_func) (unsigned long address, + unsigned char *source, + unsigned long size, int flags); +typedef void (*__gus_callback) (); +typedef void (*__gus_callback_3) (unsigned int, unsigned int, unsigned int); + +/* Structure used to keep track of all on-board GUS memory */ +typedef struct __struct_gus_mcb { + struct __struct_gus_mcb *next; /* Next MCB in chain */ + struct __struct_gus_mcb *prev; /* Previous MCB in chain */ + unsigned int addr; /* GUS DRAM address */ + unsigned int size; /* Memory block size */ + int free; /* 1: block is free */ +} __gus_mcb; + +/* Structure defining overall GUS state/information */ +typedef struct __gus_state_s { + unsigned int port; /* Base I/O port (0x220, 0x240, ...) */ + unsigned int irq[2]; /* GF1 IRQ and MIDI IRQ */ + unsigned int dma[2]; /* Play / record DMA */ + unsigned int ram; /* Memory size (K), i.e. 256, 1024 etc */ + unsigned int version; /* GUS version (see GUS_CARD_VERSION_XXX in libgus.h */ + unsigned int freq; /* Current mixing frequency */ + unsigned int voices; /* Active voices (14-32) */ + unsigned int dynmask; /* Dynamically allocated voices mask */ + unsigned int timer_base; /* The relative timer speed in percents (def: 100) */ + volatile unsigned int t1_ticks; /* Incremented per each timer1 tick */ + volatile unsigned int t2_ticks; /* Incremented per each timer2 tick */ + volatile unsigned int t1_countdown; /* t1_callback is called when this reaches zero */ + volatile unsigned int t2_countdown; /* t2_callback is called when this reaches zero */ + unsigned int t1_multiple; /* Timer1 handler is called once per such many ticks */ + unsigned int t2_multiple; /* Timer2 handler is called once per such many ticks */ + struct irq_handle *gf1_irq; /* The interrupt handler for GF1 events */ + dma_buffer *dma_buff; /* Pre-allocated DMA buffer */ + __gus_callback dma_callback; /* Routine called at end of DMA transfers */ + __gus_callback t1_callback; /* Routine called on Timer1 events */ + __gus_callback t2_callback; /* Routine called on Timer1 events */ + __gus_callback timer_callback; /* Called once per TEMPO ticks */ + __gus_callback_3 wt_callback; /* Routine called on WaveTable events */ + __gus_callback_3 vl_callback; /* Routine called on Volume ramp events */ + __gus_mcb *mcb; /* Chained list of memory control blocks */ + __gus_transfer_func transfer; /* Best working function for DRAM transfer */ + gus_instrument_t *instr; /* The list of registered instruments */ + unsigned short mixer; /* Current mixer register state */ + unsigned char dma_rate; /* One of GF1M_DMAR_RATEX constants defined above */ + unsigned char timer_ctl; /* Timer control register value (2x8/2x9) */ + unsigned char timer_ctl_reg; /* Timer control register value (GF1/0x45) */ + boolean ok; /* Is the information below okay? */ + boolean open; /* 1 if between gus_open() and gus_close() */ + boolean ics; /* Is it equipped with an ICS mixer? */ + boolean ics_flipped; /* rev 5 (3.7) has flipped R/L mixer */ + boolean codec; /* Is it equipped with a GUS MAX codec? */ + boolean interwave; /* GUS InterWave card */ + volatile boolean dma_active; /* DMA is transferring data */ + volatile boolean cmd_pool_ready; /* Flush cmd_pool during timer interrupt */ + unsigned char cmd_voice; /* Pool selection index cache */ + unsigned int cmd_pool_top; /* Command pool top */ + unsigned char *cmd_pool; /* Async commands pool */ + /* The following data is for private use only by interrupt routines! */ + gus_wave_t *cur_wave[32]; /* Currently played waves */ + boolean voice_kick[32]; /* Kick wave on next volume ramp IRQ */ + unsigned int kick_offs[32]; /* Sample start position on kick */ + unsigned short cur_vol[32]; /* Current voice volumes */ + unsigned int cur_voice; /* Current voice */ + unsigned int eow_ignore; /* Temp ignore end-of-wave IRQ for these voices */ +} __gus_state; + +extern __gus_state gus; +extern void __gus_delay(); + +static unsigned long __gus_convert_addr16(unsigned long address) +{ + return ((address & 0x0003ffff) >> 1) | (address & ~0x0003ffff); +} + +/* The XXX_slow routines cannot be used outside IRQ handler! */ +static inline void __gus_outregb_slow(unsigned char reg, unsigned char value) +{ + outportb(GF1_REGSEL, reg); + outportb(GF1_DATA_HIGH, value); + __gus_delay(); + outportb(GF1_DATA_HIGH, value); +} + +static inline void __gus_outregw_slow(unsigned char reg, unsigned short value) +{ + outportb(GF1_REGSEL, reg); + outportw(GF1_DATA, value); + __gus_delay(); + outportw(GF1_DATA, value); +} + +static inline void __gus_outregb(unsigned char reg, unsigned char value) +{ + outportb(GF1_REGSEL, reg); + outportb(GF1_DATA_HIGH, value); +} + +static inline void __gus_outregw(unsigned char reg, unsigned short value) +{ + outportb(GF1_REGSEL, reg); + outportw(GF1_DATA, value); +} + +static inline unsigned char __gus_inregb(unsigned char reg) +{ + if (reg < 0x10) + reg |= GF1R_READ_MASK; + outportb(GF1_REGSEL, reg); + return inportb(GF1_DATA_HIGH); +} + +static inline unsigned short __gus_inregw(unsigned char reg) +{ + if (reg < 0x10) + reg |= GF1R_READ_MASK; + outportb(GF1_REGSEL, reg); + return inportw(GF1_DATA); +} + +static inline void __gus_set_dram_address(unsigned int address) +{ + __gus_outregb(GF1R_DRAM_HIGH, address >> 16); + __gus_outregw(GF1R_DRAM_LOW, address); +} + +static inline unsigned char __gus_peek(unsigned int address) +{ + __gus_set_dram_address(address); + return inportb(GF1_DRAM); +} + +static inline void __gus_poke(unsigned int address, unsigned char value) +{ + __gus_set_dram_address(address); + outportb(GF1_DRAM, value); +} + +static inline void __gus_select_voice(unsigned char voice) +{ + outportb(GF1_VOICESEL, voice); +} + +static inline void __gus_set_current(unsigned char mode, + unsigned long address) +{ + if (mode & GF1VC_DATA16) + address = __gus_convert_addr16(address); + __gus_outregw_slow(GF1R_ACC_HIGH, address >> 11); + __gus_outregw_slow(GF1R_ACC_LOW, address << 5); +} + +static inline void __gus_set_loop_start(unsigned char mode, + unsigned long address) +{ + if (mode & GF1VC_DATA16) + address = __gus_convert_addr16(address); + __gus_outregw_slow(GF1R_START_HIGH, address >> 11); + __gus_outregw_slow(GF1R_START_LOW, address << 5); +} + +static inline void __gus_set_loop_end(unsigned char mode, + unsigned long address) +{ + address--; + if (mode & GF1VC_DATA16) + address = __gus_convert_addr16(address); + __gus_outregw_slow(GF1R_END_HIGH, address >> 11); + __gus_outregw_slow(GF1R_END_LOW, address << 5); +} + +static inline void __gus_mixer_output(boolean state) +{ + if (state) + gus.mixer &= ~GF1M_MIXER_NO_OUTPUT; + else + gus.mixer |= GF1M_MIXER_NO_OUTPUT; + outportb(GF1_MIX_CTRL, gus.mixer); + /* Dummy read to avoid touching DMA latches */ + __gus_inregb(GF1R_BALANCE); +} + +/* Inline routines for working with command pools */ + +/* WARNING: no bounds checking due to performance reasons */ +#define __POOL_VALUE(type,value) \ + *((unsigned type *)&gus.cmd_pool [gus.cmd_pool_top]) = value; \ + gus.cmd_pool_top += sizeof (type); + +static inline void __pool_command(unsigned char command) +{ + __POOL_VALUE(char, command); +} + +static inline void __pool_command_b(unsigned char command, unsigned char arg) +{ + __POOL_VALUE(char, command); + __POOL_VALUE(char, arg); +} + +static inline void __pool_command_w(unsigned char command, unsigned short arg) +{ + __POOL_VALUE(char, command); + __POOL_VALUE(short, arg); +} + +static inline void __pool_command_l(unsigned char command, unsigned long arg) +{ + __POOL_VALUE(char, command); + __POOL_VALUE(long, arg); +} + +static inline void __pool_select_voice(unsigned char voice) +{ + if (gus.cmd_voice != voice) + __pool_command_b(PCMD_VOICE, gus.cmd_voice = voice); +} + +#undef __POOL_VALUE + +#ifdef DEBUG +/* Debug dump of GUS DRAM heap */ +extern void __gus_mem_dump(); +#endif + +#endif /* __DOSGUS_H__ */ + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/dos/dosirq.c b/libs/mikmod/drivers/dos/dosirq.c new file mode 100644 index 0000000..9b0e21f --- /dev/null +++ b/libs/mikmod/drivers/dos/dosirq.c @@ -0,0 +1,323 @@ +/* + Implementation of IRQ routines on DOS + Copyright (C) 1999 by Andrew Zabolotny, + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "dosirq.h" + +#include +#include +#include +#include +#include +#include +#include "mikmod.h" /* for MikMod_malloc() & co */ + +unsigned int __irq_stack_size = 0x4000; +unsigned int __irq_stack_count = 1; + +static void __int_stub_template (void) +{ +/* *INDENT-OFF* */ + asm(" pushal\n" + " pushl %ds\n" + " pushl %es\n" + " pushl %fs\n" + " pushl %gs\n" + " movw $0x1234,%ax\n" /* Get DPMI data selector */ + " movw %ax,%ds\n" /* Set DS and ES to data selector */ + " movw %ax,%es\n" + " movl $0x12345678,%ebx\n" /* Interrupt stack top */ + " movl (%ebx),%ecx\n" + " movl %ecx,%edx\n" + " subl $0x12345678,%ecx\n" /* Subtract irq_stack_count */ + " movl %ecx,(%ebx)\n" + " movw %ss,%si\n" /* Save old SS:ESP */ + " movl %esp,%edi\n" + " movl %edx,%esp\n" /* Set SS:ESP to interrupt stack */ + " movw %ax,%ss\n" + " pushl %esi\n" + " pushl %edi\n" + " pushl %ebx\n" + " pushl %edx\n" + " call 1f\n" /* Call user interrupt handler */ + "1: popl %edx\n" + " popl %ebx\n" + " movl %edx,(%ebx)\n" + " popl %edi\n" + " popl %esi\n" + " movl %edi,%esp\n" /* Restore old SS:ESP */ + " movw %si,%ss\n" + " popl %gs\n" + " popl %fs\n" + " popl %es\n" + " popl %ds\n" + " popal\n" + " iret\n"); +/* *INDENT-ON* */ +} + +#include + +static int _allocate_iret_wrapper(_go32_dpmi_seginfo * info) +{ + unsigned char *irqtpl = (unsigned char *)__int_stub_template; + unsigned char *irqend, *irqwrapper, *tmp; + __dpmi_meminfo handler_info; + unsigned int wrappersize; + + /* First, skip until pushal */ + while (*irqtpl != 0x60) + irqtpl++; + /* Now find the iret */ + irqend = irqtpl; + while (*irqend++ != 0xcf); + + wrappersize = 4 + __irq_stack_size * __irq_stack_count + 4 + + ((long)irqend - (long)irqtpl); + irqwrapper = (unsigned char *) MikMod_malloc(wrappersize); + /* Lock the wrapper */ + handler_info.address = __djgpp_base_address + (unsigned long)irqwrapper; + handler_info.size = wrappersize; + if (__dpmi_lock_linear_region(&handler_info)) { + MikMod_free(irqwrapper); + return -1; + } + + /* First comes the interrupt wrapper size */ + *(unsigned long *)irqwrapper = wrappersize; + + /* Next comes the interrupt stack */ + tmp = irqwrapper + 4 + __irq_stack_size * __irq_stack_count; + + /* The following dword is interrupt stack pointer */ + *((void **)tmp) = tmp; + tmp += 4; + + /* Now comes the interrupt wrapper itself */ + memcpy(tmp, irqtpl, irqend - irqtpl); + *(unsigned short *)(tmp + 9) = _my_ds(); + *(unsigned long *)(tmp + 16) = (unsigned long)tmp - 4; + *(unsigned long *)(tmp + 26) = __irq_stack_size; + *(unsigned long *)(tmp + 46) = + info->pm_offset - (unsigned long)(tmp + 50); + + info->pm_offset = (unsigned long)tmp; + info->pm_selector = _my_cs(); + + return 0; +} + +static void _free_iret_wrapper(_go32_dpmi_seginfo * info) +{ + __dpmi_meminfo handler_info; + + info->pm_offset -= 4 + __irq_stack_size * __irq_stack_count + 4; + + handler_info.address = __djgpp_base_address + info->pm_offset; + handler_info.size = *(unsigned long *)info->pm_offset; + __dpmi_unlock_linear_region(&handler_info); + + MikMod_free((void *)info->pm_offset); +} + +struct irq_handle *irq_hook(int irqno, void (*handler)(), unsigned long size) +{ + int interrupt; + struct irq_handle *irq; + __dpmi_version_ret version; + __dpmi_meminfo handler_info, struct_info; + _go32_dpmi_seginfo info; + unsigned long old_sel, old_ofs; + + __dpmi_get_version(&version); + if (irqno < 8) + interrupt = version.master_pic + irqno; + else + interrupt = version.slave_pic + (irqno - 8); + + if (_go32_dpmi_get_protected_mode_interrupt_vector(interrupt, &info)) + return NULL; + + old_sel = info.pm_selector; + old_ofs = info.pm_offset; + + info.pm_offset = (unsigned long)handler; + if (_allocate_iret_wrapper(&info)) + return NULL; + + /* Lock the interrupt handler in memory */ + handler_info.address = __djgpp_base_address + (unsigned long)handler; + handler_info.size = size; + if (__dpmi_lock_linear_region(&handler_info)) { + _free_iret_wrapper(&info); + return NULL; + } + + irq = (struct irq_handle *) MikMod_malloc(sizeof(struct irq_handle)); + irq->c_handler = handler; + irq->handler_size = size; + irq->handler = info.pm_offset; + irq->prev_selector = old_sel; + irq->prev_offset = old_ofs; + irq->int_num = interrupt; + irq->irq_num = irqno; + irq->pic_base = irqno < 8 ? PIC1_BASE : PIC2_BASE; + + struct_info.address = __djgpp_base_address + (unsigned long)irq; + struct_info.size = sizeof(struct irq_handle); + if (__dpmi_lock_linear_region(&struct_info)) { + MikMod_free(irq); + __dpmi_unlock_linear_region(&handler_info); + _free_iret_wrapper(&info); + return NULL; + } + + _go32_dpmi_set_protected_mode_interrupt_vector(interrupt, &info); + + irq->pic_mask = irq_state(irq); + return irq; +} + +void irq_unhook(struct irq_handle *irq) +{ + _go32_dpmi_seginfo info; + __dpmi_meminfo mem_info; + + if (!irq) + return; + + /* Restore the interrupt vector */ + irq_disable(irq); + info.pm_offset = irq->prev_offset; + info.pm_selector = irq->prev_selector; + _go32_dpmi_set_protected_mode_interrupt_vector(irq->int_num, &info); + + /* Unlock the interrupt handler */ + mem_info.address = __djgpp_base_address + (unsigned long)irq->c_handler; + mem_info.size = irq->handler_size; + __dpmi_unlock_linear_region(&mem_info); + + /* Unlock the irq_handle structure */ + mem_info.address = __djgpp_base_address + (unsigned long)irq; + mem_info.size = sizeof(struct irq_handle); + __dpmi_unlock_linear_region(&mem_info); + + info.pm_offset = irq->handler; + _free_iret_wrapper(&info); + + /* If IRQ was enabled before we hooked, restore enabled state */ + if (irq->pic_mask) + irq_enable(irq); + else + irq_disable(irq); + + MikMod_free(irq); +} + +/*---------------------------------------------- IRQ detection mechanism -----*/ +static struct irq_handle *__irqs[16]; +static int (*__irq_confirm) (int irqno); +static volatile unsigned int __irq_mask; +static volatile unsigned int __irq_count[16]; + +#define DECLARE_IRQ_HANDLER(irqno) \ +static void __irq##irqno##_handler () \ +{ \ + if (irq_check (__irqs [irqno]) && __irq_confirm (irqno)) \ + { \ + __irq_count [irqno]++; \ + __irq_mask |= (1 << irqno); \ + } \ + irq_ack (__irqs [irqno]); \ +} + +/* *INDENT-OFF* */ +DECLARE_IRQ_HANDLER(0) +DECLARE_IRQ_HANDLER(1) +DECLARE_IRQ_HANDLER(2) +DECLARE_IRQ_HANDLER(3) +DECLARE_IRQ_HANDLER(4) +DECLARE_IRQ_HANDLER(5) +DECLARE_IRQ_HANDLER(6) +DECLARE_IRQ_HANDLER(7) +DECLARE_IRQ_HANDLER(8) +DECLARE_IRQ_HANDLER(9) +DECLARE_IRQ_HANDLER(10) +DECLARE_IRQ_HANDLER(11) +DECLARE_IRQ_HANDLER(12) +DECLARE_IRQ_HANDLER(13) +DECLARE_IRQ_HANDLER(14) +DECLARE_IRQ_HANDLER(15) +/* *INDENT-ON* */ + +static void (*__irq_handlers[16]) () = { + __irq0_handler, __irq1_handler, __irq2_handler, __irq3_handler, + __irq4_handler, __irq5_handler, __irq6_handler, __irq7_handler, + __irq8_handler, __irq9_handler, __irq10_handler, __irq11_handler, + __irq12_handler, __irq13_handler, __irq14_handler, __irq15_handler}; + +void irq_detect_start(unsigned int irqs, int (*irq_confirm) (int irqno)) +{ + int i; + + __irq_mask = 0; + __irq_confirm = irq_confirm; + memset(&__irqs, 0, sizeof(__irqs)); + memset((void *) &__irq_count, 0, sizeof(__irq_count)); + + /* Hook all specified IRQs */ + for (i = 1; i <= 15; i++) + if (irqs & (1 << i)) { + __irqs[i] = irq_hook(i, __irq_handlers[i], 200); + /* Enable the interrupt */ + irq_enable(__irqs[i]); + } + /* Enable IRQ2 if we need at least one IRQ above 7 */ + if (irqs & 0xff00) + _irq_enable(2); +} + +void irq_detect_end() +{ + int i; + for (i = 15; i >= 1; i--) + if (__irqs[i]) + irq_unhook(__irqs[i]); +} + +int irq_detect_get(int irqno, unsigned int *irqmask) +{ + int oldirq = disable(); + int count = __irq_count[irqno]; + *irqmask = __irq_mask; + __irq_mask = 0; + if (oldirq) + enable(); + return count; +} + +void irq_detect_clear() +{ + int oldirq = disable(); + memset((void *) &__irq_count, 0, sizeof(__irq_count)); + __irq_mask = 0; + if (oldirq) + enable(); +} + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/dos/dosirq.h b/libs/mikmod/drivers/dos/dosirq.h new file mode 100644 index 0000000..eaf60a1 --- /dev/null +++ b/libs/mikmod/drivers/dos/dosirq.h @@ -0,0 +1,122 @@ +/* + Interface for IRQ routines on DOS + Copyright (C) 1999 by Andrew Zabolotny, + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __DOSIRQ_H__ +#define __DOSIRQ_H__ + +#include + +#define PIC1_BASE 0x20 /* PIC1 base */ +#define PIC2_BASE 0xA0 /* PIC2 base */ + +struct irq_handle { + void (*c_handler) (); /* The real interrupt handler */ + unsigned long handler_size; /* The size of interrupt handler */ + unsigned long handler; /* Interrupt wrapper address */ + unsigned long prev_selector; /* Selector of previous handler */ + unsigned long prev_offset; /* Offset of previous handler */ + unsigned char irq_num; /* IRQ number */ + unsigned char int_num; /* Interrupt number */ + unsigned char pic_base; /* PIC base (0x20 or 0xA0) */ + unsigned char pic_mask; /* Old PIC mask state */ +}; + +/* Return the enabled state for specific IRQ */ +static inline unsigned char irq_state(struct irq_handle * irq) +{ + return ((~inportb(irq->pic_base + 1)) & (0x01 << (irq->irq_num & 7))); +} + +/* Acknowledge the end of interrupt */ +static inline void _irq_ack(int irqno) +{ + outportb(irqno > 7 ? PIC2_BASE : PIC1_BASE, 0x60 | (irqno & 7)); + /* For second controller we also should acknowledge first controller */ + if (irqno > 7) + outportb(PIC1_BASE, 0x20); /* 0x20, 0x62? */ +} + +/* Acknowledge the end of interrupt */ +static inline void irq_ack(struct irq_handle * irq) +{ + outportb(irq->pic_base, 0x60 | (irq->irq_num & 7)); + /* For second controller we also should acknowledge first controller */ + if (irq->pic_base != PIC1_BASE) + outportb(PIC1_BASE, 0x20); /* 0x20, 0x62? */ +} + +/* Mask (disable) the particular IRQ given his ordinal */ +static inline void _irq_disable(int irqno) +{ + unsigned int port_no = (irqno < 8 ? PIC1_BASE : PIC2_BASE) + 1; + outportb(port_no, inportb(port_no) | (1 << (irqno & 7))); +} + +/* Unmask (enable) the particular IRQ given its ordinal */ +static inline void _irq_enable(int irqno) +{ + unsigned int port_no = (irqno < 8 ? PIC1_BASE : PIC2_BASE) + 1; + outportb(port_no, inportb(port_no) & ~(1 << (irqno & 7))); +} + +/* Mask (disable) the particular IRQ given its irq_handle structure */ +static inline void irq_disable(struct irq_handle * irq) +{ + outportb(irq->pic_base + 1, + inportb(irq->pic_base + 1) | (1 << (irq->irq_num & 7))); +} + +/* Unmask (enable) the particular IRQ given its irq_handle structure */ +static inline void irq_enable(struct irq_handle * irq) +{ + outportb(irq->pic_base + 1, + inportb(irq->pic_base + 1) & ~(1 << (irq->irq_num & 7))); +} + +/* Check if a specific IRQ is pending: return 0 is no */ +static inline int irq_check(struct irq_handle * irq) +{ + outportb(irq->pic_base, 0x0B); /* Read IRR vector */ + return (inportb(irq->pic_base) & (1 << (irq->irq_num & 7))); +} + +/* Hook a specific IRQ; NOTE: IRQ is disabled upon return, irq_enable() it */ +extern struct irq_handle *irq_hook(int irqno, void (*handler)(), + unsigned long size); +/* Unhook a previously hooked IRQ */ +extern void irq_unhook(struct irq_handle * irq); +/* Start IRQ detection process (IRQ list is given with irq mask) */ +/* irq_confirm should return "1" if the IRQ really comes from the device */ +extern void irq_detect_start(unsigned int irqs, + int (*irq_confirm) (int irqno)); +/* Finish IRQ detection process */ +extern void irq_detect_end(); +/* Get the count of specific irqno that happened */ +extern int irq_detect_get(int irqno, unsigned int *irqmask); +/* Clear IRQ counters */ +extern void irq_detect_clear(); + +/* The size of interrupt stack */ +extern unsigned int __irq_stack_size; +/* The number of nested interrupts that can be handled */ +extern unsigned int __irq_stack_count; + +#endif /* __DOSIRQ_H__ */ + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/dos/dossb.c b/libs/mikmod/drivers/dos/dossb.c new file mode 100644 index 0000000..344ac85 --- /dev/null +++ b/libs/mikmod/drivers/dos/dossb.c @@ -0,0 +1,575 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + Sound Blaster I/O routines, common for SB8, SBPro and SB16 + Written by Andrew Zabolotny + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef DRV_SB + +#include +#include +#include +#include +#include +#include +#include + +#include "dossb.h" + +/********************************************* Private variables/routines *****/ + +__sb_state sb; + +/* Wait for SoundBlaster for some time */ +#if !defined(__GNUC__) || (__GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ == 0) +# define _func_noinline volatile /* match original code */ +# define _func_noclone +#else +/* avoid warnings from newer gcc: + * "function definition has qualified void return type" and + * function return types not compatible due to 'volatile' */ +# define _func_noinline __attribute__((__noinline__)) +# if (__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 5) +# define _func_noclone +# else +# define _func_noclone __attribute__((__noclone__)) +# endif +#endif +_func_noinline +_func_noclone + void __sb_wait() +{ + inportb(SB_DSP_RESET); + inportb(SB_DSP_RESET); + inportb(SB_DSP_RESET); + inportb(SB_DSP_RESET); + inportb(SB_DSP_RESET); + inportb(SB_DSP_RESET); +} + +static void sb_irq() +{ + /* Make sure its not a spurious IRQ */ + if (!irq_check(sb.irq_handle)) + return; + + sb.irqcount++; + + /* Acknowledge DMA transfer is complete */ + if (sb.mode & SBMODE_16BITS) + __sb_dsp_ack_dma16(); + else + __sb_dsp_ack_dma8(); + + /* SoundBlaster 1.x cannot do autoinit ... */ + if (sb.dspver < SBVER_20) + __sb_dspreg_outwlh(SBDSP_DMA_PCM8, (sb.dma_buff->size >> 1) - 1); + + /* Send EOI */ + irq_ack(sb.irq_handle); + + enable(); + if (sb.timer_callback) + sb.timer_callback(); +} + +static void sb_irq_end() +{ +} + +static boolean __sb_reset() +{ + /* Disable the output */ + sb_output(FALSE); + + /* Clear pending ints if any */ + __sb_dsp_ack_dma8(); + __sb_dsp_ack_dma16(); + + /* Reset the DSP */ + outportb(SB_DSP_RESET, SBM_DSP_RESET); + __sb_wait(); + __sb_wait(); + outportb(SB_DSP_RESET, 0); + + /* Now wait for AA coming from datain port */ + if (__sb_dsp_in() != 0xaa) + return FALSE; + + /* Finally, get the DSP version */ + if ((sb.dspver = __sb_dsp_version()) == 0xffff) + return FALSE; + /* Check again */ + if (sb.dspver != __sb_dsp_version()) + return FALSE; + + return TRUE; +} + +/***************************************************** SB detection stuff *****/ + +static int __sb_irq_irqdetect(int irqno) +{ + __sb_dsp_ack_dma8(); + return 1; +} + +static void __sb_irq_dmadetect() +{ + /* Make sure its not a spurious IRQ */ + if (!irq_check(sb.irq_handle)) + return; + + sb.irqcount++; + + /* Acknowledge DMA transfer is complete */ + if (sb.mode & SBMODE_16BITS) + __sb_dsp_ack_dma16(); + else + __sb_dsp_ack_dma8(); + + /* Send EOI */ + irq_ack(sb.irq_handle); +} + +static boolean __sb_detect() +{ + /* First find the port number */ + if (!sb.port) { + int i; + for (i = 5; i >= 0; i--) { + sb.port = 0x210 + i * 0x10; + if (__sb_reset()) + break; + } + if (i < 0) { + sb.port = 0; + return FALSE; + } + } + + /* Now detect the IRQ and DMA numbers */ + if (!sb.irq) { + unsigned int irqmask, sbirqmask, sbirqcount; + unsigned long timer; + + /* IRQ can be one of 2,3,5,7,10 */ + irq_detect_start(0x04ac, __sb_irq_irqdetect); + + /* Prepare timeout counter */ + _farsetsel(_dos_ds); + timer = _farnspeekl(0x46c); + + sbirqmask = 0; + sbirqcount = 10; /* Emit 10 SB irqs */ + + /* Tell SoundBlaster to emit IRQ for 8-bit transfers */ + __sb_dsp_out(SBDSP_GEN_IRQ8); + __sb_wait(); + for (;;) { + irq_detect_get(0, &irqmask); + if (irqmask) { + sbirqmask |= irqmask; + if (!--sbirqcount) + break; + __sb_dsp_out(SBDSP_GEN_IRQ8); + } + if (_farnspeekl(0x46c) - timer >= 9) /* Wait ~1/2 secs */ + break; + } + if (sbirqmask) + for (sb.irq = 15; sb.irq > 0; sb.irq--) + if (irq_detect_get(sb.irq, &irqmask) == 10) + break; + + irq_detect_end(); + if (!sb.irq) + return FALSE; + } + + /* Detect the 8-bit and 16-bit DMAs */ + if (!sb.dma8 || ((sb.dspver >= SBVER_16) && !sb.dma16)) { + static int __dma8[] = { 0, 1, 3 }; + static int __dma16[] = { 5, 6, 7 }; + int *dma; + + sb_output(FALSE); + /* Temporary hook SB IRQ */ + sb.irq_handle = irq_hook(sb.irq, __sb_irq_dmadetect, 200); + irq_enable(sb.irq_handle); + if (sb.irq > 7) + _irq_enable(2); + + /* Start a short DMA transfer and check if IRQ happened */ + for (;;) { + int i; + unsigned int timer, oldcount; + + if (!sb.dma8) + dma = &sb.dma8; + else if ((sb.dspver >= SBVER_16) && !sb.dma16) + dma = &sb.dma16; + else + break; + + for (i = 0; i < 3; i++) { + boolean success = 1; + + *dma = (dma == &sb.dma8) ? __dma8[i] : __dma16[i]; + oldcount = sb.irqcount; + + dma_disable(*dma); + dma_set_mode(*dma, DMA_MODE_WRITE); + dma_clear_ff(*dma); + dma_set_count(*dma, 2); + dma_enable(*dma); + + __sb_dspreg_out(SBDSP_SET_TIMING, 206); /* 20KHz */ + if (dma == &sb.dma8) { + sb.mode = 0; + __sb_dspreg_outwlh(SBDSP_DMA_PCM8, 1); + } else { + sb.mode = SBMODE_16BITS; + __sb_dspreg_out(SBDSP_DMA_GENERIC16, 0); + __sb_dsp_out(0); + __sb_dsp_out(1); + } + + _farsetsel(_dos_ds); + timer = _farnspeekl(0x46c); + + while (oldcount == sb.irqcount) + if (_farnspeekl(0x46c) - timer >= 2) { + success = 0; + break; + } + dma_disable(*dma); + if (success) + break; + *dma = 0; + } + if (!*dma) + break; + } + + irq_unhook(sb.irq_handle); + sb.irq_handle = NULL; + if (!sb.dma8 || ((sb.dspver >= SBVER_16) && !sb.dma16)) + return FALSE; + } + return TRUE; +} + +/*************************************************** High-level interface *****/ + +/* Detect whenever SoundBlaster is present and fill "sb" structure */ +boolean sb_detect() +{ + char *env; + + /* Try to find the port and DMA from environment */ + env = getenv("BLASTER"); + + while (env && *env) { + /* Skip whitespace */ + while ((*env == ' ') || (*env == '\t')) + env++; + if (!*env) + break; + + switch (*env++) { + case 'A': + case 'a': + if (!sb.port) + sb.port = strtol(env, &env, 16); + break; + case 'E': + case 'e': + if (!sb.aweport) + sb.aweport = strtol(env, &env, 16); + break; + case 'I': + case 'i': + if (!sb.irq) + sb.irq = strtol(env, &env, 10); + break; + case 'D': + case 'd': + if (!sb.dma8) + sb.dma8 = strtol(env, &env, 10); + break; + case 'H': + case 'h': + if (!sb.dma16) + sb.dma16 = strtol(env, &env, 10); + break; + default: + /* Skip other values (H == MIDI, T == model, any other?) */ + while (*env && (*env != ' ') && (*env != '\t')) + env++; + break; + } + } + + /* Try to detect missing sound card parameters */ + __sb_detect(); + + if (!sb.port || !sb.irq || !sb.dma8) + return FALSE; + + if (!__sb_reset()) + return FALSE; + + if ((sb.dspver >= SBVER_16) && !sb.dma16) + return FALSE; + + if (sb.dspver >= SBVER_PRO) + sb.caps |= SBMODE_STEREO; + if (sb.dspver >= SBVER_16 && sb.dma16) + sb.caps |= SBMODE_16BITS; + if (sb.dspver < SBVER_20) + sb.maxfreq_mono = 22222; + else + sb.maxfreq_mono = 45454; + if (sb.dspver <= SBVER_16) + sb.maxfreq_stereo = 22727; + else + sb.maxfreq_stereo = 45454; + + sb.ok = 1; + return TRUE; +} + +/* Reset SoundBlaster */ +void sb_reset() +{ + sb_stop_dma(); + __sb_reset(); +} + +/* Start working with SoundBlaster */ +boolean sb_open() +{ + __dpmi_meminfo struct_info; + + if (!sb.ok) + if (!sb_detect()) + return FALSE; + + if (sb.open) + return FALSE; + + /* Now lock the sb structure in memory */ + struct_info.address = __djgpp_base_address + (unsigned long)&sb; + struct_info.size = sizeof(sb); + if (__dpmi_lock_linear_region(&struct_info)) + return FALSE; + + /* Hook the SB IRQ */ + sb.irq_handle = irq_hook(sb.irq, sb_irq, (long)sb_irq_end - (long)sb_irq); + if (!sb.irq_handle) { + __dpmi_unlock_linear_region(&struct_info); + return FALSE; + } + + /* Enable the interrupt */ + irq_enable(sb.irq_handle); + if (sb.irq > 7) + _irq_enable(2); + + sb.open++; + + return TRUE; +} + +/* Finish working with SoundBlaster */ +boolean sb_close() +{ + __dpmi_meminfo struct_info; + if (!sb.open) + return FALSE; + + sb.open--; + + /* Stop/free DMA buffer */ + sb_stop_dma(); + + /* Unhook IRQ */ + irq_unhook(sb.irq_handle); + sb.irq_handle = NULL; + + /* Unlock the sb structure */ + struct_info.address = __djgpp_base_address + (unsigned long)&sb; + struct_info.size = sizeof(sb); + __dpmi_unlock_linear_region(&struct_info); + + return TRUE; +} + +/* Enable/disable stereo DSP mode */ +/* Enable/disable speaker output */ +void sb_output(boolean enable) +{ + __sb_dsp_out(enable ? SBDSP_SPEAKER_ENA : SBDSP_SPEAKER_DIS); +} + +/* Start playing from DMA buffer */ +boolean sb_start_dma(unsigned char mode, unsigned int freq) +{ + int dmachannel = (mode & SBMODE_16BITS) ? sb.dma16 : sb.dma8; + int dmabuffsize; + unsigned int tc = 0; /* timing constant (<=sbpro only) */ + + /* Stop DMA transfer if it is enabled */ + sb_stop_dma(); + + /* Sanity check */ + if ((mode & SBMODE_MASK & sb.caps) != (mode & SBMODE_MASK)) + return FALSE; + + /* Check this SB can perform at requested frequency */ + if (((mode & SBMODE_STEREO) && (freq > sb.maxfreq_stereo)) + || (!(mode & SBMODE_STEREO) && (freq > sb.maxfreq_mono))) + return FALSE; + + /* Check the timing constant here to avoid failing later */ + if (sb.dspver < SBVER_16) { + /* SBpro cannot do signed transfer */ + if (mode & SBMODE_SIGNED) + return FALSE; + + /* Old SBs have a different way on setting DMA timing constant */ + tc = freq; + if (mode & SBMODE_STEREO) + tc *= 2; + tc = 1000000 / tc; + if (tc > 255) + return FALSE; + } + + sb.mode = mode; + + /* Get a DMA buffer enough for a 1/4sec interval... 4K <= dmasize <= 32K */ + dmabuffsize = freq; + if (mode & SBMODE_STEREO) + dmabuffsize *= 2; + if (mode & SBMODE_16BITS) + dmabuffsize *= 2; + dmabuffsize >>= 2; + if (dmabuffsize < 4096) + dmabuffsize = 4096; + if (dmabuffsize > 32768) + dmabuffsize = 32768; + dmabuffsize = (dmabuffsize + 255) & 0xffffff00; + + sb.dma_buff = dma_allocate(dmachannel, dmabuffsize); + if (!sb.dma_buff) + return FALSE; + + /* Fill DMA buffer with silence */ + dmabuffsize = sb.dma_buff->size; + if (mode & SBMODE_SIGNED) + memset(sb.dma_buff->linear, 0, dmabuffsize); + else + memset(sb.dma_buff->linear, 0x80, dmabuffsize); + + /* Prime DMA for transfer */ + dma_start(sb.dma_buff, dmabuffsize, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); + + /* Tell SoundBlaster to start transfer */ + if (sb.dspver >= SBVER_16) { /* SB16 */ + __sb_dspreg_outwhl(SBDSP_SET_RATE, freq); + + /* Start DMA->DAC transfer */ + __sb_dspreg_out(SBM_GENDAC_AUTOINIT | SBM_GENDAC_FIFO | + ((mode & SBMODE_16BITS) ? SBDSP_DMA_GENERIC16 : + SBDSP_DMA_GENERIC8), + ((mode & SBMODE_SIGNED) ? SBM_GENDAC_SIGNED : 0) | + ((mode & SBMODE_STEREO) ? SBM_GENDAC_STEREO : 0)); + + /* Write the length of transfer */ + dmabuffsize = (dmabuffsize >> 2) - 1; + __sb_dsp_out(dmabuffsize); + __sb_dsp_out(dmabuffsize >> 8); + } else { + __sb_dspreg_out(SBDSP_SET_TIMING, 256 - tc); + dmabuffsize = (dmabuffsize >> 1) - 1; + if (sb.dspver >= SBVER_20) { /* SB 2.0/Pro */ + /* Set stereo mode */ + __sb_stereo((mode & SBMODE_STEREO) ? TRUE : FALSE); + __sb_dspreg_outwlh(SBDSP_SET_DMA_BLOCK, dmabuffsize); + if (sb.dspver >= SBVER_PRO) + __sb_dsp_out(SBDSP_HS_DMA_DAC8_AUTO); + else + __sb_dsp_out(SBDSP_DMA_PCM8_AUTO); + } else { /* Original SB */ + /* Start DMA->DAC transfer */ + __sb_dspreg_outwlh(SBDSP_DMA_PCM8, dmabuffsize); + } + } + + return TRUE; +} + +/* Stop playing from DMA buffer */ +void sb_stop_dma() +{ + if (!sb.dma_buff) + return; + + if (sb.mode & SBMODE_16BITS) + __sb_dsp_out(SBDSP_DMA_HALT16); + else + __sb_dsp_out(SBDSP_DMA_HALT8); + + dma_disable(sb.dma_buff->channel); + dma_free(sb.dma_buff); + sb.dma_buff = NULL; +} + +/* Query current position/total size of the DMA buffer */ +void sb_query_dma(unsigned int *dma_size, unsigned int *dma_pos) +{ + unsigned int dma_left; + *dma_size = sb.dma_buff->size; + /* It can happen we try to read DMA count when HI/LO bytes will be + inconsistent */ + for (;;) { + unsigned int dma_left_test; + dma_clear_ff(sb.dma_buff->channel); + dma_left_test = dma_get_count(sb.dma_buff->channel); + dma_left = dma_get_count(sb.dma_buff->channel); + if ((dma_left >= dma_left_test) && (dma_left - dma_left_test < 10)) + break; + } + *dma_pos = *dma_size - dma_left; +} + +#endif /* DRV_SB */ + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/dos/dossb.h b/libs/mikmod/drivers/dos/dossb.h new file mode 100644 index 0000000..cb8dc70 --- /dev/null +++ b/libs/mikmod/drivers/dos/dossb.h @@ -0,0 +1,341 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + SoundBlaster and compatible soundcards definitions + +==============================================================================*/ + +#ifndef __DOSSB_H__ +#define __DOSSB_H__ + +#include "dosdma.h" +#include "dosirq.h" + +#define SB_FM_LEFT_STATUS (sb.port + 0x00) /* (r) Left FM status */ +#define SB_FM_LEFT_REGSEL (sb.port + 0x00) /* (w) Left FM register select */ +#define SB_FM_LEFT_DATA (sb.port + 0x01) /* (w) Left FM data */ +#define SB_FM_RIGHT_STATUS (sb.port + 0x02) /* (r) Right FM status */ +#define SB_FM_RIGHT_REGSEL (sb.port + 0x02) /* (w) Right FM register select */ +#define SB_FM_RIGHT_DATA (sb.port + 0x03) /* (w) Right FM data */ +#define SB_MIXER_REGSEL (sb.port + 0x04) /* (w) Mixer register select */ +#define SB_MIXER_DATA (sb.port + 0x05) /* (rw)Mixer data */ +#define SB_DSP_RESET (sb.port + 0x06) /* (w) DSP reset */ +#define SB_FM_STATUS (sb.port + 0x08) /* (r) FM status */ +#define SB_FM_REGSEL (sb.port + 0x08) /* (w) FM register select */ +#define SB_FM_DATA (sb.port + 0x09) /* (w) FM data */ +#define SB_DSP_DATA_IN (sb.port + 0x0a) /* (r) DSP data input */ +#define SB_DSP_DATA_OUT (sb.port + 0x0c) /* (w) DSP data output */ +#define SB_DSP_DATA_OUT_STATUS (sb.port + 0x0c) /* (r) DSP data output status */ +#define SB_DSP_TIMER_IRQ (sb.port + 0x0d) /* (r) clear timer IRQ? */ +#define SB_DSP_DATA_IN_STATUS (sb.port + 0x0e) /* (r) DSP data input status */ +#define SB_DSP_DMA8_IRQ (sb.port + 0x0e) /* (r) Acknowledge 8-bit DMA transfer */ +#define SB_DSP_DMA16_IRQ (sb.port + 0x0f) /* (r) Acknowledge 16-bit DMA transfer */ + +/* DSP commands */ +#define SBDSP_ASP_STATUS 0x03 /* ASP Status (SB16ASP) */ +#define SBDSP_STATUS_OLD 0x04 /* DSP Status (Obsolete) (SB2.0-Pro2) */ +#define SBDSP_DIRECT_DAC 0x10 /* Direct DAC, 8-bit (SB) */ +#define SBDSP_DMA_PCM8 0x14 /* DMA DAC, 8-bit (SB) */ +#define SBDSP_DMA_ADPCM2 0x16 /* DMA DAC, 2-bit ADPCM (SB) */ +#define SBDSP_DMA_ADPCM2R 0x17 /* DMA DAC, 2-bit ADPCM Reference (SB) */ +#define SBDSP_DMA_PCM8_AUTO 0x1C /* Auto-Initialize DMA DAC, 8-bit (SB2.0) */ +#define SBDSP_DMA_ADPCM2R_AUTO 0x1F /* Auto-Initialize DMA DAC, 2-bit ADPCM Reference (SB2.0) */ +#define SBDSP_DIRECT_ADC 0x20 /* Direct ADC, 8-bit (SB) */ +#define SBDSP_DMA_ADC8 0x24 /* DMA ADC, 8-bit (SB) */ +#define SBDSP_DIRECT_ADC8_BURST 0x28 /* Direct ADC, 8-bit (Burst) (SB-Pro2) */ +#define SBDSP_DMA_ADC8_AUTO 0x2C /* Auto-Initialize DMA ADC, 8-bit (SB2.0) */ +#define SBDSP_MIDI_READ_POLL 0x30 /* MIDI Read Poll (SB) */ +#define SBDSP_MIDI_READ_IRQ 0x31 /* MIDI Read Interrupt (SB) */ +#define SBDSP_MIDI_READ_TIME 0x32 /* MIDI Read Timestamp Poll (SB???) */ +#define SBDSP_MIDI_READ_TIME_IRQ 0x33 /* MIDI Read Timestamp Interrupt (SB???) */ +#define SBDSP_MIDI_RW_POLL 0x34 /* MIDI Read Poll + Write Poll (UART) (SB2.0) */ +#define SBDSP_MIDI_RW_IRQ 0x35 /* MIDI Read Interrupt + Write Poll (UART) (SB2.0???) */ +#define SBDSP_MIDI_RW_TIME_IRQ 0x37 /* MIDI Read Timestamp Interrupt + Write Poll (UART) (SB2.0???) */ +#define SBDSP_MIDI_WRITE_POLL 0x38 /* MIDI Write Poll (SB) */ +#define SBDSP_SET_TIMING 0x40 /* Set Time Constant (SB) */ +#define SBDSP_SET_RATE 0x41 /* Set Sample Rate, Hz (SB16) */ +#define SBDSP_DMA_CONT8_AUTO 0x45 /* Continue Auto-Initialize DMA, 8-bit (SB16) */ +#define SBDSP_DMA_CONT16_AUTO 0x47 /* Continue Auto-Initialize DMA, 16-bit (SB16) */ +#define SBDSP_SET_DMA_BLOCK 0x48 /* Set DMA Block Size (SB2.0) */ +#define SBDSP_DMA_ADPCM4 0x74 /* DMA DAC, 4-bit ADPCM (SB) */ +#define SBDSP_DMA_ADPCM4_REF 0x75 /* DMA DAC, 4-bit ADPCM Reference (SB) */ +#define SBDSP_DMA_ADPCM26 0x76 /* DMA DAC, 2.6-bit ADPCM (SB) */ +#define SBDSP_DMA_ADPCM26_REF 0x77 /* DMA DAC, 2.6-bit ADPCM Reference (SB) */ +#define SBDSP_DMA_ADPCM4R_AUTO 0x7D /* Auto-Initialize DMA DAC, 4-bit ADPCM Reference (SB2.0) */ +#define SBDSP_DMA_ADPCM26R_AUTO 0x7F /* Auto-Initialize DMA DAC, 2.6-bit ADPCM Reference (SB2.0) */ +#define SBDSP_DISABLE_DAC 0x80 /* Silence DAC (SB) */ +#define SBDSP_HS_DMA_DAC8_AUTO 0x90 /* Auto-Initialize DMA DAC, 8-bit (High Speed) (SB2.0-Pro2) */ +#define SBDSP_HS_DMA_ADC8_AUTO 0x98 /* Auto-Initialize DMA ADC, 8-bit (High Speed) (SB2.0-Pro2) */ +#define SBDSP_STEREO_ADC_DIS 0xA0 /* Disable Stereo Input Mode (SBPro Only) */ +#define SBDSP_STEREO_ADC_ENA 0xA8 /* Enable Stereo Input Mode (SBPro Only) */ +#define SBDSP_DMA_GENERIC16 0xB0 /* Generic DAC/ADC DMA (16-bit) (SB16) */ +#define SBDSP_DMA_GENERIC8 0xC0 /* Generic DAC/ADC DMA (8-bit) (SB16) */ +#define SBDSP_DMA_HALT8 0xD0 /* Halt DMA Operation, 8-bit (SB) */ +#define SBDSP_SPEAKER_ENA 0xD1 /* Enable Speaker (SB) */ +#define SBDSP_SPEAKER_DIS 0xD3 /* Disable Speaker (SB) */ +#define SBDSP_DMA_CONT8 0xD4 /* Continue DMA Operation, 8-bit (SB) */ +#define SBDSP_DMA_HALT16 0xD5 /* Halt DMA Operation, 16-bit (SB16) */ +#define SBDSP_DMA_CONT16 0xD6 /* Continue DMA Operation, 16-bit (SB16) */ +#define SBDSP_SPEAKER_STATUS 0xD8 /* Speaker Status (SB) */ +#define SBDSP_DMA_EXIT16_AUTO 0xD9 /* Exit Auto-Initialize DMA Operation, 16-bit (SB16) */ +#define SBDSP_DMA_EXIT8_AUTO 0xDA /* Exit Auto-Initialize DMA Operation, 8-bit (SB2.0) */ +#define SBDSP_IDENTIFY 0xE0 /* DSP Identification (SB2.0) */ +#define SBDSP_VERSION 0xE1 /* DSP Version (SB) */ +#define SBDSP_COPYRIGHT 0xE3 /* DSP Copyright (SBPro2???) */ +#define SBDSP_WRITE_TEST 0xE4 /* Write Test Register (SB2.0) */ +#define SBDSP_READ_TEST 0xE8 /* Read Test Register (SB2.0) */ +#define SBDSP_SINE_GEN 0xF0 /* Sine Generator (SB) */ +#define SBDSP_AUX_STATUS_PRO 0xF1 /* DSP Auxiliary Status (Obsolete) (SB-Pro2) */ +#define SBDSP_GEN_IRQ8 0xF2 /* IRQ Request, 8-bit (SB) */ +#define SBDSP_GEN_IRQ16 0xF3 /* IRQ Request, 16-bit (SB16) */ +#define SBDSP_STATUS 0xFB /* DSP Status (SB16) */ +#define SBDSP_AUX_STATUS_16 0xFC /* DSP Auxiliary Status (SB16) */ +#define SBDSP_CMD_STATUS 0xFD /* DSP Command Status (SB16) */ + +/* Mixer commands */ +#define SBMIX_RESET 0x00 /* Reset Write SBPro */ +#define SBMIX_STATUS 0x01 /* Status Read SBPro */ +#define SBMIX_MASTER_LEVEL1 0x02 /* Master Volume Read/Write SBPro Only */ +#define SBMIX_DAC_LEVEL 0x04 /* DAC Level Read/Write SBPro */ +#define SBMIX_FM_OUTPUT 0x06 /* FM Output Control Read/Write SBPro Only */ +#define SBMIX_MIC_LEVEL 0x0A /* Microphone Level Read/Write SBPro */ +#define SBMIX_INPUT_SELECT 0x0C /* Input/Filter Select Read/Write SBPro Only */ +#define SBMIX_OUTPUT_SELECT 0x0E /* Output/Stereo Select Read/Write SBPro Only */ +#define SBMIX_FM_LEVEL 0x22 /* Master Volume Read/Write SBPro */ +#define SBMIX_MASTER_LEVEL 0x26 /* FM Level Read/Write SBPro */ +#define SBMIX_CD_LEVEL 0x28 /* CD Audio Level Read/Write SBPro */ +#define SBMIX_LINEIN_LEVEL 0x2E /* Line In Level Read/Write SBPro */ +#define SBMIX_MASTER_LEVEL_L 0x30 /* Master Volume Left Read/Write SB16 */ +#define SBMIX_MASTER_LEVEL_R 0x31 /* Master Volume Right Read/Write SB16 */ +#define SBMIX_DAC_LEVEL_L 0x32 /* DAC Level Left Read/Write SB16 */ +#define SBMIX_DAC_LEVEL_R 0x33 /* DAC Level Right Read/Write SB16 */ +#define SBMIX_FM_LEVEL_L 0x34 /* FM Level Left Read/Write SB16 */ +#define SBMIX_FM_LEVEL_R 0x35 /* FM Level Right Read/Write SB16 */ +#define SBMIX_CD_LEVEL_L 0x36 /* CD Audio Level Left Read/Write SB16 */ +#define SBMIX_CD_LEVEL_R 0x37 /* CD Audio Level Right Read/Write SB16 */ +#define SBMIX_LINEIN_LEVEL_L 0x38 /* Line In Level Left Read/Write SB16 */ +#define SBMIX_LINEIN_LEVEL_R 0x39 /* Line In Level Right Read/Write SB16 */ +#define SBMIX_MIC_LEVEL_16 0x3A /* Microphone Level Read/Write SB16 */ +#define SBMIX_PCSPK_LEVEL 0x3B /* PC Speaker Level Read/Write SB16 */ +#define SBMIX_OUTPUT_CONTROL 0x3C /* Output Control Read/Write SB16 */ +#define SBMIX_INPUT_CONTROL_L 0x3D /* Input Control Left Read/Write SB16 */ +#define SBMIX_INPUT_CONTROL_R 0x3E /* Input Control Right Read/Write SB16 */ +#define SBMIX_INPUT_GAIN_L 0x3F /* Input Gain Control Left Read/Write SB16 */ +#define SBMIX_INPUT_GAIN_R 0x40 /* Input Gain Control Right Read/Write SB16 */ +#define SBMIX_OUTPUT_GAIN_L 0x41 /* Output Gain Control Left Read/Write SB16 */ +#define SBMIX_OUTPUT_GAIN_R 0x42 /* Output Gain Control Right Read/Write SB16 */ +#define SBMIX_AGC_CONTROL 0x43 /* Automatic Gain Control (AGC) Read/Write SB16 */ +#define SBMIX_TREBLE_L 0x44 /* Treble Left Read/Write SB16 */ +#define SBMIX_TREBLE_R 0x45 /* Treble Right Read/Write SB16 */ +#define SBMIX_BASS_L 0x46 /* Bass Left Read/Write SB16 */ +#define SBMIX_BASS_R 0x47 /* Bass Right Read/Write SB16 */ +#define SBMIX_IRQ_SELECT 0x80 /* IRQ Select Read/Write SB16 */ +#define SBMIX_DMA_SELECT 0x81 /* DMA Select Read/Write SB16 */ +#define SBMIX_IRQ_STATUS 0x82 /* IRQ Status Read SB16 */ + +/* SB_DSP_DATA_OUT_STATUS and SB_DSP_DATA_IN_STATUS bits */ +#define SBM_DSP_READY 0x80 + +/* SB_DSP_RESET / SBMIX_RESET */ +#define SBM_DSP_RESET 0x01 + +/* SBMIX_OUTPUT_SELECT */ +#define SBM_MIX_STEREO 0x02 +#define SBM_MIX_FILTER 0x20 + +/* SBDSP_DMA_GENERIC16/SBDSP_DMA_GENERIC8 */ +#define SBM_GENDAC_FIFO 0x02 +#define SBM_GENDAC_AUTOINIT 0x04 +#define SBM_GENDAC_ADC 0x08 +/* Second (mode) byte */ +#define SBM_GENDAC_SIGNED 0x10 +#define SBM_GENDAC_STEREO 0x20 + +/* DSP version masks */ +#define SBVER_10 0x0100 /* Original SoundBlaster */ +#define SBVER_15 0x0105 /* SoundBlaster 1.5 */ +#define SBVER_20 0x0200 /* SoundBlaster 2.0 */ +#define SBVER_PRO 0x0300 /* SoundBlaster Pro */ +#define SBVER_PRO2 0x0301 /* SoundBlaster Pro 2 */ +#define SBVER_16 0x0400 /* SoundBlaster 16 */ +#define SBVER_AWE32 0x040c /* SoundBlaster AWE32 */ + +typedef unsigned char boolean; + +#ifndef FALSE +#define FALSE 0 +#define TRUE 1 +#endif + +/* Play mode bits */ +#define SBMODE_16BITS 0x0001 +#define SBMODE_STEREO 0x0002 +#define SBMODE_SIGNED 0x0004 + +/* Mask for capabilities that never change */ +#define SBMODE_MASK (SBMODE_16BITS | SBMODE_STEREO) + +/* You can fill some members of this struct (i.e. port,irq,dma) before + * calling sb_detect() or sb_open()... this will ignore environment settings. + */ +typedef struct __sb_state_s { + boolean ok; /* Are structure contents valid? */ + int port, aweport; /* sb/awe32 base port */ + int irq; /* SoundBlaster IRQ */ + int dma8, dma16; /* 8-bit and 16-bit DMAs */ + int maxfreq_mono; /* Maximum discretization frequency / mono mode */ + int maxfreq_stereo; /* Maximum discretization frequency / stereo mode */ + unsigned short dspver; /* DSP version number */ + struct irq_handle *irq_handle; /* The interrupt handler */ + dma_buffer *dma_buff; /* Pre-allocated DMA buffer */ + unsigned char caps; /* SoundBlaster capabilities (SBMODE_XXX) */ + unsigned char mode; /* Current SB mode (SBMODE_XXX) */ + boolean open; /* Whenever the card has been opened */ + volatile int irqcount; /* Incremented on each IRQ... for diagnostics */ + void (*timer_callback) (); /* Called TWICE per buffer play */ +} __sb_state; + +extern __sb_state sb; + +extern void __sb_wait(); + +static inline boolean __sb_dsp_ready_in() +{ + int count; + for (count = 10000; count >= 0; count--) + if (inportb(SB_DSP_DATA_IN_STATUS) & SBM_DSP_READY) + return TRUE; + return FALSE; +} + +static inline boolean __sb_dsp_ready_out() +{ + int count; + for (count = 10000; count >= 0; count--) + if ((inportb(SB_DSP_DATA_OUT_STATUS) & SBM_DSP_READY) == 0) + return TRUE; + return FALSE; +} + +static inline void __sb_dsp_out(unsigned char reg) +{ + __sb_dsp_ready_out(); + outportb(SB_DSP_DATA_OUT, reg); +} + +static inline unsigned char __sb_dsp_in() +{ + __sb_dsp_ready_in(); + return inportb(SB_DSP_DATA_IN); +} + +static inline void __sb_dspreg_out(unsigned char reg, unsigned char val) +{ + __sb_dsp_out(reg); + __sb_dsp_out(val); +} + +static inline void __sb_dspreg_outwlh(unsigned char reg, unsigned short val) +{ + __sb_dsp_out(reg); + __sb_dsp_out(val); + __sb_dsp_out(val >> 8); +} + +static inline void __sb_dspreg_outwhl(unsigned char reg, unsigned short val) +{ + __sb_dsp_out(reg); + __sb_dsp_out(val >> 8); + __sb_dsp_out(val); +} + +static inline unsigned char __sb_dspreg_in(unsigned char reg) +{ + __sb_dsp_out(reg); + return __sb_dsp_in(); +} + +static inline void __sb_dsp_ack_dma8() +{ + inportb(SB_DSP_DMA8_IRQ); +} + +static inline void __sb_dsp_ack_dma16() +{ + inportb(SB_DSP_DMA16_IRQ); +} + +static inline unsigned short __sb_dsp_version() +{ + unsigned short ver; + __sb_dsp_out(SBDSP_VERSION); + __sb_dsp_ready_in(); + ver = ((unsigned short)__sb_dsp_in()) << 8; + ver |= __sb_dsp_in(); + return ver; +} + +static inline void __sb_mixer_out(unsigned char reg, unsigned char val) +{ + outportb(SB_MIXER_REGSEL, reg); + outportb(SB_MIXER_DATA, val); +} + +static inline unsigned char __sb_mixer_in(unsigned char reg) +{ + outportb(SB_MIXER_REGSEL, reg); + return inportb(SB_MIXER_DATA); +} + +/* Enable stereo transfers: sbpro mode only */ +static inline void __sb_stereo(boolean stereo) +{ + unsigned char val = __sb_mixer_in(SBMIX_OUTPUT_SELECT); + if (stereo) + val |= SBM_MIX_STEREO; + else + val &= ~SBM_MIX_STEREO; + __sb_mixer_out(SBMIX_OUTPUT_SELECT, val); +} + +/* Detect whenever SoundBlaster is present and fill "sb" structure */ +extern boolean sb_detect(); +/* Reset SoundBlaster */ +extern void sb_reset(); +/* Start working with SoundBlaster */ +extern boolean sb_open(); +/* Finish working with SoundBlaster */ +extern boolean sb_close(); +/* Enable/disable speaker output */ +extern void sb_output(boolean enable); +/* Start playing from DMA buffer in either 8/16 bit mono/stereo */ +extern boolean sb_start_dma(unsigned char mode, unsigned int freq); +/* Stop playing from DMA buffer */ +extern void sb_stop_dma(); +/* Query current position/total size of the DMA buffer */ +extern void sb_query_dma(unsigned int *dma_size, unsigned int *dma_pos); + +#endif /* __DOSSB_H__ */ + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/dos/doswss.c b/libs/mikmod/drivers/dos/doswss.c new file mode 100644 index 0000000..41cdf38 --- /dev/null +++ b/libs/mikmod/drivers/dos/doswss.c @@ -0,0 +1,577 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + Windows Sound System I/O routines (CS42XX, ESS18XX, GUS+DaughterBoard etc) + Written by Andrew Zabolotny + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef DRV_WSS + +#include +#include +#include +#include +#include +#include +#include + +#include "doswss.h" + +/********************************************* Private variables/routines *****/ + +__wss_state wss; + +/* WSS frequency rates... lower bit selects one of two frequency generators */ +static unsigned int wss_rates[14][2] = { + {5510, 0x00 | WSSM_XTAL2}, + {6620, 0x0E | WSSM_XTAL2}, + {8000, 0x00 | WSSM_XTAL1}, + {9600, 0x0E | WSSM_XTAL1}, + {11025, 0x02 | WSSM_XTAL2}, + {16000, 0x02 | WSSM_XTAL1}, + {18900, 0x04 | WSSM_XTAL2}, + {22050, 0x06 | WSSM_XTAL2}, + {27420, 0x04 | WSSM_XTAL1}, + {32000, 0x06 | WSSM_XTAL1}, + {33075, 0x0C | WSSM_XTAL2}, + {37800, 0x08 | WSSM_XTAL2}, + {44100, 0x0A | WSSM_XTAL2}, + {48000, 0x0C | WSSM_XTAL1} +}; + +static void wss_irq() +{ + /* Make sure its not a spurious IRQ */ + if (!irq_check(wss.irq_handle)) + return; + + wss.irqcount++; + + /* Clear IRQ status */ + outportb(WSS_STATUS, 0); + + /* Write transfer count again */ + __wss_outreg(WSSR_COUNT_LOW, wss.samples & 0xff); + __wss_outreg(WSSR_COUNT_HIGH, wss.samples >> 8); + irq_ack(wss.irq_handle); + + enable(); + if (wss.timer_callback) + wss.timer_callback(); +} + +static void wss_irq_end() +{ +} + +/* WSS accepts some conventional values instead of frequency in Hz... */ +static unsigned char __wss_getrate(unsigned int *freq) +{ + int i, best = -1, delta = 0xffff; + + for (i = 0; i < 14; i++) { + int newdelta = abs(wss_rates[i][0] - *freq); + if (newdelta < delta) + best = i, delta = newdelta; + } + + *freq = wss_rates[best][0]; + return wss_rates[best][1]; +} + +/* Check if we really have a WSS compatible card on given address */ +static boolean __wss_ping() +{ + /* Disable CODEC operations first */ + __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); + /* Now put some harmless values in registers and check them */ + __wss_outreg(WSSR_COUNT_LOW, 0xaa); + __wss_outreg(WSSR_COUNT_HIGH, 0x55); + return (__wss_inreg(WSSR_COUNT_LOW) == 0xaa) + && (__wss_inreg(WSSR_COUNT_HIGH) == 0x55); +} + +static boolean __wss_reset() +{ + int count; + + /* Disable output */ + wss_output(FALSE); + + /* Now select the test/initialization register */ + count = 10000; + while (inportb(WSS_ADDR) != WSSR_TEST_INIT) { + outportb(WSS_ADDR, WSSR_TEST_INIT); + if (!--count) + return FALSE; + } + + count = 10000; + while (inportb(WSS_DATA) & WSSM_CALIB_IN_PROGRESS) { + outportb(WSS_ADDR, WSSR_TEST_INIT); + if (!--count) + return FALSE; + } + + /* Enable playback IRQ */ + __wss_regbit_set(WSSR_PIN_CTRL, WSSM_IRQ_ENABLE); + __wss_outreg(WSSR_IRQ_STATUS, WSSM_PLAYBACK_IRQ); + + /* Clear IRQ status */ + outportb(WSS_STATUS, 0); + + return TRUE; +} + +static boolean __wss_setformat(unsigned char format) +{ + int count; + + outportb(WSS_ADDR, WSSM_MCE | WSSR_PLAY_FORMAT); + outportb(WSS_DATA, format); + inportb(WSS_DATA); /* ERRATA SHEETS ... */ + inportb(WSS_DATA); /* ERRATA SHEETS ... */ + + /* Wait end of syncronization ... */ + if (!__wss_wait()) + return FALSE; + + /* Turn off the ModeChangeEnable bit: do it until it works */ + count = 10000; + while (inportb(WSS_ADDR) != WSSR_PLAY_FORMAT) { + outportb(WSS_ADDR, WSSR_PLAY_FORMAT); + if (!--count) + return FALSE; + } + + return __wss_reset(); +} + +/**************************************************** WSS detection stuff *****/ + +static int __wss_irq_irqdetect(int irqno) +{ + unsigned char status = inportb(WSS_STATUS); + /* Clear IRQ status */ + outportb(WSS_STATUS, 0); + /* Reset transfer counter */ + __wss_outreg(WSSR_COUNT_LOW, 0); + __wss_outreg(WSSR_COUNT_HIGH, 0); + return (status & WSSM_INT); +} + +static boolean __wss_detect() +{ + /* First find the port number */ + if (!wss.port) { + static unsigned int wss_ports[] = + { 0x32c, 0x530, 0x604, 0xE80, 0xF40 }; + int i; + for (i = 0; i < 5; i++) { + wss.port = wss_ports[i]; + if (__wss_ping()) + break; + } + if (i < 0) { + wss.port = 0; + return FALSE; + } + } + + /* Now disable output */ + wss_output(FALSE); + + /* Detect the DMA channel */ + if (!wss.dma) { + static int __dma[] = { 0, 1, 3 }; + int i; + + /* Enable playback IRQ */ + __wss_regbit_set(WSSR_PIN_CTRL, WSSM_IRQ_ENABLE); + __wss_outreg(WSSR_IRQ_STATUS, WSSM_PLAYBACK_IRQ); + + /* Start a short DMA transfer and check if DMA count is zero */ + for (i = 0; i < 3; i++) { + unsigned int timer, status, freq = 44100; + + wss.dma = __dma[i]; + + dma_disable(wss.dma); + dma_set_mode(wss.dma, DMA_MODE_WRITE); + dma_clear_ff(wss.dma); + dma_set_count(wss.dma, 10); + dma_enable(wss.dma); + + /* Clear IRQ status */ + outportb(WSS_STATUS, 0); + + __wss_setformat(__wss_getrate(&freq)); + __wss_outreg(WSSR_COUNT_LOW, 1); + __wss_outreg(WSSR_COUNT_HIGH, 0); + /* Tell codec to start transfer */ + __wss_regbit_set(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); + + _farsetsel(_dos_ds); + timer = _farnspeekl(0x46c); + + while (_farnspeekl(0x46c) - timer <= 2) + if (dma_get_count(wss.dma) == 0) + break; + __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); + dma_disable(wss.dma); + + /* Now check if DMA transfer count is zero and an IRQ is pending */ + status = inportb(WSS_STATUS); + outportb(WSS_STATUS, 0); + if ((dma_get_count(wss.dma) == 0) && (status & WSSM_INT)) + break; + + wss.dma = 0; + } + + if (!wss.dma) + return FALSE; + } + + /* Now detect the IRQ number */ + if (!wss.irq) { + unsigned int i, irqmask, freq = 5510; + unsigned long timer, delta = 0x7fffffff; + + /* IRQ can be one of 2,3,5,7,10 */ + irq_detect_start(0x04ac, __wss_irq_irqdetect); + + dma_disable(wss.dma); + dma_set_mode(wss.dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); + dma_clear_ff(wss.dma); + dma_set_count(wss.dma, 1); + dma_enable(wss.dma); + + __wss_setformat(__wss_getrate(&freq)); + + /* Clear IRQ status */ + outportb(WSS_STATUS, 0); + + __wss_outreg(WSSR_COUNT_LOW, 0); + __wss_outreg(WSSR_COUNT_HIGH, 0); + + /* Prepare timeout counter */ + _farsetsel(_dos_ds); + timer = _farnspeekl(0x46c); + while (timer == _farnspeekl(0x46c)); + timer = _farnspeekl(0x46c); + + /* Reset all IRQ counters */ + irq_detect_clear(); + + /* Tell codec to start transfer */ + __wss_regbit_set(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); + + /* Now wait 1/18 seconds */ + while (timer == _farnspeekl(0x46c)); + __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); + dma_disable(wss.dma); + + /* Given frequency 5510Hz, a buffer size of 1 byte and a time interval + of 1/18.2 second, we should have received about 302 interrupts */ + for (i = 2; i <= 10; i++) { + int count = abs(302 - irq_detect_get(i, &irqmask)); + if (count < delta) + wss.irq = i, delta = count; + } + if (delta > 150) + wss.irq = 0; + + irq_detect_end(); + if (!wss.irq) + return FALSE; + } + + return TRUE; +} + +/*************************************************** High-level interface *****/ + +/* Detect whenever WSS is present and fill "wss" structure */ +boolean wss_detect() +{ + char *env; + + /* Try to find the port and DMA from environment */ + env = getenv("WSS"); + + while (env && *env) { + /* Skip whitespace */ + while ((*env == ' ') || (*env == '\t')) + env++; + if (!*env) + break; + + switch (*env++) { + case 'A': + case 'a': + if (!wss.port) + wss.port = strtol(env, &env, 16); + break; + case 'I': + case 'i': + if (!wss.irq) + wss.irq = strtol(env, &env, 10); + break; + case 'D': + case 'd': + if (!wss.dma) + wss.dma = strtol(env, &env, 10); + break; + default: + /* Skip other values */ + while (*env && (*env != ' ') && (*env != '\t')) + env++; + break; + } + } + + /* Try to fill the gaps in wss hardware parameters */ + __wss_detect(); + + if (!wss.port || !wss.irq || !wss.dma) + return FALSE; + + if (!__wss_ping()) + return FALSE; + + if (!__wss_reset()) + return FALSE; + + wss.ok = 1; + return TRUE; +} + +/* Reset WSS */ +void wss_reset() +{ + wss_stop_dma(); + __wss_reset(); +} + +/* Open WSS for usage */ +boolean wss_open() +{ + __dpmi_meminfo struct_info; + + if (!wss.ok) + if (!wss_detect()) + return FALSE; + + if (wss.open) + return FALSE; + + /* Now lock the wss structure in memory */ + struct_info.address = __djgpp_base_address + (unsigned long)&wss; + struct_info.size = sizeof(wss); + if (__dpmi_lock_linear_region(&struct_info)) + return FALSE; + + /* Hook the WSS IRQ */ + wss.irq_handle = + irq_hook(wss.irq, wss_irq, (long)wss_irq_end - (long)wss_irq); + if (!wss.irq_handle) { + __dpmi_unlock_linear_region(&struct_info); + return FALSE; + } + + /* Enable the interrupt */ + irq_enable(wss.irq_handle); + if (wss.irq > 7) + _irq_enable(2); + + wss.open++; + + return TRUE; +} + +/* Finish working with WSS */ +boolean wss_close() +{ + __dpmi_meminfo struct_info; + if (!wss.open) + return FALSE; + + wss.open--; + + /* Stop/free DMA buffer */ + wss_stop_dma(); + + /* Unhook IRQ */ + irq_unhook(wss.irq_handle); + wss.irq_handle = NULL; + + /* Unlock the wss structure */ + struct_info.address = __djgpp_base_address + (unsigned long)&wss; + struct_info.size = sizeof(wss); + __dpmi_unlock_linear_region(&struct_info); + + return TRUE; +} + +/* Adjust frequency rate to nearest WSS available */ +unsigned int wss_adjust_freq(unsigned int freq) +{ + __wss_getrate(&freq); + return freq; +} + +/* Enable/disable speaker output */ +/* Start playing from DMA buffer in either 8/16 bit mono/stereo */ +boolean wss_start_dma(unsigned char mode, unsigned int freq) +{ + int dmabuffsize; + unsigned char format; + + /* Stop DMA transfer if it is enabled */ + wss_stop_dma(); + + /* Sanity check: we support only 8-bit unsigned and 16-bit signed formats */ + if (((mode & WSSMODE_16BITS) && !(mode & WSSMODE_SIGNED)) + || (!(mode & WSSMODE_16BITS) && (mode & WSSMODE_SIGNED))) + return FALSE; + + /* Find the nearest frequency divisor (rate) */ + format = __wss_getrate(&freq); + wss.mode = mode; + + /* Get a DMA buffer enough for a 1sec interval... 4K <= dmasize <= 32K */ + dmabuffsize = freq; + if (mode & WSSMODE_STEREO) + dmabuffsize *= 2; + if (mode & WSSMODE_16BITS) + dmabuffsize *= 2; + dmabuffsize >>= 2; + if (dmabuffsize < 4096) + dmabuffsize = 4096; + if (dmabuffsize > 32768) + dmabuffsize = 32768; + dmabuffsize = (dmabuffsize + 255) & 0xffffff00; + + wss.dma_buff = dma_allocate(wss.dma, dmabuffsize); + if (!wss.dma_buff) + return FALSE; + + /* Fill DMA buffer with silence */ + dmabuffsize = wss.dma_buff->size; + if (mode & WSSMODE_SIGNED) + memset(wss.dma_buff->linear, 0, dmabuffsize); + else + memset(wss.dma_buff->linear, 0x80, dmabuffsize); + + /* Check data size and build a WSSR_PLAY_FORMAT value accordingly */ + wss.samples = dmabuffsize; + if (mode & WSSMODE_16BITS) { + wss.samples >>= 1; + format |= WSSM_16BITS; + } + + if (mode & WSSMODE_STEREO) { + wss.samples >>= 1; + format |= WSSM_STEREO; + } + + if (!__wss_setformat(format)) { + wss_stop_dma(); + return FALSE; + } + + /* Prime DMA for transfer */ + dma_start(wss.dma_buff, dmabuffsize, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); + + /* Tell codec how many samples to transfer */ + wss.samples = (wss.samples >> 1) - 1; + __wss_outreg(WSSR_COUNT_LOW, wss.samples & 0xff); + __wss_outreg(WSSR_COUNT_HIGH, wss.samples >> 8); + + /* Tell codec to start transfer */ + __wss_regbit_set(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); + + return TRUE; +} + +/* Stop playing from DMA buffer */ +void wss_stop_dma() +{ + if (!wss.dma_buff) + return; + + __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE); + dma_disable(wss.dma); + dma_free(wss.dma_buff); + wss.dma_buff = NULL; +} + +/* Query current position/total size of the DMA buffer */ +void wss_query_dma(unsigned int *dma_size, unsigned int *dma_pos) +{ + unsigned int dma_left; + *dma_size = wss.dma_buff->size; + /* It can happen we try to read DMA count when HI/LO bytes will be + inconsistent */ + for (;;) { + unsigned int dma_left_test; + dma_clear_ff(wss.dma); + dma_left_test = dma_get_count(wss.dma); + dma_left = dma_get_count(wss.dma); + if ((dma_left >= dma_left_test) && (dma_left - dma_left_test < 10)) + break; + } + *dma_pos = *dma_size - dma_left; +} + +void wss_output(boolean enable) +{ + if (enable) + wss.curlevel = wss.level; + else + wss.curlevel = 0x3f; + + __wss_outreg(WSSR_MASTER_L, wss.curlevel); + __wss_outreg(WSSR_MASTER_R, wss.curlevel); +} + +void wss_level(int level) +{ + if (level < 0) + level = 0; + if (level > 63) + level = 63; + wss.curlevel = wss.level = level ^ 63; + + __wss_outreg(WSSR_MASTER_L, wss.curlevel); + __wss_outreg(WSSR_MASTER_R, wss.curlevel); +} + +#endif /* DRV_WSS */ + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/dos/doswss.h b/libs/mikmod/drivers/dos/doswss.h new file mode 100644 index 0000000..ae77bb5 --- /dev/null +++ b/libs/mikmod/drivers/dos/doswss.h @@ -0,0 +1,217 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Windows Sound System and compatible soundcards definitions + +==============================================================================*/ + +#ifndef __DOSWSS_H__ +#define __DOSWSS_H__ + +#include "dosdma.h" +#include "dosirq.h" + +#define WSS_ADDR (wss.port + 0x04) +#define WSS_DATA (wss.port + 0x05) +#define WSS_STATUS (wss.port + 0x06) +#define WSS_PIO (wss.port + 0x07) + +/* WSS_ADDR: Bits 0-4 select an internal register to read/write */ +#define WSSR_INPUT_L 0x00 /* Left input control register */ +#define WSSR_INPUT_R 0x01 /* RIght input control register */ +#define WSSR_AUX1_L 0x02 /* Left Aux #1 input control */ +#define WSSR_AUX1_R 0x03 /* Right Aux #1 input control */ +#define WSSR_CD_L 0x04 /* Left Aux #2 input control */ +#define WSSR_CD_R 0x05 /* Right Aux #2 input control */ +#define WSSR_MASTER_L 0x06 /* Left output control */ +#define WSSR_MASTER_R 0x07 /* Right output control */ +#define WSSR_PLAY_FORMAT 0x08 /* Clock and data format */ +#define WSSR_IFACE_CTRL 0x09 /* Interface control */ +#define WSSR_PIN_CTRL 0x0a /* Pin control */ +#define WSSR_TEST_INIT 0x0b /* Test and initialization */ +#define WSSR_MISC_INFO 0x0c /* Miscellaneaous information */ +#define WSSR_LOOPBACK 0x0d /* Digital Mix */ +#define WSSR_COUNT_HIGH 0x0e /* Playback Upper Base Count */ +#define WSSR_COUNT_LOW 0x0f /* Playback Lower Base Count */ +#define WSSR_ALT_FEATURE_1 0x10 /* alternate #1 feature enable */ +#define WSSR_ALT_FEATURE_2 0x11 /* alternate #2 feature enable */ +#define WSSR_LINE_IN_L 0x12 /* left line input control */ +#define WSSR_LINE_IN_R 0x13 /* right line input control */ +#define WSSR_TIMER_LOW 0x14 /* timer low byte */ +#define WSSR_TIMER_HIGH 0x15 /* timer high byte */ +#define WSSR_IRQ_STATUS 0x18 /* irq status register */ +#define WSSR_MONO_IO_CTRL 0x1a /* mono input/output control */ +#define WSSR_REC_FORMAT 0x1c /* record format */ +#define WSSR_REC_COUNT_HIGH 0x1e /* record upper count */ +#define WSSR_REC_COUNT_LOW 0x1f /* record lower count */ + +/* WSS_ADDR bits 7-5 definition */ +#define WSSM_INIT 0x80 /* Codec is initializing */ +#define WSSM_MCE 0x40 /* Mode change enable */ +#define WSSM_TRD 0x20 /* Transfer Request Disable */ +/* bits 4-0 are indirect register address (0-15) */ + +/* WSS_STATUS bit masks */ +#define WSSM_CUL 0x80 /* Capture data upper/lower byte */ +#define WSSM_CLR 0x40 /* Capture left/right sample */ +#define WSSM_CRDY 0x20 /* Capture data read */ +#define WSSM_SOUR 0x10 /* Playback over/under run error */ +#define WSSM_PUL 0x08 /* Playback upper/lower byte */ +#define WSSM_PLR 0x04 /* Playback left/right sample */ +#define WSSM_PRDY 0x02 /* Playback data register read */ +#define WSSM_INT 0x01 /* interrupt status */ + +/* Definitions for output level registers */ +#define WSSM_MUTE 0x80 /* Mute this output source */ +/* bits 5-0 are left output attenuation select (0-63) */ +/* bits 5-0 are right output attenuation select (0-63) */ + +/* Definitions for clock and data format register (WSSR_PLAY_FORMAT) */ +#define WSSM_STEREO 0x10 /* stero mode */ +#define WSSM_ULAW_8 0x20 /* 8-bit U-law companded */ +#define WSSM_16BITS 0x40 /* 16 bit twos complement data - little endian */ +#define WSSM_ALAW_8 0x60 /* 8-bit A-law companded */ +#define WSSM_16BITS_BE 0xc0 /* 16-bit twos complement data - big endian */ +#define WSSM_ADPCM_16 0xa0 /* 16-bit ADPCM */ +/* Bits 3-1 define frequency divisor */ +#define WSSM_XTAL1 0x00 /* 24.576 crystal */ +#define WSSM_XTAL2 0x01 /* 16.9344 crystal */ + +/* Definitions for interface control register (WSSR_IFACE_CTRL) */ +#define WSSM_CAPTURE_PIO 0x80 /* Capture PIO enable */ +#define WSSM_PLAYBACK_PIO 0x40 /* Playback PIO enable */ +#define WSSM_AUTOCALIB 0x08 /* auto calibrate */ +#define WSSM_SINGLE_DMA 0x04 /* Use single DMA channel */ +#define WSSM_PLAYBACK_ENABLE 0x01 /* playback enable */ + +/* Definitions for Pin control register (WSSR_PIN_CTRL) */ +#define WSSM_IRQ_ENABLE 0x02 /* interrupt enable */ +#define WSSM_XCTL1 0x40 /* external control #1 */ +#define WSSM_XCTL0 0x80 /* external control #0 */ + +/* Definitions for WSSR_TEST_INIT register */ +#define WSSM_CALIB_IN_PROGRESS 0x20 /* auto calibrate in progress */ + +/* Definitions for misc control register (WSR_MISC_INFO) */ +#define WSSM_MODE2 0x40 /* MODE 2 */ +#define WSSM_MODE3 0x6c /* MODE 3 - enhanced mode */ + +/* Definitions for codec irq status (WSSR_IRQ_STATUS) */ +#define WSSM_PLAYBACK_IRQ 0x10 +#define WSSM_RECORD_IRQ 0x20 +#define WSSM_TIMER_IRQ 0x40 + +typedef unsigned char boolean; + +#ifndef FALSE +#define FALSE 0 +#define TRUE 1 +#endif + +/* Play mode bits */ +#define WSSMODE_16BITS 0x0001 +#define WSSMODE_STEREO 0x0002 +#define WSSMODE_SIGNED 0x0004 + +/* You can fill some members of this struct (i.e. port,irq,dma) before + * calling wss_detect() or wss_open()... this will ignore environment settings. + */ +typedef struct __wss_state_s { + boolean ok; /* Set if this structure is properly filled */ + int port; /* Base codec port */ + int irq; /* codec IRQ */ + int dma; /* codec DMA */ + struct irq_handle *irq_handle; /* The interrupt handler */ + dma_buffer *dma_buff; /* Pre-allocated DMA buffer */ + unsigned char mode; /* Current WSS mode (WSSMODE_XXX) */ + boolean open; /* Whenever the card has been opened */ + int samples; /* Number of samples in DMA buffer */ + unsigned char level; /* Output level (63..0): doesn't change when mute */ + unsigned char curlevel; /* Current output level (63(min)..0(max)) */ + volatile int irqcount; /* Incremented on each IRQ... for diagnostics */ + void (*timer_callback) (); /* Called TWICE per buffer play */ +} __wss_state; + +extern __wss_state wss; + +/* Wait until codec finishes initialization */ +static inline boolean __wss_wait() +{ + int count; + for (count = 10000; count >= 0; count--) + if (!(inportb(WSS_ADDR) & WSSM_INIT)) + return TRUE; + return FALSE; +} + +static inline void __wss_outreg(unsigned char reg, unsigned char val) +{ + outportb(WSS_ADDR, reg); + outportb(WSS_DATA, val); +} + +static inline unsigned char __wss_inreg(unsigned char reg) +{ + outportb(WSS_ADDR, reg); + return inportb(WSS_DATA); +} + +/* Set some bits in a specific register */ +static inline void __wss_regbit_set(unsigned char reg, unsigned char mask) +{ + outportb(WSS_ADDR, reg); + outportb(WSS_DATA, inportb(WSS_DATA) | mask); +} + +/* Reset some bits in a specific register */ +static inline void __wss_regbit_reset(unsigned char reg, unsigned char mask) +{ + outportb(WSS_ADDR, reg); + outportb(WSS_DATA, inportb(WSS_DATA) & ~mask); +} + +/* Detect whenever WSS is present and fill "wss" structure */ +extern boolean wss_detect(); +/* Reset WSS */ +extern void wss_reset(); +/* Open WSS for usage */ +extern boolean wss_open(); +/* Finish working with WSS */ +extern boolean wss_close(); +/* Enable/disable speaker output */ +extern void wss_output(boolean enable); +/* Adjust frequency rate to nearest WSS available */ +extern unsigned int wss_adjust_freq(unsigned int freq); +/* Start playing from DMA buffer in either 8/16 bit mono/stereo */ +extern boolean wss_start_dma(unsigned char mode, unsigned int freq); +/* Stop playing from DMA buffer */ +extern void wss_stop_dma(); +/* Query current position/total size of the DMA buffer */ +extern void wss_query_dma(unsigned int *dma_size, unsigned int *dma_pos); +/* Set output level (0(min)-63(max)) */ +extern void wss_level(int level); + +#endif /* __DOSWSS_H__ */ + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/dos/libgus.h b/libs/mikmod/drivers/dos/libgus.h new file mode 100644 index 0000000..0d66ee9 --- /dev/null +++ b/libs/mikmod/drivers/dos/libgus.h @@ -0,0 +1,402 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Linux libGUS-alike library for DOS, used by drv_ultra.c under DOS. + +==============================================================================*/ + +/* + Current limitations: + - Only a subset of libgus is supported + - Only one GUS card is supported (due to the fact that ULTRASND environment + variable is used) + - No Interwawe support (if IW works the old way, it's ok). +*/ + +#ifndef __LIBGUS_H__ +#define __LIBGUS_H__ + +#include + +#define __LITTLE_ENDIAN + +typedef struct _gus_info_t gus_info_t; +typedef struct _gus_instrument_t gus_instrument_t; +typedef struct _gus_wave_t gus_wave_t; +typedef struct _gus_layer_t gus_layer_t; + +#define GUS_CARD_VERSION_CLASSIC 0x0024 /* revision 2.4 */ +#define GUS_CARD_VERSION_CLASSIC1 0x0034 /* revision 3.4? */ +#define GUS_CARD_VERSION_CLASSIC_ICS 0x0037 /* revision 3.7 (ICS mixer) */ +#define GUS_CARD_VERSION_EXTREME 0x0050 /* GUS Extreme */ +#define GUS_CARD_VERSION_ACE 0x0090 /* GUS ACE */ +#define GUS_CARD_VERSION_MAX 0x00a0 /* GUS MAX - revision 10 */ +#define GUS_CARD_VERSION_MAX1 0x00a1 /* GUS MAX - revision 11 */ +#define GUS_CARD_VERSION_PNP 0x0100 /* GUS Plug & Play */ + +#define GUS_STRU_INFO_F_DB16 0x00000001 /* 16-bit daughter board present */ +#define GUS_STRU_INFO_F_PCM 0x00000004 /* GF1 PCM during SYNTH enabled */ +#define GUS_STRU_INFO_F_ENHANCED 0x00000008 /* InterWave - enhanced mode */ +#define GUS_STRU_INFO_F_DAEMON 0x00000010 /* instrument daemon is present */ + +struct _gus_info_t { + unsigned char id[8]; /* id of this card (warning! maybe unterminated!!!) */ + + unsigned int flags; /* some info flags - see to GUS_STRU_INFO_F_XXXX */ + unsigned int version; /* see to GUS_CARD_VERSION_XXXX constants */ + + unsigned short port; + unsigned short irq; + unsigned short dma1; /* DMA1 - GF1 download & codec record */ + unsigned short dma2; /* DMA2 - GF1 record & codec playback */ + + unsigned int mixing_freq; /* mixing frequency in Hz */ + + unsigned int memory_size; /* in bytes */ + unsigned int memory_free; /* in bytes */ + unsigned int memory_block_8; /* largest free 8-bit block in memory */ + unsigned int memory_block_16; /* largest free 16-bit block in memory */ +}; + +/* struct gus_instrument_t - mode */ + +#define GUS_INSTR_SIMPLE 0x00 /* simple format - for MOD players */ +#define GUS_INSTR_PATCH 0x01 /* old GF1 patch format */ +#define GUS_INSTR_COUNT 2 + +#define GUS_INSTR_F_NORMAL 0x0000 /* normal mode */ +#define GUS_INSTR_F_NOT_FOUND 0x0001 /* instrument can't be loaded */ +#define GUS_INSTR_F_ALIAS 0x0002 /* alias */ +#define GUS_INSTR_F_NOT_LOADED 0x00ff /* instrument not loaded (not found) */ + +#define GUS_INSTR_E_NONE 0x0000 /* exclusion mode - none */ +#define GUS_INSTR_E_SINGLE 0x0001 /* exclude single - single note from this instrument */ +#define GUS_INSTR_E_MULTIPLE 0x0002 /* exclude multiple - stop only same note from this instrument */ + +#define GUS_INSTR_L_NONE 0x0000 /* not layered */ +#define GUS_INSTR_L_ON 0x0001 /* layered */ +#define GUS_INSTR_L_VELOCITY 0x0002 /* layered by velocity */ +#define GUS_INSTR_L_FREQUENCY 0x0003 /* layered by frequency */ + +struct _gus_instrument_t { + union { + unsigned int instrument;/* instrument number */ + } number; + + char *name; /* name of this instrument or NULL */ + + unsigned int mode:8, /* see to GUS_INSTR_XXXX */ + flags:8, /* see to GUS_INSTR_F_XXXX */ + exclusion:4, /* see to GUS_INSTR_E_XXXX */ + layer:4; /* see to GUS_INSTR_L_XXXX */ + unsigned short exclusion_group; /* 0 - none, 1-65535 */ + + struct { + unsigned char effect1:4,/* use global effect if available */ + effect2:4; /* use global effect if available */ + unsigned char effect1_depth;/* 0-127 */ + unsigned char effect2_depth;/* 0-127 */ + } patch; + + union { + gus_layer_t *layer; /* first layer */ + unsigned int alias; /* pointer to instrument */ + } info; + gus_instrument_t *next; /* next instrument */ +}; + +struct _gus_layer_t { + unsigned char mode; /* see to GUS_INSTR_XXXX constants */ + + gus_wave_t *wave; + gus_layer_t *next; +}; + +/* bits for format variable in gus_wave_t */ + +#define GUS_WAVE_16BIT 0x0001 /* 16-bit wave */ +#define GUS_WAVE_UNSIGNED 0x0002 /* unsigned wave */ +#define GUS_WAVE_INVERT 0x0002 /* same as unsigned wave */ +#define GUS_WAVE_BACKWARD 0x0004 /* forward mode */ +#define GUS_WAVE_LOOP 0x0008 /* loop mode */ +#define GUS_WAVE_BIDIR 0x0010 /* bidirectional mode */ +#define GUS_WAVE_ULAW 0x0020 /* uLaw compressed wave */ +#define GUS_WAVE_RAM 0x0040 /* wave is _preloaded_ in RAM (it is used for ROM simulation) */ +#define GUS_WAVE_ROM 0x0080 /* wave is in ROM */ +#define GUS_WAVE_DELTA 0x0100 + +#define GUS_WAVE_PATCH_ENVELOPE 0x01 /* envelopes on */ +#define GUS_WAVE_PATCH_SUSTAIN 0x02 /* sustain mode */ + +struct _gus_wave_t { + unsigned char mode; /* see to GUS_INSTR_XXXX constants */ + unsigned char format; /* see to GUS_WAVE_XXXX constants */ + unsigned int size; /* size of waveform in bytes */ + unsigned int start; /* start offset in bytes * 16 (lowest 4 bits - fraction) */ + unsigned int loop_start; /* bits loop start offset in bytes * 16 (lowest 4 bits - fraction) */ + unsigned int loop_end; /* loop start offset in bytes * 16 (lowest 4 bits - fraction) */ + unsigned short loop_repeat; /* loop repeat - 0 = forever */ + struct { + unsigned int memory; /* begin of waveform in GUS's memory */ + unsigned char *ptr; /* pointer to waveform in system memory */ + } begin; + + struct { + unsigned char flags; + unsigned int sample_rate; + unsigned int low_frequency;/* low frequency range for this waveform */ + unsigned int high_frequency;/* high frequency range for this waveform */ + unsigned int root_frequency;/* root frequency for this waveform */ + signed short tune; + unsigned char balance; + unsigned char envelope_rate[6]; + unsigned char envelope_offset[6]; + unsigned char tremolo_sweep; + unsigned char tremolo_rate; + unsigned char tremolo_depth; + unsigned char vibrato_sweep; + unsigned char vibrato_rate; + unsigned char vibrato_depth; + unsigned short scale_frequency; + unsigned short scale_factor;/* 0-2048 or 0-2 */ + } patch; + + gus_wave_t *next; +}; + +/* defines for gus_memory_reset () */ +#define GUS_DOWNLOAD_MODE_NORMAL 0x0000 +#define GUS_DOWNLOAD_MODE_TEST 0x0001 + +/* + A subset of libgus functions (used by MikMod Ultrasound driver) +*/ +int gus_cards(void); + /* + * return value: number of GUS cards installed in system or + * zero if driver isn't installed + */ +int gus_close(int card); + /* + * close file (gus synthesizer) previously opened with gusOpen function + * return value: zero if success + */ +int gus_do_flush(void); + /* + * return value: zero if command queue was successfully flushed + * in non block mode - number of written bytes + */ +void gus_do_tempo(unsigned int tempo); + /* + * set new tempo + */ +void gus_do_voice_frequency(unsigned char voice, unsigned int freq); + /* + * set voice frequency in Hz + */ +void gus_do_voice_pan(unsigned char voice, unsigned short pan); + /* + * set voice pan (0-16384) (full left - full right) + */ +void gus_do_voice_start(unsigned char voice, unsigned int program, + unsigned int freq, unsigned short volume, + unsigned short pan); + /* + * start voice + * voice : voice # + * program : program # or ~0 = current + * freq : frequency in Hz + * volume : volume level (0-16384) or ~0 = current + * pan : pan level (0-16384) or ~0 = current + */ +void gus_do_voice_start_position(unsigned char voice, unsigned int program, + unsigned int freq, unsigned short volume, + unsigned short pan, unsigned int position); + /* + * start voice + * voice : voice # + * program : program # or ~0 = current + * freq : frequency in Hz + * volume : volume level (0-16384) or ~0 = current + * pan : pan level (0-16384) or ~0 = current + * position : offset to wave in bytes * 16 (lowest 4 bits - fraction) + */ +void gus_do_voice_stop(unsigned char voice, unsigned char mode); + /* + * stop voice + * mode = 0 : stop voice now + * mode = 1 : disable wave loop and finish it + */ +void gus_do_voice_volume(unsigned char voice, unsigned short vol); + /* + * set voice volume level 0-16384 (linear) + */ +void gus_do_wait(unsigned int ticks); + /* + * wait x ticks - this command is block separator + * all commands between blocks are interpreted in the begining of one tick + */ +int gus_get_voice_status(int voice); + /* + * THIS IS NOT A FUNCTION OF ORIGINAL libGUS! + * Return voice status: -1 on error, 0 if voice stopped, 1 if playing + */ +int gus_get_handle(void); + /* + * return value: file handle (descriptor) for /dev/gus + */ +int gus_info(gus_info_t * info, int reread); + /* + * return value: filled info variable with actual values + * (look at gus.h header file for more informations) + * version field: 0x0024 - GUS revision 2.4 + * 0x0035 - GUS revision 3.7 with flipped mixer channels + * 0x0037 - GUS revision 3.7 + * 0x0090 - GUS ACE + * 0x00a0 - GUS MAX revision 10 + * 0x00a1 - GUS MAX revision 11 + * 0x0100 - InterWave (full version) + * flags field: see to GUS_STRU_INFO_F_???? constants (gus.h header file) + * port field: port number (for example 0x220) + * irq field: irq number (for example 11) + * dma1 field: dma1 number (for example 5) + * dma2 field: dma2 number (for example 6) + * note: dma1 and dma2 could be same in case of only one dma channel used + */ +int gus_memory_alloc(gus_instrument_t * instrument); + /* + * input value: look at gus.h for more details about gus_instrument_t structure + * return value: zero if instrument was successfully allocated + */ +int gus_memory_free(gus_instrument_t * instrument); + /* + * input value: look at gus.h for more details about gus_instrument_t structure + * return value: zero if instrument was successfully removed + */ +int gus_memory_size(void); + /* + * return value: gus memory size in bytes + */ +int gus_memory_free_size(void); + /* + * return value: unused gus memory in bytes + * warning: reset function must be called before + */ +int gus_memory_free_block(int w_16bit); + /* + * return value: current largest free block for 8-bit or 16-bit wave + */ +int gus_memory_pack(void); + /* + * return value: zero if success + */ +int gus_memory_reset(int mode); + /* + * input value: see to GUS_DOWNLOAD_MODE_XXXX constants (gus.h) + * return value: zero if samples & instruments was successfully removed + * from GF1 memory manager + */ + +int gus_open(int card, size_t queue_buffer_size, int non_block); + /* + * input values: card number, + * size of command queue buffer (512-1MB) + * buffer is allocated dynamically, + * non block mode + * return value: zero if success + * note 1: this function must be called as first + * open file /dev/gus + * note 2: you can open more cards with one process + */ +int gus_queue_flush(void); + /* + * return value: zero if command queue was successfully flushed + */ +int gus_queue_read_set_size(int items); + /* + * input value: echo buffer size in items (if 0 - erase echo buffer) + */ +int gus_queue_write_set_size(int items); + /* + * input value: write queue size in items (each item have 8 bytes) + */ +int gus_reset(int voices, unsigned int channel_voices); + /* + * input values: active voices and channel voices (for dynamic allocation) + * return value: number of active voices if reset was successfull (GF1 chip active) + */ +int gus_reset_engine_only(void); + /* + * return value: same as gus_reset function + * note: this command doesn't change number of active + * voices and doesn't do hardware reset + */ +int gus_select(int card); + /* + * select specified card + * return value: zero if success + */ +int gus_timer_start(void); + /* + * return value: zero if successfull + */ +int gus_timer_stop(void); + /* + * return value: zero if timer was stoped + */ +int gus_timer_continue(void); + /* + * return value: zero if timer will be continue + */ +int gus_timer_tempo(int ticks); + /* + * return value: zero if setup was success + */ +int gus_timer_base(int base); + /* + * return value: zero if setup was success (default timebase = 100) + */ + +void gus_convert_delta(unsigned int type, unsigned char *dest, + unsigned char *src, size_t size); + /* + * note: dest and src pointers can be equal + */ + +void gus_timer_callback(void (*timer_callback) ()); + /* + * Set a callback to be called once per tempo tick + */ + +int gus_dma_usage (int use); + /* + * Tell GUS library to use/to not use DMA for sample transfer. + * In some environments/on some hardware platforms you will need + * to disable DMA in order to function properly. You should call + * this function before opening the card. + */ + +#endif /* __LIBGUS_H__ */ + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/drv_oss.c b/libs/mikmod/drivers/drv_oss.c new file mode 100644 index 0000000..64957f7 --- /dev/null +++ b/libs/mikmod/drivers/drv_oss.c @@ -0,0 +1,417 @@ +/* MikMod sound library + (c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Driver for output on Linux and FreeBSD Open Sound System (OSS) (/dev/dsp) + +==============================================================================*/ + +/* + + Written by Chris Conn + + Extended by Miodrag Vallat: + - compatible with all OSS/Voxware versions on Linux/i386, at least + - support for uLaw output (for sparc systems) + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mikmod_internals.h" + +#ifdef DRV_OSS + +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#ifdef HAVE_FCNTL_H +#include +#endif +#include +#include +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_SOUNDCARD_H +#include /* Linux and newer BSD versions - OSS standart */ +#elif defined(HAVE_MACHINE_SOUNDCARD_H) +#include /* Some old BSD versions */ +#elif defined(HAVE_SOUNDCARD_H) +#include /* Some old BSD versions and also newer OpenBSD versions */ +#endif + +/* Compatibility with old versions of OSS + (Voxware <= 2.03, Linux kernel < 1.1.31) */ +#ifndef AFMT_S16_LE +#define AFMT_S16_LE 16 +#endif +#ifndef AFMT_S16_BE +#define AFMT_S16_BE 0x20 +#endif +#ifndef AFMT_U8 +#define AFMT_U8 8 +#endif +#ifndef SNDCTL_DSP_SETFMT +#define SNDCTL_DSP_SETFMT SNDCTL_DSP_SAMPLESIZE +#endif + +/* Compatibility with not-so-old versions of OSS + (OSS < 3.7, Linux kernel < 2.1.16) */ +#ifndef AFMT_S16_NE +#if defined(_AIX) || defined(AIX) || defined(sparc) || defined(HPPA) || defined(PPC) +#define AFMT_S16_NE AFMT_S16_BE +#else +#define AFMT_S16_NE AFMT_S16_LE +#endif +#endif + +/* Compatibility with OSS 4.x: AFMT_FLOAT is documented + as "not recommended" on the OSS website. This post + on the OSS mailing lists says: "In general AFMT_FLOAT + is not supported by OSS except in some special cases." + (http://sf.net/p/opensound/mailman/message/28840674/) */ +#ifndef AFMT_FLOAT +#define AFMT_FLOAT 0x00004000 +#endif + +static int sndfd=-1; +static SBYTE *audiobuffer=NULL; +static int buffersize; +static int play_precision; + +#define DEFAULT_CARD 0 +static int card=DEFAULT_CARD; + +#ifdef SNDCTL_DSP_SETFRAGMENT + +#define DEFAULT_FRAGSIZE 14 +#define DEFAULT_NUMFRAGS 16 + +static int fragsize=DEFAULT_FRAGSIZE; +static int numfrags=DEFAULT_NUMFRAGS; + +#endif + +static void OSS_CommandLine(const CHAR *cmdline) +{ + CHAR *ptr; + +#ifdef SNDCTL_DSP_SETFRAGMENT + if((ptr=MD_GetAtom("buffer",cmdline,0)) != NULL) { + fragsize=atoi(ptr); + if((fragsize<7)||(fragsize>17)) fragsize=DEFAULT_FRAGSIZE; + MikMod_free(ptr); + } + if((ptr=MD_GetAtom("count",cmdline,0)) != NULL) { + numfrags=atoi(ptr); + if((numfrags<2)||(numfrags>255)) numfrags=DEFAULT_NUMFRAGS; + MikMod_free(ptr); + } +#endif + if((ptr=MD_GetAtom("card",cmdline,0)) != NULL) { + card = atoi(ptr); + if((card<0)||(card>99)) card=DEFAULT_CARD; + MikMod_free(ptr); + } +} + +static char *OSS_GetDeviceName(void) +{ + static char sounddevice[20]; + + /* First test for devfs enabled Linux sound devices */ + if (card) + sprintf(sounddevice,"/dev/sound/dsp%d",card); + else + strcpy(sounddevice,"/dev/sound/dsp"); + if(!access(sounddevice,F_OK)) + return sounddevice; + + sprintf(sounddevice,"/dev/dsp%d",card); + if (!card) { + /* prefer /dev/dsp0 over /dev/dsp, as /dev/dsp might be a symbolic link + to something else than /dev/dsp0. Revert to /dev/dsp if /dev/dsp0 + does not exist. */ + if(access("/dev/dsp0",F_OK)) + strcpy(sounddevice,"/dev/dsp"); + } + + return sounddevice; +} + +static BOOL OSS_IsThere(void) +{ + /* under Linux, and perhaps other Unixes, access()ing the device is not + enough since it won't fail if the machine doesn't have sound support + in the kernel or sound hardware */ + int fd; + + if((fd=open(OSS_GetDeviceName(),O_WRONLY|O_NONBLOCK))>=0) { + close(fd); + return 1; + } + return (errno==EACCES?1:0); +} + +static int OSS_Init_internal(void) +{ + int play_stereo,play_rate; + int orig_precision,orig_stereo; + int formats; +#if SOUND_VERSION >= 301 + audio_buf_info buffinf; +#endif + +#ifdef SNDCTL_DSP_GETFMTS + /* Ask device for supported formats */ + if(ioctl(sndfd,SNDCTL_DSP_GETFMTS,&formats)<0) { + _mm_errno=MMERR_OPENING_AUDIO; + return 1; + } +#else + formats=AFMT_S16_NE|AFMT_U8; +#endif + + orig_precision=play_precision=(md_mode&DMODE_FLOAT)? AFMT_FLOAT : + (md_mode&DMODE_16BITS)? AFMT_S16_NE : AFMT_U8; + + /* Device does not support the format we would prefer... */ + if(!(formats & play_precision)) { + if(play_precision==AFMT_FLOAT) { + _mm_errno=MMERR_NO_FLOAT32; + return 1; + } + /* We could try 8 bit sound if available */ + if(play_precision==AFMT_S16_NE &&(formats&AFMT_U8)) { + _mm_errno=MMERR_8BIT_ONLY; + return 1; + } +#ifdef AFMT_MU_LAW + /* We could try uLaw if available */ + if(formats&AFMT_MU_LAW) { + if((md_mode&DMODE_STEREO)||(md_mode&DMODE_16BITS)|| + md_mixfreq!=8000) { + _mm_errno=MMERR_ULAW; + return 1; + } else + orig_precision=play_precision=AFMT_MU_LAW; + } else +#endif + /* Otherwise, just abort */ + { + _mm_errno=MMERR_OSS_SETSAMPLESIZE; + return 1; + } + } + + if((ioctl(sndfd,SNDCTL_DSP_SETFMT,&play_precision)<0)|| + (orig_precision!=play_precision)) { + _mm_errno=MMERR_OSS_SETSAMPLESIZE; + return 1; + } +#ifdef SNDCTL_DSP_CHANNELS + orig_stereo=play_stereo=(md_mode&DMODE_STEREO)?2:1; + if((ioctl(sndfd,SNDCTL_DSP_CHANNELS,&play_stereo)<0)|| + (orig_stereo!=play_stereo)) { + _mm_errno=MMERR_OSS_SETSTEREO; + return 1; + } +#else + orig_stereo=play_stereo=(md_mode&DMODE_STEREO)?1:0; + if((ioctl(sndfd,SNDCTL_DSP_STEREO,&play_stereo)<0)|| + (orig_stereo!=play_stereo)) { + _mm_errno=MMERR_OSS_SETSTEREO; + return 1; + } +#endif + + play_rate=md_mixfreq; + if((ioctl(sndfd,SNDCTL_DSP_SPEED,&play_rate)<0)) { + _mm_errno=MMERR_OSS_SETSPEED; + return 1; + } + md_mixfreq=play_rate; + +#if SOUND_VERSION >= 301 + /* This call fails on Linux/PPC */ + if((ioctl(sndfd,SNDCTL_DSP_GETOSPACE,&buffinf)<0)) + ioctl(sndfd,SNDCTL_DSP_GETBLKSIZE,&buffinf.fragsize); + if(!(audiobuffer=(SBYTE*)MikMod_malloc(buffinf.fragsize))) + return 1; + + buffersize = buffinf.fragsize; +#else + ioctl(sndfd,SNDCTL_DSP_GETBLKSIZE,&buffersize); + if(!(audiobuffer=(SBYTE*)MikMod_malloc(buffersize))) + return 1; +#endif + + return VC_Init(); +} + +static int OSS_Init(void) +{ +#ifdef SNDCTL_DSP_SETFRAGMENT + int fragmentsize; +#endif + + if((sndfd=open(OSS_GetDeviceName(),O_WRONLY))<0) { + _mm_errno=MMERR_OPENING_AUDIO; + return 1; + } + +#ifdef SNDCTL_DSP_SETFRAGMENT + if((fragsize==DEFAULT_FRAGSIZE)&&(getenv("MM_FRAGSIZE"))) { + fragsize=atoi(getenv("MM_FRAGSIZE")); + if((fragsize<7)||(fragsize>17)) fragsize=DEFAULT_FRAGSIZE; + } + if((numfrags==DEFAULT_NUMFRAGS)&&(getenv("MM_NUMFRAGS"))) { + numfrags=atoi(getenv("MM_NUMFRAGS")); + if((numfrags<2)||(numfrags>255)) numfrags=DEFAULT_NUMFRAGS; + } + + fragmentsize=(numfrags<<16)|fragsize; + + if(ioctl(sndfd,SNDCTL_DSP_SETFRAGMENT,&fragmentsize)<0) { + _mm_errno=MMERR_OSS_SETFRAGMENT; + return 1; + } +#endif + + return OSS_Init_internal(); +} + +static void OSS_Exit_internal(void) +{ + VC_Exit(); + MikMod_free(audiobuffer); + audiobuffer = NULL; +} + +static void OSS_Exit(void) +{ + OSS_Exit_internal(); + + if (sndfd>=0) { + close(sndfd); + sndfd=-1; + } +} + +static void OSS_PlayStop(void) +{ + VC_PlayStop(); + + ioctl(sndfd,SNDCTL_DSP_POST,0); +} + +static void OSS_Update(void) +{ + int done; + +#if SOUND_VERSION >= 301 + audio_buf_info buffinf; + + buffinf.fragments = 2; + for(;;) { + /* This call fails on Linux/PPC */ + if ((ioctl(sndfd,SNDCTL_DSP_GETOSPACE,&buffinf)<0)) { + buffinf.fragments--; + buffinf.fragsize = buffinf.bytes = buffersize; + } + if(!buffinf.fragments) + break; + done=VC_WriteBytes(audiobuffer,buffinf.fragsize>buffinf.bytes? + buffinf.bytes:buffinf.fragsize); +#ifdef AFMT_MU_LAW + if (play_precision==AFMT_MU_LAW) + unsignedtoulaw((char *)audiobuffer,done); +#endif + write(sndfd,audiobuffer,done); + } +#else + done=VC_WriteBytes(audiobuffer,buffersize); +#ifdef AFMT_MU_LAW + if (play_precision==AFMT_MU_LAW) + unsignedtoulaw(audiobuffer,done); +#endif + write(sndfd,audiobuffer,done); +#endif +} + +static int OSS_Reset(void) +{ + OSS_Exit_internal(); + ioctl(sndfd,SNDCTL_DSP_RESET,0); + return OSS_Init_internal(); +} + +MIKMODAPI MDRIVER drv_oss={ + NULL, + "Open Sound System", + "Open Sound System driver v1.7", + 0,255, + "oss", +#ifdef SNDCTL_DSP_SETFRAGMENT + "buffer:r:7,17,14:Audio buffer log2 size\n" + "count:r:2,255,16:Audio buffer count\n" +#endif + "card:r:0,99,0:Sound card id\n", + OSS_CommandLine, + OSS_IsThere, + VC_SampleLoad, + VC_SampleUnload, + VC_SampleSpace, + VC_SampleLength, + OSS_Init, + OSS_Exit, + OSS_Reset, + VC_SetNumVoices, + VC_PlayStart, + OSS_PlayStop, + OSS_Update, + NULL, + VC_VoiceSetVolume, + VC_VoiceGetVolume, + VC_VoiceSetFrequency, + VC_VoiceGetFrequency, + VC_VoiceSetPanning, + VC_VoiceGetPanning, + VC_VoicePlay, + VC_VoiceStop, + VC_VoiceStopped, + VC_VoiceGetPosition, + VC_VoiceRealVolume +}; + +#else + +MISSING(drv_oss); + +#endif + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/drv_sb.c b/libs/mikmod/drivers/drv_sb.c new file mode 100644 index 0000000..a0c65c6 --- /dev/null +++ b/libs/mikmod/drivers/drv_sb.c @@ -0,0 +1,235 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Driver for SoundBlaster/Pro/16/AWE32 under DOS + +==============================================================================*/ + +/* + + Written by Andrew Zabolotny + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef DRV_SB + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif + +#include "mikmod_internals.h" + +#include "dossb.h" + +static void SB_CommandLine(const CHAR *cmdline) +{ + char *ptr, *end; + + if ((ptr=MD_GetAtom("port",cmdline,0)) != NULL) { + sb.port = strtol(ptr, &end, 16); + MikMod_free(ptr); + } + if ((ptr=MD_GetAtom("irq",cmdline,0)) != NULL) { + sb.irq = strtol(ptr, &end, 10); + MikMod_free(ptr); + } + if ((ptr=MD_GetAtom("dma",cmdline,0)) != NULL) { + sb.dma8 = strtol(ptr, &end, 10); + MikMod_free(ptr); + } + if ((ptr=MD_GetAtom("hidma",cmdline,0)) != NULL) { + sb.dma16 = strtol(ptr, &end, 10); + MikMod_free(ptr); + } +} + +static BOOL SB_IsThere(void) +{ + return sb_detect(); +} + +static int SB_Init(void) +{ + if (!sb_open()) { + _mm_errno = MMERR_INVALID_DEVICE; + return 1; + } + + /* Adjust md_mode according to sound card capabilities */ + if (!(sb.caps & SBMODE_STEREO)) + md_mode &= ~DMODE_STEREO; + if (!(sb.caps & SBMODE_16BITS)) + md_mode &= ~DMODE_16BITS; + + if (md_mixfreq < 4000) + md_mixfreq = 4000; + if (md_mode & DMODE_STEREO) { + if (md_mixfreq > sb.maxfreq_stereo) + md_mixfreq = sb.maxfreq_stereo; + } else { + if (md_mixfreq > sb.maxfreq_mono) + md_mixfreq = sb.maxfreq_mono; + } + + return VC_Init(); +} + +static void SB_Exit(void) +{ + VC_Exit(); + sb_close(); +} + +/* The last buffer byte filled with sound */ +static unsigned int buff_tail = 0; + +static void SB_Callback(void) +{ + unsigned int dma_size, dma_pos; + ULONG (*mixer)(SBYTE *buf, ULONG todo); + + sb_query_dma(&dma_size, &dma_pos); + /* There isn't much sense in filling less than 256 bytes */ + dma_pos &= ~255; + + /* If nothing to mix, quit */ + if (buff_tail == dma_pos) + return; + + if (Player_Paused_internal()) + mixer = VC_SilenceBytes; + else + mixer = VC_WriteBytes; + + /* If DMA pointer still didn't wrapped around ... */ + if (dma_pos > buff_tail) { + buff_tail += mixer ((SBYTE *)(sb.dma_buff->linear + buff_tail), dma_pos - buff_tail); + /* If we arrived right to the DMA buffer end, jump to the beginning */ + if (buff_tail >= dma_size) + buff_tail = 0; + } else { + /* If wrapped around, fill first to the end of buffer */ + mixer ((SBYTE *)(sb.dma_buff->linear + buff_tail), dma_size - buff_tail); + /* Now fill from buffer beginning to current DMA pointer */ + buff_tail = mixer ((SBYTE *)sb.dma_buff->linear, dma_pos); + } +} + +static void SB_Update(void) +{ + /* Do nothing: the real update is done during SB interrupts */ +} + +static int SB_PlayStart (void) +{ + if (VC_PlayStart()) + return 1; + + /* Enable speaker output */ + sb_output(TRUE); + + /* Set our routine to be called during SB IRQs */ + buff_tail = 0; + sb.timer_callback = SB_Callback; + + /* Start cyclic DMA transfer */ + if (!sb_start_dma(((md_mode & DMODE_16BITS) ? SBMODE_16BITS | SBMODE_SIGNED : 0) | + ((md_mode & DMODE_STEREO) ? SBMODE_STEREO : 0), md_mixfreq)) + { + _mm_errno = MMERR_DOSSB_STARTDMA; + return 1; + } + + return 0; +} + +static int SB_Reset(void) +{ + sb_reset(); + VC_Exit(); + return VC_Init(); +} + +static void SB_PlayStop(void) +{ + sb.timer_callback = NULL; + sb_output(FALSE); + sb_stop_dma(); + VC_PlayStop(); +} + +MDRIVER drv_sb = +{ + NULL, + "Sound Blaster", + "Sound Blaster Orig/2.0/Pro/16 v1.0", + 0, 255, + "sb", + "port:c:220,230,240,250,260,270,280,32C,530,604,E80,F40,220:Sound Blaster base I/O port\n" + "irq:c:2,3,5,7,10,5:Sound Blaster IRQ\n" + "dma:c:0,1,3,1:Sound Blaster 8 bit DMA channel\n" + "hidma:c:5,6,7,5:Sound Blaster 16 bit DMA channel (SB16/AWE32 only)\n", + + SB_CommandLine, + SB_IsThere, + VC_SampleLoad, + VC_SampleUnload, + VC_SampleSpace, + VC_SampleLength, + SB_Init, + SB_Exit, + SB_Reset, + VC_SetNumVoices, + SB_PlayStart, + SB_PlayStop, + SB_Update, + NULL, + VC_VoiceSetVolume, + VC_VoiceGetVolume, + VC_VoiceSetFrequency, + VC_VoiceGetFrequency, + VC_VoiceSetPanning, + VC_VoiceGetPanning, + VC_VoicePlay, + VC_VoiceStop, + VC_VoiceStopped, + VC_VoiceGetPosition, + VC_VoiceRealVolume +}; + +#else /* DRV_SB */ + +#include "mikmod_internals.h" +MISSING(drv_sb); + +#endif + +/* ex:set ts=4: */ diff --git a/libs/mikmod/drivers/drv_sdl.c b/libs/mikmod/drivers/drv_sdl.c new file mode 100644 index 0000000..e5a1ba2 --- /dev/null +++ b/libs/mikmod/drivers/drv_sdl.c @@ -0,0 +1,205 @@ +/* MikMod sound library + (c) 1998-2005 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. + */ + +/*============================================================================== + + libmikmod driver for audio output on SDL-supported platforms. + Initially written by Paul Spark + Rewrite/major fixes by O. Sezer + + ==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mikmod_internals.h" + +#ifdef DRV_SDL + +#include +#if defined(SDL_FRAMEWORK) || defined(NO_SDL_CONFIG) +#include +#else +#include "SDL.h" +#endif + +#define NUMSAMPLES 256 /* a fair default for md_mixfreq <= 11025 Hz */ +static SDL_AudioSpec spec; +static int enabled = 0; + +static void SDLSoundCallback(void *userdata, Uint8 *stream, int len) +{ + if (!enabled) return; + if (enabled < 0) { + if (++enabled == 0) + enabled = 1; + return; + } + + MUTEX_LOCK(vars); + if (Player_Paused_internal()) { + VC_SilenceBytes((SBYTE *) stream, (ULONG)len); + } + else + { + int got = (int) VC_WriteBytes((SBYTE *) stream, (ULONG)len); + if (got < len) { /* fill the rest with silence, then */ + VC_SilenceBytes((SBYTE *) &stream[got], (ULONG)(len-got)); + } + } + MUTEX_UNLOCK(vars); +} + +static BOOL SetupSDLAudio(void) +{ + SDL_AudioSpec wanted; + + wanted.freq = md_mixfreq; + wanted.format = +#if (SDL_MAJOR_VERSION >= 2) + (md_mode & DMODE_FLOAT) ? AUDIO_F32SYS : +#endif + (md_mode & DMODE_16BITS)? AUDIO_S16SYS : AUDIO_U8; + wanted.channels = (md_mode & DMODE_STEREO)? 2 : 1; + wanted.samples = (md_mixfreq <= 11025)? (NUMSAMPLES ) : + (md_mixfreq <= 22050)? (NUMSAMPLES * 2) : + (md_mixfreq <= 44100)? (NUMSAMPLES * 4) : + (NUMSAMPLES * 8); + wanted.userdata = NULL; + wanted.callback = SDLSoundCallback; + + if (SDL_OpenAudio(&wanted, &spec) < 0) { + _mm_errno=MMERR_OPENING_AUDIO; + return 0; + } + + return 1; +} + +static BOOL SDLDrv_IsPresent(void) +{ + if ((SDL_WasInit(SDL_INIT_AUDIO)) == 0) { + if (SDL_Init(SDL_INIT_AUDIO) < 0) + return 0; + SDL_QuitSubSystem(SDL_INIT_AUDIO); + } + return 1; +} + +static int SDLDrv_Init(void) +{ +#if (SDL_MAJOR_VERSION < 2) + if (md_mode & DMODE_FLOAT) { + _mm_errno=MMERR_NO_FLOAT32; + return 1; + } +#endif + if (SDL_Init(SDL_INIT_AUDIO) < 0) { + _mm_errno=MMERR_OPENING_AUDIO; + return 1; + } + if (!SetupSDLAudio()) { + return 1; + } + + md_mode |= DMODE_SOFT_MUSIC; + if (VC_Init()) + return 1; + + enabled = -2; /* delay the callback 2 frames */ + return 0; +} + +static void SDLDrv_Exit(void) +{ + SDL_LockAudio(); + enabled = 0; + SDL_UnlockAudio(); + SDL_CloseAudio(); + VC_Exit(); +} + +static int SDLDrv_Reset(void) +{ + SDLDrv_Exit(); + return SDLDrv_Init(); +} + +static void SDLDrv_Update( void ) +{ +/* do nothing */ +} + +static void SDLDrv_PlayStop(void) +{ + SDL_PauseAudio(1); + VC_PlayStop(); +} + +static int SDLDrv_PlayStart(void) +{ + if (VC_PlayStart()) + return 1; + SDL_PauseAudio(0); + return 0; +} + +MIKMODAPI struct MDRIVER drv_sdl = +{ + NULL, + "SDL", + "SDL Driver v1.3", + 0,255, + "sdl", + NULL, + NULL, + SDLDrv_IsPresent, + VC_SampleLoad, + VC_SampleUnload, + VC_SampleSpace, + VC_SampleLength, + SDLDrv_Init, + SDLDrv_Exit, + SDLDrv_Reset, + VC_SetNumVoices, + SDLDrv_PlayStart, + SDLDrv_PlayStop, + SDLDrv_Update, + NULL, + VC_VoiceSetVolume, + VC_VoiceGetVolume, + VC_VoiceSetFrequency, + VC_VoiceGetFrequency, + VC_VoiceSetPanning, + VC_VoiceGetPanning, + VC_VoicePlay, + VC_VoiceStop, + VC_VoiceStopped, + VC_VoiceGetPosition, + VC_VoiceRealVolume +}; + +#else + +MISSING(drv_sdl); + +#endif + diff --git a/libs/mikmod/drivers/drv_ultra.c b/libs/mikmod/drivers/drv_ultra.c new file mode 100644 index 0000000..dbd0b7c --- /dev/null +++ b/libs/mikmod/drivers/drv_ultra.c @@ -0,0 +1,1020 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Driver for Gravis Ultrasound cards using libGUS. + A subset of libGUS is provided for DOS/DJGPP and OS/2 + +==============================================================================*/ + +/* + + Written by Andy Lo A Foe + + Updated to work with later versions of both the ultrasound driver and + libmikmod by C. Ray C. + + Major fixes by Andrew Zabolotny + + Ported to OS/2 and DOS. + + Eight-bit samples are not converted to 16-bit anymore. + + Samples are no longer kept in normal memory. + + Removed sample 'unclick' logic... libGUS does unclick internally. + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mikmod_internals.h" + +#ifdef DRV_ULTRA + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_MEMORY_H +#include +#endif + +#ifdef MIKMOD_DYNAMIC +#include +#endif +#include +#include +#include + +#include + +#if !defined(GUS_INSTR_SIMPLE) || !defined(GUS_WAVE_BIDIR) +#error libgus version is too old +#endif +/* just in case */ +#ifndef LIBGUS_VERSION_MAJOR +#define LIBGUS_VERSION_MAJOR 0x0003 +#endif + +/* DOS/DJGPP and OS/2 libGUS'es have gus_get_voice_status() */ +#if defined(__EMX__) || defined(__DJGPP__) +#define HAVE_VOICE_STATUS +#else +#include +#endif + + +#ifdef MIKMOD_DYNAMIC +/* runtime link with libgus */ +static int (*_libgus_cards) (void); + +#if LIBGUS_VERSION_MAJOR < 0x0004 +static int (*_libgus_close) (int); +static int (*_libgus_do_flush) (void); +static void (*_libgus_do_tempo) (unsigned int); +static void (*_libgus_do_voice_frequency) (unsigned char, unsigned int); +static void (*_libgus_do_voice_pan) (unsigned char, unsigned short); +static void (*_libgus_do_voice_start) (unsigned char, unsigned int, + unsigned int, unsigned short, + unsigned short); +static void (*_libgus_do_voice_start_position) (unsigned char, unsigned int, + unsigned int, unsigned short, + unsigned short, unsigned int); +static void (*_libgus_do_voice_stop) (unsigned char, unsigned char); +static void (*_libgus_do_voice_volume) (unsigned char, unsigned short); +static void (*_libgus_do_wait) (unsigned int); +static int (*_libgus_get_handle) (void); +static int (*_libgus_info) (gus_info_t *, int); +static int (*_libgus_memory_alloc) (gus_instrument_t *); +static int (*_libgus_memory_free) (gus_instrument_t *); +static int (*_libgus_memory_free_size) (void); +static int (*_libgus_memory_pack) (void); +static int (*_libgus_open) (int, size_t, int); +static int (*_libgus_queue_flush) (void); +static int (*_libgus_queue_read_set_size) (int); +static int (*_libgus_queue_write_set_size) (int); +static int (*_libgus_reset) (int, unsigned int); +static int (*_libgus_select) (int); +static int (*_libgus_timer_start) (void); +static int (*_libgus_timer_stop) (void); +static int (*_libgus_timer_tempo) (int); +#else +static int (*_libgus_close) (void*); +static int (*_libgus_do_flush) (void*); +static void (*_libgus_do_tempo) (void*, unsigned int); +static void (*_libgus_do_voice_frequency) (void*, unsigned char, unsigned int); +static void (*_libgus_do_voice_pan) (void*, unsigned char,unsigned short); +static void (*_libgus_do_voice_start) (void*, unsigned char, unsigned int, + unsigned int, unsigned short, + unsigned short); +static void (*_libgus_do_voice_start_position) (void*, unsigned char, unsigned int, + unsigned int,unsigned short, + unsigned short, unsigned int); +static void (*_libgus_do_voice_stop) (void*, unsigned char, unsigned char); +static void (*_libgus_do_voice_volume) (void*, unsigned char, unsigned short); +static void (*_libgus_do_wait) (void*, unsigned int); +static int (*_libgus_get_file_descriptor) (void*); +static int (*_libgus_info) (void*, gus_info_t*, int); +static int (*_libgus_memory_alloc) (void*, gus_instrument_t*); +static int (*_libgus_memory_free) (void*, gus_instrument_t*); +static int (*_libgus_memory_free_size) (void*); +static int (*_libgus_memory_pack) (void*); +static int (*_libgus_open) (void**, int, int, size_t, int); +static int (*_libgus_queue_flush) (void*); +static int (*_libgus_queue_read_set_size) (void*, int); +static int (*_libgus_queue_write_set_size) (void*, int); +static int (*_libgus_reset) (void*, int, unsigned int); +static int (*_libgus_timer_start)(void*); +static int (*_libgus_timer_stop) (void*); +static int (*_libgus_timer_tempo) (void*, int); +#endif +#ifndef HAVE_RTLD_GLOBAL +#define RTLD_GLOBAL (0) +#endif +static void *libgus = NULL; + +#else +/* compile-time link with libgus */ +#define _libgus_cards gus_cards +#define _libgus_close gus_close +#define _libgus_do_flush gus_do_flush +#define _libgus_do_tempo gus_do_tempo +#define _libgus_do_voice_frequency gus_do_voice_frequency +#define _libgus_do_voice_pan gus_do_voice_pan +#define _libgus_do_voice_start gus_do_voice_start +#define _libgus_do_voice_start_position gus_do_voice_start_position +#define _libgus_do_voice_stop gus_do_voice_stop +#define _libgus_do_voice_volume gus_do_voice_volume +#define _libgus_do_wait gus_do_wait +#if LIBGUS_VERSION_MAJOR < 0x0004 +#define _libgus_get_handle gus_get_handle +#else +#define _libgus_get_file_descriptor gus_get_file_descriptor +#endif +#define _libgus_info gus_info +#define _libgus_memory_alloc gus_memory_alloc +#define _libgus_memory_free gus_memory_free +#define _libgus_memory_free_size gus_memory_free_size +#define _libgus_memory_pack gus_memory_pack +#define _libgus_open gus_open +#define _libgus_queue_flush gus_queue_flush +#define _libgus_queue_read_set_size gus_queue_read_set_size +#define _libgus_queue_write_set_size gus_queue_write_set_size +#define _libgus_reset gus_reset +#if LIBGUS_VERSION_MAJOR < 0x0004 +#define _libgus_select gus_select +#endif +#define _libgus_timer_start gus_timer_start +#define _libgus_timer_stop gus_timer_stop +#define _libgus_timer_tempo gus_timer_tempo +#endif + +#define libgus_cards _libgus_cards /* same between v3 and v4 */ +#define libgus_open _libgus_open /* different between v3 and v4: must use #ifdef */ +#define libgus_close _libgus_close /* different between v3 and v4: must use #ifdef */ +/* the following can be handled easily by macros: v4 only adds them the handle as the first param */ +#if LIBGUS_VERSION_MAJOR < 0x0004 +#define libgus_get_handle _libgus_get_handle /* only in v3 */ +#define libgus_do_flush _libgus_do_flush +#define libgus_do_tempo _libgus_do_tempo +#define libgus_do_voice_frequency _libgus_do_voice_frequency +#define libgus_do_voice_pan _libgus_do_voice_pan +#define libgus_do_voice_start _libgus_do_voice_start +#define libgus_do_voice_start_position _libgus_do_voice_start_position +#define libgus_do_voice_stop _libgus_do_voice_stop +#define libgus_do_voice_volume _libgus_do_voice_volume +#define libgus_do_wait _libgus_do_wait +#define libgus_info _libgus_info +#define libgus_memory_alloc _libgus_memory_alloc +#define libgus_memory_free _libgus_memory_free +#define libgus_memory_free_size _libgus_memory_free_size +#define libgus_memory_pack _libgus_memory_pack +#define libgus_queue_flush _libgus_queue_flush +#define libgus_queue_read_set_size _libgus_queue_read_set_size +#define libgus_queue_write_set_size _libgus_queue_write_set_size +#define libgus_reset _libgus_reset +#define libgus_select _libgus_select +#define libgus_timer_start _libgus_timer_start +#define libgus_timer_stop _libgus_timer_stop +#define libgus_timer_tempo _libgus_timer_tempo +#else +#define libgus_get_file_descriptor _libgus_get_file_descriptor /* only in v4 */ +#define libgus_do_flush() _libgus_do_flush(ultra_h) +#define libgus_do_tempo(t) _libgus_do_tempo(ultra_h,t) +#define libgus_do_voice_frequency(a,b) _libgus_do_voice_frequency(ultra_h,a,b) +#define libgus_do_voice_pan(a,b) _libgus_do_voice_pan(ultra_h,a,b) +#define libgus_do_voice_start(a,b,c,d,e) _libgus_do_voice_start(ultra_h,a,b,c,d,e) +#define libgus_do_voice_start_position(a,b,c,d,e,f) _libgus_do_voice_start_position(ultra_h,a,b,c,d,e,f) +#define libgus_do_voice_stop(a,b) _libgus_do_voice_stop(ultra_h,a,b) +#define libgus_do_voice_volume(a,b) _libgus_do_voice_volume(ultra_h,a,b) +#define libgus_do_wait(a) _libgus_do_wait(ultra_h,a) +#define libgus_info(a,b) _libgus_info(ultra_h,a,b) +#define libgus_memory_alloc(a) _libgus_memory_alloc(ultra_h,a) +#define libgus_memory_free(a) _libgus_memory_free(ultra_h,a) +#define libgus_memory_free_size() _libgus_memory_free_size(ultra_h) +#define libgus_memory_pack() _libgus_memory_pack(ultra_h) +#define libgus_queue_flush() _libgus_queue_flush(ultra_h) +#define libgus_queue_read_set_size(a) _libgus_queue_read_set_size(ultra_h,a) +#define libgus_queue_write_set_size(a) _libgus_queue_write_set_size(ultra_h,a) +#define libgus_reset(a,b) _libgus_reset(ultra_h,a,b) +#define libgus_timer_start() _libgus_timer_start(ultra_h) +#define libgus_timer_stop() _libgus_timer_stop(ultra_h) +#define libgus_timer_tempo(a) _libgus_timer_tempo(ultra_h,a) +#endif + +#define GUS_SAMPLES 256 /* Max. GUS samples loadable */ +#define GUS_CHANNELS 32 /* Max. GUS channels available */ +#define SIZE_OF_SEQBUF (8 * 1024) /* Size of the sequence buffer */ +#define ULTRA_PAN_MIDDLE (16383 >> 1) /* Middle balance position */ + +#define CH_FREQ 1 +#define CH_VOL 2 +#define CH_PAN 4 + +/* This structure holds the current state of a GUS voice channel. */ +typedef struct GUS_VOICE { + UBYTE kick; + UBYTE active; + UWORD flags; + SWORD handle; + ULONG start; + ULONG size; + ULONG reppos; + ULONG repend; + ULONG frq; + int vol; + int decvol; + int pan; + + int changes; +#ifndef HAVE_VOICE_STATUS + time_t started; +#endif +} GUS_VOICE; + +/* Global declarations follow */ + +static SAMPLE *samples[GUS_SAMPLES]; /* sample handles */ +static GUS_VOICE voices[GUS_CHANNELS]; /* channel status */ + +static int ultra_dev = 0; /* GUS index, if more than one card */ +#if LIBGUS_VERSION_MAJOR < 0x0004 +static int ultra_card = -1; /* returned by gus_open(ultra_dev,,) - must be same as ultra_dev */ +#else +static void* ultra_h = NULL; /* GUS handle */ +#endif +static int ultra_fd = -1; /* GUS file descriptor */ + + +#ifdef MIKMOD_DYNAMIC +static int Ultra_Link(void) +{ + if (libgus) + return 0; + + /* load libgus.so */ +#if LIBGUS_VERSION_MAJOR < 0x0004 + libgus = dlopen("libgus.so.3", RTLD_LAZY | RTLD_GLOBAL); +#else + libgus = dlopen("libgus.so.4", RTLD_LAZY | RTLD_GLOBAL); +#endif + if (!libgus) /* then this won't succeed either, but whatever.. */ + libgus = dlopen("libgus.so", RTLD_LAZY | RTLD_GLOBAL); + if (!libgus) + return 1; + + /* resolve function references */ +#define IMPORT_SYMBOL(x,ret,params) \ + if (!(_lib##x = (ret (*)params) dlsym(libgus, #x))) return 1 + + IMPORT_SYMBOL(gus_cards, int, (void)); +#if LIBGUS_VERSION_MAJOR < 0x0004 + IMPORT_SYMBOL(gus_close, int, (int)); + IMPORT_SYMBOL(gus_do_flush, int, (void)); + IMPORT_SYMBOL(gus_do_tempo, void, (unsigned int)); + IMPORT_SYMBOL(gus_do_voice_frequency, void, (unsigned char, unsigned int)); + IMPORT_SYMBOL(gus_do_voice_pan, void, (unsigned char, unsigned short)); + IMPORT_SYMBOL(gus_do_voice_start, void, (unsigned char, unsigned int, unsigned int, unsigned short, unsigned short)); + IMPORT_SYMBOL(gus_do_voice_start_position, void, (unsigned char, unsigned int, unsigned int, unsigned short, unsigned short, unsigned int)); + IMPORT_SYMBOL(gus_do_voice_stop, void, (unsigned char, unsigned char)); + IMPORT_SYMBOL(gus_do_voice_volume, void, (unsigned char, unsigned short)); + IMPORT_SYMBOL(gus_do_wait, void, (unsigned int)); + IMPORT_SYMBOL(gus_get_handle, int, (void)); + IMPORT_SYMBOL(gus_info, int, (gus_info_t *, int)); + IMPORT_SYMBOL(gus_memory_alloc, int, (gus_instrument_t *)); + IMPORT_SYMBOL(gus_memory_free, int, (gus_instrument_t *)); + IMPORT_SYMBOL(gus_memory_free_size, int, (void)); + IMPORT_SYMBOL(gus_memory_pack, int, (void)); + IMPORT_SYMBOL(gus_open, int, (int, size_t, int)); + IMPORT_SYMBOL(gus_queue_flush, int, (void)); + IMPORT_SYMBOL(gus_queue_read_set_size, int, (int)); + IMPORT_SYMBOL(gus_queue_write_set_size, int, (int)); + IMPORT_SYMBOL(gus_reset, int, (int, unsigned int)); + IMPORT_SYMBOL(gus_select, int, (int)); + IMPORT_SYMBOL(gus_timer_start, int, (void)); + IMPORT_SYMBOL(gus_timer_stop, int, (void)); + IMPORT_SYMBOL(gus_timer_tempo, int, (int)); +#else + IMPORT_SYMBOL(gus_close, int, (void*)); + IMPORT_SYMBOL(gus_do_flush, int, (void*)); + IMPORT_SYMBOL(gus_do_tempo, void, (void*, unsigned int)); + IMPORT_SYMBOL(gus_do_voice_frequency, void, (void*, unsigned char, unsigned int)); + IMPORT_SYMBOL(gus_do_voice_pan, void, (void*, unsigned char, unsigned short)); + IMPORT_SYMBOL(gus_do_voice_start, void, (void*, unsigned char, unsigned int, unsigned int, unsigned short, unsigned short)); + IMPORT_SYMBOL(gus_do_voice_start_position, void, (void*, unsigned char, unsigned int, unsigned int, unsigned short, unsigned short, unsigned int)); + IMPORT_SYMBOL(gus_do_voice_stop, void, (void*, unsigned char, unsigned char)); + IMPORT_SYMBOL(gus_do_voice_volume, void, (void*, unsigned char, unsigned short)); + IMPORT_SYMBOL(gus_do_wait, void, (void*, unsigned int)); + IMPORT_SYMBOL(gus_get_file_descriptor, int, (void*)); + IMPORT_SYMBOL(gus_info, int, (void*, gus_info_t *, int)); + IMPORT_SYMBOL(gus_memory_alloc, int, (void*, gus_instrument_t *)); + IMPORT_SYMBOL(gus_memory_free, int, (void*, gus_instrument_t *)); + IMPORT_SYMBOL(gus_memory_free_size, int, (void*)); + IMPORT_SYMBOL(gus_memory_pack, int, (void*)); + IMPORT_SYMBOL(gus_open, int, (void**, int, int, size_t, int)); + IMPORT_SYMBOL(gus_queue_flush, int, (void*)); + IMPORT_SYMBOL(gus_queue_read_set_size, int, (void*, int)); + IMPORT_SYMBOL(gus_queue_write_set_size, int, (void*, int)); + IMPORT_SYMBOL(gus_reset, int, (void*, int, unsigned int)); + IMPORT_SYMBOL(gus_timer_start, int, (void*)); + IMPORT_SYMBOL(gus_timer_stop, int, (void*)); + IMPORT_SYMBOL(gus_timer_tempo, int, (void*, int)); +#endif +#undef IMPORT_SYMBOL + + return 0; +} + +static void Ultra_Unlink(void) +{ + _libgus_cards = NULL; + _libgus_close = NULL; + _libgus_do_flush = NULL; + _libgus_do_tempo = NULL; + _libgus_do_voice_frequency = NULL; + _libgus_do_voice_pan = NULL; + _libgus_do_voice_start = NULL; + _libgus_do_voice_start_position = NULL; + _libgus_do_voice_stop = NULL; + _libgus_do_voice_volume = NULL; + _libgus_do_wait = NULL; +#if LIBGUS_VERSION_MAJOR < 0x0004 + _libgus_get_handle = NULL; +#else + _libgus_get_file_descriptor = NULL; +#endif + _libgus_info = NULL; + _libgus_memory_alloc = NULL; + _libgus_memory_free = NULL; + _libgus_memory_free_size = NULL; + _libgus_memory_pack = NULL; + _libgus_open = NULL; + _libgus_queue_flush = NULL; + _libgus_queue_read_set_size = NULL; + _libgus_queue_write_set_size = NULL; + _libgus_reset = NULL; +#if LIBGUS_VERSION_MAJOR < 0x0004 + _libgus_select = NULL; +#endif + _libgus_timer_start = NULL; + _libgus_timer_stop = NULL; + _libgus_timer_tempo = NULL; + + if (libgus) { + dlclose(libgus); + libgus = NULL; + } +} +#endif + +static void Ultra_CommandLine(const CHAR *cmdline) +{ + CHAR *ptr = MD_GetAtom("card", cmdline, 0); + + if (ptr) { + int buf = atoi(ptr); + + if (buf >= 0 && buf <= 8) + ultra_dev = buf; + MikMod_free(ptr); + } +#ifdef __DJGPP__ + ptr = MD_GetAtom("dma", cmdline, 0); + if (ptr) { + gus_dma_usage (atoi(ptr)); + MikMod_free(ptr); + } +#endif +} + +/* Checks for the presence of GUS cards */ +static BOOL Ultra_IsThere(void) +{ + BOOL retval; + +#ifdef MIKMOD_DYNAMIC + if (Ultra_Link()) + return 0; +#endif + retval = libgus_cards()? 1 : 0; +#ifdef MIKMOD_DYNAMIC + Ultra_Unlink(); +#endif + return retval; +} + +/* Load a new sample directly into GUS DRAM and return a handle */ +static SWORD Ultra_SampleLoad(struct SAMPLOAD *sload, int type) +{ + int handle; + SAMPLE *s = sload->sample; + gus_instrument_t instrument; + gus_layer_t layer; + gus_wave_t wave; + unsigned char *buffer; + unsigned int length, loopstart, loopend; + + /* Find empty slot to put sample in */ + for (handle = 0; handle < GUS_SAMPLES; handle++) + if (!samples[handle]) + break; + + if (handle == GUS_SAMPLES) { + _mm_errno = MMERR_OUT_OF_HANDLES; + return -1; + } + + /* Fill an gus_instrument_t structure and feed it to libgus. We can + download 8 and 16 bit, both signed and unsigned samples into GUS, so + don't bother much about formats here. */ + + /* convert position/length data from samples to bytes */ + length = s->length; + loopstart = s->loopstart; + loopend = s->loopend ? s->loopend : length; + /* sanity checks */ + if (loopend > length) + loopend = length; + if (loopstart > loopend) + loopstart = loopend; + if (s->flags & SF_16BITS) { + length <<= 1; + loopstart <<= 1; + loopend <<= 1; + } + + /* Load sample into normal memory */ + if (!(buffer = (unsigned char *) MikMod_malloc(length))) { + _mm_errno = MMERR_SAMPLE_TOO_BIG; + return -1; + } + + if (SL_Load(buffer, sload, s->length)) { + MikMod_free(buffer); + return -1; + } + + samples[handle] = s; + + memset(&wave, 0, sizeof(wave)); + memset(&layer, 0, sizeof(layer)); + memset(&instrument, 0, sizeof(instrument)); + + wave.format = + ((s->flags & SF_SIGNED) ? 0 : GUS_WAVE_INVERT) | + ((s->flags & SF_16BITS) ? GUS_WAVE_16BIT : 0) | + ((s->flags & SF_DELTA ) ? GUS_WAVE_DELTA : 0) | + ((s->flags & SF_LOOP ) ? GUS_WAVE_LOOP : 0) | + ((s->flags & SF_BIDI ) ? GUS_WAVE_BIDIR : 0); + wave.begin.ptr = buffer; + wave.loop_start = loopstart << 4; + wave.loop_end = loopend << 4; + wave.size = length; + + layer.wave = &wave; + + instrument.mode = layer.mode = wave.mode = GUS_INSTR_SIMPLE; + instrument.number.instrument = handle; + instrument.info.layer = &layer; + + /* Download the sample to GUS RAM */ + if (libgus_memory_alloc(&instrument)) { + MikMod_free(buffer); + _mm_errno = MMERR_SAMPLE_TOO_BIG; + return -1; + } + + MikMod_free(buffer); + return handle; +} + +/* Discards a sample from the GUS memory and mark handle as free */ +static void Ultra_SampleUnload(SWORD handle) +{ + gus_instrument_t instrument; + + if (handle >= GUS_SAMPLES || handle < 0 || !samples[handle]) + return; + + memset(&instrument, 0, sizeof(instrument)); + instrument.mode = GUS_INSTR_SIMPLE; + instrument.number.instrument = handle; + libgus_memory_free(&instrument); + samples[handle] = NULL; +} + +/* Reports available sample space */ +static ULONG Ultra_SampleSpace(int type) +{ + libgus_memory_pack(); + return (libgus_memory_free_size()); +} + +/* Reports the size of a sample */ +static ULONG Ultra_SampleLength(int type, SAMPLE *s) +{ + if (!s) + return 0; + + if (s->flags & SF_16BITS) + return ((s->length << 1) + 31) & ~31; + else + return ( s->length + 15) & ~15; +} + +/* Initializes the driver */ +static int Ultra_Init_internal(void) +{ + gus_info_t info; + +#if LIBGUS_VERSION_MAJOR < 0x0004 + if ((ultra_card = libgus_open(ultra_dev, SIZE_OF_SEQBUF, 0)) < 0) { + _mm_errno = (errno == ENOMEM)? MMERR_OUT_OF_MEMORY : MMERR_INVALID_DEVICE; + return 1; + } + libgus_select(ultra_card); + ultra_fd = libgus_get_handle(); +#else + if (libgus_open(&ultra_h, ultra_dev, 0, SIZE_OF_SEQBUF, GUS_OPEN_FLAG_NONE) < 0) { + _mm_errno = (errno == ENOMEM)? MMERR_OUT_OF_MEMORY : MMERR_INVALID_DEVICE; + return 1; + } + ultra_fd = libgus_get_file_descriptor(ultra_h); +#endif + + /* We support only 16-bit stereo with 44K mixing frequency. On UltraSound + Classic mixing frequency depends on number of channels, on Interwave it + is always 44KHz. */ + md_mode |= DMODE_16BITS | DMODE_STEREO; + md_mixfreq = info.mixing_freq; + + libgus_info(&info, 0); +#ifdef MIKMOD_DEBUG + switch (info.version) { + case 0x24: + fputs("GUS 2.4", stderr); + break; + case 0x35: + fputs("GUS 3.7 (flipped)", stderr); + break; + case 0x37: + fputs("GUS 3.7", stderr); + break; + case 0x90: + fputs("GUS ACE", stderr); + break; + case 0xa0: + fputs("GUS MAX 10", stderr); + break; + case 0xa1: + fputs("GUS MAX 11", stderr); + break; + case 0x100: + fputs("Interwave/GUS PnP", stderr); + break; + default: + fprintf(stderr, "Unknown GUS type %x", info.version); + break; + } + fprintf(stderr, " with %dKb RAM on board\n", info.memory_size >> 10); +#endif + + /* Zero the voice states and sample handles */ + memset (&voices, 0, sizeof (voices)); + memset (&samples, 0, sizeof (samples)); + + return 0; +} + +static int Ultra_Init(void) +{ +#ifdef MIKMOD_DYNAMIC + if (Ultra_Link()) { + _mm_errno = MMERR_DYNAMIC_LINKING; + return 1; + } +#endif + return Ultra_Init_internal(); +} + +/* Closes the driver */ +static void Ultra_Exit_internal(void) +{ +#if LIBGUS_VERSION_MAJOR < 0x0004 + if (ultra_card >= 0) { + ultra_card = -1; + libgus_close(ultra_dev); + } +#else + if (ultra_h) { + libgus_close(ultra_h); + ultra_h = NULL; + } +#endif + ultra_fd = -1; +} + +static void Ultra_Exit(void) +{ + Ultra_Exit_internal(); +#ifdef MIKMOD_DYNAMIC + Ultra_Unlink(); +#endif +} + +/* Poor man's reset function */ +static int Ultra_Reset(void) +{ + Ultra_Exit_internal(); + return Ultra_Init_internal(); +} + +static int Ultra_SetNumVoices(void) +{ + return 0; +} + +/* Module player tick function */ +static void UltraPlayer(void) +{ + int channel, panning; + struct GUS_VOICE *voice; + static BOOL ultra_pause = 1; /* paused status */ + + md_player(); + + if (ultra_pause != Player_Paused()) + if ((ultra_pause = Player_Paused())) + for (channel = 0, voice = voices; channel < md_numchn; + channel++, voice++) { + libgus_do_voice_volume (channel, 0); + voices->changes |= (CH_VOL | CH_FREQ | CH_PAN); + } + + if (ultra_pause) + return; + + for (channel = 0, voice = voices; channel < md_numchn; channel++, voice++) { + panning = (voice->pan == PAN_SURROUND) ? + ULTRA_PAN_MIDDLE : (voice->pan << 6); + + if (voice->kick) { + voice->kick = 0; + voice->decvol = voice->vol; + if (voice->start > 0) + libgus_do_voice_start_position(channel, voice->handle, + voice->frq, voice->vol << 6, panning, voice->start << 4); + else + libgus_do_voice_start(channel, voice->handle, voice->frq, + voice->vol << 6, voice->pan << 6); + } else { + if (voice->changes & CH_FREQ) + libgus_do_voice_frequency(channel, voice->frq); + if (voice->changes & CH_VOL) + libgus_do_voice_volume(channel, voice->vol << 6); + if (voice->changes & CH_PAN) + libgus_do_voice_pan(channel, panning); + if (voice->decvol) + voice->decvol -= 4; + } + voice->changes = 0; + } +} + +/* Play sound */ +#if defined(__DJGPP__) || defined(__EMX__) +static void Ultra_Callback(void) +{ + UltraPlayer(); + libgus_do_flush(); +} + +static void Ultra_Update(void) +{ + static UWORD ultra_bpm = 0; /* current GUS tempo */ + + /* All real work is done during GF1 timer 1 interrupt */ + if (ultra_bpm != md_bpm) { + libgus_do_tempo((md_bpm * 50) / 125); + ultra_bpm = md_bpm; + } +} + +#else +static void Ultra_Update(void) +{ + fd_set write_fds; + int need_write; + static UWORD ultra_bpm = 0; /* current GUS tempo */ + + if (ultra_bpm != md_bpm) { + libgus_do_tempo((md_bpm * 50) / 125); + ultra_bpm = md_bpm; + } + + UltraPlayer(); + + do { + need_write = libgus_do_flush(); + + FD_ZERO(&write_fds); + do { + FD_SET(ultra_fd, &write_fds); + + select(ultra_fd + 1, NULL, &write_fds, NULL, NULL); + } while (!FD_ISSET(ultra_fd, &write_fds)); + } while(need_write > 0); + + /* Wait so that all voice commands gets executed */ + libgus_do_wait (1); +} + +#endif + +/* Start playback */ +static int Ultra_PlayStart(void) +{ + int t; + gus_info_t info; + + for (t = 0; t < md_numchn; t++) { + voices[t].flags = 0; + voices[t].handle = 0; + voices[t].size = 0; + voices[t].start = 0; + voices[t].reppos = 0; + voices[t].repend = 0; + voices[t].changes = 0; + voices[t].kick = 0; + voices[t].frq = 10000; + voices[t].vol = 0; + voices[t].pan = ULTRA_PAN_MIDDLE; + voices[t].active = 0; + } + +#if LIBGUS_VERSION_MAJOR < 0x0004 + libgus_select(ultra_card); +#endif + if (libgus_reset(md_numchn, 0) < 0) { + _mm_errno = MMERR_GUS_RESET; + return 1; + } + + /* Query mixing frequency */ + libgus_info(&info, 0); + md_mixfreq = info.mixing_freq; + + libgus_queue_write_set_size(1024); + libgus_queue_read_set_size(128); + + if (libgus_timer_start() < 0) { + _mm_errno = MMERR_GUS_TIMER; + return 1; + } + +#if defined(__DJGPP__) || defined(__EMX__) + gus_timer_callback(Ultra_Callback); +#endif + + libgus_timer_tempo(50); + + for (t = 0; t < md_numchn; t++) { + libgus_do_voice_pan(t, ULTRA_PAN_MIDDLE); + libgus_do_voice_volume(t, 0 << 8); + } + + libgus_do_flush(); + + return 0; +} + +/* Stop playback */ +static void Ultra_PlayStop(void) +{ + int voice; + + libgus_queue_flush(); + libgus_timer_stop(); + libgus_queue_write_set_size(0); + libgus_queue_read_set_size(0); + for(voice = 0; voice < md_numchn; voice++) + libgus_do_voice_stop(voice, 0); + +#if defined(__DJGPP__) || defined(__EMX__) + gus_timer_callback(NULL); +#endif + + libgus_do_flush(); +} + +/* Set the volume for the given voice */ +static void Ultra_VoiceSetVolume(UBYTE voice, UWORD vol) +{ + if (voice < md_numchn) + if (vol != voices[voice].vol) { + voices[voice].decvol = + voices[voice].vol = vol; + voices[voice].changes |= CH_VOL; + } +} + +/* Returns the volume of the given voice */ +static UWORD Ultra_VoiceGetVolume(UBYTE voice) +{ + return (voice < md_numchn) ? voices[voice].vol : 0; +} + +/* Set the pitch for the given voice */ +static void Ultra_VoiceSetFrequency(UBYTE voice, ULONG frq) +{ + if (voice < md_numchn) + if (frq != voices[voice].frq) { + voices[voice].frq = frq; + voices[voice].changes |= CH_FREQ; + } +} + +/* Returns the frequency of the given voice */ +static ULONG Ultra_VoiceGetFrequency(UBYTE voice) +{ + return (voice < md_numchn) ? voices[voice].frq : 0; +} + +/* Set the panning position for the given voice */ +static void Ultra_VoiceSetPanning(UBYTE voice, ULONG pan) +{ + if (voice < md_numchn) + if (pan != voices[voice].pan) { + voices[voice].pan = pan; + voices[voice].changes |= CH_PAN; + } +} + +/* Returns the panning of the given voice */ +static ULONG Ultra_VoiceGetPanning(UBYTE voice) +{ + return (voice < md_numchn) ? voices[voice].pan : 0; +} + +/* Start a new sample on a voice */ +static void Ultra_VoicePlay(UBYTE voice, SWORD handle, ULONG start, + ULONG size, ULONG reppos, ULONG repend, + UWORD flags) +{ + if ((voice >= md_numchn) || (start >= size)) + return; + + if (flags & SF_LOOP) + if (repend > size) + repend = size; + + voices[voice].flags = flags; + voices[voice].handle = handle; + voices[voice].start = start; + voices[voice].size = size; + voices[voice].reppos = reppos; + voices[voice].repend = repend; + voices[voice].kick = 1; + voices[voice].active = 1; +#ifndef HAVE_VOICE_STATUS + voices[voice].started = time(NULL); +#endif +} + +/* Stops a voice */ +static void Ultra_VoiceStop(UBYTE voice) +{ + if (voice < md_numchn) + voices[voice].active = 0; +} + +/* Returns whether a voice is stopped */ +static BOOL Ultra_VoiceStopped(UBYTE voice) +{ + if (voice >= md_numchn) + return 1; + +#ifdef HAVE_VOICE_STATUS + if (voices[voice].active) + return gus_get_voice_status(voice) ? 0 : 1; + else + return 1; +#else + if (voices[voice].active) { + /* is sample looping ? */ + if (voices[voice].flags & (SF_LOOP | SF_BIDI)) + return 0; + else + if (time(NULL) - voices[voice].started >= + ((voices[voice].size - voices[voice].start + md_mixfreq -1) + / md_mixfreq)) { + voices[voice].active = 0; + return 1; + } + return 0; + } else + return 1; +#endif +} + +/* Returns current voice position */ +static SLONG Ultra_VoiceGetPosition(UBYTE voice) +{ + /* NOTE This information can not be determined. */ + return -1; +} + +/* Returns voice real volume */ +static ULONG Ultra_VoiceRealVolume(UBYTE voice) +{ + int retval = 0; + if (!Ultra_VoiceStopped (voice)) { + /* NOTE This information can not be accurately determined. */ + retval = (voices [voice].decvol) << 8; + if (retval > 0xffff) retval = 0xffff; + } + return retval; +} + +MDRIVER drv_ultra = { + NULL, + "Gravis Ultrasound native mode", + "Gravis Ultrasound native mode driver v1.2", + 0, (GUS_CHANNELS)-1, + "ultra", +#ifdef __DJGPP__ + "dma:b:1:Use DMA for transferring samples into GUS DRAM\n" +#endif + "card:r:0,8,0:Soundcard number\n", + + Ultra_CommandLine, + Ultra_IsThere, + Ultra_SampleLoad, + Ultra_SampleUnload, + Ultra_SampleSpace, + Ultra_SampleLength, + Ultra_Init, + Ultra_Exit, + Ultra_Reset, + Ultra_SetNumVoices, + Ultra_PlayStart, + Ultra_PlayStop, + Ultra_Update, + NULL, + Ultra_VoiceSetVolume, + Ultra_VoiceGetVolume, + Ultra_VoiceSetFrequency, + Ultra_VoiceGetFrequency, + Ultra_VoiceSetPanning, + Ultra_VoiceGetPanning, + Ultra_VoicePlay, + Ultra_VoiceStop, + Ultra_VoiceStopped, + Ultra_VoiceGetPosition, + Ultra_VoiceRealVolume +}; +#else + +MISSING(drv_ultra); + +#endif /* DRV_ULTRA */ + +/* ex:set ts=8: */ diff --git a/libs/mikmod/drivers/drv_wss.c b/libs/mikmod/drivers/drv_wss.c new file mode 100644 index 0000000..a89d5ec --- /dev/null +++ b/libs/mikmod/drivers/drv_wss.c @@ -0,0 +1,217 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Driver for Windows Sound System under DOS + +==============================================================================*/ + +/* + + Written by Andrew Zabolotny + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef DRV_WSS + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif + +#include "mikmod_internals.h" + +#include "doswss.h" + +static void WSS_CommandLine(const CHAR *cmdline) +{ + char *ptr, *end; + + if ((ptr=MD_GetAtom("port",cmdline,0)) != NULL) { + wss.port = strtol(ptr, &end, 16); + MikMod_free(ptr); + } + if ((ptr=MD_GetAtom("irq",cmdline,0)) != NULL) { + wss.irq = strtol(ptr, &end, 10); + MikMod_free(ptr); + } + if ((ptr=MD_GetAtom("dma",cmdline,0)) != NULL) { + wss.dma = strtol(ptr, &end, 10); + MikMod_free(ptr); + } +} + +static BOOL WSS_IsThere(void) +{ + return wss_detect(); +} + +static int WSS_Init(void) +{ + if (!wss_open()) { + _mm_errno = MMERR_INVALID_DEVICE; + return 1; + } + + /* Adjust mixing frequency according to card capabilities */ + md_mixfreq = wss_adjust_freq(md_mixfreq); + + return VC_Init(); +} + +static void WSS_Exit(void) +{ + VC_Exit(); + wss_close(); +} + +/* The last buffer byte filled with sound */ +static unsigned int buff_tail = 0; + +static void WSS_Callback(void) +{ + unsigned int dma_size, dma_pos; + ULONG (*mixer)(SBYTE *buf, ULONG todo); + + wss_query_dma(&dma_size, &dma_pos); + /* There isn't much sense in filling less than 256 bytes */ + dma_pos &= ~255; + + /* If nothing to mix, quit */ + if (buff_tail == dma_pos) + return; + + if (Player_Paused_internal()) + mixer = VC_SilenceBytes; + else + mixer = VC_WriteBytes; + + /* If DMA pointer still didn't wrapped around ... */ + if (dma_pos > buff_tail) { + buff_tail += mixer ((SBYTE *)(wss.dma_buff->linear + buff_tail), dma_pos - buff_tail); + /* If we arrived right to the DMA buffer end, jump to the beginning */ + if (buff_tail >= dma_size) + buff_tail = 0; + } else { + /* If wrapped around, fill first to the end of buffer */ + mixer ((SBYTE *)(wss.dma_buff->linear + buff_tail), dma_size - buff_tail); + /* Now fill from buffer beginning to current DMA pointer */ + buff_tail = mixer ((SBYTE *)wss.dma_buff->linear, dma_pos); + } +} + +static void WSS_Update(void) +{ + /* Do nothing: the real update is done during SB interrupts */ +} + +static int WSS_PlayStart(void) +{ + if (VC_PlayStart()) + return 1; + + /* Set our routine to be called during WSS IRQs */ + buff_tail = 0; + wss.timer_callback = WSS_Callback; + + /* Start cyclic DMA transfer */ + if (!wss_start_dma(((md_mode & DMODE_16BITS) ? WSSMODE_16BITS | WSSMODE_SIGNED : 0) | + ((md_mode & DMODE_STEREO) ? WSSMODE_STEREO : 0), md_mixfreq)) + { + _mm_errno = MMERR_DOSWSS_STARTDMA; + return 1; + } + + /* Enable speaker output */ + wss_output(TRUE); + + return 0; +} + +static int WSS_Reset(void) +{ + wss_reset(); + VC_Exit(); + return VC_Init(); +} + +static void WSS_PlayStop(void) +{ + wss.timer_callback = NULL; + wss_output(FALSE); + wss_stop_dma(); + VC_PlayStop(); +} + +MDRIVER drv_wss = +{ + NULL, + "Windows Sound System", + "Windows Sound System (CS423*,ESS*) v1.0", + 0, 255, + "wss", + "port:c:32C,530,604,E80,F40,530:Windows Sound System base I/O port\n" + "irq:c:2,3,5,7,10,5:Windows Sound System IRQ\n" + "dma:c:0,1,3,0:Windows Sound System DMA channel\n", + + WSS_CommandLine, + WSS_IsThere, + VC_SampleLoad, + VC_SampleUnload, + VC_SampleSpace, + VC_SampleLength, + WSS_Init, + WSS_Exit, + WSS_Reset, + VC_SetNumVoices, + WSS_PlayStart, + WSS_PlayStop, + WSS_Update, + NULL, + VC_VoiceSetVolume, + VC_VoiceGetVolume, + VC_VoiceSetFrequency, + VC_VoiceGetFrequency, + VC_VoiceSetPanning, + VC_VoiceGetPanning, + VC_VoicePlay, + VC_VoiceStop, + VC_VoiceStopped, + VC_VoiceGetPosition, + VC_VoiceRealVolume +}; + +#else /* ifdef DRV_WSS */ + +#include "mikmod_internals.h" +MISSING(drv_wss); + +#endif + +/* ex:set ts=4: */ diff --git a/libs/mikmod/include/mikmod.h b/libs/mikmod/include/mikmod.h new file mode 100644 index 0000000..6bfe938 --- /dev/null +++ b/libs/mikmod/include/mikmod.h @@ -0,0 +1,879 @@ +/* MikMod sound library + (c) 1998-2014 Miodrag Vallat and others - see the AUTHORS file + for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + MikMod sound library include file + + ==============================================================================*/ + +#ifndef _MIKMOD_H_ +#define _MIKMOD_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ========== Compiler magic for shared libraries + * + * ========== NOTE TO WINDOWS DEVELOPERS: + * If you are compiling for Windows and will link to the static library + * (libmikmod.a with MinGW, or mikmod_static.lib with MSVC or LCC, etc), + * you must define MIKMOD_STATIC in your project. Otherwise, dllimport + * will be assumed. + */ +#if defined(_WIN32) || defined(__CYGWIN__) +# if defined(MIKMOD_BUILD) && defined(DLL_EXPORT) /* building libmikmod as a dll for windows */ +# define MIKMODAPI __declspec(dllexport) +# elif defined(MIKMOD_BUILD) || defined(MIKMOD_STATIC) /* building or using static libmikmod for windows */ +# define MIKMODAPI +# else +# define MIKMODAPI __declspec(dllimport) /* using libmikmod dll for windows */ +# endif +#elif defined(__OS2__) && defined(__WATCOMC__) +# if defined(MIKMOD_BUILD) && defined(__SW_BD) /* building libmikmod as a dll for os/2 */ +# define MIKMODAPI __declspec(dllexport) +# else +# define MIKMODAPI /* using dll or static libmikmod for os/2 */ +# endif +/* SYM_VISIBILITY should be defined if both the compiler + * and the target support the visibility attributes. the + * configury does that automatically. for the standalone + * makefiles, etc, the developer should add the required + * flags, i.e.: -DSYM_VISIBILITY -fvisibility=hidden */ +#elif defined(MIKMOD_BUILD) && defined(SYM_VISIBILITY) +# define MIKMODAPI __attribute__((visibility("default"))) +#else +# define MIKMODAPI +#endif + +/* + * ========== Library version + */ + +#define LIBMIKMOD_VERSION_MAJOR 3L +#define LIBMIKMOD_VERSION_MINOR 3L +#define LIBMIKMOD_REVISION 10L + +#define LIBMIKMOD_VERSION \ + ((LIBMIKMOD_VERSION_MAJOR<<16)| \ + (LIBMIKMOD_VERSION_MINOR<< 8)| \ + (LIBMIKMOD_REVISION)) + +MIKMODAPI extern long MikMod_GetVersion(void); + +/* + * ========== Dependency platform headers + */ + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#include +#define _MIKMOD_WIN32 +#endif + +#if defined(__DJGPP__) || defined(MSDOS) || defined(__MSDOS__) || defined(__DOS__) +#define _MIKMOD_DOS +#endif + +#if defined(__OS2__) || defined(__EMX__) +#define INCL_DOSSEMAPHORES +#include +#include +#define _MIKMOD_OS2 +#endif + +#if defined(__MORPHOS__) || defined(__AROS__) || defined(_AMIGA) || defined(__AMIGA__) || defined(__amigaos__) || defined(AMIGAOS) +#include +#define _MIKMOD_AMIGA +#endif + +/* + * ========== Platform independent-type definitions + * (pain when it comes to cross-platform maintenance..) + */ + +#if !(defined(_MIKMOD_OS2) || defined(_MIKMOD_WIN32)) +typedef char CHAR; +#endif + +/* BOOL: 0=false, <>0 true -- 16 bits on Amiga, int-wide on others. */ +#if !(defined(_MIKMOD_OS2) || defined(_MIKMOD_WIN32) || defined(_MIKMOD_AMIGA)) +typedef int BOOL; +#endif + +/* 1 byte, signed and unsigned: */ +typedef signed char SBYTE; +#ifndef _MIKMOD_AMIGA +typedef unsigned char UBYTE; +#endif + +/* 2 bytes, signed and unsigned: */ +#ifndef __LCC__ +typedef signed short int SWORD; +#endif +#if !(defined(__LCC__) || defined(_MIKMOD_AMIGA)) +typedef unsigned short int UWORD; +#endif + +/* 4 bytes, signed and unsigned: */ +#if defined(_LP64) || defined(__LP64__) || defined(__arch64__) || defined(__alpha) || defined(__x86_64) || defined(__powerpc64__) + /* 64 bit architectures: */ +typedef signed int SLONG; +#if !(defined(_WIN32) || defined(_MIKMOD_AMIGA)) +typedef unsigned int ULONG; +#endif + +#else /* 32 bit architectures: */ +typedef signed long int SLONG; +#if !(defined(_MIKMOD_OS2) || defined(_MIKMOD_WIN32) || defined(_MIKMOD_AMIGA)) +typedef unsigned long int ULONG; +#endif +#endif + +/* make sure types are of correct sizes: */ +typedef int __mikmod_typetest [ + ( + (sizeof(SBYTE)==1) && (sizeof(UBYTE)==1) + && (sizeof(SWORD)==2) && (sizeof(UWORD)==2) + && (sizeof(SLONG)==4) && (sizeof(ULONG)==4) +#ifndef _MIKMOD_AMIGA + && (sizeof(BOOL) == sizeof(int)) +#endif + && (sizeof(CHAR) == sizeof(char)) + ) * 2 - 1 ]; + +/* + * ========== Error codes + */ + +enum { + MMERR_OPENING_FILE = 1, + MMERR_OUT_OF_MEMORY, + MMERR_DYNAMIC_LINKING, + + MMERR_SAMPLE_TOO_BIG, + MMERR_OUT_OF_HANDLES, + MMERR_UNKNOWN_WAVE_TYPE, + + MMERR_LOADING_PATTERN, + MMERR_LOADING_TRACK, + MMERR_LOADING_HEADER, + MMERR_LOADING_SAMPLEINFO, + MMERR_NOT_A_MODULE, + MMERR_NOT_A_STREAM, + MMERR_MED_SYNTHSAMPLES, + MMERR_ITPACK_INVALID_DATA, + + MMERR_DETECTING_DEVICE, + MMERR_INVALID_DEVICE, + MMERR_INITIALIZING_MIXER, + MMERR_OPENING_AUDIO, + MMERR_8BIT_ONLY, + MMERR_16BIT_ONLY, + MMERR_STEREO_ONLY, + MMERR_ULAW, + MMERR_NON_BLOCK, + + MMERR_AF_AUDIO_PORT, + + MMERR_AIX_CONFIG_INIT, + MMERR_AIX_CONFIG_CONTROL, + MMERR_AIX_CONFIG_START, + + MMERR_GUS_SETTINGS, + MMERR_GUS_RESET, + MMERR_GUS_TIMER, + + MMERR_HP_SETSAMPLESIZE, + MMERR_HP_SETSPEED, + MMERR_HP_CHANNELS, + MMERR_HP_AUDIO_OUTPUT, + MMERR_HP_AUDIO_DESC, + MMERR_HP_BUFFERSIZE, + + MMERR_OSS_SETFRAGMENT, + MMERR_OSS_SETSAMPLESIZE, + MMERR_OSS_SETSTEREO, + MMERR_OSS_SETSPEED, + + MMERR_SGI_SPEED, + MMERR_SGI_16BIT, + MMERR_SGI_8BIT, + MMERR_SGI_STEREO, + MMERR_SGI_MONO, + + MMERR_SUN_INIT, + + MMERR_OS2_MIXSETUP, + MMERR_OS2_SEMAPHORE, + MMERR_OS2_TIMER, + MMERR_OS2_THREAD, + + MMERR_DS_PRIORITY, + MMERR_DS_BUFFER, + MMERR_DS_FORMAT, + MMERR_DS_NOTIFY, + MMERR_DS_EVENT, + MMERR_DS_THREAD, + MMERR_DS_UPDATE, + + MMERR_WINMM_HANDLE, + MMERR_WINMM_ALLOCATED, + MMERR_WINMM_DEVICEID, + MMERR_WINMM_FORMAT, + MMERR_WINMM_UNKNOWN, + + MMERR_MAC_SPEED, + MMERR_MAC_START, + + MMERR_OSX_UNKNOWN_DEVICE, + MMERR_OSX_BAD_PROPERTY, + MMERR_OSX_UNSUPPORTED_FORMAT, + MMERR_OSX_SET_STEREO, + MMERR_OSX_BUFFER_ALLOC, + MMERR_OSX_ADD_IO_PROC, + MMERR_OSX_DEVICE_START, + MMERR_OSX_PTHREAD, + + MMERR_DOSWSS_STARTDMA, + MMERR_DOSSB_STARTDMA, + + MMERR_NO_FLOAT32,/* should actually be after MMERR_ULAW or something */ + + MMERR_OPENAL_CREATECTX, + MMERR_OPENAL_CTXCURRENT, + MMERR_OPENAL_GENBUFFERS, + MMERR_OPENAL_GENSOURCES, + MMERR_OPENAL_SOURCE, + MMERR_OPENAL_QUEUEBUFFERS, + MMERR_OPENAL_UNQUEUEBUFFERS, + MMERR_OPENAL_BUFFERDATA, + MMERR_OPENAL_GETSOURCE, + MMERR_OPENAL_SOURCEPLAY, + MMERR_OPENAL_SOURCESTOP, + + MMERR_ALSA_NOCONFIG, + MMERR_ALSA_SETPARAMS, + MMERR_ALSA_SETFORMAT, + MMERR_ALSA_SETRATE, + MMERR_ALSA_SETCHANNELS, + MMERR_ALSA_BUFFERSIZE, + MMERR_ALSA_PCM_START, + MMERR_ALSA_PCM_WRITE, + MMERR_ALSA_PCM_RECOVER, + + MMERR_SNDIO_SETPARAMS, + MMERR_SNDIO_BADPARAMS, + + MMERR_MAX +}; + +/* + * ========== Error handling + */ + +typedef void (MikMod_handler)(void); +typedef MikMod_handler *MikMod_handler_t; + +MIKMODAPI extern int MikMod_errno; +MIKMODAPI extern BOOL MikMod_critical; +MIKMODAPI extern const char *MikMod_strerror(int); + +MIKMODAPI extern MikMod_handler_t MikMod_RegisterErrorHandler(MikMod_handler_t); + +/* + * ========== Library initialization and core functions + */ + +struct MDRIVER; + +MIKMODAPI extern void MikMod_RegisterAllDrivers(void); + +MIKMODAPI extern CHAR* MikMod_InfoDriver(void); +MIKMODAPI extern void MikMod_RegisterDriver(struct MDRIVER*); +MIKMODAPI extern int MikMod_DriverFromAlias(const CHAR*); +MIKMODAPI extern struct MDRIVER *MikMod_DriverByOrdinal(int); + +MIKMODAPI extern int MikMod_Init(const CHAR*); +MIKMODAPI extern void MikMod_Exit(void); +MIKMODAPI extern int MikMod_Reset(const CHAR*); +MIKMODAPI extern int MikMod_SetNumVoices(int,int); +MIKMODAPI extern BOOL MikMod_Active(void); +MIKMODAPI extern int MikMod_EnableOutput(void); +MIKMODAPI extern void MikMod_DisableOutput(void); +MIKMODAPI extern void MikMod_Update(void); + +MIKMODAPI extern BOOL MikMod_InitThreads(void); +MIKMODAPI extern void MikMod_Lock(void); +MIKMODAPI extern void MikMod_Unlock(void); + +MIKMODAPI extern void* MikMod_malloc(size_t); +MIKMODAPI extern void* MikMod_calloc(size_t,size_t); +MIKMODAPI extern void* MikMod_realloc(void*,size_t); +MIKMODAPI extern CHAR* MikMod_strdup(const CHAR*); +MIKMODAPI extern void MikMod_free(void*); /* frees if ptr != NULL */ + +/* + * ========== Reader, Writer + */ + +typedef struct MREADER { + int (*Seek)(struct MREADER*,long,int); + long (*Tell)(struct MREADER*); + BOOL (*Read)(struct MREADER*,void*,size_t); + int (*Get)(struct MREADER*); + BOOL (*Eof)(struct MREADER*); + long iobase; + long prev_iobase; +} MREADER; + +typedef struct MWRITER { + int (*Seek)(struct MWRITER*, long, int); + long (*Tell)(struct MWRITER*); + BOOL (*Write)(struct MWRITER*, const void*, size_t); + int (*Put)(struct MWRITER*, int); +} MWRITER; + +/* + * ========== Samples + */ + +/* Sample playback should not be interrupted */ +#define SFX_CRITICAL 1 + +/* Sample format [loading and in-memory] flags: */ +#define SF_16BITS 0x0001 +#define SF_STEREO 0x0002 +#define SF_SIGNED 0x0004 +#define SF_BIG_ENDIAN 0x0008 +#define SF_DELTA 0x0010 +#define SF_ITPACKED 0x0020 + +#define SF_FORMATMASK 0x003F + +/* General Playback flags */ + +#define SF_LOOP 0x0100 +#define SF_BIDI 0x0200 +#define SF_REVERSE 0x0400 +#define SF_SUSTAIN 0x0800 + +#define SF_PLAYBACKMASK 0x0C00 + +/* Module-only Playback Flags */ + +#define SF_OWNPAN 0x1000 +#define SF_UST_LOOP 0x2000 + +#define SF_EXTRAPLAYBACKMASK 0x3000 + +/* Panning constants */ +#define PAN_LEFT 0 +#define PAN_HALFLEFT 64 +#define PAN_CENTER 128 +#define PAN_HALFRIGHT 192 +#define PAN_RIGHT 255 +#define PAN_SURROUND 512 /* panning value for Dolby Surround */ + +typedef struct SAMPLE { + SWORD panning; /* panning (0-255 or PAN_SURROUND) */ + ULONG speed; /* Base playing speed/frequency of note */ + UBYTE volume; /* volume 0-64 */ + UWORD inflags; /* sample format on disk */ + UWORD flags; /* sample format in memory */ + ULONG length; /* length of sample (in samples!) */ + ULONG loopstart; /* repeat position (relative to start, in samples) */ + ULONG loopend; /* repeat end */ + ULONG susbegin; /* sustain loop begin (in samples) \ Not Supported */ + ULONG susend; /* sustain loop end / Yet! */ + + /* Variables used by the module player only! (ignored for sound effects) */ + UBYTE globvol; /* global volume */ + UBYTE vibflags; /* autovibrato flag stuffs */ + UBYTE vibtype; /* Vibratos moved from INSTRUMENT to SAMPLE */ + UBYTE vibsweep; + UBYTE vibdepth; + UBYTE vibrate; + CHAR* samplename; /* name of the sample */ + + /* Values used internally only */ + UWORD avibpos; /* autovibrato pos [player use] */ + UBYTE divfactor; /* for sample scaling, maintains proper period slides */ + ULONG seekpos; /* seek position in file */ + SWORD handle; /* sample handle used by individual drivers */ + void (*onfree)(void *ctx); /* called from Sample_Free if not NULL */ + void *ctx; /* context passed to previous function*/ +} SAMPLE; + +/* Sample functions */ + +MIKMODAPI extern SAMPLE *Sample_LoadRaw(const CHAR *,ULONG rate, ULONG channel, ULONG flags); +MIKMODAPI extern SAMPLE *Sample_LoadRawFP(FILE *fp,ULONG rate,ULONG channel, ULONG flags); +MIKMODAPI extern SAMPLE *Sample_LoadRawMem(const char *buf, int len, ULONG rate, ULONG channel, ULONG flags); +MIKMODAPI extern SAMPLE *Sample_LoadRawGeneric(MREADER*reader,ULONG rate, ULONG channel, ULONG flags); + +MIKMODAPI extern SAMPLE *Sample_Load(const CHAR*); +MIKMODAPI extern SAMPLE *Sample_LoadFP(FILE*); +MIKMODAPI extern SAMPLE *Sample_LoadMem(const char *buf, int len); +MIKMODAPI extern SAMPLE *Sample_LoadGeneric(MREADER*); +MIKMODAPI extern void Sample_Free(SAMPLE*); +MIKMODAPI extern SBYTE Sample_Play(SAMPLE*,ULONG,UBYTE); + +MIKMODAPI extern void Voice_SetVolume(SBYTE,UWORD); +MIKMODAPI extern UWORD Voice_GetVolume(SBYTE); +MIKMODAPI extern void Voice_SetFrequency(SBYTE,ULONG); +MIKMODAPI extern ULONG Voice_GetFrequency(SBYTE); +MIKMODAPI extern void Voice_SetPanning(SBYTE,ULONG); +MIKMODAPI extern ULONG Voice_GetPanning(SBYTE); +MIKMODAPI extern void Voice_Play(SBYTE,SAMPLE*,ULONG); +MIKMODAPI extern void Voice_Stop(SBYTE); +MIKMODAPI extern BOOL Voice_Stopped(SBYTE); +MIKMODAPI extern SLONG Voice_GetPosition(SBYTE); +MIKMODAPI extern ULONG Voice_RealVolume(SBYTE); + +/* + * ========== Internal module representation (UniMod) + */ + +/* + Instrument definition - for information only, the only field which may be + of use in user programs is the name field +*/ + +/* Instrument note count */ +#define INSTNOTES 120 + +/* Envelope point */ +typedef struct ENVPT { + SWORD pos; + SWORD val; +} ENVPT; + +/* Envelope point count */ +#define ENVPOINTS 32 + +/* Instrument structure */ +typedef struct INSTRUMENT { + CHAR* insname; + + UBYTE flags; + UWORD samplenumber[INSTNOTES]; + UBYTE samplenote[INSTNOTES]; + + UBYTE nnatype; + UBYTE dca; /* duplicate check action */ + UBYTE dct; /* duplicate check type */ + UBYTE globvol; + UWORD volfade; + SWORD panning; /* instrument-based panning var */ + + UBYTE pitpansep; /* pitch pan separation (0 to 255) */ + UBYTE pitpancenter; /* pitch pan center (0 to 119) */ + UBYTE rvolvar; /* random volume varations (0 - 100%) */ + UBYTE rpanvar; /* random panning varations (0 - 100%) */ + + /* volume envelope */ + UBYTE volflg; /* bit 0: on 1: sustain 2: loop */ + UBYTE volpts; + UBYTE volsusbeg; + UBYTE volsusend; + UBYTE volbeg; + UBYTE volend; + ENVPT volenv[ENVPOINTS]; + /* panning envelope */ + UBYTE panflg; /* bit 0: on 1: sustain 2: loop */ + UBYTE panpts; + UBYTE pansusbeg; + UBYTE pansusend; + UBYTE panbeg; + UBYTE panend; + ENVPT panenv[ENVPOINTS]; + /* pitch envelope */ + UBYTE pitflg; /* bit 0: on 1: sustain 2: loop */ + UBYTE pitpts; + UBYTE pitsusbeg; + UBYTE pitsusend; + UBYTE pitbeg; + UBYTE pitend; + ENVPT pitenv[ENVPOINTS]; +} INSTRUMENT; + +struct MP_CONTROL; +struct MP_VOICE; + +/* + Module definition +*/ + +/* maximum master channels supported */ +#define UF_MAXCHAN 64 + +/* Module flags */ +#define UF_XMPERIODS 0x0001 /* XM periods / finetuning */ +#define UF_LINEAR 0x0002 /* LINEAR periods (UF_XMPERIODS must be set) */ +#define UF_INST 0x0004 /* Instruments are used */ +#define UF_NNA 0x0008 /* IT: NNA used, set numvoices rather + than numchn */ +#define UF_S3MSLIDES 0x0010 /* uses old S3M volume slides */ +#define UF_BGSLIDES 0x0020 /* continue volume slides in the background */ +#define UF_HIGHBPM 0x0040 /* MED: can use >255 bpm */ +#define UF_NOWRAP 0x0080 /* XM-type (i.e. illogical) pattern break + semantics */ +#define UF_ARPMEM 0x0100 /* IT: need arpeggio memory */ +#define UF_FT2QUIRKS 0x0200 /* emulate some FT2 replay quirks */ +#define UF_PANNING 0x0400 /* module uses panning effects or have + non-tracker default initial panning */ + +typedef struct MODULE { + /* general module information */ + CHAR* songname; /* name of the song */ + CHAR* modtype; /* string type of module loaded */ + CHAR* comment; /* module comments */ + + UWORD flags; /* See module flags above */ + UBYTE numchn; /* number of module channels */ + UBYTE numvoices; /* max # voices used for full NNA playback */ + UWORD numpos; /* number of positions in this song */ + UWORD numpat; /* number of patterns in this song */ + UWORD numins; /* number of instruments */ + UWORD numsmp; /* number of samples */ + + struct INSTRUMENT* instruments; /* all instruments */ + struct SAMPLE* samples; /* all samples */ + + UBYTE realchn; /* real number of channels used */ + UBYTE totalchn; /* total number of channels used (incl NNAs) */ + + /* playback settings */ + UWORD reppos; /* restart position */ + UBYTE initspeed; /* initial song speed */ + UWORD inittempo; /* initial song tempo */ + UBYTE initvolume; /* initial global volume (0 - 128) */ + UWORD panning[UF_MAXCHAN]; /* panning positions */ + UBYTE chanvol[UF_MAXCHAN]; /* channel positions */ + UWORD bpm; /* current beats-per-minute speed */ + UWORD sngspd; /* current song speed */ + SWORD volume; /* song volume (0-128) (or user volume) */ + + BOOL extspd; /* extended speed flag (default enabled) */ + BOOL panflag; /* panning flag (default enabled) */ + BOOL wrap; /* wrap module ? (default disabled) */ + BOOL loop; /* allow module to loop ? (default enabled) */ + BOOL fadeout; /* volume fade out during last pattern */ + + UWORD patpos; /* current row number */ + SWORD sngpos; /* current song position */ + ULONG sngtime; /* current song time in 2^-10 seconds */ + + SWORD relspd; /* relative speed factor */ + + /* internal module representation */ + UWORD numtrk; /* number of tracks */ + UBYTE** tracks; /* array of numtrk pointers to tracks */ + UWORD* patterns; /* array of Patterns */ + UWORD* pattrows; /* array of number of rows for each pattern */ + UWORD* positions; /* all positions */ + + BOOL forbid; /* if true, no player update! */ + UWORD numrow; /* number of rows on current pattern */ + UWORD vbtick; /* tick counter (counts from 0 to sngspd) */ + UWORD sngremainder;/* used for song time computation */ + + struct MP_CONTROL* control; /* Effects Channel info (size pf->numchn) */ + struct MP_VOICE* voice; /* Audio Voice information (size md_numchn) */ + + UBYTE globalslide; /* global volume slide rate */ + UBYTE pat_repcrazy;/* module has just looped to position -1 */ + UWORD patbrk; /* position where to start a new pattern */ + UBYTE patdly; /* patterndelay counter (command memory) */ + UBYTE patdly2; /* patterndelay counter (real one) */ + SWORD posjmp; /* flag to indicate a jump is needed... */ + UWORD bpmlimit; /* threshold to detect bpm or speed values */ +} MODULE; + + +/* This structure is used to query current playing voices status */ +typedef struct VOICEINFO { + INSTRUMENT* i; /* Current channel instrument */ + SAMPLE* s; /* Current channel sample */ + SWORD panning; /* panning position */ + SBYTE volume; /* channel's "global" volume (0..64) */ + UWORD period; /* period to play the sample at */ + UBYTE kick; /* if true = sample has been restarted */ +} VOICEINFO; + +/* + * ========== Module loaders + */ + +struct MLOADER; + +MIKMODAPI extern CHAR* MikMod_InfoLoader(void); +MIKMODAPI extern void MikMod_RegisterAllLoaders(void); +MIKMODAPI extern void MikMod_RegisterLoader(struct MLOADER*); + +MIKMODAPI extern struct MLOADER load_669; /* 669 and Extended-669 (by Tran/Renaissance) */ +MIKMODAPI extern struct MLOADER load_amf; /* DMP Advanced Module Format (by Otto Chrons) */ +MIKMODAPI extern struct MLOADER load_asy; /* ASYLUM Music Format 1.0 */ +MIKMODAPI extern struct MLOADER load_dsm; /* DSIK internal module format */ +MIKMODAPI extern struct MLOADER load_far; /* Farandole Composer (by Daniel Potter) */ +MIKMODAPI extern struct MLOADER load_gdm; /* General DigiMusic (by Edward Schlunder) */ +MIKMODAPI extern struct MLOADER load_gt2; /* Graoumf tracker */ +MIKMODAPI extern struct MLOADER load_it; /* Impulse Tracker (by Jeffrey Lim) */ +MIKMODAPI extern struct MLOADER load_imf; /* Imago Orpheus (by Lutz Roeder) */ +MIKMODAPI extern struct MLOADER load_med; /* Amiga MED modules (by Teijo Kinnunen) */ +MIKMODAPI extern struct MLOADER load_m15; /* Soundtracker 15-instrument */ +MIKMODAPI extern struct MLOADER load_mod; /* Standard 31-instrument Module loader */ +MIKMODAPI extern struct MLOADER load_mtm; /* Multi-Tracker Module (by Renaissance) */ +MIKMODAPI extern struct MLOADER load_okt; /* Amiga Oktalyzer */ +MIKMODAPI extern struct MLOADER load_stm; /* ScreamTracker 2 (by Future Crew) */ +MIKMODAPI extern struct MLOADER load_stx; /* STMIK 0.2 (by Future Crew) */ +MIKMODAPI extern struct MLOADER load_s3m; /* ScreamTracker 3 (by Future Crew) */ +MIKMODAPI extern struct MLOADER load_ult; /* UltraTracker (by MAS) */ +MIKMODAPI extern struct MLOADER load_umx; /* Unreal UMX container of Epic Games */ +MIKMODAPI extern struct MLOADER load_uni; /* MikMod and APlayer internal module format */ +MIKMODAPI extern struct MLOADER load_xm; /* FastTracker 2 (by Triton) */ + +/* + * ========== Module player + */ + +MIKMODAPI extern MODULE* Player_Load(const CHAR*,int,BOOL); +MIKMODAPI extern MODULE* Player_LoadFP(FILE*,int,BOOL); +MIKMODAPI extern MODULE* Player_LoadMem(const char *buffer,int len,int maxchan,BOOL curious); +MIKMODAPI extern MODULE* Player_LoadGeneric(MREADER*,int,BOOL); +MIKMODAPI extern CHAR* Player_LoadTitle(const CHAR*); +MIKMODAPI extern CHAR* Player_LoadTitleFP(FILE*); +MIKMODAPI extern CHAR* Player_LoadTitleMem(const char *buffer,int len); +MIKMODAPI extern CHAR* Player_LoadTitleGeneric(MREADER*); + +MIKMODAPI extern void Player_Free(MODULE*); +MIKMODAPI extern void Player_Start(MODULE*); +MIKMODAPI extern BOOL Player_Active(void); +MIKMODAPI extern void Player_Stop(void); +MIKMODAPI extern void Player_TogglePause(void); +MIKMODAPI extern BOOL Player_Paused(void); +MIKMODAPI extern void Player_NextPosition(void); +MIKMODAPI extern void Player_PrevPosition(void); +MIKMODAPI extern void Player_SetPosition(UWORD); +MIKMODAPI extern BOOL Player_Muted(UBYTE); +MIKMODAPI extern void Player_SetVolume(SWORD); +MIKMODAPI extern MODULE* Player_GetModule(void); +MIKMODAPI extern void Player_SetSpeed(UWORD); +MIKMODAPI extern void Player_SetTempo(UWORD); +MIKMODAPI extern void Player_Unmute(SLONG,...); +MIKMODAPI extern void Player_Mute(SLONG,...); +MIKMODAPI extern void Player_ToggleMute(SLONG,...); +MIKMODAPI extern int Player_GetChannelVoice(UBYTE); +MIKMODAPI extern UWORD Player_GetChannelPeriod(UBYTE); +MIKMODAPI extern int Player_QueryVoices(UWORD numvoices, VOICEINFO *vinfo); +MIKMODAPI extern int Player_GetRow(void); +MIKMODAPI extern int Player_GetOrder(void); + +typedef void (*MikMod_player_t)(void); +typedef void (*MikMod_callback_t)(unsigned char *data, size_t len); + +MIKMODAPI extern MikMod_player_t MikMod_RegisterPlayer(MikMod_player_t); + +#define MUTE_EXCLUSIVE 32000 +#define MUTE_INCLUSIVE 32001 + +/* + * ========== Drivers + */ + +enum { + MD_MUSIC = 0, + MD_SNDFX +}; + +enum { + MD_HARDWARE = 0, + MD_SOFTWARE +}; + +/* Mixing flags */ + +/* These ones take effect only after MikMod_Init or MikMod_Reset */ +#define DMODE_16BITS 0x0001 /* enable 16 bit output */ +#define DMODE_STEREO 0x0002 /* enable stereo output */ +#define DMODE_SOFT_SNDFX 0x0004 /* Process sound effects via software mixer */ +#define DMODE_SOFT_MUSIC 0x0008 /* Process music via software mixer */ +#define DMODE_HQMIXER 0x0010 /* Use high-quality (slower) software mixer */ +#define DMODE_FLOAT 0x0020 /* enable float output */ +/* These take effect immediately. */ +#define DMODE_SURROUND 0x0100 /* enable surround sound */ +#define DMODE_INTERP 0x0200 /* enable interpolation */ +#define DMODE_REVERSE 0x0400 /* reverse stereo */ +#define DMODE_SIMDMIXER 0x0800 /* enable SIMD mixing */ +#define DMODE_NOISEREDUCTION 0x1000 /* Low pass filtering */ + + +struct SAMPLOAD; + +typedef struct MDRIVER { + struct MDRIVER* next; + const CHAR* Name; + const CHAR* Version; + + UBYTE HardVoiceLimit; /* Limit of hardware mixer voices */ + UBYTE SoftVoiceLimit; /* Limit of software mixer voices */ + + const CHAR* Alias; + const CHAR* CmdLineHelp; + + void (*CommandLine) (const CHAR*); + BOOL (*IsPresent) (void); + SWORD (*SampleLoad) (struct SAMPLOAD*,int); + void (*SampleUnload) (SWORD); + ULONG (*FreeSampleSpace) (int); + ULONG (*RealSampleLength) (int,struct SAMPLE*); + int (*Init) (void); + void (*Exit) (void); + int (*Reset) (void); + int (*SetNumVoices) (void); + int (*PlayStart) (void); + void (*PlayStop) (void); + void (*Update) (void); + void (*Pause) (void); + void (*VoiceSetVolume) (UBYTE,UWORD); + UWORD (*VoiceGetVolume) (UBYTE); + void (*VoiceSetFrequency)(UBYTE,ULONG); + ULONG (*VoiceGetFrequency)(UBYTE); + void (*VoiceSetPanning) (UBYTE,ULONG); + ULONG (*VoiceGetPanning) (UBYTE); + void (*VoicePlay) (UBYTE,SWORD,ULONG,ULONG,ULONG,ULONG,UWORD); + void (*VoiceStop) (UBYTE); + BOOL (*VoiceStopped) (UBYTE); + SLONG (*VoiceGetPosition) (UBYTE); + ULONG (*VoiceRealVolume) (UBYTE); +} MDRIVER; + +/* These variables can be changed at ANY time and results will be immediate */ +MIKMODAPI extern UBYTE md_volume; /* global sound volume (0-128) */ +MIKMODAPI extern UBYTE md_musicvolume; /* volume of song */ +MIKMODAPI extern UBYTE md_sndfxvolume; /* volume of sound effects */ +MIKMODAPI extern UBYTE md_reverb; /* 0 = none; 15 = chaos */ +MIKMODAPI extern UBYTE md_pansep; /* 0 = mono; 128 == 100% (full left/right) */ + +/* The variables below can be changed at any time, but changes will not be + implemented until MikMod_Reset is called. A call to MikMod_Reset may result + in a skip or pop in audio (depending on the soundcard driver and the settings + changed). */ +MIKMODAPI extern UWORD md_device; /* device */ +MIKMODAPI extern UWORD md_mixfreq; /* mixing frequency */ +MIKMODAPI extern UWORD md_mode; /* mode. See DMODE_? flags above */ + +/* The following variable should not be changed! */ +MIKMODAPI extern MDRIVER* md_driver; /* Current driver in use. */ + +/* Known drivers list */ + +MIKMODAPI extern struct MDRIVER drv_nos; /* no sound */ +MIKMODAPI extern struct MDRIVER drv_pipe; /* piped output */ +MIKMODAPI extern struct MDRIVER drv_raw; /* raw file disk writer [music.raw] */ +MIKMODAPI extern struct MDRIVER drv_stdout; /* output to stdout */ +MIKMODAPI extern struct MDRIVER drv_wav; /* RIFF WAVE file disk writer [music.wav] */ +MIKMODAPI extern struct MDRIVER drv_aiff; /* AIFF file disk writer [music.aiff] */ + +MIKMODAPI extern struct MDRIVER drv_ultra; /* Linux Ultrasound driver */ +MIKMODAPI extern struct MDRIVER drv_sam9407;/* Linux sam9407 driver */ + +MIKMODAPI extern struct MDRIVER drv_AF; /* Dec Alpha AudioFile */ +MIKMODAPI extern struct MDRIVER drv_ahi; /* Amiga AHI */ +MIKMODAPI extern struct MDRIVER drv_aix; /* AIX audio device */ +MIKMODAPI extern struct MDRIVER drv_alsa; /* Advanced Linux Sound Architecture (ALSA) */ +MIKMODAPI extern struct MDRIVER drv_esd; /* Enlightened sound daemon (EsounD) */ +MIKMODAPI extern struct MDRIVER drv_pulseaudio; /* PulseAudio */ +MIKMODAPI extern struct MDRIVER drv_hp; /* HP-UX audio device */ +MIKMODAPI extern struct MDRIVER drv_nas; /* Network Audio System (NAS) */ +MIKMODAPI extern struct MDRIVER drv_oss; /* OpenSound System (Linux,FreeBSD...) */ +MIKMODAPI extern struct MDRIVER drv_openal; /* OpenAL driver */ +MIKMODAPI extern struct MDRIVER drv_sdl; /* SDL audio driver */ +MIKMODAPI extern struct MDRIVER drv_sgi; /* SGI audio library */ +MIKMODAPI extern struct MDRIVER drv_sndio; /* OpenBSD sndio */ +MIKMODAPI extern struct MDRIVER drv_sun; /* Sun/NetBSD/OpenBSD audio device */ + +MIKMODAPI extern struct MDRIVER drv_dart; /* OS/2 Direct Audio RealTime */ +MIKMODAPI extern struct MDRIVER drv_os2; /* OS/2 MMPM/2 */ + +MIKMODAPI extern struct MDRIVER drv_ds; /* Win32 DirectSound driver */ +MIKMODAPI extern struct MDRIVER drv_xaudio2;/* Win32 XAudio2 driver */ +MIKMODAPI extern struct MDRIVER drv_win; /* Win32 multimedia API driver */ + +MIKMODAPI extern struct MDRIVER drv_mac; /* Macintosh Sound Manager driver */ +MIKMODAPI extern struct MDRIVER drv_osx; /* MacOS X CoreAudio Driver */ + +MIKMODAPI extern struct MDRIVER drv_dc; /* Dreamcast driver */ +MIKMODAPI extern struct MDRIVER drv_gp32; /* GP32 Sound driver */ +MIKMODAPI extern struct MDRIVER drv_psp; /* PlayStation Portable driver */ + +MIKMODAPI extern struct MDRIVER drv_wss; /* DOS WSS driver */ +MIKMODAPI extern struct MDRIVER drv_sb; /* DOS S/B driver */ + +MIKMODAPI extern struct MDRIVER drv_osles; /* OpenSL ES driver for android */ + +/*========== Virtual channel mixer interface (for user-supplied drivers only) */ + +MIKMODAPI extern int VC_Init(void); +MIKMODAPI extern void VC_Exit(void); +MIKMODAPI extern void VC_SetCallback(MikMod_callback_t callback); +MIKMODAPI extern int VC_SetNumVoices(void); +MIKMODAPI extern ULONG VC_SampleSpace(int); +MIKMODAPI extern ULONG VC_SampleLength(int,SAMPLE*); + +MIKMODAPI extern int VC_PlayStart(void); +MIKMODAPI extern void VC_PlayStop(void); + +MIKMODAPI extern SWORD VC_SampleLoad(struct SAMPLOAD*,int); +MIKMODAPI extern void VC_SampleUnload(SWORD); + +MIKMODAPI extern ULONG VC_WriteBytes(SBYTE*,ULONG); +MIKMODAPI extern ULONG VC_SilenceBytes(SBYTE*,ULONG); + +MIKMODAPI extern void VC_VoiceSetVolume(UBYTE,UWORD); +MIKMODAPI extern UWORD VC_VoiceGetVolume(UBYTE); +MIKMODAPI extern void VC_VoiceSetFrequency(UBYTE,ULONG); +MIKMODAPI extern ULONG VC_VoiceGetFrequency(UBYTE); +MIKMODAPI extern void VC_VoiceSetPanning(UBYTE,ULONG); +MIKMODAPI extern ULONG VC_VoiceGetPanning(UBYTE); +MIKMODAPI extern void VC_VoicePlay(UBYTE,SWORD,ULONG,ULONG,ULONG,ULONG,UWORD); + +MIKMODAPI extern void VC_VoiceStop(UBYTE); +MIKMODAPI extern BOOL VC_VoiceStopped(UBYTE); +MIKMODAPI extern SLONG VC_VoiceGetPosition(UBYTE); +MIKMODAPI extern ULONG VC_VoiceRealVolume(UBYTE); + +#ifdef __cplusplus +} +#endif + +#endif + +/* ex:set ts=4: */ diff --git a/libs/mikmod/include/mikmod_ctype.h b/libs/mikmod/include/mikmod_ctype.h new file mode 100644 index 0000000..ea32e9e --- /dev/null +++ b/libs/mikmod/include/mikmod_ctype.h @@ -0,0 +1,84 @@ +/* Locale insensitive ctype.h functions taken from the RPM library. + * RPM is Copyright (c) 1998 by Red Hat Software, Inc. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef MIKMOD_CTYPE_H +#define MIKMOD_CTYPE_H + +static inline int mik_isascii(int c) { + return ((c & ~0x7f) == 0); +} + +static inline int mik_islower(int c) { + return (c >= 'a' && c <= 'z'); +} + +static inline int mik_isupper(int c) { + return (c >= 'A' && c <= 'Z'); +} + +static inline int mik_isalpha(int c) { + return (mik_islower(c) || mik_isupper(c)); +} + +static inline int mik_isdigit(int c) { + return (c >= '0' && c <= '9'); +} + +static inline int mik_isxdigit(int c) { + return (mik_isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); +} + +static inline int mik_isalnum(int c) { + return (mik_isalpha(c) || mik_isdigit(c)); +} + +static inline int mik_isblank(int c) { + return (c == ' ' || c == '\t'); +} + +static inline int mik_isspace(int c) { + switch (c) { + case ' ': case '\t': + case '\n': case '\r': + case '\f': case '\v': return 1; + } + return 0; +} + +static inline int mik_isgraph(int c) { + return (c > 0x20 && c <= 0x7e); +} + +static inline int mik_isprint(int c) { + return (c >= 0x20 && c <= 0x7e); +} + +static inline int mik_toascii(int c) { + return (c & 0x7f); +} + +static inline int mik_tolower(int c) { + return ((mik_isupper(c)) ? (c | ('a' - 'A')) : c); +} + +static inline int mik_toupper(int c) { + return ((mik_islower(c)) ? (c & ~('a' - 'A')) : c); +} + +#endif /* MIKMOD_CTYPE_H */ diff --git a/libs/mikmod/include/mikmod_internals.h b/libs/mikmod/include/mikmod_internals.h new file mode 100644 index 0000000..e90e6cd --- /dev/null +++ b/libs/mikmod/include/mikmod_internals.h @@ -0,0 +1,866 @@ +/* MikMod sound library + (c) 1998, 1999, 2005 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + MikMod sound library internal definitions + + ==============================================================================*/ + +#ifndef _MIKMOD_INTERNALS_H +#define _MIKMOD_INTERNALS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#if defined(_MSC_VER) && !defined(__cplusplus) && !defined(HAVE_CONFIG_H) +#define inline __inline +#endif + +#ifndef MIKMOD_UNIX +#if (defined(unix) || defined(__unix__) || defined(__unix) || \ + (defined(__APPLE__) && defined(__MACH__))) && \ + !(defined(__DJGPP__) || defined(_WIN32) || defined(__OS2__) || defined(__EMX__)) +#define MIKMOD_UNIX 1 +#endif +#endif /* MIKMOD_UNIX */ + +#include "mikmod.h" + +/*========== More type definitions */ + +/* SLONGLONG: 64bit, signed */ +#if !defined(_WIN32) && \ + (defined(_LP64) || defined(__LP64__) || defined(__arch64__) || defined(__alpha) || defined(__x64_64) || defined(__powerpc64__)) +typedef long SLONGLONG; +#define NATIVE_64BIT_INT +#elif defined(_WIN64) /* win64 is LLP64, not LP64 */ +#define NATIVE_64BIT_INT +typedef long long SLONGLONG; +#elif defined(__WATCOMC__) +typedef __int64 SLONGLONG; +#elif defined(_WIN32) && !defined(__MWERKS__) +typedef LONGLONG SLONGLONG; +#elif defined(macintosh) && !TYPE_LONGLONG +#include +typedef SInt64 SLONGLONG; +#else +typedef long long SLONGLONG; +#endif +typedef int __s64_typetest [(sizeof(SLONGLONG)==8) * 2 - 1]; + +/* pointer-sized signed int (ssize_t/intptr_t) : */ +#if defined(_WIN64) /* win64 is LLP64, not LP64 */ +typedef long long SINTPTR_T; +#else +/* long should be pointer-sized for all others : */ +typedef long SINTPTR_T; +#endif +typedef int __iptr_typetest [(sizeof(SINTPTR_T)==sizeof(void*)) * 2 - 1]; + +/*========== Error handling */ + +#define _mm_errno MikMod_errno +#define _mm_critical MikMod_critical +extern MikMod_handler_t _mm_errorhandler; + +/*========== MT stuff */ + +#ifdef HAVE_PTHREAD +#include +#define DECLARE_MUTEX(name) \ + extern pthread_mutex_t _mm_mutex_##name +#define MUTEX_LOCK(name) \ + pthread_mutex_lock(&_mm_mutex_##name) +#define MUTEX_UNLOCK(name) \ + pthread_mutex_unlock(&_mm_mutex_##name) + +#elif defined(__OS2__)||defined(__EMX__) +#define DECLARE_MUTEX(name) \ + extern HMTX _mm_mutex_##name +#define MUTEX_LOCK(name) \ + if(_mm_mutex_##name)\ + DosRequestMutexSem(_mm_mutex_##name,SEM_INDEFINITE_WAIT) +#define MUTEX_UNLOCK(name) \ + if(_mm_mutex_##name)\ + DosReleaseMutexSem(_mm_mutex_##name) + +#elif defined(_WIN32) +#include +#define DECLARE_MUTEX(name) \ + extern HANDLE _mm_mutex_##name +#define MUTEX_LOCK(name) \ + if(_mm_mutex_##name)\ + WaitForSingleObject(_mm_mutex_##name,INFINITE) +#define MUTEX_UNLOCK(name) \ + if(_mm_mutex_##name)\ + ReleaseMutex(_mm_mutex_##name) + +#else +#define DECLARE_MUTEX(name) \ + extern void *_mm_mutex_##name +#define MUTEX_LOCK(name) +#define MUTEX_UNLOCK(name) +#endif + +DECLARE_MUTEX(lists); +DECLARE_MUTEX(vars); + +/*========== Replacement funcs */ + +extern int _mm_strcasecmp (const char *__s1, const char *__s2); + +/*========== Portable file I/O */ + +extern MREADER* _mm_new_mem_reader(const void *buffer, long len); +extern void _mm_delete_mem_reader(MREADER *reader); + +extern MREADER* _mm_new_file_reader(FILE* fp); +extern void _mm_delete_file_reader(MREADER*); + +extern MWRITER* _mm_new_file_writer(FILE *fp); +extern void _mm_delete_file_writer(MWRITER*); + +extern BOOL _mm_FileExists(const CHAR *fname); + +#define _mm_write_SBYTE(x,y) y->Put(y,(int)x) +#define _mm_write_UBYTE(x,y) y->Put(y,(int)x) + +#define _mm_read_SBYTE(x) (SBYTE)x->Get(x) +#define _mm_read_UBYTE(x) (UBYTE)x->Get(x) +#define _mm_skip_BYTE(x) (void)x->Get(x) + +#define _mm_write_SBYTES(x,y,z) z->Write(z,(const void *)x,y) +#define _mm_write_UBYTES(x,y,z) z->Write(z,(const void *)x,y) +#define _mm_read_SBYTES(x,y,z) z->Read(z,(void *)x,y) +#define _mm_read_UBYTES(x,y,z) z->Read(z,(void *)x,y) + +#define _mm_fseek(x,y,z) x->Seek(x,y,z) +#define _mm_ftell(x) x->Tell(x) +#define _mm_rewind(x) _mm_fseek(x,0,SEEK_SET) + +#define _mm_eof(x) x->Eof(x) + +extern void _mm_iobase_setcur(MREADER*); +extern void _mm_iobase_revert(MREADER*); +extern FILE* _mm_fopen(const CHAR *, const CHAR *); +extern int _mm_fclose(FILE *); +extern void _mm_write_string(const CHAR*,MWRITER*); +extern BOOL _mm_read_string (CHAR*,int,MREADER*); + +extern SWORD _mm_read_M_SWORD(MREADER*); +extern SWORD _mm_read_I_SWORD(MREADER*); +extern UWORD _mm_read_M_UWORD(MREADER*); +extern UWORD _mm_read_I_UWORD(MREADER*); + +extern SLONG _mm_read_M_SLONG(MREADER*); +extern SLONG _mm_read_I_SLONG(MREADER*); +extern ULONG _mm_read_M_ULONG(MREADER*); +extern ULONG _mm_read_I_ULONG(MREADER*); + +extern BOOL _mm_read_M_SWORDS(SWORD*,int,MREADER*); +extern BOOL _mm_read_I_SWORDS(SWORD*,int,MREADER*); +extern BOOL _mm_read_M_UWORDS(UWORD*,int,MREADER*); +extern BOOL _mm_read_I_UWORDS(UWORD*,int,MREADER*); + +extern BOOL _mm_read_M_SLONGS(SLONG*,int,MREADER*); +extern BOOL _mm_read_I_SLONGS(SLONG*,int,MREADER*); +extern BOOL _mm_read_M_ULONGS(ULONG*,int,MREADER*); +extern BOOL _mm_read_I_ULONGS(ULONG*,int,MREADER*); + +extern void _mm_write_M_SWORD(SWORD,MWRITER*); +extern void _mm_write_I_SWORD(SWORD,MWRITER*); +extern void _mm_write_M_UWORD(UWORD,MWRITER*); +extern void _mm_write_I_UWORD(UWORD,MWRITER*); + +extern void _mm_write_M_SLONG(SLONG,MWRITER*); +extern void _mm_write_I_SLONG(SLONG,MWRITER*); +extern void _mm_write_M_ULONG(ULONG,MWRITER*); +extern void _mm_write_I_ULONG(ULONG,MWRITER*); + +extern void _mm_write_M_SWORDS(SWORD*,int,MWRITER*); +extern void _mm_write_I_SWORDS(SWORD*,int,MWRITER*); +extern void _mm_write_M_UWORDS(UWORD*,int,MWRITER*); +extern void _mm_write_I_UWORDS(UWORD*,int,MWRITER*); + +extern void _mm_write_M_SLONGS(SLONG*,int,MWRITER*); +extern void _mm_write_I_SLONGS(SLONG*,int,MWRITER*); +extern void _mm_write_M_ULONGS(ULONG*,int,MWRITER*); +extern void _mm_write_I_ULONGS(ULONG*,int,MWRITER*); + +/*========== Samples */ + +/* This is a handle of sorts attached to any sample registered with + SL_RegisterSample. Generally, this only need be used or changed by the + loaders and drivers of mikmod. */ +typedef struct SAMPLOAD { + struct SAMPLOAD *next; + + ULONG length; /* length of sample (in samples!) */ + ULONG loopstart; /* repeat position (relative to start, in samples) */ + ULONG loopend; /* repeat end */ + UWORD infmt,outfmt; + int scalefactor; + SAMPLE* sample; + MREADER* reader; +} SAMPLOAD; + +/*========== Sample and waves loading interface */ + +extern void SL_HalveSample(SAMPLOAD*,int); +extern void SL_Sample8to16(SAMPLOAD*); +extern void SL_Sample16to8(SAMPLOAD*); +extern void SL_SampleSigned(SAMPLOAD*); +extern void SL_SampleUnsigned(SAMPLOAD*); +extern int SL_LoadSamples(void); +extern SAMPLOAD* SL_RegisterSample(SAMPLE*,int,MREADER*); +extern int SL_Load(void*,SAMPLOAD*,ULONG); +extern BOOL SL_Init(SAMPLOAD*); +extern void SL_Exit(SAMPLOAD*); + +/*========== Internal module representation (UniMod) interface */ + +/* number of notes in an octave */ +#define OCTAVE 12 + +extern void UniSetRow(UBYTE*); +extern UBYTE UniGetByte(void); +extern UWORD UniGetWord(void); +extern UBYTE* UniFindRow(UBYTE*,UWORD); +extern void UniSkipOpcode(void); +extern void UniReset(void); +extern void UniWriteByte(UBYTE); +extern void UniWriteWord(UWORD); +extern void UniNewline(void); +extern UBYTE* UniDup(void); +extern BOOL UniInit(void); +extern void UniCleanup(void); +extern void UniEffect(UWORD,UWORD); +#define UniInstrument(x) UniEffect(UNI_INSTRUMENT,x) +#define UniNote(x) UniEffect(UNI_NOTE,x) +extern void UniPTEffect(UBYTE,UBYTE); +extern void UniVolEffect(UWORD,UBYTE); + +/*========== Module Commands */ + +enum { + /* Simple note */ + UNI_NOTE = 1, + /* Instrument change */ + UNI_INSTRUMENT, + /* Protracker effects */ + UNI_PTEFFECT0, /* arpeggio */ + UNI_PTEFFECT1, /* porta up */ + UNI_PTEFFECT2, /* porta down */ + UNI_PTEFFECT3, /* porta to note */ + UNI_PTEFFECT4, /* vibrato */ + UNI_PTEFFECT5, /* dual effect 3+A */ + UNI_PTEFFECT6, /* dual effect 4+A */ + UNI_PTEFFECT7, /* tremolo */ + UNI_PTEFFECT8, /* pan */ + UNI_PTEFFECT9, /* sample offset */ + UNI_PTEFFECTA, /* volume slide */ + UNI_PTEFFECTB, /* pattern jump */ + UNI_PTEFFECTC, /* set volume */ + UNI_PTEFFECTD, /* pattern break */ + UNI_PTEFFECTE, /* extended effects */ + UNI_PTEFFECTF, /* set speed */ + /* Scream Tracker effects */ + UNI_S3MEFFECTA, /* set speed */ + UNI_S3MEFFECTD, /* volume slide */ + UNI_S3MEFFECTE, /* porta down */ + UNI_S3MEFFECTF, /* porta up */ + UNI_S3MEFFECTI, /* tremor */ + UNI_S3MEFFECTQ, /* retrig */ + UNI_S3MEFFECTR, /* tremolo */ + UNI_S3MEFFECTT, /* set tempo */ + UNI_S3MEFFECTU, /* fine vibrato */ + UNI_KEYOFF, /* note off */ + /* Fast Tracker effects */ + UNI_KEYFADE, /* note fade */ + UNI_VOLEFFECTS, /* volume column effects */ + UNI_XMEFFECT4, /* vibrato */ + UNI_XMEFFECT6, /* dual effect 4+A */ + UNI_XMEFFECTA, /* volume slide */ + UNI_XMEFFECTE1, /* fine porta up */ + UNI_XMEFFECTE2, /* fine porta down */ + UNI_XMEFFECTEA, /* fine volume slide up */ + UNI_XMEFFECTEB, /* fine volume slide down */ + UNI_XMEFFECTG, /* set global volume */ + UNI_XMEFFECTH, /* global volume slide */ + UNI_XMEFFECTL, /* set envelope position */ + UNI_XMEFFECTP, /* pan slide */ + UNI_XMEFFECTX1, /* extra fine porta up */ + UNI_XMEFFECTX2, /* extra fine porta down */ + /* Impulse Tracker effects */ + UNI_ITEFFECTG, /* porta to note */ + UNI_ITEFFECTH, /* vibrato */ + UNI_ITEFFECTI, /* tremor (xy not incremented) */ + UNI_ITEFFECTM, /* set channel volume */ + UNI_ITEFFECTN, /* slide / fineslide channel volume */ + UNI_ITEFFECTP, /* slide / fineslide channel panning */ + UNI_ITEFFECTT, /* slide tempo */ + UNI_ITEFFECTU, /* fine vibrato */ + UNI_ITEFFECTW, /* slide / fineslide global volume */ + UNI_ITEFFECTY, /* panbrello */ + UNI_ITEFFECTZ, /* resonant filters */ + UNI_ITEFFECTS0, + /* UltraTracker effects */ + UNI_ULTEFFECT9, /* Sample fine offset */ + /* OctaMED effects */ + UNI_MEDSPEED, + UNI_MEDEFFECTF1,/* play note twice */ + UNI_MEDEFFECTF2,/* delay note */ + UNI_MEDEFFECTF3,/* play note three times */ + /* Oktalyzer effects */ + UNI_OKTARP, /* arpeggio */ + + UNI_LAST +}; + +extern const UWORD unioperands[UNI_LAST]; + +/* IT / S3M Extended SS effects: */ +enum { + SS_GLISSANDO = 1, + SS_FINETUNE, + SS_VIBWAVE, + SS_TREMWAVE, + SS_PANWAVE, + SS_FRAMEDELAY, + SS_S7EFFECTS, + SS_PANNING, + SS_SURROUND, + SS_HIOFFSET, + SS_PATLOOP, + SS_NOTECUT, + SS_NOTEDELAY, + SS_PATDELAY +}; + +/* IT Volume column effects */ +enum { + VOL_VOLUME = 1, + VOL_PANNING, + VOL_VOLSLIDE, + VOL_PITCHSLIDEDN, + VOL_PITCHSLIDEUP, + VOL_PORTAMENTO, + VOL_VIBRATO +}; + +/* IT resonant filter information */ + +#define UF_MAXMACRO 0x10 +#define UF_MAXFILTER 0x100 + +#define FILT_CUT 0x80 +#define FILT_RESONANT 0x81 + +typedef struct FILTER { + UBYTE filter,inf; +} FILTER; + +/*========== Instruments */ + +/* Instrument format flags */ +#define IF_OWNPAN 1 +#define IF_PITCHPAN 2 + +/* Envelope flags: */ +#define EF_ON 1 +#define EF_SUSTAIN 2 +#define EF_LOOP 4 +#define EF_VOLENV 8 + +/* New Note Action Flags */ +#define NNA_CUT 0 +#define NNA_CONTINUE 1 +#define NNA_OFF 2 +#define NNA_FADE 3 + +#define NNA_MASK 3 + +#define DCT_OFF 0 +#define DCT_NOTE 1 +#define DCT_SAMPLE 2 +#define DCT_INST 3 + +#define DCA_CUT 0 +#define DCA_OFF 1 +#define DCA_FADE 2 + +#define KEY_KICK 0 +#define KEY_OFF 1 +#define KEY_FADE 2 +#define KEY_KILL (KEY_OFF|KEY_FADE) + +#define KICK_ABSENT 0 +#define KICK_NOTE 1 +#define KICK_KEYOFF 2 +#define KICK_ENV 4 + +#define AV_IT 1 /* IT vs. XM vibrato info */ + +/*========== Playing */ + +#define POS_NONE (-2) /* no loop position defined */ + +#define LAST_PATTERN (UWORD)(-1) /* special ``end of song'' pattern */ + +typedef struct ENVPR { + UBYTE flg; /* envelope flag */ + UBYTE pts; /* number of envelope points */ + UBYTE susbeg; /* envelope sustain index begin */ + UBYTE susend; /* envelope sustain index end */ + UBYTE beg; /* envelope loop begin */ + UBYTE end; /* envelope loop end */ + SWORD p; /* current envelope counter */ + UWORD a; /* envelope index a */ + UWORD b; /* envelope index b */ + ENVPT* env; /* envelope points */ +} ENVPR; + +typedef struct MP_CHANNEL { + INSTRUMENT* i; + SAMPLE *s; + UBYTE sample; /* which sample number */ + UBYTE note; /* the audible note as heard, direct rep of period */ + SWORD outvolume; /* output volume (vol + sampcol + instvol) */ + SBYTE chanvol; /* channel's "global" volume */ + UWORD fadevol; /* fading volume rate */ + SWORD panning; /* panning position */ + UBYTE kick; /* if true = sample has to be restarted */ + UBYTE kick_flag; /* kick has been true */ + UWORD period; /* period to play the sample at */ + UBYTE nna; /* New note action type + master/slave flags */ + + UBYTE volflg; /* volume envelope settings */ + UBYTE panflg; /* panning envelope settings */ + UBYTE pitflg; /* pitch envelope settings */ + + UBYTE keyoff; /* if true = fade out and stuff */ + SWORD handle; /* which sample-handle */ + UBYTE notedelay; /* (used for note delay) */ + SLONG start; /* The starting byte index in the sample */ +} MP_CHANNEL; + +typedef struct MP_CONTROL { + struct MP_CHANNEL main; + + struct MP_VOICE* slave; /* Audio Slave of current effects control channel */ + + UBYTE slavechn; /* Audio Slave of current effects control channel */ + UBYTE muted; /* if set, channel not played */ + UWORD ultoffset; /* fine sample offset memory */ + UBYTE anote; /* the note that indexes the audible */ + UBYTE oldnote; + SWORD ownper; + SWORD ownvol; + UBYTE dca; /* duplicate check action */ + UBYTE dct; /* duplicate check type */ + UBYTE* row; /* row currently playing on this channel */ + SBYTE retrig; /* retrig value (0 means don't retrig) */ + ULONG speed; /* what finetune to use */ + SWORD volume; /* amiga volume (0 t/m 64) to play the sample at */ + + SWORD tmpvolume; /* tmp volume */ + UWORD tmpperiod; /* tmp period */ + UWORD wantedperiod;/* period to slide to (with effect 3 or 5) */ + + UBYTE arpmem; /* arpeggio command memory */ + UBYTE pansspd; /* panslide speed */ + UWORD slidespeed; + UWORD portspeed; /* noteslide speed (toneportamento) */ + + UBYTE s3mtremor; /* s3m tremor (effect I) counter */ + UBYTE s3mtronof; /* s3m tremor ontime/offtime */ + UBYTE s3mvolslide;/* last used volslide */ + SBYTE sliding; + UBYTE s3mrtgspeed;/* last used retrig speed */ + UBYTE s3mrtgslide;/* last used retrig slide */ + + UBYTE glissando; /* glissando (0 means off) */ + UBYTE wavecontrol; + + SBYTE vibpos; /* current vibrato position */ + UBYTE vibspd; /* "" speed */ + UBYTE vibdepth; /* "" depth */ + + SBYTE trmpos; /* current tremolo position */ + UBYTE trmspd; /* "" speed */ + UBYTE trmdepth; /* "" depth */ + + UBYTE fslideupspd; + UBYTE fslidednspd; + UBYTE fportupspd; /* fx E1 (extra fine portamento up) data */ + UBYTE fportdnspd; /* fx E2 (extra fine portamento dn) data */ + UBYTE ffportupspd;/* fx X1 (extra fine portamento up) data */ + UBYTE ffportdnspd;/* fx X2 (extra fine portamento dn) data */ + + ULONG hioffset; /* last used high order of sample offset */ + UWORD soffset; /* last used low order of sample-offset (effect 9) */ + + UBYTE sseffect; /* last used Sxx effect */ + UBYTE ssdata; /* last used Sxx data info */ + UBYTE chanvolslide;/* last used channel volume slide */ + + UBYTE panbwave; /* current panbrello waveform */ + UBYTE panbpos; /* current panbrello position */ + SBYTE panbspd; /* "" speed */ + UBYTE panbdepth; /* "" depth */ + + UWORD newsamp; /* set to 1 upon a sample / inst change */ + UBYTE voleffect; /* Volume Column Effect Memory as used by IT */ + UBYTE voldata; /* Volume Column Data Memory */ + + SWORD pat_reppos; /* patternloop position */ + UWORD pat_repcnt; /* times to loop */ +} MP_CONTROL; + +/* Used by NNA only player (audio control. AUDTMP is used for full effects + control). */ +typedef struct MP_VOICE { + struct MP_CHANNEL main; + + ENVPR venv; + ENVPR penv; + ENVPR cenv; + + UWORD avibpos; /* autovibrato pos */ + UWORD aswppos; /* autovibrato sweep pos */ + + ULONG totalvol; /* total volume of channel (before global mixings) */ + + BOOL mflag; + SWORD masterchn; + UWORD masterperiod; + + MP_CONTROL* master; /* index of "master" effects channel */ +} MP_VOICE; + +/*========== Loaders */ + +typedef struct MLOADER { + struct MLOADER* next; + const CHAR* type; + const CHAR* version; + BOOL (*Init)(void); + BOOL (*Test)(void); + BOOL (*Load)(BOOL); + void (*Cleanup)(void); + CHAR* (*LoadTitle)(void); +} MLOADER; + +/* internal loader variables */ +extern MREADER* modreader; +extern MODULE of; /* static unimod loading space */ +extern const UWORD finetune[16]; +extern const UWORD npertab[7*OCTAVE];/* used by the original MOD loaders */ + +extern SBYTE remap[UF_MAXCHAN]; /* for removing empty channels */ +extern UBYTE* poslookup; /* lookup table for pattern jumps after + blank pattern removal */ +extern UWORD poslookupcnt; +extern UWORD* origpositions; + +extern BOOL filters; /* resonant filters in use */ +extern UBYTE activemacro; /* active midi macro number for Sxx */ +extern UBYTE filtermacros[UF_MAXMACRO]; /* midi macro settings */ +extern FILTER filtersettings[UF_MAXFILTER]; /* computed filter settings */ + +extern int* noteindex; + +/*========== Internal loader interface */ + +extern BOOL ReadComment(UWORD); +extern BOOL ReadLinedComment(UWORD,UWORD); +extern BOOL AllocPositions(int); +extern BOOL AllocPatterns(void); +extern BOOL AllocTracks(void); +extern BOOL AllocInstruments(void); +extern BOOL AllocSamples(void); +extern CHAR* DupStr(const CHAR*, UWORD, BOOL); + +/* loader utility functions */ +extern int* AllocLinear(void); +extern void FreeLinear(void); +extern int speed_to_finetune(ULONG,int); +extern void S3MIT_ProcessCmd(UBYTE,UBYTE,unsigned int); +extern void S3MIT_CreateOrders(BOOL); + +/* flags for S3MIT_ProcessCmd */ +#define S3MIT_OLDSTYLE 1 /* behave as old scream tracker */ +#define S3MIT_IT 2 /* behave as impulse tracker */ +#define S3MIT_SCREAM 4 /* enforce scream tracker specific limits */ + +/* used to convert c4spd to linear XM periods (IT and IMF loaders). */ +extern UWORD getlinearperiod(UWORD,ULONG); +extern ULONG getfrequency(UWORD,ULONG); + +/* loader shared data */ +#define STM_NTRACKERS 3 +extern const CHAR *STM_Signatures[STM_NTRACKERS]; + +/*========== Player interface */ + +extern int Player_Init(MODULE*); +extern void Player_Exit(MODULE*); +extern void Player_HandleTick(void); + +/*========== UnPackers */ + +typedef BOOL (*MUNPACKER) (struct MREADER*, + void** /* unpacked data out */ , + long* /* unpacked data size */ ); +extern BOOL PP20_Unpack(MREADER*, void**, long*); +extern BOOL MMCMP_Unpack(MREADER*, void**, long*); +extern BOOL XPK_Unpack(MREADER*, void**, long*); +extern BOOL S404_Unpack(MREADER*, void**, long*); + +/*========== Drivers */ + +/* max. number of handles a driver has to provide. (not strict) */ +#define MAXSAMPLEHANDLES 384 + +/* These variables can be changed at ANY time and results will be immediate */ +extern UWORD md_bpm; /* current song / hardware BPM rate */ + +/* Variables below can be changed via MD_SetNumVoices at any time. However, a + call to MD_SetNumVoicess while the driver is active will cause the sound to + skip slightly. */ +extern UBYTE md_numchn; /* number of song + sound effects voices */ +extern UBYTE md_sngchn; /* number of song voices */ +extern UBYTE md_sfxchn; /* number of sound effects voices */ +extern UBYTE md_hardchn; /* number of hardware mixed voices */ +extern UBYTE md_softchn; /* number of software mixed voices */ + +/* This is for use by the hardware drivers only. It points to the registered + tickhandler function. */ +extern void (*md_player)(void); + +extern SWORD MD_SampleLoad(SAMPLOAD*,int); +extern void MD_SampleUnload(SWORD); +extern ULONG MD_SampleSpace(int); +extern ULONG MD_SampleLength(int,SAMPLE*); + +/* uLaw conversion */ +extern void unsignedtoulaw(char *,int); + +/* Parameter extraction helper */ +extern CHAR *MD_GetAtom(const CHAR*, const CHAR*, BOOL); + +/* Internal software mixer stuff */ +extern void VC_SetupPointers(void); +extern int VC1_Init(void); +extern int VC2_Init(void); + +#if (MIKMOD_UNIX) +/* POSIX helper functions */ +extern BOOL MD_Access(const CHAR *); +extern int MD_DropPrivileges(void); +#endif + +/* Macro to define a missing driver, yet allowing binaries to dynamically link + with the library without missing symbol errors */ +#define MISSING(a) MDRIVER a = { NULL, NULL, NULL, 0, 0 } + +/*========== Prototypes for non-MT safe versions of some public functions */ + +extern void _mm_registerdriver(struct MDRIVER*); +extern void _mm_registerloader(struct MLOADER*); +extern BOOL MikMod_Active_internal(void); +extern void MikMod_DisableOutput_internal(void); +extern int MikMod_EnableOutput_internal(void); +extern void MikMod_Exit_internal(void); +extern int MikMod_SetNumVoices_internal(int,int); +extern void Player_Exit_internal(MODULE*); +extern void Player_Stop_internal(void); +extern BOOL Player_Paused_internal(void); +extern void Sample_Free_internal(SAMPLE*); +extern void Voice_Play_internal(SBYTE,SAMPLE*,ULONG); +extern void Voice_SetFrequency_internal(SBYTE,ULONG); +extern void Voice_SetPanning_internal(SBYTE,ULONG); +extern void Voice_SetVolume_internal(SBYTE,UWORD); +extern void Voice_Stop_internal(SBYTE); +extern BOOL Voice_Stopped_internal(SBYTE); + +extern int VC1_PlayStart(void); +extern int VC2_PlayStart(void); +extern void VC1_PlayStop(void); +extern void VC2_PlayStop(void); +extern int VC1_SetNumVoices(void); +extern int VC2_SetNumVoices(void); + +extern MikMod_callback_t vc_callback; + +#ifdef __cplusplus +} +#endif + +/*========== SIMD mixing routines */ +#undef HAVE_ALTIVEC +#undef HAVE_SSE2 +#if defined(MIKMOD_SIMD) + +#if (defined(__ppc__) || defined(__ppc64__)) && defined(__VEC__) && !(defined(__GNUC__) && (__GNUC__ < 3)) +#define HAVE_ALTIVEC + +#elif defined(__GNUC__) && defined(__SSE2__) /* x86 / x86_64 */ +#define HAVE_SSE2 + +#elif defined(_MSC_VER) && (_MSC_VER >= 1300) && (defined(_M_IX86) || defined(_M_AMD64)) +/* FIXME: emmintrin.h requires VC6 processor pack or VC2003+ */ +#define HAVE_SSE2 +/* avoid some warnings */ +#pragma warning(disable:4761) +#pragma warning(disable:4391) +#pragma warning(disable:4244) + +#endif /* AltiVec/SSE2 */ +#endif /* MIKMOD_SIMD */ + +/*========== SIMD mixing helper functions =============*/ + +#if defined(_WIN64) +# if defined(_MSC_VER) +# define IS_ALIGNED_16(ptr) (!((__int64)(ptr) & 15i64)) +# else /* GCC, LCC, .. */ +# define IS_ALIGNED_16(ptr) (!((long long)(ptr) & 15LL)) +# endif +#else /* long cast should be OK for all else */ +#define IS_ALIGNED_16(ptr) (!((long)(ptr) & 15L)) +#endif + +/* Altivec helper function */ +#if defined HAVE_ALTIVEC + +#define simd_m128i vector signed int +#define simd_m128 vector float + +#ifdef __GNUC__ +#include +#endif + +/* Helper functions */ + +/* Set single float across the four values */ +static inline vector float vec_mul(const vector float a, const vector float b) { + return vec_madd(a, b, (const vector float)(0.f)); +} + +/* Set single float across the four values */ +static inline vector float vec_load_ps1(const float *pF) { + vector float data = vec_lde(0, pF); + return vec_splat(vec_perm(data, data, vec_lvsl(0, pF)), 0); +} + +/* Set vector to 0 */ +static inline vector float vec_setzero() { + return (vector float) (0.); +} + +static inline vector signed char vec_set1_8(unsigned char splatchar) { + vector unsigned char splatmap = vec_lvsl(0, &splatchar); + vector unsigned char result = vec_lde(0, &splatchar); + splatmap = vec_splat(splatmap, 0); + return (vector signed char)vec_perm(result, result, splatmap); +} + +#define PERM_A0 0x00,0x01,0x02,0x03 +#define PERM_A1 0x04,0x05,0x06,0x07 +#define PERM_A2 0x08,0x09,0x0A,0x0B +#define PERM_A3 0x0C,0x0D,0x0E,0x0F +#define PERM_B0 0x10,0x11,0x12,0x13 +#define PERM_B1 0x14,0x15,0x16,0x17 +#define PERM_B2 0x18,0x19,0x1A,0x1B +#define PERM_B3 0x1C,0x1D,0x1E,0x1F + +/* Equivalent to _mm_unpacklo_epi32 */ +static inline vector signed int vec_unpacklo(vector signed int a, vector signed int b) { + return vec_perm(a, b, (vector unsigned char)(PERM_A0,PERM_A1,PERM_B0,PERM_B1)); +} + +/* Equivalent to _mm_srli_si128(a, 8) (without the zeroing in high part). */ +static inline vector signed int vec_hiqq(vector signed int a) { + vector signed int b = vec_splat_s32(0); + return vec_perm(a, b, (vector unsigned char)(PERM_A2,PERM_A3,PERM_B2,PERM_B3)); +} + +/* vec_sra is max +15. We have to do in two times ... */ +#define EXTRACT_SAMPLE_SIMD_F(srce, var, size, mul) var = vec_mul(vec_ctf(vec_sra(vec_ld(0, (vector signed int const *)(srce)), vec_splat_u32(BITSHIFT-size)),0), mul); +#define EXTRACT_SAMPLE_SIMD_0(srce, var) var = vec_sra(vec_sra(vec_ld(0, (vector signed int const *)(srce)), vec_splat_u32(15)), vec_splat_u32(BITSHIFT+16-15-0)); +#define EXTRACT_SAMPLE_SIMD_8(srce, var) var = vec_sra(vec_sra(vec_ld(0, (vector signed int const *)(srce)), vec_splat_u32(15)), vec_splat_u32(BITSHIFT+16-15-8)); +#define EXTRACT_SAMPLE_SIMD_16(srce, var) var = vec_sra(vec_ld(0, (vector signed int const *)(srce)), vec_splat_u32(BITSHIFT+16-16)); +#define PUT_SAMPLE_SIMD_W(dste, v1, v2) vec_st(vec_packs(v1, v2), 0, dste); +#define PUT_SAMPLE_SIMD_B(dste, v1, v2, v3, v4) vec_st(vec_add(vec_packs((vector signed short)vec_packs(v1, v2), (vector signed short)vec_packs(v3, v4)), vec_set1_8(128)), 0, dste); +#define PUT_SAMPLE_SIMD_F(dste, v1) vec_st(v1, 0, dste); +#define LOAD_PS1_SIMD(ptr) vec_load_ps1(ptr) + +#elif defined HAVE_SSE2 + +#include + +/* SSE2 helper function */ + +static __inline __m128i mm_hiqq(const __m128i a) { + return _mm_srli_si128(a, 8); /* get the 64bit upper part. new 64bit upper is undefined (zeroed is fine). */ +} + +/* 128-bit mixing macros */ +#define EXTRACT_SAMPLE_SIMD(srce, var, size) var = _mm_srai_epi32(_mm_load_si128((__m128i const *)(srce)), BITSHIFT+16-size); +#define EXTRACT_SAMPLE_SIMD_F(srce, var, size, mul) var = _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_load_si128((__m128i const *)(srce)), BITSHIFT-size)), mul); +#define EXTRACT_SAMPLE_SIMD_0(srce, var) EXTRACT_SAMPLE_SIMD(srce, var, 0) +#define EXTRACT_SAMPLE_SIMD_8(srce, var) EXTRACT_SAMPLE_SIMD(srce, var, 8) +#define EXTRACT_SAMPLE_SIMD_16(srce, var) EXTRACT_SAMPLE_SIMD(srce, var, 16) +#define PUT_SAMPLE_SIMD_W(dste, v1, v2) _mm_store_si128((__m128i*)(dste), _mm_packs_epi32(v1, v2)); +#define PUT_SAMPLE_SIMD_B(dste, v1, v2, v3, v4) _mm_store_si128((__m128i*)(dste), _mm_add_epi8(_mm_packs_epi16(_mm_packs_epi32(v1, v2), _mm_packs_epi32(v3, v4)), _mm_set1_epi8(128))); +#define PUT_SAMPLE_SIMD_F(dste, v1) _mm_store_ps((float*)(dste), v1); +#define LOAD_PS1_SIMD(ptr) _mm_load_ps1(ptr) +#define simd_m128i __m128i +#define simd_m128 __m128 + +#endif + +#if defined(HAVE_SSE2) || defined(HAVE_ALTIVEC) +/* MikMod_amalloc() returns a 16 byte aligned zero-filled + memory in SIMD-enabled builds. + - the returned memory can be freed with MikMod_afree() + - the returned memory CAN NOT be realloc()'ed safely. */ +#ifdef __cplusplus +extern "C" { +#endif +void* MikMod_amalloc(size_t); +void MikMod_afree(void *); /* frees if ptr != NULL */ +#ifdef __cplusplus +} +#endif + +#else /* NO SIMD */ +#define MikMod_amalloc MikMod_malloc +#define MikMod_afree MikMod_free +#endif + +#endif /* _MIKMOD_INTERNALS_H */ + +/* ex:set ts=4: */ diff --git a/libs/mikmod/loaders/load_it.c b/libs/mikmod/loaders/load_it.c new file mode 100644 index 0000000..efe63a9 --- /dev/null +++ b/libs/mikmod/loaders/load_it.c @@ -0,0 +1,1020 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file + AUTHORS for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Impulse tracker (IT) module loader + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" +#include "mikmod_ctype.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Module structure */ + +/* header */ +typedef struct ITHEADER { + CHAR songname[26]; + UBYTE blank01[2]; + UWORD ordnum; + UWORD insnum; + UWORD smpnum; + UWORD patnum; + UWORD cwt; /* Created with tracker (y.xx = 0x0yxx) */ + UWORD cmwt; /* Compatible with tracker ver > than val. */ + UWORD flags; + UWORD special; /* bit 0 set = song message attached */ + UBYTE globvol; + UBYTE mixvol; /* mixing volume [ignored] */ + UBYTE initspeed; + UBYTE inittempo; + UBYTE pansep; /* panning separation between channels */ + UBYTE zerobyte; + UWORD msglength; + ULONG msgoffset; + UBYTE blank02[4]; + UBYTE pantable[64]; + UBYTE voltable[64]; +} ITHEADER; + +/* sample information */ +typedef struct ITSAMPLE { + CHAR filename[12]; + UBYTE zerobyte; + UBYTE globvol; + UBYTE flag; + UBYTE volume; + UBYTE panning; + CHAR sampname[28]; + UWORD convert; /* sample conversion flag */ + ULONG length; + ULONG loopbeg; + ULONG loopend; + ULONG c5spd; + ULONG susbegin; + ULONG susend; + ULONG sampoffset; + UBYTE vibspeed; + UBYTE vibdepth; + UBYTE vibrate; + UBYTE vibwave; /* 0=sine, 1=rampdown, 2=square, 3=random (speed ignored) */ +} ITSAMPLE; + +/* instrument information */ + +#define ITENVCNT 25 +#define ITNOTECNT 120 +typedef struct ITINSTHEADER { + ULONG size; /* (dword) Instrument size */ + CHAR filename[12]; /* (char) Instrument filename */ + UBYTE zerobyte; /* (byte) Instrument type (always 0) */ + UBYTE volflg; + UBYTE volpts; + UBYTE volbeg; /* (byte) Volume loop start (node) */ + UBYTE volend; /* (byte) Volume loop end (node) */ + UBYTE volsusbeg; /* (byte) Volume sustain begin (node) */ + UBYTE volsusend; /* (byte) Volume Sustain end (node) */ + UBYTE panflg; + UBYTE panpts; + UBYTE panbeg; /* (byte) channel loop start (node) */ + UBYTE panend; /* (byte) channel loop end (node) */ + UBYTE pansusbeg; /* (byte) channel sustain begin (node) */ + UBYTE pansusend; /* (byte) channel Sustain end (node) */ + UBYTE pitflg; + UBYTE pitpts; + UBYTE pitbeg; /* (byte) pitch loop start (node) */ + UBYTE pitend; /* (byte) pitch loop end (node) */ + UBYTE pitsusbeg; /* (byte) pitch sustain begin (node) */ + UBYTE pitsusend; /* (byte) pitch Sustain end (node) */ + UWORD blank; + UBYTE globvol; + UBYTE chanpan; + UWORD fadeout; /* Envelope end / NNA volume fadeout */ + UBYTE dnc; /* Duplicate note check */ + UBYTE dca; /* Duplicate check action */ + UBYTE dct; /* Duplicate check type */ + UBYTE nna; /* New Note Action [0,1,2,3] */ + UWORD trkvers; /* tracker version used to save [files only] */ + UBYTE ppsep; /* Pitch-pan Separation */ + UBYTE ppcenter; /* Pitch-pan Center */ + UBYTE rvolvar; /* random volume varations */ + UBYTE rpanvar; /* random panning varations */ + UWORD numsmp; /* Number of samples in instrument [files only] */ + CHAR name[26]; /* Instrument name */ + UBYTE blank01[6]; + UWORD samptable[ITNOTECNT];/* sample for each note [note / samp pairs] */ + UBYTE volenv[200]; /* volume envelope (IT 1.x stuff) */ + UBYTE oldvoltick[ITENVCNT];/* volume tick position (IT 1.x stuff) */ + UBYTE volnode[ITENVCNT]; /* amplitude of volume nodes */ + UWORD voltick[ITENVCNT]; /* tick value of volume nodes */ + SBYTE pannode[ITENVCNT]; /* panenv - node points */ + UWORD pantick[ITENVCNT]; /* tick value of panning nodes */ + SBYTE pitnode[ITENVCNT]; /* pitchenv - node points */ + UWORD pittick[ITENVCNT]; /* tick value of pitch nodes */ +} ITINSTHEADER; + +/* unpacked note */ + +typedef struct ITNOTE { + UBYTE note,ins,volpan,cmd,inf; +} ITNOTE; + +/*========== Loader data */ + +static ULONG *paraptr=NULL; /* parapointer array (see IT docs) */ +static ITHEADER *mh=NULL; +static ITNOTE *itpat=NULL; /* allocate to space for one full pattern */ +static UBYTE *mask=NULL; /* arrays allocated to 64 elements and used for */ +static ITNOTE *last=NULL; /* uncompressing IT's pattern information */ +static int numtrk=0; +static unsigned int old_effect; /* if set, use S3M old-effects stuffs */ + +static const CHAR* IT_Version[]={ + "ImpulseTracker . ", + "Compressed ImpulseTracker . ", + "ImpulseTracker 2.14p3", + "Compressed ImpulseTracker 2.14p3", + "ImpulseTracker 2.14p4", + "Compressed ImpulseTracker 2.14p4", +}; + +/* table for porta-to-note command within volume/panning column */ +static const UBYTE portatable[10]= {0,1,4,8,16,32,64,96,128,255}; + +/*========== Loader code */ + +static BOOL IT_Test(void) +{ + UBYTE id[4]; + + if(!_mm_read_UBYTES(id,4,modreader)) return 0; + if(!memcmp(id,"IMPM",4)) return 1; + return 0; +} + +static BOOL IT_Init(void) +{ + if(!(mh=(ITHEADER*)MikMod_malloc(sizeof(ITHEADER)))) return 0; + if(!(poslookup=(UBYTE*)MikMod_malloc(256*sizeof(UBYTE)))) return 0; + if(!(itpat=(ITNOTE*)MikMod_malloc(200*64*sizeof(ITNOTE)))) return 0; + if(!(mask=(UBYTE*)MikMod_malloc(64*sizeof(UBYTE)))) return 0; + if(!(last=(ITNOTE*)MikMod_malloc(64*sizeof(ITNOTE)))) return 0; + + return 1; +} + +static void IT_Cleanup(void) +{ + FreeLinear(); + + MikMod_free(mh); + MikMod_free(poslookup); + MikMod_free(itpat); + MikMod_free(mask); + MikMod_free(last); + MikMod_free(paraptr); + MikMod_free(origpositions); + mh=NULL; + poslookup=NULL; + itpat=NULL; + mask=NULL; + last=NULL; + paraptr=NULL; + origpositions=NULL; +} + +/* Because so many IT files have 64 channels as the set number used, but really + only use far less (usually from 8 to 24 still), I had to make this function, + which determines the number of channels that are actually USED by a pattern. + + NOTE: You must first seek to the file location of the pattern before calling + this procedure. + + Returns 0 on error +*/ +static BOOL IT_GetNumChannels(UWORD patrows) +{ + int row=0,flag,ch; + + do { + if((flag=_mm_read_UBYTE(modreader))==EOF) { + _mm_errno=MMERR_LOADING_PATTERN; + return 0; + } + if(!flag) + row++; + else { + ch=(flag-1)&63; + remap[ch]=0; + if(flag & 128) mask[ch]=_mm_read_UBYTE(modreader); + if(mask[ch]&1) _mm_skip_BYTE(modreader); + if(mask[ch]&2) _mm_skip_BYTE(modreader); + if(mask[ch]&4) _mm_skip_BYTE(modreader); + if(mask[ch]&8) { _mm_skip_BYTE(modreader);_mm_skip_BYTE(modreader); } + } + } while(rownote=n->note=_mm_read_UBYTE(modreader))==255) + l->note=n->note=253; + if(mask[ch]&2) + l->ins=n->ins=_mm_read_UBYTE(modreader); + if(mask[ch]&4) + l->volpan=n->volpan=_mm_read_UBYTE(modreader); + if(mask[ch]&8) { + l->cmd=n->cmd=_mm_read_UBYTE(modreader); + l->inf=n->inf=_mm_read_UBYTE(modreader); + } + if(mask[ch]&16) + n->note=l->note; + if(mask[ch]&32) + n->ins=l->ins; + if(mask[ch]&64) + n->volpan=l->volpan; + if(mask[ch]&128) { + n->cmd=l->cmd; + n->inf=l->inf; + } + } + } while(rowsongname,26,modreader); + _mm_read_UBYTES(mh->blank01,2,modreader); + mh->ordnum =_mm_read_I_UWORD(modreader); + mh->insnum =_mm_read_I_UWORD(modreader); + mh->smpnum =_mm_read_I_UWORD(modreader); + mh->patnum =_mm_read_I_UWORD(modreader); + mh->cwt =_mm_read_I_UWORD(modreader); + mh->cmwt =_mm_read_I_UWORD(modreader); + mh->flags =_mm_read_I_UWORD(modreader); + mh->special =_mm_read_I_UWORD(modreader); + mh->globvol =_mm_read_UBYTE(modreader); + mh->mixvol =_mm_read_UBYTE(modreader); + mh->initspeed =_mm_read_UBYTE(modreader); + mh->inittempo =_mm_read_UBYTE(modreader); + mh->pansep =_mm_read_UBYTE(modreader); + mh->zerobyte =_mm_read_UBYTE(modreader); + mh->msglength =_mm_read_I_UWORD(modreader); + mh->msgoffset =_mm_read_I_ULONG(modreader); + _mm_read_UBYTES(mh->blank02,4,modreader); + _mm_read_UBYTES(mh->pantable,64,modreader); + _mm_read_UBYTES(mh->voltable,64,modreader); + + if(_mm_eof(modreader)) { + _mm_errno=MMERR_LOADING_HEADER; + return 0; + } + + /* set module variables */ + of.songname = DupStr(mh->songname,26,0); /* make a cstr of songname */ + of.reppos = 0; + of.numpat = mh->patnum; + of.numins = mh->insnum; + of.numsmp = mh->smpnum; + of.initspeed = mh->initspeed; + of.inittempo = mh->inittempo; + of.initvolume = mh->globvol; + of.flags |= UF_BGSLIDES | UF_ARPMEM; + if (!(mh->flags & 1)) + of.flags |= UF_PANNING; + of.bpmlimit=32; + + if(mh->songname[25]) { + of.numvoices=1+mh->songname[25]; +#ifdef MIKMOD_DEBUG + fprintf(stderr,"Embedded IT limitation to %d voices\n",of.numvoices); +#endif + } + + /* set the module type */ + /* 2.17 : IT 2.14p4 */ + /* 2.16 : IT 2.14p3 with resonant filters */ + /* 2.15 : IT 2.14p3 (improved compression) */ + if((mh->cwt<=0x219)&&(mh->cwt>=0x217)) + of.modtype=MikMod_strdup(IT_Version[mh->cmwt<0x214?4:5]); + else if (mh->cwt>=0x215) + of.modtype=MikMod_strdup(IT_Version[mh->cmwt<0x214?2:3]); + else { + of.modtype = MikMod_strdup(IT_Version[mh->cmwt<0x214?0:1]); + of.modtype[mh->cmwt<0x214?15:26] = (mh->cwt>>8)+'0'; + of.modtype[mh->cmwt<0x214?17:28] = ((mh->cwt>>4)&0xf)+'0'; + of.modtype[mh->cmwt<0x214?18:29] = ((mh->cwt)&0xf)+'0'; + } + + if(mh->flags&8) + of.flags |= UF_XMPERIODS | UF_LINEAR; + + if((mh->cwt>=0x106)&&(mh->flags&16)) + old_effect=S3MIT_OLDSTYLE; + else + old_effect=0; + + /* set panning positions */ + if (mh->flags & 1) + for(t=0;t<64;t++) { + mh->pantable[t]&=0x7f; + if(mh->pantable[t]<64) + of.panning[t]=mh->pantable[t]<<2; + else if(mh->pantable[t]==64) + of.panning[t]=255; + else if(mh->pantable[t]==100) + of.panning[t]=PAN_SURROUND; + else if(mh->pantable[t]==127) + of.panning[t]=PAN_CENTER; + else { + _mm_errno=MMERR_LOADING_HEADER; + return 0; + } + } + else + for(t=0;t<64;t++) + of.panning[t]=PAN_CENTER; + + /* set channel volumes */ + memcpy(of.chanvol,mh->voltable,64); + + /* read the order data */ + if(!AllocPositions(mh->ordnum)) return 0; + if(!(origpositions=(UWORD*)MikMod_calloc(mh->ordnum,sizeof(UWORD)))) return 0; + + for(t=0;tordnum;t++) { + origpositions[t]=_mm_read_UBYTE(modreader); + if((origpositions[t]>mh->patnum)&&(origpositions[t]<254)) + origpositions[t]=255; + } + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + poslookupcnt=mh->ordnum; + S3MIT_CreateOrders(curious); + + if(!(paraptr=(ULONG*)MikMod_malloc((mh->insnum+mh->smpnum+of.numpat)* + sizeof(ULONG)))) return 0; + + /* read the instrument, sample, and pattern parapointers */ + _mm_read_I_ULONGS(paraptr,mh->insnum+mh->smpnum+of.numpat,modreader); + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* Check for and load midi information for resonant filters */ + if(mh->cmwt>=0x216) { + if(mh->special&8) { + IT_LoadMidiConfiguration(modreader); + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + } else + IT_LoadMidiConfiguration(NULL); + filters=1; + } + + /* Check for and load song comment */ + if((mh->special&1)&&(mh->cwt>=0x104)&&(mh->msglength)) { + _mm_fseek(modreader,(long)(mh->msgoffset),SEEK_SET); + if(!ReadComment(mh->msglength)) return 0; + } + + if(!(mh->flags&4)) of.numins=of.numsmp; + if(!AllocSamples()) return 0; + + if(!AllocLinear()) return 0; + + /* Load all samples */ + q = of.samples; + for(t=0;tsmpnum;t++) { + ITSAMPLE s; + + /* seek to sample position */ + _mm_fseek(modreader,(long)(paraptr[mh->insnum+t]+4),SEEK_SET); + + /* load sample info */ + _mm_read_string(s.filename,12,modreader); + s.zerobyte = _mm_read_UBYTE(modreader); + s.globvol = _mm_read_UBYTE(modreader); + s.flag = _mm_read_UBYTE(modreader); + s.volume = _mm_read_UBYTE(modreader); + _mm_read_string(s.sampname,26,modreader); + s.convert = _mm_read_UBYTE(modreader); + s.panning = _mm_read_UBYTE(modreader); + s.length = _mm_read_I_ULONG(modreader); + s.loopbeg = _mm_read_I_ULONG(modreader); + s.loopend = _mm_read_I_ULONG(modreader); + s.c5spd = _mm_read_I_ULONG(modreader); + s.susbegin = _mm_read_I_ULONG(modreader); + s.susend = _mm_read_I_ULONG(modreader); + s.sampoffset = _mm_read_I_ULONG(modreader); + s.vibspeed = _mm_read_UBYTE(modreader); + s.vibdepth = _mm_read_UBYTE(modreader); + s.vibrate = _mm_read_UBYTE(modreader); + s.vibwave = _mm_read_UBYTE(modreader); + + /* Generate an error if c5spd is > 512k, or samplelength > 256 megs + (nothing would EVER be that high) */ + + if(_mm_eof(modreader)||(s.c5spd>0x7ffffL)||(s.length>0xfffffffUL)) { + _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + + /* Reality check for sample loop information */ + if((s.flag&16)&& + ((s.loopbeg>0xfffffffUL)||(s.loopend>0xfffffffUL))) { + _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + + q->samplename = DupStr(s.sampname,26,0); + q->speed = s.c5spd / 2; + q->panning = ((s.panning&127)==64)?255:(s.panning&127)<<2; + q->length = s.length; + q->loopstart = s.loopbeg; + q->loopend = s.loopend; + q->volume = s.volume; + q->globvol = s.globvol; + q->seekpos = s.sampoffset; + + /* Convert speed to XM linear finetune */ + if(of.flags&UF_LINEAR) + q->speed=speed_to_finetune(s.c5spd,t); + + if(s.panning&128) q->flags|=SF_OWNPAN; + + if(s.vibrate) { + q->vibflags |= AV_IT; + q->vibtype = s.vibwave; + q->vibsweep = s.vibrate * 2; + q->vibdepth = s.vibdepth; + q->vibrate = s.vibspeed; + } + + if(s.flag&2) q->flags|=SF_16BITS; + if((s.flag&8)&&(mh->cwt>=0x214)) { + q->flags|=SF_ITPACKED; + /*compressed=1;*/ + } + if(s.flag&16) q->flags|=SF_LOOP; + if(s.flag&64) q->flags|=SF_BIDI; + + if(mh->cwt>=0x200) { + if(s.convert&1) q->flags|=SF_SIGNED; + if(s.convert&4) q->flags|=SF_DELTA; + } + q++; + } + + /* Load instruments if instrument mode flag enabled */ + if(mh->flags&4) { + if(!AllocInstruments()) return 0; + d=of.instruments; + of.flags|=UF_NNA|UF_INST; + + for(t=0;tinsnum;t++) { + ITINSTHEADER ih; + + /* seek to instrument position */ + _mm_fseek(modreader,paraptr[t]+4,SEEK_SET); + + /* load instrument info */ + _mm_read_string(ih.filename,12,modreader); + ih.zerobyte = _mm_read_UBYTE(modreader); + if(mh->cwt<0x200) { + /* load IT 1.xx inst header */ + ih.volflg = _mm_read_UBYTE(modreader); + ih.volbeg = _mm_read_UBYTE(modreader); + ih.volend = _mm_read_UBYTE(modreader); + ih.volsusbeg = _mm_read_UBYTE(modreader); + ih.volsusend = _mm_read_UBYTE(modreader); + _mm_read_I_UWORD(modreader); + ih.fadeout = _mm_read_I_UWORD(modreader); + ih.nna = _mm_read_UBYTE(modreader); + ih.dnc = _mm_read_UBYTE(modreader); + } else { + /* Read IT200+ header */ + ih.nna = _mm_read_UBYTE(modreader); + ih.dct = _mm_read_UBYTE(modreader); + ih.dca = _mm_read_UBYTE(modreader); + ih.fadeout = _mm_read_I_UWORD(modreader); + ih.ppsep = _mm_read_UBYTE(modreader); + ih.ppcenter = _mm_read_UBYTE(modreader); + ih.globvol = _mm_read_UBYTE(modreader); + ih.chanpan = _mm_read_UBYTE(modreader); + ih.rvolvar = _mm_read_UBYTE(modreader); + ih.rpanvar = _mm_read_UBYTE(modreader); + } + + ih.trkvers = _mm_read_I_UWORD(modreader); + ih.numsmp = _mm_read_UBYTE(modreader); + _mm_skip_BYTE(modreader); + _mm_read_string(ih.name,26,modreader); + _mm_read_UBYTES(ih.blank01,6,modreader); + _mm_read_I_UWORDS(ih.samptable,ITNOTECNT,modreader); + if(mh->cwt<0x200) { + /* load IT 1xx volume envelope */ + _mm_read_UBYTES(ih.volenv,200,modreader); + for(lp=0;lp ITENVCNT) \ + ih. name##pts = ITENVCNT; \ + ih. name##beg =_mm_read_UBYTE(modreader); \ + ih. name##end =_mm_read_UBYTE(modreader); \ + ih. name##susbeg=_mm_read_UBYTE(modreader); \ + ih. name##susend=_mm_read_UBYTE(modreader); \ + for(lp=0;lp ITENVCNT) \ + ih. name/**/pts = ITENVCNT; \ + ih. name/**/beg =_mm_read_UBYTE(modreader); \ + ih. name/**/end =_mm_read_UBYTE(modreader); \ + ih. name/**/susbeg=_mm_read_UBYTE(modreader); \ + ih. name/**/susend=_mm_read_UBYTE(modreader); \ + for(lp=0;lpvolflg|=EF_VOLENV; + d->insname = DupStr(ih.name,26,0); + d->nnatype = ih.nna & NNA_MASK; + + if(mh->cwt<0x200) { + d->volfade=ih.fadeout<< 6; + if(ih.dnc) { + d->dct=DCT_NOTE; + d->dca=DCA_CUT; + } + + if(ih.volflg&1) d->volflg|=EF_ON; + if(ih.volflg&2) d->volflg|=EF_LOOP; + if(ih.volflg&4) d->volflg|=EF_SUSTAIN; + + /* XM conversion of IT envelope Array */ + d->volbeg = ih.volbeg; + d->volend = ih.volend; + d->volsusbeg = ih.volsusbeg; + d->volsusend = ih.volsusend; + + if(ih.volflg&1) { + for(u=0;uvolpts]!=0xff) { + d->volenv[d->volpts].val=(ih.volnode[d->volpts]<<2); + d->volenv[d->volpts].pos=ih.oldvoltick[d->volpts]; + d->volpts++; + } else + break; + } + } else { + d->panning=((ih.chanpan&127)==64)?255:(ih.chanpan&127)<<2; + if(!(ih.chanpan&128)) d->flags|=IF_OWNPAN; + + if(!(ih.ppsep & 128)) { + d->pitpansep=ih.ppsep<<2; + d->pitpancenter=ih.ppcenter; + d->flags|=IF_PITCHPAN; + } + d->globvol=ih.globvol>>1; + d->volfade=ih.fadeout<<5; + d->dct =ih.dct; + d->dca =ih.dca; + + if(mh->cwt>=0x204) { + d->rvolvar = ih.rvolvar; + d->rpanvar = ih.rpanvar; + } + +#if defined __STDC__ || defined _MSC_VER || defined MPW_C +#define IT_ProcessEnvelope(name) \ + if(ih. name##flg&1) d-> name##flg|=EF_ON; \ + if(ih. name##flg&2) d-> name##flg|=EF_LOOP; \ + if(ih. name##flg&4) d-> name##flg|=EF_SUSTAIN; \ + d-> name##pts=ih. name##pts; \ + d-> name##beg=ih. name##beg; \ + d-> name##end=ih. name##end; \ + d-> name##susbeg=ih. name##susbeg; \ + d-> name##susend=ih. name##susend; \ + \ + for(u=0;u name##env[u].pos=ih. name##tick[u]; \ + \ + if((d-> name##flg&EF_ON)&&(d-> name##pts<2)) \ + d-> name##flg&=~EF_ON +#else +#define IT_ProcessEnvelope(name) \ + if(ih. name/**/flg&1) d-> name/**/flg|=EF_ON; \ + if(ih. name/**/flg&2) d-> name/**/flg|=EF_LOOP; \ + if(ih. name/**/flg&4) d-> name/**/flg|=EF_SUSTAIN; \ + d-> name/**/pts=ih. name/**/pts; \ + d-> name/**/beg=ih. name/**/beg; \ + d-> name/**/end=ih. name/**/end; \ + d-> name/**/susbeg=ih. name/**/susbeg; \ + d-> name/**/susend=ih. name/**/susend; \ + \ + for(u=0;u name/**/env[u].pos=ih. name/**/tick[u]; \ + \ + if((d-> name/**/flg&EF_ON)&&(d-> name/**/pts<2)) \ + d-> name/**/flg&=~EF_ON +#endif + + IT_ProcessEnvelope(vol); + + for(u=0;uvolenv[u].val=(ih.volnode[u]<<2); + + IT_ProcessEnvelope(pan); + for(u=0;upanenv[u].val= + ih.pannode[u]==32?255:(ih.pannode[u]+32)<<2; + + IT_ProcessEnvelope(pit); + for(u=0;upitenv[u].val=ih.pitnode[u]+32; +#undef IT_ProcessEnvelope + + if(ih.pitflg&0x80) { + /* filter envelopes not supported yet */ + d->pitflg&=~EF_ON; + ih.pitpts=ih.pitbeg=ih.pitend=0; +#ifdef MIKMOD_DEBUG + { + static int warn=0; + + if(!warn) + fprintf(stderr, "\rFilter envelopes not supported yet\n"); + warn=1; + } +#endif + } + } + + for(u=0;usamplenote[u]=(ih.samptable[u]&255); + d->samplenumber[u]= + (ih.samptable[u]>>8)?((ih.samptable[u]>>8)-1):0xffff; + if(d->samplenumber[u]>=of.numsmp) + d->samplenote[u]=255; + else if (of.flags&UF_LINEAR) { + int note=(int)d->samplenote[u]+noteindex[d->samplenumber[u]]; + d->samplenote[u]=(note<0)?0:(note>255?255:note); + } + } + + d++; + } + } else if(of.flags & UF_LINEAR) { + if(!AllocInstruments()) return 0; + d=of.instruments; + of.flags|=UF_INST; + + for(t=0;tsmpnum;t++,d++) + for(u=0;usamplenumber[u]>=of.numsmp) + d->samplenote[u]=255; + else { + int note=(int)d->samplenote[u]+noteindex[d->samplenumber[u]]; + d->samplenote[u]=(note<0)?0:(note>255?255:note); + } + } + } + + /* Figure out how many channels this song actually uses */ + of.numchn=0; + memset(remap,-1,UF_MAXCHAN*sizeof(UBYTE)); + for(t=0;tinsnum+mh->smpnum+t]) { /* 0 -> empty 64 row pattern */ + _mm_fseek(modreader,((long)paraptr[mh->insnum+mh->smpnum+t]),SEEK_SET); + _mm_read_I_UWORD(modreader); + /* read pattern length (# of rows) + Impulse Tracker never creates patterns with less than 32 rows, + but some other trackers do, so we only check for more than 256 + rows */ + packlen=_mm_read_I_UWORD(modreader); + if(packlen>256) { + _mm_errno=MMERR_LOADING_PATTERN; + return 0; + } + _mm_read_I_ULONG(modreader); + if(!IT_GetNumChannels(packlen)) return 0; + } + } + + /* give each of them a different number */ + for(t=0;tinsnum+mh->smpnum+t]) { /* 0 -> empty 64 row pattern */ + of.pattrows[t]=64; + for(u=0;uinsnum+mh->smpnum+t]),SEEK_SET); + packlen=_mm_read_I_UWORD(modreader); + (void)packlen; /* unused */ + of.pattrows[t]=_mm_read_I_UWORD(modreader); + _mm_read_I_ULONG(modreader); + if(!IT_ReadPattern(of.pattrows[t])) return 0; + } + } + + return 1; +} + +static CHAR *IT_LoadTitle(void) +{ + CHAR s[26]; + + _mm_fseek(modreader,4,SEEK_SET); + if(!_mm_read_UBYTES(s,26,modreader)) return NULL; + + return(DupStr(s,26,0)); +} + +/*========== Loader information */ + +MIKMODAPI MLOADER load_it={ + NULL, + "IT", + "IT (Impulse Tracker)", + IT_Init, + IT_Test, + IT_Load, + IT_Cleanup, + IT_LoadTitle +}; + +/* ex:set ts=4: */ diff --git a/libs/mikmod/loaders/load_mod.c b/libs/mikmod/loaders/load_mod.c new file mode 100644 index 0000000..6fffac0 --- /dev/null +++ b/libs/mikmod/loaders/load_mod.c @@ -0,0 +1,514 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file + AUTHORS for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Generic MOD loader (Protracker, StarTracker, FastTracker, etc) + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" +#include "mikmod_ctype.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Module structure */ + +typedef struct MSAMPINFO { + CHAR samplename[23]; /* 22 in module, 23 in memory */ + UWORD length; + UBYTE finetune; + UBYTE volume; + UWORD reppos; + UWORD replen; +} MSAMPINFO; + +typedef struct MODULEHEADER { + CHAR songname[21]; /* the songname.. 20 in module, 21 in memory */ + MSAMPINFO samples[31]; /* all sampleinfo */ + UBYTE songlength; /* number of patterns used */ + UBYTE magic1; /* should be 127 */ + UBYTE positions[128]; /* which pattern to play at pos */ + UBYTE magic2[4]; /* string "M.K." or "FLT4" or "FLT8" */ +} MODULEHEADER; + +typedef struct MODTYPE { + CHAR id[5]; + UBYTE channels; + CHAR *name; +} MODTYPE; + +typedef struct MODNOTE { + UBYTE a, b, c, d; +} MODNOTE; + +/*========== Loader variables */ + +#define MODULEHEADERSIZE 0x438 + +static CHAR protracker[] = "Protracker"; +static CHAR startrekker[] = "Startrekker"; +static CHAR fasttracker[] = "Fasttracker"; +static CHAR oktalyser[] = "Oktalyser"; +static CHAR oktalyzer[] = "Oktalyzer"; +static CHAR taketracker[] = "TakeTracker"; +static CHAR orpheus[] = "Imago Orpheus (MOD format)"; + +static MODULEHEADER *mh = NULL; +static MODNOTE *patbuf = NULL; +static int modtype, trekker; + +/*========== Loader code */ + +/* given the module ID, determine the number of channels and the tracker + description ; also alters modtype */ +static BOOL MOD_CheckType(UBYTE *id, UBYTE *numchn, CHAR **descr) +{ + modtype = trekker = 0; + + /* Protracker and variants */ + if ((!memcmp(id, "M.K.", 4)) || (!memcmp(id, "M!K!", 4))) { + *descr = protracker; + modtype = 0; + *numchn = 4; + return 1; + } + + /* Star Tracker */ + if (((!memcmp(id, "FLT", 3)) || (!memcmp(id, "EXO", 3))) && + (mik_isdigit(id[3]))) { + *descr = startrekker; + modtype = trekker = 1; + *numchn = id[3] - '0'; + if (*numchn == 4 || *numchn == 8) + return 1; +#ifdef MIKMOD_DEBUG + else + fprintf(stderr, "\rUnknown FLT%d module type\n", *numchn); +#endif + return 0; + } + + /* Oktalyzer (Amiga) */ + if (!memcmp(id, "OKTA", 4)) { + *descr = oktalyzer; + modtype = 1; + *numchn = 8; + return 1; + } + + /* Oktalyser (Atari) */ + if (!memcmp(id, "CD81", 4)) { + *descr = oktalyser; + modtype = 1; + *numchn = 8; + return 1; + } + + /* Fasttracker */ + if ((!memcmp(id + 1, "CHN", 3)) && (mik_isdigit(id[0]))) { + *descr = fasttracker; + modtype = 1; + *numchn = id[0] - '0'; + return 1; + } + /* Fasttracker or Taketracker */ + if (((!memcmp(id + 2, "CH", 2)) || (!memcmp(id + 2, "CN", 2))) + && (mik_isdigit(id[0])) && (mik_isdigit(id[1]))) { + if (id[3] == 'H') { + *descr = fasttracker; + modtype = 2; /* this can also be Imago Orpheus */ + } else { + *descr = taketracker; + modtype = 1; + } + *numchn = (id[0] - '0') * 10 + (id[1] - '0'); + return 1; + } + + return 0; +} + +static BOOL MOD_Test(void) +{ + UBYTE id[4], numchn; + CHAR *descr; + + _mm_fseek(modreader, MODULEHEADERSIZE, SEEK_SET); + if (!_mm_read_UBYTES(id, 4, modreader)) + return 0; + + if (MOD_CheckType(id, &numchn, &descr)) + return 1; + + return 0; +} + +static BOOL MOD_Init(void) +{ + if (!(mh = (MODULEHEADER *)MikMod_malloc(sizeof(MODULEHEADER)))) + return 0; + return 1; +} + +static void MOD_Cleanup(void) +{ + MikMod_free(mh); + MikMod_free(patbuf); + mh=NULL; + patbuf=NULL; +} + +/* +Old (amiga) noteinfo: + +_____byte 1_____ byte2_ _____byte 3_____ byte4_ +/ \ / \ / \ / \ +0000 0000-00000000 0000 0000-00000000 + +Upper four 12 bits for Lower four Effect command. +bits of sam- note period. bits of sam- +ple number. ple number. + +*/ + +static UBYTE ConvertNote(MODNOTE *n, UBYTE lasteffect) +{ + UBYTE instrument, effect, effdat, note; + UWORD period; + UBYTE lastnote = 0; + + /* extract the various information from the 4 bytes that make up a note */ + instrument = (n->a & 0x10) | (n->c >> 4); + period = (((UWORD)n->a & 0xf) << 8) + n->b; + effect = n->c & 0xf; + effdat = n->d; + + /* Convert the period to a note number */ + note = 0; + if (period) { + for (note = 0; note < 7 * OCTAVE; note++) + if (period >= npertab[note]) + break; + if (note == 7 * OCTAVE) + note = 0; + else + note++; + } + + if (instrument) { + /* if instrument does not exist, note cut */ + if ((instrument > 31) || (!mh->samples[instrument - 1].length)) { + UniPTEffect(0xc, 0); + if (effect == 0xc) + effect = effdat = 0; + } else { + /* Protracker handling */ + if (!modtype) { + /* if we had a note, then change instrument... */ + if (note) + UniInstrument(instrument - 1); + /* ...otherwise, only adjust volume... */ + else { + /* ...unless an effect was specified, which forces a new + note to be played */ + if (effect || effdat) { + UniInstrument(instrument - 1); + note = lastnote; + } else + UniPTEffect(0xc, + mh->samples[instrument - + 1].volume & 0x7f); + } + } else { + /* Fasttracker handling */ + UniInstrument(instrument - 1); + if (!note) + note = lastnote; + } + } + } + if (note) { + UniNote(note + 2 * OCTAVE - 1); + lastnote = note; + } + + /* Convert pattern jump from Dec to Hex */ + if (effect == 0xd) + effdat = (((effdat & 0xf0) >> 4) * 10) + (effdat & 0xf); + + /* Volume slide, up has priority */ + if ((effect == 0xa) && (effdat & 0xf) && (effdat & 0xf0)) + effdat &= 0xf0; + + /* Handle ``heavy'' volumes correctly */ + if ((effect == 0xc) && (effdat > 0x40)) + effdat = 0x40; + + /* An isolated 100, 200 or 300 effect should be ignored (no + "standalone" porta memory in mod files). However, a sequence such + as 1XX, 100, 100, 100 is fine. */ + if ((!effdat) && ((effect == 1)||(effect == 2)||(effect ==3)) && + (lasteffect < 0x10) && (effect != lasteffect)) + effect = 0; + + UniPTEffect(effect, effdat); + if (effect == 8) + of.flags |= UF_PANNING; + + return effect; +} + +static UBYTE *ConvertTrack(MODNOTE *n, int numchn) +{ + int t; + UBYTE lasteffect = 0x10; /* non existant effect */ + + UniReset(); + for (t = 0; t < 64; t++) { + lasteffect = ConvertNote(n,lasteffect); + UniNewline(); + n += numchn; + } + return UniDup(); +} + +/* Loads all patterns of a modfile and converts them into the 3 byte format. */ +static BOOL ML_LoadPatterns(void) +{ + int t, s, tracks = 0; + + if (!AllocPatterns()) + return 0; + if (!AllocTracks()) + return 0; + + /* Allocate temporary buffer for loading and converting the patterns */ + if (!(patbuf = (MODNOTE *)MikMod_calloc(64U * of.numchn, sizeof(MODNOTE)))) + return 0; + + if (trekker && of.numchn == 8) { + /* Startrekker module dual pattern */ + for (t = 0; t < of.numpat; t++) { + for (s = 0; s < (64U * 4); s++) { + patbuf[s].a = _mm_read_UBYTE(modreader); + patbuf[s].b = _mm_read_UBYTE(modreader); + patbuf[s].c = _mm_read_UBYTE(modreader); + patbuf[s].d = _mm_read_UBYTE(modreader); + } + for (s = 0; s < 4; s++) + if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s, 4))) + return 0; + for (s = 0; s < (64U * 4); s++) { + patbuf[s].a = _mm_read_UBYTE(modreader); + patbuf[s].b = _mm_read_UBYTE(modreader); + patbuf[s].c = _mm_read_UBYTE(modreader); + patbuf[s].d = _mm_read_UBYTE(modreader); + } + for (s = 0; s < 4; s++) + if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s, 4))) + return 0; + } + } else { + /* Generic module pattern */ + for (t = 0; t < of.numpat; t++) { + /* Load the pattern into the temp buffer and convert it */ + for (s = 0; s < (64U * of.numchn); s++) { + patbuf[s].a = _mm_read_UBYTE(modreader); + patbuf[s].b = _mm_read_UBYTE(modreader); + patbuf[s].c = _mm_read_UBYTE(modreader); + patbuf[s].d = _mm_read_UBYTE(modreader); + } + for (s = 0; s < of.numchn; s++) + if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s, of.numchn))) + return 0; + } + } + return 1; +} + +static BOOL MOD_Load(BOOL curious) +{ + int t, scan; + SAMPLE *q; + MSAMPINFO *s; + CHAR *descr; + + /* try to read module header */ + _mm_read_string((CHAR *)mh->songname, 20, modreader); + mh->songname[20] = 0; /* just in case */ + + for (t = 0; t < 31; t++) { + s = &mh->samples[t]; + _mm_read_string(s->samplename, 22, modreader); + s->samplename[22] = 0; /* just in case */ + s->length = _mm_read_M_UWORD(modreader); + s->finetune = _mm_read_UBYTE(modreader); + s->volume = _mm_read_UBYTE(modreader); + s->reppos = _mm_read_M_UWORD(modreader); + s->replen = _mm_read_M_UWORD(modreader); + } + + mh->songlength = _mm_read_UBYTE(modreader); + + /* this fixes mods which declare more than 128 positions. + * eg: beatwave.mod */ + if (mh->songlength > 128) { mh->songlength = 128; } + + mh->magic1 = _mm_read_UBYTE(modreader); + _mm_read_UBYTES(mh->positions, 128, modreader); + _mm_read_UBYTES(mh->magic2, 4, modreader); + + if (_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* set module variables */ + of.initspeed = 6; + of.inittempo = 125; + if (!(MOD_CheckType(mh->magic2, &of.numchn, &descr))) { + _mm_errno = MMERR_NOT_A_MODULE; + return 0; + } + if (trekker && of.numchn == 8) + for (t = 0; t < 128; t++) + /* if module pretends to be FLT8, yet the order table + contains odd numbers, chances are it's a lying FLT4... */ + if (mh->positions[t] & 1) { + of.numchn = 4; + break; + } + if (trekker && of.numchn == 8) + for (t = 0; t < 128; t++) + mh->positions[t] >>= 1; + + of.songname = DupStr(mh->songname, 21, 1); + of.numpos = mh->songlength; + of.reppos = 0; + + /* Count the number of patterns */ + of.numpat = 0; + for (t = 0; t < of.numpos; t++) + if (mh->positions[t] > of.numpat) + of.numpat = mh->positions[t]; + + /* since some old modules embed extra patterns, we have to check the + whole list to get the samples' file offsets right - however we can find + garbage here, so check carefully */ + scan = 1; + for (t = of.numpos; t < 128; t++) + if (mh->positions[t] >= 0x80) + scan = 0; + if (scan) + for (t = of.numpos; t < 128; t++) { + if (mh->positions[t] > of.numpat) + of.numpat = mh->positions[t]; + if ((curious) && (mh->positions[t])) + of.numpos = t + 1; + } + of.numpat++; + of.numtrk = of.numpat * of.numchn; + + if (!AllocPositions(of.numpos)) + return 0; + for (t = 0; t < of.numpos; t++) + of.positions[t] = mh->positions[t]; + + /* Finally, init the sampleinfo structures */ + of.numins = of.numsmp = 31; + if (!AllocSamples()) + return 0; + s = mh->samples; + q = of.samples; + for (t = 0; t < of.numins; t++) { + /* convert the samplename */ + q->samplename = DupStr(s->samplename, 23, 1); + /* init the sampleinfo variables and convert the size pointers */ + q->speed = finetune[s->finetune & 0xf]; + q->volume = s->volume & 0x7f; + q->loopstart = (ULONG)s->reppos << 1; + q->loopend = q->loopstart + ((ULONG)s->replen << 1); + q->length = (ULONG)s->length << 1; + q->flags = SF_SIGNED; + /* Imago Orpheus creates MODs with 16 bit samples, check */ + if ((modtype == 2) && (s->volume & 0x80)) { + q->flags |= SF_16BITS; + descr = orpheus; + } + if (s->replen > 2) + q->flags |= SF_LOOP; + + s++; + q++; + } + + of.modtype = MikMod_strdup(descr); + + if (!ML_LoadPatterns()) + return 0; + + return 1; +} + +static CHAR *MOD_LoadTitle(void) +{ + CHAR s[21]; + + _mm_fseek(modreader, 0, SEEK_SET); + if (!_mm_read_UBYTES(s, 20, modreader)) + return NULL; + s[20] = 0; /* just in case */ + + return (DupStr(s, 21, 1)); +} + +/*========== Loader information */ + +MIKMODAPI MLOADER load_mod = { + NULL, + "Standard module", + "MOD (31 instruments)", + MOD_Init, + MOD_Test, + MOD_Load, + MOD_Cleanup, + MOD_LoadTitle +}; + +/* ex:set ts=4: */ diff --git a/libs/mikmod/loaders/load_s3m.c b/libs/mikmod/loaders/load_s3m.c new file mode 100644 index 0000000..039650e --- /dev/null +++ b/libs/mikmod/loaders/load_s3m.c @@ -0,0 +1,476 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file + AUTHORS for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Screamtracker (S3M) module loader + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Module structure */ + +/* header */ +typedef struct S3MHEADER { + CHAR songname[28]; + UBYTE t1a; + UBYTE type; + UBYTE unused1[2]; + UWORD ordnum; + UWORD insnum; + UWORD patnum; + UWORD flags; + UWORD tracker; + UWORD fileformat; + CHAR scrm[4]; + UBYTE mastervol; + UBYTE initspeed; + UBYTE inittempo; + UBYTE mastermult; + UBYTE ultraclick; + UBYTE pantable; + UBYTE unused2[8]; + UWORD special; + UBYTE channels[32]; +} S3MHEADER; + +/* sample information */ +typedef struct S3MSAMPLE { + UBYTE type; + CHAR filename[12]; + UBYTE memsegh; + UWORD memsegl; + ULONG length; + ULONG loopbeg; + ULONG loopend; + UBYTE volume; + UBYTE dsk; + UBYTE pack; + UBYTE flags; + ULONG c2spd; + UBYTE unused[12]; + CHAR sampname[28]; + CHAR scrs[4]; +} S3MSAMPLE; + +typedef struct S3MNOTE { + UBYTE note,ins,vol,cmd,inf; +} S3MNOTE; + +/*========== Loader variables */ + +static S3MNOTE *s3mbuf = NULL; /* pointer to a complete S3M pattern */ +static S3MHEADER *mh = NULL; +static UWORD *paraptr = NULL; /* parapointer array (see S3M docs) */ +static unsigned int tracker; /* tracker id */ + +/* tracker identifiers */ +#define NUMTRACKERS 4 +static const CHAR * S3M_Version[] = { + "Screamtracker x.xx", + "Imago Orpheus x.xx (S3M format)", + "Impulse Tracker x.xx (S3M format)", + "Unknown tracker x.xx (S3M format)", + "Impulse Tracker 2.14p3 (S3M format)", + "Impulse Tracker 2.14p4 (S3M format)" +}; +/* version number position in above array */ +static const int numeric[NUMTRACKERS]={14,14,16,16}; + +/*========== Loader code */ + +static BOOL S3M_Test(void) +{ + UBYTE id[4]; + + _mm_fseek(modreader,0x2c,SEEK_SET); + if(!_mm_read_UBYTES(id,4,modreader)) return 0; + if(!memcmp(id,"SCRM",4)) return 1; + return 0; +} + +static BOOL S3M_Init(void) +{ + if(!(s3mbuf=(S3MNOTE*)MikMod_malloc(32*64*sizeof(S3MNOTE)))) return 0; + if(!(mh=(S3MHEADER*)MikMod_malloc(sizeof(S3MHEADER)))) return 0; + if(!(poslookup=(UBYTE*)MikMod_malloc(sizeof(UBYTE)*256))) return 0; + memset(poslookup,-1,256); + + return 1; +} + +static void S3M_Cleanup(void) +{ + MikMod_free(s3mbuf); + MikMod_free(paraptr); + MikMod_free(poslookup); + MikMod_free(mh); + MikMod_free(origpositions); + s3mbuf=NULL; + paraptr=NULL; + poslookup=NULL; + mh=NULL; + origpositions=NULL; +} + +/* Because so many s3m files have 16 channels as the set number used, but really + only use far less (usually 8 to 12 still), I had to make this function, which + determines the number of channels that are actually USED by a pattern. + + For every channel that's used, it sets the appropriate array entry of the + global variable 'remap' + + NOTE: You must first seek to the file location of the pattern before calling + this procedure. + + Returns 0 on fail. */ +static BOOL S3M_GetNumChannels(void) +{ + int row=0,flag,ch; + + while(row<64) { + flag=_mm_read_UBYTE(modreader); + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_PATTERN; + return 0; + } + + if(flag) { + ch=flag&31; + if(mh->channels[ch]<32) remap[ch] = 0; + if(flag&32) {_mm_skip_BYTE(modreader);_mm_skip_BYTE(modreader);} + if(flag&64) _mm_skip_BYTE(modreader); + if(flag&128){_mm_skip_BYTE(modreader);_mm_skip_BYTE(modreader);} + } else row++; + } + return 1; +} + +static BOOL S3M_ReadPattern(void) +{ + int row=0,flag,ch; + S3MNOTE *n,dummy; + + /* clear pattern data */ + memset(s3mbuf,255,32*64*sizeof(S3MNOTE)); + + while(row<64) { + flag=_mm_read_UBYTE(modreader); + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_PATTERN; + return 0; + } + + if(flag) { + ch=remap[flag&31]; + + if(ch!=-1) + n=&s3mbuf[(64U*ch)+row]; + else + n=&dummy; + + if(flag&32) { + n->note=_mm_read_UBYTE(modreader); + n->ins=_mm_read_UBYTE(modreader); + } + if(flag&64) { + n->vol=_mm_read_UBYTE(modreader); + if (n->vol>64) n->vol=64; + } + if(flag&128) { + n->cmd=_mm_read_UBYTE(modreader); + n->inf=_mm_read_UBYTE(modreader); + } + } else row++; + } + return 1; +} + +static UBYTE* S3M_ConvertTrack(S3MNOTE* tr) +{ + int t; + + UniReset(); + for(t=0;t<64;t++) { + UBYTE note,ins,vol; + + note=tr[t].note; + ins=tr[t].ins; + vol=tr[t].vol; + + if((ins)&&(ins!=255)) UniInstrument(ins-1); + if(note!=255) { + if(note==254) { + UniPTEffect(0xc,0); /* note cut command */ + vol=255; + } else + UniNote(((note>>4)*OCTAVE)+(note&0xf)); /* normal note */ + } + if(vol<255) UniPTEffect(0xc,vol); + + S3MIT_ProcessCmd(tr[t].cmd,tr[t].inf, + tracker == 1 ? S3MIT_OLDSTYLE | S3MIT_SCREAM : S3MIT_OLDSTYLE); + UniNewline(); + } + return UniDup(); +} + +static BOOL S3M_Load(BOOL curious) +{ + int t,u,track = 0; + SAMPLE *q; + UBYTE pan[32]; + + /* try to read module header */ + _mm_read_string(mh->songname,28,modreader); + mh->t1a =_mm_read_UBYTE(modreader); + mh->type =_mm_read_UBYTE(modreader); + _mm_read_UBYTES(mh->unused1,2,modreader); + mh->ordnum =_mm_read_I_UWORD(modreader); + mh->insnum =_mm_read_I_UWORD(modreader); + mh->patnum =_mm_read_I_UWORD(modreader); + mh->flags =_mm_read_I_UWORD(modreader); + mh->tracker =_mm_read_I_UWORD(modreader); + mh->fileformat =_mm_read_I_UWORD(modreader); + _mm_read_string(mh->scrm,4,modreader); + mh->mastervol =_mm_read_UBYTE(modreader); + mh->initspeed =_mm_read_UBYTE(modreader); + mh->inittempo =_mm_read_UBYTE(modreader); + mh->mastermult =_mm_read_UBYTE(modreader); + mh->ultraclick =_mm_read_UBYTE(modreader); + mh->pantable =_mm_read_UBYTE(modreader); + _mm_read_UBYTES(mh->unused2,8,modreader); + mh->special =_mm_read_I_UWORD(modreader); + _mm_read_UBYTES(mh->channels,32,modreader); + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* then we can decide the module type */ + tracker=mh->tracker>>12; + if((!tracker)||(tracker>=NUMTRACKERS)) + tracker=NUMTRACKERS-1; /* unknown tracker */ + else { + if(mh->tracker>=0x3217) + tracker=NUMTRACKERS+1; /* IT 2.14p4 */ + else if(mh->tracker>=0x3216) + tracker=NUMTRACKERS; /* IT 2.14p3 */ + else tracker--; + } + of.modtype = MikMod_strdup(S3M_Version[tracker]); + if(trackertracker>>8) &0xf)+'0'; + of.modtype[numeric[tracker]+2] = ((mh->tracker>>4)&0xf)+'0'; + of.modtype[numeric[tracker]+3] = ((mh->tracker)&0xf)+'0'; + } + /* set module variables */ + of.songname = DupStr(mh->songname,28,0); + of.numpat = mh->patnum; + of.reppos = 0; + of.numins = of.numsmp = mh->insnum; + of.initspeed = mh->initspeed; + of.inittempo = mh->inittempo; + of.initvolume = mh->mastervol<<1; + of.flags |= UF_ARPMEM | UF_PANNING; + if((mh->tracker==0x1300)||(mh->flags&64)) + of.flags|=UF_S3MSLIDES; + of.bpmlimit = 32; + + /* read the order data */ + if(!AllocPositions(mh->ordnum)) return 0; + if(!(origpositions=(UWORD*)MikMod_calloc(mh->ordnum,sizeof(UWORD)))) return 0; + + for(t=0;tordnum;t++) { + origpositions[t]=_mm_read_UBYTE(modreader); + if((origpositions[t]>=mh->patnum)&&(origpositions[t]<254)) + origpositions[t]=255/*mh->patnum-1*/; + } + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + poslookupcnt=mh->ordnum; + S3MIT_CreateOrders(curious); + + if(!(paraptr=(UWORD*)MikMod_malloc((of.numins+of.numpat)*sizeof(UWORD)))) + return 0; + + /* read the instrument+pattern parapointers */ + _mm_read_I_UWORDS(paraptr,of.numins+of.numpat,modreader); + + if(mh->pantable==252) { + /* read the panning table (ST 3.2 addition. See below for further + portions of channel panning [past reampper]). */ + _mm_read_UBYTES(pan,32,modreader); + } + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* load samples */ + if(!AllocSamples()) return 0; + q = of.samples; + for(t=0;t 64000 && tracker == 1) + s.length = 64000; + + if(_mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + + q->samplename = DupStr(s.sampname,28,0); + q->speed = s.c2spd; + q->length = s.length; + q->loopstart = s.loopbeg; + q->loopend = s.loopend; + q->volume = s.volume; + q->seekpos = (((ULONG)s.memsegh)<<16|s.memsegl)<<4; + + if(s.flags&1) q->flags |= SF_LOOP; + if(s.flags&4) q->flags |= SF_16BITS; + if(mh->fileformat==1) q->flags |= SF_SIGNED; + + /* don't load sample if it doesn't have the SCRS tag */ + if(memcmp(s.scrs,"SCRS",4)) q->length = 0; + + q++; + } + + /* determine the number of channels actually used. */ + of.numchn = 0; + memset(remap,-1,32*sizeof(UBYTE)); + for(t=0;tchannels[t]<32)&&(remap[t]!=-1)) { + if(mh->channels[t]<8) + of.panning[remap[t]]=0x30; + else + of.panning[remap[t]]=0xc0; + } + if(mh->pantable==252) + /* set panning positions according to panning table (new for st3.2) */ + for(t=0;t<32;t++) + if((pan[t]&0x20)&&(mh->channels[t]<32)&&(remap[t]!=-1)) + of.panning[remap[t]]=(pan[t]&0xf)<<4; + + /* load pattern info */ + of.numtrk=of.numpat*of.numchn; + if(!AllocTracks()) return 0; + if(!AllocPatterns()) return 0; + + for(t=0;t +#endif + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Module structure */ + +typedef struct XMHEADER { + CHAR id[17]; /* ID text: 'Extended module: ' */ + CHAR songname[21]; /* Module name */ + CHAR trackername[20]; /* Tracker name */ + UWORD version; /* Version number */ + ULONG headersize; /* Header size */ + UWORD songlength; /* Song length (in patten order table) */ + UWORD restart; /* Restart position */ + UWORD numchn; /* Number of channels (2,4,6,8,10,...,32) */ + UWORD numpat; /* Number of patterns (max 256) */ + UWORD numins; /* Number of instruments (max 128) */ + UWORD flags; + UWORD tempo; /* Default tempo */ + UWORD bpm; /* Default BPM */ + UBYTE orders[256]; /* Pattern order table */ +} XMHEADER; + +typedef struct XMINSTHEADER { + ULONG size; /* Instrument size */ + CHAR name[22]; /* Instrument name */ + UBYTE type; /* Instrument type (always 0) */ + UWORD numsmp; /* Number of samples in instrument */ + ULONG ssize; +} XMINSTHEADER; + +#define XMENVCNT (12*2) +#define XMNOTECNT (8*OCTAVE) +typedef struct XMPATCHHEADER { + UBYTE what[XMNOTECNT]; /* Sample number for all notes */ + UWORD volenv[XMENVCNT]; /* Points for volume envelope */ + UWORD panenv[XMENVCNT]; /* Points for panning envelope */ + UBYTE volpts; /* Number of volume points */ + UBYTE panpts; /* Number of panning points */ + UBYTE volsus; /* Volume sustain point */ + UBYTE volbeg; /* Volume loop start point */ + UBYTE volend; /* Volume loop end point */ + UBYTE pansus; /* Panning sustain point */ + UBYTE panbeg; /* Panning loop start point */ + UBYTE panend; /* Panning loop end point */ + UBYTE volflg; /* Volume type: bit 0: On; 1: Sustain; 2: Loop */ + UBYTE panflg; /* Panning type: bit 0: On; 1: Sustain; 2: Loop */ + UBYTE vibflg; /* Vibrato type */ + UBYTE vibsweep; /* Vibrato sweep */ + UBYTE vibdepth; /* Vibrato depth */ + UBYTE vibrate; /* Vibrato rate */ + UWORD volfade; /* Volume fadeout */ +} XMPATCHHEADER; + +typedef struct XMWAVHEADER { + ULONG length; /* Sample length */ + ULONG loopstart; /* Sample loop start */ + ULONG looplength; /* Sample loop length */ + UBYTE volume; /* Volume */ + SBYTE finetune; /* Finetune (signed byte -128..+127) */ + UBYTE type; /* Loop type */ + UBYTE panning; /* Panning (0-255) */ + SBYTE relnote; /* Relative note number (signed byte) */ + UBYTE reserved; + CHAR samplename[22]; /* Sample name */ + UBYTE vibtype; /* Vibrato type */ + UBYTE vibsweep; /* Vibrato sweep */ + UBYTE vibdepth; /* Vibrato depth */ + UBYTE vibrate; /* Vibrato rate */ +} XMWAVHEADER; + +typedef struct XMPATHEADER { + ULONG size; /* Pattern header length */ + UBYTE packing; /* Packing type (always 0) */ + UWORD numrows; /* Number of rows in pattern (1..256) */ + SWORD packsize; /* Packed patterndata size */ +} XMPATHEADER; + +typedef struct XMNOTE { + UBYTE note,ins,vol,eff,dat; +} XMNOTE; + +/*========== Loader variables */ + +static XMNOTE *xmpat=NULL; +static XMHEADER *mh=NULL; + +/* increment unit for sample array reallocation */ +#define XM_SMPINCR 64 +static ULONG *nextwav=NULL; +static XMWAVHEADER *wh=NULL,*s=NULL; + +/*========== Loader code */ + +static BOOL XM_Test(void) +{ + UBYTE id[38]; + + if(!_mm_read_UBYTES(id,38,modreader)) return 0; + if(memcmp(id,"Extended Module: ",17)) return 0; + if(id[37]==0x1a) return 1; + return 0; +} + +static BOOL XM_Init(void) +{ + if(!(mh=(XMHEADER *)MikMod_malloc(sizeof(XMHEADER)))) return 0; + return 1; +} + +static void XM_Cleanup(void) +{ + MikMod_free(mh); + mh=NULL; +} + +static int XM_ReadNote(XMNOTE* n) +{ + UBYTE cmp,result=1; + + memset(n,0,sizeof(XMNOTE)); + cmp=_mm_read_UBYTE(modreader); + + if(cmp&0x80) { + if(cmp&1) { result++;n->note = _mm_read_UBYTE(modreader); } + if(cmp&2) { result++;n->ins = _mm_read_UBYTE(modreader); } + if(cmp&4) { result++;n->vol = _mm_read_UBYTE(modreader); } + if(cmp&8) { result++;n->eff = _mm_read_UBYTE(modreader); } + if(cmp&16) { result++;n->dat = _mm_read_UBYTE(modreader); } + } else { + n->note = cmp; + n->ins = _mm_read_UBYTE(modreader); + n->vol = _mm_read_UBYTE(modreader); + n->eff = _mm_read_UBYTE(modreader); + n->dat = _mm_read_UBYTE(modreader); + result += 4; + } + return result; +} + +static UBYTE* XM_Convert(XMNOTE* xmtrack,UWORD rows) +{ + int t; + UBYTE note,ins,vol,eff,dat; + + UniReset(); + for(t=0;tnote; + ins = xmtrack->ins; + vol = xmtrack->vol; + eff = xmtrack->eff; + dat = xmtrack->dat; + + if(note) { + if(note>XMNOTECNT) + UniEffect(UNI_KEYFADE,0); + else + UniNote(note-1); + } + if(ins) UniInstrument(ins-1); + + switch(vol>>4) { + case 0x6: /* volslide down */ + if(vol&0xf) UniEffect(UNI_XMEFFECTA,vol&0xf); + break; + case 0x7: /* volslide up */ + if(vol&0xf) UniEffect(UNI_XMEFFECTA,vol<<4); + break; + + /* volume-row fine volume slide is compatible with protracker + EBx and EAx effects i.e. a zero nibble means DO NOT SLIDE, as + opposed to 'take the last sliding value'. */ + case 0x8: /* finevol down */ + UniPTEffect(0xe,0xb0|(vol&0xf)); + break; + case 0x9: /* finevol up */ + UniPTEffect(0xe,0xa0|(vol&0xf)); + break; + case 0xa: /* set vibrato speed */ + UniEffect(UNI_XMEFFECT4,vol<<4); + break; + case 0xb: /* vibrato */ + UniEffect(UNI_XMEFFECT4,vol&0xf); + break; + case 0xc: /* set panning */ + UniPTEffect(0x8,vol<<4); + break; + case 0xd: /* panning slide left (only slide when data not zero) */ + if(vol&0xf) UniEffect(UNI_XMEFFECTP,vol&0xf); + break; + case 0xe: /* panning slide right (only slide when data not zero) */ + if(vol&0xf) UniEffect(UNI_XMEFFECTP,vol<<4); + break; + case 0xf: /* tone porta */ + UniPTEffect(0x3,vol<<4); + break; + default: + if((vol>=0x10)&&(vol<=0x50)) + UniPTEffect(0xc,vol-0x10); + } + + switch(eff) { + case 0x4: + UniEffect(UNI_XMEFFECT4,dat); + break; + case 0x6: + UniEffect(UNI_XMEFFECT6,dat); + break; + case 0xa: + UniEffect(UNI_XMEFFECTA,dat); + break; + case 0xe: /* Extended effects */ + switch(dat>>4) { + case 0x1: /* XM fine porta up */ + UniEffect(UNI_XMEFFECTE1,dat&0xf); + break; + case 0x2: /* XM fine porta down */ + UniEffect(UNI_XMEFFECTE2,dat&0xf); + break; + case 0xa: /* XM fine volume up */ + UniEffect(UNI_XMEFFECTEA,dat&0xf); + break; + case 0xb: /* XM fine volume down */ + UniEffect(UNI_XMEFFECTEB,dat&0xf); + break; + default: + UniPTEffect(eff,dat); + } + break; + case 'G'-55: /* G - set global volume */ + UniEffect(UNI_XMEFFECTG,dat>64?128:dat<<1); + break; + case 'H'-55: /* H - global volume slide */ + UniEffect(UNI_XMEFFECTH,dat); + break; + case 'K'-55: /* K - keyOff and KeyFade */ + UniEffect(UNI_KEYFADE,dat); + break; + case 'L'-55: /* L - set envelope position */ + UniEffect(UNI_XMEFFECTL,dat); + break; + case 'P'-55: /* P - panning slide */ + UniEffect(UNI_XMEFFECTP,dat); + break; + case 'R'-55: /* R - multi retrig note */ + UniEffect(UNI_S3MEFFECTQ,dat); + break; + case 'T'-55: /* T - Tremor */ + UniEffect(UNI_S3MEFFECTI,dat); + break; + case 'X'-55: + switch(dat>>4) { + case 1: /* X1 - Extra Fine Porta up */ + UniEffect(UNI_XMEFFECTX1,dat&0xf); + break; + case 2: /* X2 - Extra Fine Porta down */ + UniEffect(UNI_XMEFFECTX2,dat&0xf); + break; + } + break; + default: + if(eff<=0xf) { + /* the pattern jump destination is written in decimal, + but it seems some poor tracker software writes them + in hexadecimal... (sigh) */ + if (eff==0xd) + /* don't change anything if we're sure it's in hexa */ + if ((((dat&0xf0)>>4)<=9)&&((dat&0xf)<=9)) + /* otherwise, convert from dec to hex */ + dat=(((dat&0xf0)>>4)*10)+(dat&0xf); + UniPTEffect(eff,dat); + } + break; + } + UniNewline(); + xmtrack++; + } + return UniDup(); +} + +static BOOL LoadPatterns(BOOL dummypat) +{ + int t,u,v,numtrk; + + if(!AllocTracks()) return 0; + if(!AllocPatterns()) return 0; + + numtrk=0; + for(t=0;tnumpat;t++) { + XMPATHEADER ph; + + ph.size =_mm_read_I_ULONG(modreader); + if (ph.size<(mh->version==0x0102?8:9)) { + _mm_errno=MMERR_LOADING_PATTERN; + return 0; + } + ph.packing =_mm_read_UBYTE(modreader); + if(ph.packing) { + _mm_errno=MMERR_LOADING_PATTERN; + return 0; + } + if(mh->version==0x0102) + ph.numrows =_mm_read_UBYTE(modreader)+1; + else + ph.numrows =_mm_read_I_UWORD(modreader); + ph.packsize =_mm_read_I_UWORD(modreader); + + ph.size-=(mh->version==0x0102?8:9); + if(ph.size) + _mm_fseek(modreader,ph.size,SEEK_CUR); + + of.pattrows[t]=ph.numrows; + + if(ph.numrows) { + if(!(xmpat=(XMNOTE*)MikMod_calloc(ph.numrows*of.numchn,sizeof(XMNOTE)))) + return 0; + + /* when packsize is 0, don't try to load a pattern.. it's empty. */ + if(ph.packsize) + for(u=0;upos; + + for (u = 1; u < pts; u++, prev++, cur++) { + if (cur->pos < prev->pos) { + if (cur->pos < 0x100) { + if (cur->pos > old) /* same hex century */ + tmp = cur->pos + (prev->pos - old); + else + tmp = cur->pos | ((prev->pos + 0x100) & 0xff00); + old = cur->pos; + cur->pos = tmp; +#ifdef MIKMOD_DEBUG + fprintf(stderr, "\rbroken envelope position(%d/%d), %d %d -> %d\n", + u, pts, prev->pos, old, cur->pos); +#endif + } else { +#ifdef MIKMOD_DEBUG + /* different brokenness style... fix unknown */ + fprintf(stderr, "\rbroken envelope position(%d/%d), %d %d\n", + u, pts, old, cur->pos); +#endif + old = cur->pos; + } + } else + old = cur->pos; + } +} + +static BOOL LoadInstruments(void) +{ + int t,u, ck; + INSTRUMENT *d; + ULONG next=0; + UWORD wavcnt=0; + + if(!AllocInstruments()) return 0; + d=of.instruments; + for(t=0;tsamplenumber,0xff,INSTNOTES*sizeof(UWORD)); + + /* read instrument header */ + headend = _mm_ftell(modreader); + ih.size = _mm_read_I_ULONG(modreader); + headend += ih.size; + ck = _mm_ftell(modreader); + _mm_fseek(modreader,0,SEEK_END); + if ((headend<0) || (_mm_ftell(modreader)insname = DupStr(ih.name,22,1); + + if((SWORD)ih.size>29) { + ih.ssize = _mm_read_I_ULONG(modreader); + if(((SWORD)ih.numsmp>0)&&(ih.numsmp<=XMNOTECNT)) { + XMPATCHHEADER pth; + int p; + + _mm_read_UBYTES (pth.what,XMNOTECNT,modreader); + _mm_read_I_UWORDS (pth.volenv, XMENVCNT, modreader); + _mm_read_I_UWORDS (pth.panenv, XMENVCNT, modreader); + pth.volpts = _mm_read_UBYTE(modreader); + pth.panpts = _mm_read_UBYTE(modreader); + pth.volsus = _mm_read_UBYTE(modreader); + pth.volbeg = _mm_read_UBYTE(modreader); + pth.volend = _mm_read_UBYTE(modreader); + pth.pansus = _mm_read_UBYTE(modreader); + pth.panbeg = _mm_read_UBYTE(modreader); + pth.panend = _mm_read_UBYTE(modreader); + pth.volflg = _mm_read_UBYTE(modreader); + pth.panflg = _mm_read_UBYTE(modreader); + pth.vibflg = _mm_read_UBYTE(modreader); + pth.vibsweep = _mm_read_UBYTE(modreader); + pth.vibdepth = _mm_read_UBYTE(modreader); + pth.vibrate = _mm_read_UBYTE(modreader); + pth.volfade = _mm_read_I_UWORD(modreader); + + /* read the remainder of the header + (2 bytes for 1.03, 22 for 1.04) */ + if (headend>=_mm_ftell(modreader)) { + for(u=headend-_mm_ftell(modreader);u;u--) { + _mm_skip_BYTE(modreader); + } + } + + /* we can't trust the envelope point count here, as some + modules have incorrect values (K_OSPACE.XM reports 32 volume + points, for example). */ + if(pth.volpts>XMENVCNT/2) pth.volpts=XMENVCNT/2; + if(pth.panpts>XMENVCNT/2) pth.panpts=XMENVCNT/2; + + if((_mm_eof(modreader))||(pth.volpts>XMENVCNT/2)||(pth.panpts>XMENVCNT/2)) { + MikMod_free(nextwav);nextwav=NULL; + MikMod_free(wh);wh=NULL; + _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + + for(u=0;usamplenumber[u]=pth.what[u]+of.numsmp; + d->volfade = pth.volfade; + +#if defined __STDC__ || defined _MSC_VER || defined MPW_C +#define XM_ProcessEnvelope(name) \ + for (u = 0; u < (XMENVCNT >> 1); u++) { \ + d-> name##env[u].pos = pth. name##env[u << 1]; \ + d-> name##env[u].val = pth. name##env[(u << 1)+ 1]; \ + } \ + if (pth. name##flg&1) d-> name##flg|=EF_ON; \ + if (pth. name##flg&2) d-> name##flg|=EF_SUSTAIN; \ + if (pth. name##flg&4) d-> name##flg|=EF_LOOP; \ + d-> name##susbeg=d-> name##susend=pth. name##sus; \ + d-> name##beg=pth. name##beg; \ + d-> name##end=pth. name##end; \ + d-> name##pts=pth. name##pts; \ + \ + /* scale envelope */ \ + for (p=0;p name##env[p].val<<=2; \ + \ + if ((d-> name##flg&EF_ON)&&(d-> name##pts<2)) \ + d-> name##flg&=~EF_ON +#else +#define XM_ProcessEnvelope(name) \ + for (u = 0; u < (XMENVCNT >> 1); u++) { \ + d-> name/**/env[u].pos = pth. name/**/env[u << 1]; \ + d-> name/**/env[u].val = pth. name/**/env[(u << 1)+ 1]; \ + } \ + if (pth. name/**/flg&1) d-> name/**/flg|=EF_ON; \ + if (pth. name/**/flg&2) d-> name/**/flg|=EF_SUSTAIN; \ + if (pth. name/**/flg&4) d-> name/**/flg|=EF_LOOP; \ + d-> name/**/susbeg=d-> name/**/susend= \ + pth. name/**/sus; \ + d-> name/**/beg=pth. name/**/beg; \ + d-> name/**/end=pth. name/**/end; \ + d-> name/**/pts=pth. name/**/pts; \ + \ + /* scale envelope */ \ + for (p=0;p name/**/env[p].val<<=2; \ + \ + if ((d-> name/**/flg&EF_ON)&&(d-> name/**/pts<2)) \ + d-> name/**/flg&=~EF_ON +#endif + + XM_ProcessEnvelope(vol); + XM_ProcessEnvelope(pan); +#undef XM_ProcessEnvelope + + if (d->volflg & EF_ON) + FixEnvelope(d->volenv, d->volpts); + if (d->panflg & EF_ON) + FixEnvelope(d->panenv, d->panpts); + + /* Samples are stored outside the instrument struct now, so we + have to load them all into a temp area, count the of.numsmp + along the way and then do an AllocSamples() and move + everything over */ + if(mh->version>0x0103) next = 0; + for(u=0;ulength =_mm_read_I_ULONG (modreader); + s->loopstart =_mm_read_I_ULONG (modreader); + s->looplength =_mm_read_I_ULONG (modreader); + s->volume =_mm_read_UBYTE (modreader); + s->finetune =_mm_read_SBYTE (modreader); + s->type =_mm_read_UBYTE (modreader); + s->panning =_mm_read_UBYTE (modreader); + s->relnote =_mm_read_SBYTE (modreader); + s->vibtype = pth.vibflg; + s->vibsweep = pth.vibsweep; + s->vibdepth = pth.vibdepth*4; + s->vibrate = pth.vibrate; + s->reserved =_mm_read_UBYTE (modreader); + _mm_read_string(s->samplename, 22, modreader); + + nextwav[of.numsmp+u]=next; + next+=s->length; + + /* last instrument is at the end of file in version 0x0104 */ + if(_mm_eof(modreader) && (mh->version<0x0104 || tversion>0x0103) { + for(u=0;uversion<0x0104 || tid,17,modreader); + _mm_read_string(mh->songname,21,modreader); + _mm_read_string(mh->trackername,20,modreader); + mh->version =_mm_read_I_UWORD(modreader); + if((mh->version<0x102)||(mh->version>0x104)) { + _mm_errno=MMERR_NOT_A_MODULE; + return 0; + } + mh->headersize =_mm_read_I_ULONG(modreader); + mh->songlength =_mm_read_I_UWORD(modreader); + mh->restart =_mm_read_I_UWORD(modreader); + mh->numchn =_mm_read_I_UWORD(modreader); + mh->numpat =_mm_read_I_UWORD(modreader); + mh->numins =_mm_read_I_UWORD(modreader); + mh->flags =_mm_read_I_UWORD(modreader); + mh->tempo =_mm_read_I_UWORD(modreader); + mh->bpm =_mm_read_I_UWORD(modreader); + if(!mh->bpm || mh->songlength > 256) { + _mm_errno=MMERR_NOT_A_MODULE; + return 0; + } +/* _mm_read_UBYTES(mh->orders,256,modreader);*/ +/* _mm_read_UBYTES(mh->orders,mh->headersize-20,modreader);*/ + _mm_read_UBYTES(mh->orders,mh->songlength,modreader); + if(_mm_fseek(modreader, mh->headersize+60, SEEK_SET) || + _mm_eof(modreader)) { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* set module variables */ + of.initspeed = mh->tempo; + of.inittempo = mh->bpm; + strncpy(tracker,mh->trackername,20);tracker[20]=0; + for(t=20;(t>=0)&&(tracker[t]<=' ');t--) tracker[t]=0; + + /* some modules have the tracker name empty */ + if (!tracker[0]) + strcpy(tracker,"Unknown tracker"); + +#ifdef HAVE_SNPRINTF + snprintf(modtype,60,"%s (XM format %d.%02d)", + tracker,mh->version>>8,mh->version&0xff); +#else + sprintf(modtype,"%s (XM format %d.%02d)", + tracker,mh->version>>8,mh->version&0xff); +#endif + of.modtype = MikMod_strdup(modtype); + of.numchn = mh->numchn; + of.numpat = mh->numpat; + of.numtrk = (UWORD)of.numpat*of.numchn; /* get number of channels */ + of.songname = DupStr(mh->songname,20,1); + of.numpos = mh->songlength; /* copy the songlength */ + of.reppos = mh->restartsonglength?mh->restart:0; + of.numins = mh->numins; + of.flags |= UF_XMPERIODS | UF_INST | UF_NOWRAP | UF_FT2QUIRKS | + UF_PANNING; + if(mh->flags&1) of.flags |= UF_LINEAR; + of.bpmlimit = 32; + + memset(of.chanvol,64,of.numchn); /* store channel volumes */ + + if(!AllocPositions(of.numpos+1)) return 0; + for(t=0;torders[t]; + + /* We have to check for any pattern numbers in the order list greater than + the number of patterns total. If one or more is found, we set it equal to + the pattern total and make a dummy pattern to workaround the problem */ + for(t=0;t=of.numpat) { + of.positions[t]=of.numpat; + dummypat=1; + } + } + if(dummypat) { + of.numpat++;of.numtrk+=of.numchn; + } + + if(mh->version<0x0104) { + if(!LoadInstruments()) return 0; + if(!LoadPatterns(dummypat)) return 0; + for(t=0;tsamplename = DupStr(s->samplename,22,1); + q->length = s->length; + q->loopstart = s->loopstart; + q->loopend = s->loopstart+s->looplength; + q->volume = s->volume; + q->speed = s->finetune+128; + q->panning = s->panning; + q->seekpos = nextwav[u]; + q->vibtype = s->vibtype; + q->vibsweep = s->vibsweep; + q->vibdepth = s->vibdepth; + q->vibrate = s->vibrate; + + if(s->type & 0x10) { + q->length >>= 1; + q->loopstart >>= 1; + q->loopend >>= 1; + } + + q->flags|=SF_OWNPAN|SF_DELTA|SF_SIGNED; + if(s->type&0x3) q->flags|=SF_LOOP; + if(s->type&0x2) q->flags|=SF_BIDI; + if(s->type&0x10) q->flags|=SF_16BITS; + } + + d=of.instruments; + s=wh; + for(u=0;usamplenumber[t]>=of.numsmp) + d->samplenote[t]=255; + else { + int note=t+s[d->samplenumber[t]].relnote; + d->samplenote[t]=(note<0)?0:note; + } + } + + MikMod_free(wh);MikMod_free(nextwav); + wh=NULL;nextwav=NULL; + return 1; +} + +static CHAR *XM_LoadTitle(void) +{ + CHAR str[21]; + + _mm_fseek(modreader,17,SEEK_SET); + if(!_mm_read_UBYTES(str, 21, modreader)) return NULL; + + return(DupStr(str,21,1)); +} + +/*========== Loader information */ + +MIKMODAPI MLOADER load_xm={ + NULL, + "XM", + "XM (FastTracker 2)", + XM_Init, + XM_Test, + XM_Load, + XM_Cleanup, + XM_LoadTitle +}; + +/* ex:set ts=4: */ diff --git a/libs/mikmod/mmio/mmalloc.c b/libs/mikmod/mmio/mmalloc.c new file mode 100644 index 0000000..ed5fd46 --- /dev/null +++ b/libs/mikmod/mmio/mmalloc.c @@ -0,0 +1,142 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Dynamic memory routines + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_POSIX_MEMALIGN +#define _XOPEN_SOURCE 600 /* for posix_memalign */ +#endif + +#include "string.h" +#include "mikmod_internals.h" + +#if defined(HAVE_SSE2) || defined(HAVE_ALTIVEC) +#undef WIN32_ALIGNED_MALLOC +#if defined(_WIN32) && !defined(_WIN32_WCE) +# if defined(_WIN64) /* OK with MSVC and MinGW */ +# define WIN32_ALIGNED_MALLOC +# elif defined(_MSC_VER) && (_MSC_VER >= 1300) +# define WIN32_ALIGNED_MALLOC +# elif defined(__MINGW32__) + /* no guarantees that msvcrt.dll will have it */ +# endif +#endif + +#define PTRSIZE (sizeof(void*)) + +/* return a 16 byte aligned address */ +void* MikMod_amalloc(size_t size) +{ + void *d; +#if defined(HAVE_POSIX_MEMALIGN) + if (!posix_memalign(&d, 16, size)) { + memset(d, 0, size); + return d; + } +#elif defined(WIN32_ALIGNED_MALLOC) + d = _aligned_malloc(size, 16); + if (d) { + ZeroMemory(d, size); + return d; + } +#else + size_t s = (size)? ((size + (PTRSIZE-1)) & ~(PTRSIZE-1)) : PTRSIZE; + s += PTRSIZE + 16; + d = calloc(1, s); + if (d) { + char *pptr = (char *)d + PTRSIZE; + size_t err = ((size_t)pptr) & 15; + char *fptr = pptr + (16 - err); + *(size_t*)(fptr - PTRSIZE) = (size_t)d; + return fptr; + } +#endif + + _mm_errno = MMERR_OUT_OF_MEMORY; + if(_mm_errorhandler) _mm_errorhandler(); + return NULL; +} + +void MikMod_afree(void *data) +{ + if (!data) return; +#if defined(HAVE_POSIX_MEMALIGN) + free(data); +#elif defined(WIN32_ALIGNED_MALLOC) + _aligned_free(data); +#else + free((void *) *(size_t*)((unsigned char *)data - PTRSIZE)); +#endif +} +#endif /* (HAVE_SSE2) || (HAVE_ALTIVEC) */ + +void* MikMod_realloc(void *data, size_t size) +{ + if (data) return realloc(data, size); + return calloc(1, size); +} + +/* Same as malloc, but sets error variable _mm_error when fails */ +void* MikMod_malloc(size_t size) +{ + return MikMod_calloc(1, size); +} + +/* Same as calloc, but sets error variable _mm_error when fails */ +void* MikMod_calloc(size_t nitems, size_t size) +{ + void *d = calloc(nitems, size); + if (d) return d; + + _mm_errno = MMERR_OUT_OF_MEMORY; + if(_mm_errorhandler) _mm_errorhandler(); + return NULL; +} + +void MikMod_free(void *data) +{ + if (data) free(data); +} + +/* like strdup(), but the result must be freed using MikMod_free() */ +CHAR *MikMod_strdup(const CHAR *s) +{ + size_t l; + CHAR *d; + + if (!s) return NULL; + + l = strlen(s) + 1; + d = (CHAR *) MikMod_calloc(1, l * sizeof(CHAR)); + if (d) strcpy(d, s); + return d; +} + +/* ex:set ts=4: */ diff --git a/libs/mikmod/mmio/mmerror.c b/libs/mikmod/mmio/mmerror.c new file mode 100644 index 0000000..cf4c99e --- /dev/null +++ b/libs/mikmod/mmio/mmerror.c @@ -0,0 +1,315 @@ +/* MikMod sound library + (c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Error handling functions. + Register an error handler with _mm_RegisterErrorHandler() and you're all set. + +==============================================================================*/ + +/* + + The global variables _mm_errno, and _mm_critical are set before the error + handler in called. See below for the values of these variables. + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mikmod_internals.h" + +#define _mmerr_invalid "Invalid error code" + +static const char *_mm_errmsg[MMERR_MAX+1] = +{ +/* No error */ + + "No error", + +/* Generic errors */ + + "Could not open requested file", + "Out of memory", + "Dynamic linking failed", + +/* Sample errors */ + + "Out of memory to load sample", + "Out of sample handles to load sample", + "Sample format not recognized", + +/* Module errors */ + + "Failure loading module pattern", + "Failure loading module track", + "Failure loading module header", + "Failure loading sampleinfo", + "Module format not recognized", + "Module sample format not recognized", + "Synthsounds not supported in MED files", + "Compressed sample is invalid", + +/* Driver errors: */ + + "Sound device not detected", + "Device number out of range", + "Software mixer failure", + "Could not open sound device", + "This driver supports 8 bit linear output only", + "This driver supports 16 bit linear output only", + "This driver supports stereo output only", + "This driver supports uLaw output (8 bit mono, 8 kHz) only", + "Unable to set non-blocking mode for audio device", + +/* AudioFile driver errors */ +#ifdef DRV_AF + "Cannot find suitable AudioFile audio port", +#else + _mmerr_invalid, +#endif + +/* AIX driver errors */ +#ifdef DRV_AIX + "Configuration (init step) of audio device failed", + "Configuration (control step) of audio device failed", + "Configuration (start step) of audio device failed", +#else + _mmerr_invalid, _mmerr_invalid, _mmerr_invalid, +#endif + +/* Ultrasound driver errors */ +#ifdef DRV_ULTRA + "Ultrasound driver only works in 16 bit stereo 44 KHz", + "Ultrasound card could not be reset", + "Could not start Ultrasound timer", +#else + _mmerr_invalid, _mmerr_invalid, _mmerr_invalid, +#endif + +/* HP driver errors */ +#ifdef DRV_HP + "Unable to select 16bit-linear sample format", + "Could not select requested sample-rate", + "Could not select requested number of channels", + "Unable to select audio output", + "Unable to get audio description", + "Could not set transmission buffer size", +#else + _mmerr_invalid, _mmerr_invalid, _mmerr_invalid, + _mmerr_invalid, _mmerr_invalid, _mmerr_invalid, +#endif + +/* Open Sound System driver errors */ +#ifdef DRV_OSS + "Could not set fragment size", + "Could not set sample size", + "Could not set mono/stereo setting", + "Could not set sample rate", +#else + _mmerr_invalid, _mmerr_invalid, _mmerr_invalid, + _mmerr_invalid, +#endif + +/* SGI driver errors */ +#ifdef DRV_SGI + "Unsupported sample rate", + "Hardware does not support 16 bit sound", + "Hardware does not support 8 bit sound", + "Hardware does not support stereo sound", + "Hardware does not support mono sound", +#else + _mmerr_invalid, _mmerr_invalid, _mmerr_invalid, + _mmerr_invalid, _mmerr_invalid, +#endif + +/* Sun driver errors */ +#ifdef DRV_SUN + "Sound device initialization failed", +#else + _mmerr_invalid, +#endif + +/* OS/2 drivers errors */ +#if defined(DRV_OS2) || defined(DRV_DART) + "Could not set mixing parameters", +#else + _mmerr_invalid, +#endif +#ifdef DRV_OS2 + "Could not create playback semaphores", + "Could not create playback timer", + "Could not create playback thread", +#else + _mmerr_invalid, _mmerr_invalid, _mmerr_invalid, +#endif + +/* DirectSound driver errors */ +#ifdef DRV_DS + "Could not set playback priority", + "Could not create playback buffers", + "Could not set playback format", + "Could not register callback", + "Could not register event", + "Could not create playback thread", + "Could not initialize playback thread", +#else + _mmerr_invalid, _mmerr_invalid, _mmerr_invalid, + _mmerr_invalid, _mmerr_invalid, _mmerr_invalid, + _mmerr_invalid, +#endif + +/* Windows Multimedia API driver errors */ +#ifdef DRV_WIN + "Invalid device handle", + "The resource is already allocated", + "Invalid device identifier", + "Unsupported output format", + "Unknown error", +#else + _mmerr_invalid, _mmerr_invalid, _mmerr_invalid, + _mmerr_invalid, _mmerr_invalid, +#endif + +/* Macintosh driver errors */ +#ifdef DRV_MAC + "Unsupported sample rate", + "Could not start playback", +#else + _mmerr_invalid, _mmerr_invalid, +#endif + +/* MacOS X/Darwin driver errors */ +#ifdef DRV_OSX + "Unknown device", + "Bad property", + "Could not set playback format", + "Could not set mono/stereo setting", + "Could not create playback buffers", + "Could not create playback thread", + "Could not start audio device", + "Could not create buffer thread", +#else + _mmerr_invalid, _mmerr_invalid, _mmerr_invalid, + _mmerr_invalid, _mmerr_invalid, _mmerr_invalid, + _mmerr_invalid, _mmerr_invalid, +#endif + +/* DOS driver errors */ +#ifdef DRV_WSS + "WSS_STARTDMA", +#else + _mmerr_invalid, +#endif +#ifdef DRV_SB + "SB_STARTDMA", +#else + _mmerr_invalid, +#endif + +/* float32 output */ + + "This driver doesn't support 32 bit float output", + +/* OpenAL driver errors */ +#ifdef DRV_OPENAL + "Could not create context", + "Could not make context current", + "Could not create buffers", + "Could not create sources", + "Could not change source parameters", + "Could not queue buffers", + "Could not unqueue buffers", + "Could not copy buffer data", + "Could not get source parameters", + "Could not play source", + "Could not stop source", +#else + _mmerr_invalid, _mmerr_invalid, _mmerr_invalid, + _mmerr_invalid, _mmerr_invalid, _mmerr_invalid, + _mmerr_invalid, _mmerr_invalid, _mmerr_invalid, + _mmerr_invalid, _mmerr_invalid, +#endif + +/* ALSA driver errors */ +#ifdef DRV_ALSA + "No ALSA configurations available", + "Could not set ALSA output params", + "Could not set playback format", + "Could not set sample rate", + "Could not set mono/stereo setting", + "Could not get buffer size from ALSA", + "ALSA PCM start error", + "ALSA PCM write error", + "ALSA PCM recovery failure", +#else + _mmerr_invalid, _mmerr_invalid, _mmerr_invalid, + _mmerr_invalid, _mmerr_invalid, _mmerr_invalid, + _mmerr_invalid, _mmerr_invalid, _mmerr_invalid, +#endif + +/* Sndio errors */ +#ifdef DRV_SNDIO + "Could not set SNDIO output params", + "Unsupported SNDIO output params", +#else + _mmerr_invalid, _mmerr_invalid, +#endif + +/* Invalid error */ + + _mmerr_invalid +}; + +MIKMODAPI const char *MikMod_strerror(int code) +{ + if ((code<0)||(code>MMERR_MAX)) code=MMERR_MAX; + return _mm_errmsg[code]; +} + +/* User installed error callback */ +MikMod_handler_t _mm_errorhandler = NULL; +MIKMODAPI int _mm_errno = 0; +MIKMODAPI BOOL _mm_critical = 0; + +static MikMod_handler_t _mm_registererrorhandler(MikMod_handler_t proc) +{ + MikMod_handler_t oldproc=_mm_errorhandler; + + _mm_errorhandler = proc; + return oldproc; +} + +MIKMODAPI MikMod_handler_t MikMod_RegisterErrorHandler(MikMod_handler_t proc) +{ + MikMod_handler_t result; + + MUTEX_LOCK(vars); + result=_mm_registererrorhandler(proc); + MUTEX_UNLOCK(vars); + + return result; +} + +/* ex:set ts=4: */ diff --git a/libs/mikmod/mmio/mmio.c b/libs/mikmod/mmio/mmio.c new file mode 100644 index 0000000..2ab6aff --- /dev/null +++ b/libs/mikmod/mmio/mmio.c @@ -0,0 +1,537 @@ +/* MikMod sound library + (c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Portable file I/O routines + +==============================================================================*/ + +/* + + The way this module works: + + - _mm_fopen will call the errorhandler [see mmerror.c] in addition to + setting _mm_errno on exit. + - _mm_iobase is for internal use. It is used by Player_LoadFP to + ensure that it works properly with wad files. + - _mm_read_I_* and _mm_read_M_* differ : the first is for reading data + written by a little endian (intel) machine, and the second is for reading + big endian (Mac, RISC, Alpha) machine data. + - _mm_write functions work the same as the _mm_read functions. + - _mm_read_string is for reading binary strings. It is basically the same + as an fread of bytes. + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_LIMITS_H +#include +#endif + +#include +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fclose(FILE *); +extern int fgetc(FILE *); +extern int fputc(int, FILE *); +extern size_t fread(void *, size_t, size_t, FILE *); +extern int fseek(FILE *, long, int); +extern size_t fwrite(const void *, size_t, size_t, FILE *); +#endif + +#define COPY_BUFSIZE 1024 + +/* some prototypes */ +static BOOL _mm_MemReader_Eof(MREADER* reader); +static BOOL _mm_MemReader_Read(MREADER* reader,void* ptr,size_t size); +static int _mm_MemReader_Get(MREADER* reader); +static int _mm_MemReader_Seek(MREADER* reader,long offset,int whence); +static long _mm_MemReader_Tell(MREADER* reader); + +/*static long _mm_iobase = 0, temp_iobase = 0;*/ + +FILE* _mm_fopen(const CHAR* fname, const CHAR* attrib) +{ + FILE *fp; + + if(!(fp=fopen(fname,attrib))) { + _mm_errno = MMERR_OPENING_FILE; + if(_mm_errorhandler) _mm_errorhandler(); + } + return fp; +} + +BOOL _mm_FileExists(const CHAR* fname) +{ + FILE *fp; + + if(!(fp=fopen(fname,"r"))) return 0; + fclose(fp); + + return 1; +} + +int _mm_fclose(FILE *fp) +{ + return fclose(fp); +} + +/* Sets the current file-position as the new iobase */ +void _mm_iobase_setcur(MREADER* reader) +{ + reader->prev_iobase=reader->iobase; /* store old value in case of revert */ + reader->iobase=reader->Tell(reader); +} + +/* Reverts to the last known iobase value. */ +void _mm_iobase_revert(MREADER* reader) +{ + reader->iobase=reader->prev_iobase; +} + +/*========== File Reader */ + +typedef struct MFILEREADER { + MREADER core; + FILE* file; +} MFILEREADER; + +static BOOL _mm_FileReader_Eof(MREADER* reader) +{ + return feof(((MFILEREADER*)reader)->file); +} + +static BOOL _mm_FileReader_Read(MREADER* reader,void* ptr,size_t size) +{ + return !!fread(ptr,size,1,((MFILEREADER*)reader)->file); +} + +static int _mm_FileReader_Get(MREADER* reader) +{ + return fgetc(((MFILEREADER*)reader)->file); +} + +static int _mm_FileReader_Seek(MREADER* reader,long offset,int whence) +{ + return fseek(((MFILEREADER*)reader)->file, + (whence==SEEK_SET)?offset+reader->iobase:offset,whence); +} + +static long _mm_FileReader_Tell(MREADER* reader) +{ + return ftell(((MFILEREADER*)reader)->file)-reader->iobase; +} + +MREADER *_mm_new_file_reader(FILE* fp) +{ + MFILEREADER* reader=(MFILEREADER*)MikMod_calloc(1,sizeof(MFILEREADER)); + if (reader) { + reader->core.Eof =&_mm_FileReader_Eof; + reader->core.Read=&_mm_FileReader_Read; + reader->core.Get =&_mm_FileReader_Get; + reader->core.Seek=&_mm_FileReader_Seek; + reader->core.Tell=&_mm_FileReader_Tell; + reader->file=fp; + } + return (MREADER*)reader; +} + +void _mm_delete_file_reader (MREADER* reader) +{ + MikMod_free(reader); +} + +/*========== File Writer */ + +typedef struct MFILEWRITER { + MWRITER core; + FILE* file; +} MFILEWRITER; + +static int _mm_FileWriter_Seek(MWRITER* writer,long offset,int whence) +{ + return fseek(((MFILEWRITER*)writer)->file,offset,whence); +} + +static long _mm_FileWriter_Tell(MWRITER* writer) +{ + return ftell(((MFILEWRITER*)writer)->file); +} + +static BOOL _mm_FileWriter_Write(MWRITER* writer, const void* ptr, size_t size) +{ + return (fwrite(ptr,size,1,((MFILEWRITER*)writer)->file)==size); +} + +static int _mm_FileWriter_Put(MWRITER* writer,int value) +{ + return fputc(value,((MFILEWRITER*)writer)->file); +} + +MWRITER *_mm_new_file_writer(FILE* fp) +{ + MFILEWRITER* writer=(MFILEWRITER*)MikMod_calloc(1,sizeof(MFILEWRITER)); + if (writer) { + writer->core.Seek =&_mm_FileWriter_Seek; + writer->core.Tell =&_mm_FileWriter_Tell; + writer->core.Write=&_mm_FileWriter_Write; + writer->core.Put =&_mm_FileWriter_Put; + writer->file=fp; + } + return (MWRITER*) writer; +} + +void _mm_delete_file_writer (MWRITER* writer) +{ + MikMod_free (writer); +} + +/*========== Memory Reader */ + +typedef struct MMEMREADER { + MREADER core; + const void *buffer; + long len; + long pos; +} MMEMREADER; + +void _mm_delete_mem_reader(MREADER* reader) +{ + MikMod_free(reader); +} + +MREADER *_mm_new_mem_reader(const void *buffer, long len) +{ + MMEMREADER* reader=(MMEMREADER*)MikMod_calloc(1,sizeof(MMEMREADER)); + if (reader) + { + reader->core.Eof =&_mm_MemReader_Eof; + reader->core.Read=&_mm_MemReader_Read; + reader->core.Get =&_mm_MemReader_Get; + reader->core.Seek=&_mm_MemReader_Seek; + reader->core.Tell=&_mm_MemReader_Tell; + reader->buffer = buffer; + reader->len = len; + reader->pos = 0; + } + return (MREADER*)reader; +} + +static BOOL _mm_MemReader_Eof(MREADER* reader) +{ + MMEMREADER* mr = (MMEMREADER*) reader; + if (!mr) return 1; + if (mr->pos >= mr->len) return 1; + return 0; +} + +static BOOL _mm_MemReader_Read(MREADER* reader,void* ptr,size_t size) +{ + unsigned char *d; + const unsigned char *s; + MMEMREADER* mr; + long siz; + BOOL ret; + + if (!reader || !size || (size > (size_t) LONG_MAX)) + return 0; + + mr = (MMEMREADER*) reader; + siz = (long) size; + if (mr->pos >= mr->len) return 0; /* @ eof */ + if (mr->pos + siz > mr->len) { + siz = mr->len - mr->pos; + ret = 0; /* not enough remaining bytes */ + } + else { + ret = 1; + } + + s = (const unsigned char *) mr->buffer; + s += mr->pos; + mr->pos += siz; + d = (unsigned char *) ptr; + + while (siz) { + *d++ = *s++; + siz--; + } + + return ret; +} + +static int _mm_MemReader_Get(MREADER* reader) +{ + MMEMREADER* mr; + int c; + + mr = (MMEMREADER*) reader; + if (mr->pos >= mr->len) return EOF; + c = ((const unsigned char*) mr->buffer)[mr->pos]; + mr->pos++; + + return c; +} + +static int _mm_MemReader_Seek(MREADER* reader,long offset,int whence) +{ + MMEMREADER* mr; + + if (!reader) return -1; + mr = (MMEMREADER*) reader; + switch(whence) + { + case SEEK_CUR: + mr->pos += offset; + break; + case SEEK_SET: + mr->pos = reader->iobase + offset; + break; + case SEEK_END: + mr->pos = mr->len + offset; + break; + } + if (mr->pos < reader->iobase) { + mr->pos = mr->core.iobase; + return -1; + } + if (mr->pos > mr->len) { + mr->pos = mr->len; + } + return 0; +} + +static long _mm_MemReader_Tell(MREADER* reader) +{ + if (reader) { + return ((MMEMREADER*)reader)->pos - reader->iobase; + } + return 0; +} + +/*========== Write functions */ + +void _mm_write_string(const CHAR* data,MWRITER* writer) +{ + if(data) + _mm_write_UBYTES(data,strlen(data),writer); +} + +void _mm_write_M_UWORD(UWORD data,MWRITER* writer) +{ + _mm_write_UBYTE(data>>8,writer); + _mm_write_UBYTE(data&0xff,writer); +} + +void _mm_write_I_UWORD(UWORD data,MWRITER* writer) +{ + _mm_write_UBYTE(data&0xff,writer); + _mm_write_UBYTE(data>>8,writer); +} + +void _mm_write_M_ULONG(ULONG data,MWRITER* writer) +{ + _mm_write_M_UWORD(data>>16,writer); + _mm_write_M_UWORD(data&0xffff,writer); +} + +void _mm_write_I_ULONG(ULONG data,MWRITER* writer) +{ + _mm_write_I_UWORD(data&0xffff,writer); + _mm_write_I_UWORD(data>>16,writer); +} + +void _mm_write_M_SWORD(SWORD data,MWRITER* writer) +{ + _mm_write_M_UWORD((UWORD)data,writer); +} + +void _mm_write_I_SWORD(SWORD data,MWRITER* writer) +{ + _mm_write_I_UWORD((UWORD)data,writer); +} + +void _mm_write_M_SLONG(SLONG data,MWRITER* writer) +{ + _mm_write_M_ULONG((ULONG)data,writer); +} + +void _mm_write_I_SLONG(SLONG data,MWRITER* writer) +{ + _mm_write_I_ULONG((ULONG)data,writer); +} + +void _mm_write_M_SWORDS(SWORD *buffer,int cnt,MWRITER* writer) +{ + while(cnt-- > 0) _mm_write_M_SWORD(*(buffer++),writer); +} + +void _mm_write_M_UWORDS(UWORD *buffer,int cnt,MWRITER* writer) +{ + while(cnt-- > 0) _mm_write_M_UWORD(*(buffer++),writer); +} + +void _mm_write_I_SWORDS(SWORD *buffer,int cnt,MWRITER* writer) +{ + while(cnt-- > 0) _mm_write_I_SWORD(*(buffer++),writer); +} + +void _mm_write_I_UWORDS(UWORD *buffer,int cnt,MWRITER* writer) +{ + while(cnt-- > 0) _mm_write_I_UWORD(*(buffer++),writer); +} + +void _mm_write_M_SLONGS(SLONG *buffer,int cnt,MWRITER* writer) +{ + while(cnt-- > 0) _mm_write_M_SLONG(*(buffer++),writer); +} + +void _mm_write_M_ULONGS(ULONG *buffer,int cnt,MWRITER* writer) +{ + while(cnt-- > 0) _mm_write_M_ULONG(*(buffer++),writer); +} + +void _mm_write_I_SLONGS(SLONG *buffer,int cnt,MWRITER* writer) +{ + while(cnt-- > 0) _mm_write_I_SLONG(*(buffer++),writer); +} + +void _mm_write_I_ULONGS(ULONG *buffer,int cnt,MWRITER* writer) +{ + while(cnt-- > 0) _mm_write_I_ULONG(*(buffer++),writer); +} + +/*========== Read functions */ + +BOOL _mm_read_string(CHAR* buffer,int cnt,MREADER* reader) +{ + return reader->Read(reader,buffer,cnt); +} + +UWORD _mm_read_M_UWORD(MREADER* reader) +{ + UWORD result=((UWORD)_mm_read_UBYTE(reader))<<8; + result|=_mm_read_UBYTE(reader); + return result; +} + +UWORD _mm_read_I_UWORD(MREADER* reader) +{ + UWORD result=_mm_read_UBYTE(reader); + result|=((UWORD)_mm_read_UBYTE(reader))<<8; + return result; +} + +ULONG _mm_read_M_ULONG(MREADER* reader) +{ + ULONG result=((ULONG)_mm_read_M_UWORD(reader))<<16; + result|=_mm_read_M_UWORD(reader); + return result; +} + +ULONG _mm_read_I_ULONG(MREADER* reader) +{ + ULONG result=_mm_read_I_UWORD(reader); + result|=((ULONG)_mm_read_I_UWORD(reader))<<16; + return result; +} + +SWORD _mm_read_M_SWORD(MREADER* reader) +{ + return((SWORD)_mm_read_M_UWORD(reader)); +} + +SWORD _mm_read_I_SWORD(MREADER* reader) +{ + return((SWORD)_mm_read_I_UWORD(reader)); +} + +SLONG _mm_read_M_SLONG(MREADER* reader) +{ + return((SLONG)_mm_read_M_ULONG(reader)); +} + +SLONG _mm_read_I_SLONG(MREADER* reader) +{ + return((SLONG)_mm_read_I_ULONG(reader)); +} + +BOOL _mm_read_M_SWORDS(SWORD *buffer,int cnt,MREADER* reader) +{ + while(cnt-- > 0) *(buffer++)=_mm_read_M_SWORD(reader); + return !reader->Eof(reader); +} + +BOOL _mm_read_M_UWORDS(UWORD *buffer,int cnt,MREADER* reader) +{ + while(cnt-- > 0) *(buffer++)=_mm_read_M_UWORD(reader); + return !reader->Eof(reader); +} + +BOOL _mm_read_I_SWORDS(SWORD *buffer,int cnt,MREADER* reader) +{ + while(cnt-- > 0) *(buffer++)=_mm_read_I_SWORD(reader); + return !reader->Eof(reader); +} + +BOOL _mm_read_I_UWORDS(UWORD *buffer,int cnt,MREADER* reader) +{ + while(cnt-- > 0) *(buffer++)=_mm_read_I_UWORD(reader); + return !reader->Eof(reader); +} + +BOOL _mm_read_M_SLONGS(SLONG *buffer,int cnt,MREADER* reader) +{ + while(cnt-- > 0) *(buffer++)=_mm_read_M_SLONG(reader); + return !reader->Eof(reader); +} + +BOOL _mm_read_M_ULONGS(ULONG *buffer,int cnt,MREADER* reader) +{ + while(cnt-- > 0) *(buffer++)=_mm_read_M_ULONG(reader); + return !reader->Eof(reader); +} + +BOOL _mm_read_I_SLONGS(SLONG *buffer,int cnt,MREADER* reader) +{ + while(cnt-- > 0) *(buffer++)=_mm_read_I_SLONG(reader); + return !reader->Eof(reader); +} + +BOOL _mm_read_I_ULONGS(ULONG *buffer,int cnt,MREADER* reader) +{ + while(cnt-- > 0) *(buffer++)=_mm_read_I_ULONG(reader); + return !reader->Eof(reader); +} + +/* ex:set ts=4: */ diff --git a/libs/mikmod/playercode/mdreg.c b/libs/mikmod/playercode/mdreg.c new file mode 100644 index 0000000..88bb673 --- /dev/null +++ b/libs/mikmod/playercode/mdreg.c @@ -0,0 +1,162 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + Routine for registering all drivers in libmikmod for the current platform. + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mikmod_internals.h" + +static void _mm_registeralldrivers(void) +{ + /* Register network drivers */ +#ifdef DRV_AF + _mm_registerdriver(&drv_AF); +#endif +#ifdef DRV_PULSEAUDIO + _mm_registerdriver(&drv_pulseaudio); +#endif +#ifdef DRV_ESD + _mm_registerdriver(&drv_esd); +#endif +#ifdef DRV_NAS + _mm_registerdriver(&drv_nas); +#endif + + /* Register hardware drivers - hardware mixing */ +#ifdef DRV_ULTRA + _mm_registerdriver(&drv_ultra); +#endif +#ifdef DRV_SAM9407 + _mm_registerdriver(&drv_sam9407); +#endif + + /* Register multi-platform drivers -- software mixing */ +#ifdef DRV_SDL + _mm_registerdriver(&drv_sdl); +#endif +#ifdef DRV_OPENAL + _mm_registerdriver(&drv_openal); +#endif + + /* Register OS-specific hardware drivers - software mixing */ +#ifdef DRV_AHI + _mm_registerdriver(&drv_ahi); +#endif +#ifdef DRV_AIX + _mm_registerdriver(&drv_aix); +#endif +#ifdef DRV_ALSA + _mm_registerdriver(&drv_alsa); +#endif +#ifdef DRV_HP + _mm_registerdriver(&drv_hp); +#endif +#ifdef DRV_SNDIO + _mm_registerdriver(&drv_sndio); +#endif +#ifdef DRV_OSS + _mm_registerdriver(&drv_oss); +#endif +#ifdef DRV_SGI + _mm_registerdriver(&drv_sgi); +#endif +#ifdef DRV_SUN + _mm_registerdriver(&drv_sun); +#endif +#ifdef DRV_DART + _mm_registerdriver(&drv_dart); +#endif +#ifdef DRV_OS2 + _mm_registerdriver(&drv_os2); +#endif +#ifdef DRV_XAUDIO2 + _mm_registerdriver(&drv_xaudio2); +#endif +#ifdef DRV_DS + _mm_registerdriver(&drv_ds); +#endif +#ifdef DRV_WIN + _mm_registerdriver(&drv_win); +#endif +#ifdef DRV_MAC + _mm_registerdriver(&drv_mac); +#endif +#ifdef DRV_OSX + _mm_registerdriver(&drv_osx); +#endif +#ifdef DRV_DC + _mm_registerdriver(&drv_dc); +#endif +#ifdef DRV_GP32 + _mm_registerdriver(&drv_gp32); +#endif +#ifdef DRV_PSP + _mm_registerdriver(&drv_psp); +#endif +#ifdef DRV_OSLES + _mm_registerdriver(&drv_osles); +#endif + + /* dos drivers - wss first, since some cards emulate sb */ +#ifdef DRV_WSS + _mm_registerdriver(&drv_wss); +#endif +#ifdef DRV_SB + _mm_registerdriver(&drv_sb); +#endif + + /* Register disk writers */ +#ifdef DRV_WAV + _mm_registerdriver(&drv_wav); +#endif +#ifdef DRV_AIFF + _mm_registerdriver(&drv_aiff); +#endif +#ifdef DRV_RAW + _mm_registerdriver(&drv_raw); +#endif + + /* Register other drivers */ +#ifdef DRV_PIPE + _mm_registerdriver(&drv_pipe); +#endif +#if defined(DRV_STDOUT) && !defined(macintosh) + _mm_registerdriver(&drv_stdout); +#endif + + /* Register 'nosound' driver */ + _mm_registerdriver(&drv_nos); +} + +MIKMODAPI void MikMod_RegisterAllDrivers(void) +{ + MUTEX_LOCK(lists); + _mm_registeralldrivers(); + MUTEX_UNLOCK(lists); +} + +/* ex:set ts=4: */ diff --git a/libs/mikmod/playercode/mdriver.c b/libs/mikmod/playercode/mdriver.c new file mode 100644 index 0000000..b3d3d4a --- /dev/null +++ b/libs/mikmod/playercode/mdriver.c @@ -0,0 +1,968 @@ +/* MikMod sound library + (c) 1998-2014 Miodrag Vallat and others - see file AUTHORS + for a complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + These routines are used to access the available soundcard drivers. + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include + +#include "mikmod_internals.h" + +#if (MIKMOD_UNIX) +#include +#include +#endif + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +extern MODULE *pf; /* modfile being played */ + +/* EXPORTED GLOBALS */ +MIKMODAPI MDRIVER *md_driver = NULL; + +/* Initial global settings */ +MIKMODAPI UWORD md_device = 0; /* autodetect */ +MIKMODAPI UWORD md_mixfreq = 44100; +MIKMODAPI UWORD md_mode = DMODE_STEREO | DMODE_16BITS | + DMODE_SURROUND | + DMODE_SOFT_MUSIC | DMODE_SOFT_SNDFX; +MIKMODAPI UBYTE md_pansep = 128; /* 128 == 100% (full left/right) */ +MIKMODAPI UBYTE md_reverb = 0; /* no reverb */ +MIKMODAPI UBYTE md_volume = 128; /* global sound volume (0-128) */ +MIKMODAPI UBYTE md_musicvolume = 128; /* volume of song */ +MIKMODAPI UBYTE md_sndfxvolume = 128; /* volume of sound effects */ + +/* INTERNAL GLOBALS */ +UWORD md_bpm = 125; /* tempo */ + +/* Do not modify the numchn variables yourself! use MikMod_SetNumVoices() */ +UBYTE md_numchn = 0, md_sngchn = 0, md_sfxchn = 0; +UBYTE md_hardchn = 0, md_softchn= 0; + +void (*md_player)(void) = Player_HandleTick; + +MikMod_callback_t vc_callback = NULL; + +/* PRIVATE VARS */ +static MDRIVER *firstdriver = NULL; + +static volatile BOOL isplaying = 0, initialized = 0; + +static UBYTE *sfxinfo; +static int sfxpool; + +static SAMPLE **md_sample = NULL; + +/* Previous driver in use */ +static SWORD olddevice = -1; + +/* Limits the number of hardware voices to the specified amount. + This function should only be used by the low-level drivers. */ +static void LimitHardVoices(int limit) +{ + int t=0; + + if (!(md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>limit)) md_sfxchn=limit; + if (!(md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>limit)) md_sngchn=limit; + + if (!(md_mode & DMODE_SOFT_SNDFX)) + md_hardchn=md_sfxchn; + else + md_hardchn=0; + + if (!(md_mode & DMODE_SOFT_MUSIC)) md_hardchn += md_sngchn; + + while (md_hardchn>limit) { + if (++t & 1) { + if (!(md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>4)) md_sfxchn--; + } else { + if (!(md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>8)) md_sngchn--; + } + + if (!(md_mode & DMODE_SOFT_SNDFX)) + md_hardchn=md_sfxchn; + else + md_hardchn=0; + + if (!(md_mode & DMODE_SOFT_MUSIC)) + md_hardchn+=md_sngchn; + } + md_numchn=md_hardchn+md_softchn; +} + +/* Limits the number of hardware voices to the specified amount. + This function should only be used by the low-level drivers. */ +static void LimitSoftVoices(int limit) +{ + int t=0; + + if ((md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>limit)) md_sfxchn=limit; + if ((md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>limit)) md_sngchn=limit; + + if (md_mode & DMODE_SOFT_SNDFX) + md_softchn=md_sfxchn; + else + md_softchn=0; + + if (md_mode & DMODE_SOFT_MUSIC) md_softchn+=md_sngchn; + + while (md_softchn>limit) { + if (++t & 1) { + if ((md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>4)) md_sfxchn--; + } else { + if ((md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>8)) md_sngchn--; + } + + if (!(md_mode & DMODE_SOFT_SNDFX)) + md_softchn=md_sfxchn; + else + md_softchn=0; + + if (!(md_mode & DMODE_SOFT_MUSIC)) + md_softchn+=md_sngchn; + } + md_numchn=md_hardchn+md_softchn; +} + +/* Note: 'type' indicates whether the returned value should be for music or for + sound effects. */ +ULONG MD_SampleSpace(int type) +{ + if(type==MD_MUSIC) + type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE; + else if(type==MD_SNDFX) + type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE; + + return md_driver->FreeSampleSpace(type); +} + +ULONG MD_SampleLength(int type,SAMPLE* s) +{ + if(type==MD_MUSIC) + type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE; + else + if(type==MD_SNDFX) + type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE; + + return md_driver->RealSampleLength(type,s); +} + +MIKMODAPI CHAR* MikMod_InfoDriver(void) +{ + int t; + size_t len=0; + MDRIVER *l; + CHAR *list=NULL; + + MUTEX_LOCK(lists); + /* compute size of buffer */ + for(l = firstdriver; l; l = l->next) + len += 4 + (l->next ? 1 : 0) + strlen(l->Version); + + if(len) + if((list=(CHAR*)MikMod_malloc(len*sizeof(CHAR))) != NULL) { + CHAR *list_end = list; + list[0] = 0; + /* list all registered device drivers : */ + for(t = 1, l = firstdriver; l; l = l->next, t++) { + list_end += sprintf(list_end, "%2d %s%s", t, l->Version, (l->next)? "\n" : ""); + } + } + MUTEX_UNLOCK(lists); + return list; +} + +void _mm_registerdriver(struct MDRIVER* drv) +{ + MDRIVER *cruise = firstdriver; + + /* don't register a MISSING() driver */ + if ((drv->Name) && (drv->Version)) { + if (cruise) { + if ( cruise == drv ) + return; + while(cruise->next) { + cruise = cruise->next; + if ( cruise == drv ) + return; + } + cruise->next = drv; + } else + firstdriver = drv; + } +} + +MIKMODAPI void MikMod_RegisterDriver(struct MDRIVER* drv) +{ + /* if we try to register an invalid driver, or an already registered driver, + ignore this attempt */ + if ((!drv)||(drv->next)||(!drv->Name)) + return; + + MUTEX_LOCK(lists); + _mm_registerdriver(drv); + MUTEX_UNLOCK(lists); +} + +MIKMODAPI int MikMod_DriverFromAlias(const CHAR *alias) +{ + int rank=1; + MDRIVER *cruise; + + MUTEX_LOCK(lists); + cruise=firstdriver; + while(cruise) { + if (cruise->Alias) { + if (!(_mm_strcasecmp(alias,cruise->Alias))) break; + rank++; + } + cruise=cruise->next; + } + if(!cruise) rank=0; + MUTEX_UNLOCK(lists); + + return rank; +} + +MIKMODAPI MDRIVER *MikMod_DriverByOrdinal(int ordinal) +{ + MDRIVER *cruise; + + /* Allow only driver ordinals > 0 */ + if (!ordinal) return NULL; + + MUTEX_LOCK(lists); + cruise = firstdriver; + while (cruise && --ordinal) + cruise = cruise->next; + MUTEX_UNLOCK(lists); + return cruise; +} + +SWORD MD_SampleLoad(SAMPLOAD* s, int type) +{ + SWORD result; + + if(type==MD_MUSIC) + type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE; + else if(type==MD_SNDFX) + type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE; + + SL_Init(s); + result=md_driver->SampleLoad(s,type); + SL_Exit(s); + + return result; +} + +void MD_SampleUnload(SWORD handle) +{ + md_driver->SampleUnload(handle); +} + +MIKMODAPI MikMod_player_t MikMod_RegisterPlayer(MikMod_player_t player) +{ + MikMod_player_t result; + + MUTEX_LOCK(vars); + result=md_player; + md_player=player; + MUTEX_UNLOCK(vars); + + return result; +} + +MIKMODAPI void MikMod_Update(void) +{ + MUTEX_LOCK(vars); + if(isplaying) { + if((!pf)||(!pf->forbid)) + md_driver->Update(); + else { + if (md_driver->Pause) + md_driver->Pause(); + } + } + MUTEX_UNLOCK(vars); +} + +void Voice_SetVolume_internal(SBYTE voice,UWORD vol) +{ + ULONG tmp; + + if((voice<0)||(voice>=md_numchn)) return; + + /* range checks */ + if(md_musicvolume>128) md_musicvolume=128; + if(md_sndfxvolume>128) md_sndfxvolume=128; + if(md_volume>128) md_volume=128; + + tmp=(ULONG)vol*(ULONG)md_volume* + ((voiceVoiceSetVolume(voice,tmp/16384UL); +} + +MIKMODAPI void Voice_SetVolume(SBYTE voice,UWORD vol) +{ + MUTEX_LOCK(vars); + Voice_SetVolume_internal(voice,vol); + MUTEX_UNLOCK(vars); +} + +MIKMODAPI UWORD Voice_GetVolume(SBYTE voice) +{ + UWORD result=0; + + MUTEX_LOCK(vars); + if((voice>=0)&&(voiceVoiceGetVolume(voice); + MUTEX_UNLOCK(vars); + + return result; +} + +void Voice_SetFrequency_internal(SBYTE voice,ULONG frq) +{ + if((voice<0)||(voice>=md_numchn)) return; + if((md_sample[voice])&&(md_sample[voice]->divfactor)) + frq/=md_sample[voice]->divfactor; + md_driver->VoiceSetFrequency(voice,frq); +} + +MIKMODAPI void Voice_SetFrequency(SBYTE voice,ULONG frq) +{ + MUTEX_LOCK(vars); + Voice_SetFrequency_internal(voice,frq); + MUTEX_UNLOCK(vars); +} + +MIKMODAPI ULONG Voice_GetFrequency(SBYTE voice) +{ + ULONG result=0; + + MUTEX_LOCK(vars); + if((voice>=0)&&(voiceVoiceGetFrequency(voice); + MUTEX_UNLOCK(vars); + + return result; +} + +void Voice_SetPanning_internal(SBYTE voice,ULONG pan) +{ + if((voice<0)||(voice>=md_numchn)) return; + if(pan!=PAN_SURROUND) { + if(md_pansep>128) md_pansep=128; + if(md_mode & DMODE_REVERSE) pan=255-pan; + pan = (((SWORD)(pan-128)*md_pansep)/128)+128; + } + md_driver->VoiceSetPanning(voice, pan); +} + +MIKMODAPI void Voice_SetPanning(SBYTE voice,ULONG pan) +{ +#ifdef MIKMOD_DEBUG + if((pan!=PAN_SURROUND)&&((pan<0)||(pan>255))) + fprintf(stderr,"\rVoice_SetPanning called with pan=%ld\n",(long)pan); +#endif + + MUTEX_LOCK(vars); + Voice_SetPanning_internal(voice,pan); + MUTEX_UNLOCK(vars); +} + +MIKMODAPI ULONG Voice_GetPanning(SBYTE voice) +{ + ULONG result=PAN_CENTER; + + MUTEX_LOCK(vars); + if((voice>=0)&&(voiceVoiceGetPanning(voice); + MUTEX_UNLOCK(vars); + + return result; +} + +void Voice_Play_internal(SBYTE voice,SAMPLE* s,ULONG start) +{ + ULONG repend; + + if((voice<0)||(voice>=md_numchn)) return; + + md_sample[voice]=s; + repend=s->loopend; + + if(s->flags&SF_LOOP) + /* repend can't be bigger than size */ + if(repend>s->length) repend=s->length; + + md_driver->VoicePlay(voice,s->handle,start,s->length,s->loopstart,repend,s->flags); +} + +MIKMODAPI void Voice_Play(SBYTE voice,SAMPLE* s,ULONG start) +{ + if(start>s->length) return; + + MUTEX_LOCK(vars); + Voice_Play_internal(voice,s,start); + MUTEX_UNLOCK(vars); +} + +void Voice_Stop_internal(SBYTE voice) +{ + if((voice<0)||(voice>=md_numchn)) return; + if(voice>=md_sngchn) + /* It is a sound effects channel, so flag the voice as non-critical! */ + sfxinfo[voice-md_sngchn]=0; + md_driver->VoiceStop(voice); +} + +MIKMODAPI void Voice_Stop(SBYTE voice) +{ + MUTEX_LOCK(vars); + Voice_Stop_internal(voice); + MUTEX_UNLOCK(vars); +} + +BOOL Voice_Stopped_internal(SBYTE voice) +{ + if((voice<0)||(voice>=md_numchn)) return 0; + return(md_driver->VoiceStopped(voice)); +} + +MIKMODAPI BOOL Voice_Stopped(SBYTE voice) +{ + BOOL result; + + MUTEX_LOCK(vars); + result=Voice_Stopped_internal(voice); + MUTEX_UNLOCK(vars); + + return result; +} + +MIKMODAPI SLONG Voice_GetPosition(SBYTE voice) +{ + SLONG result=0; + + MUTEX_LOCK(vars); + if((voice>=0)&&(voiceVoiceGetPosition) + result=(md_driver->VoiceGetPosition(voice)); + else + result=-1; + } + MUTEX_UNLOCK(vars); + + return result; +} + +MIKMODAPI ULONG Voice_RealVolume(SBYTE voice) +{ + ULONG result=0; + + MUTEX_LOCK(vars); + if((voice>=0)&&(voiceVoiceRealVolume) + result=(md_driver->VoiceRealVolume(voice)); + MUTEX_UNLOCK(vars); + + return result; +} + +MIKMODAPI void VC_SetCallback(MikMod_callback_t callback) +{ + vc_callback = callback; +} + +static int _mm_init(const CHAR *cmdline) +{ + UWORD t; + + _mm_critical = 1; + + /* if md_device==0, try to find a device number */ + if(!md_device) { + cmdline=NULL; + + for(t=1,md_driver=firstdriver;md_driver;md_driver=md_driver->next,t++) + if(md_driver->IsPresent()) break; + + if(!md_driver) { + _mm_errno = MMERR_DETECTING_DEVICE; + if(_mm_errorhandler) _mm_errorhandler(); + md_driver = &drv_nos; + return 1; + } + + md_device = t; + } else { + /* if n>0, use that driver */ + for(t=1,md_driver=firstdriver;(md_driver)&&(t!=md_device);md_driver=md_driver->next) + t++; + + if(!md_driver) { + _mm_errno = MMERR_INVALID_DEVICE; + if(_mm_errorhandler) _mm_errorhandler(); + md_driver = &drv_nos; + return 1; + } + + /* arguments here might be necessary for the presence check to succeed */ + if(cmdline&&(md_driver->CommandLine)) + md_driver->CommandLine(cmdline); + + if(!md_driver->IsPresent()) { + _mm_errno = MMERR_DETECTING_DEVICE; + if(_mm_errorhandler) _mm_errorhandler(); + md_driver = &drv_nos; + return 1; + } + } + + olddevice = md_device; + if(md_driver->Init()) { + MikMod_Exit_internal(); + if(_mm_errorhandler) _mm_errorhandler(); + return 1; + } + + initialized=1; + _mm_critical=0; + + return 0; +} + +MIKMODAPI int MikMod_Init(const CHAR *cmdline) +{ + int result; + + MUTEX_LOCK(vars); + MUTEX_LOCK(lists); + result=_mm_init(cmdline); + MUTEX_UNLOCK(lists); + MUTEX_UNLOCK(vars); + + return result; +} + +void MikMod_Exit_internal(void) +{ + MikMod_DisableOutput_internal(); + md_driver->Exit(); + md_numchn = md_sfxchn = md_sngchn = 0; + md_driver = &drv_nos; + + MikMod_free(sfxinfo); + MikMod_free(md_sample); + md_sample = NULL; + sfxinfo = NULL; + + initialized = 0; +} + +MIKMODAPI void MikMod_Exit(void) +{ + MUTEX_LOCK(vars); + MUTEX_LOCK(lists); + MikMod_Exit_internal(); + MUTEX_UNLOCK(lists); + MUTEX_UNLOCK(vars); +} + +/* Reset the driver using the new global variable settings. + If the driver has not been initialized, it will be now. */ +static int _mm_reset(const CHAR *cmdline) +{ + BOOL wasplaying = 0; + + if(!initialized) return _mm_init(cmdline); + + if (isplaying) { + wasplaying = 1; + md_driver->PlayStop(); + } + + if((!md_driver->Reset)||(md_device != olddevice)) { + /* md_driver->Reset was NULL, or md_device was changed, so do a full + reset of the driver. */ + md_driver->Exit(); + if(_mm_init(cmdline)) { + MikMod_Exit_internal(); + if(_mm_errno) + if(_mm_errorhandler) _mm_errorhandler(); + return 1; + } + } else { + if(md_driver->Reset()) { + MikMod_Exit_internal(); + if(_mm_errno) + if(_mm_errorhandler) _mm_errorhandler(); + return 1; + } + } + + if (wasplaying) return md_driver->PlayStart(); + return 0; +} + +MIKMODAPI int MikMod_Reset(const CHAR *cmdline) +{ + int result; + + MUTEX_LOCK(vars); + MUTEX_LOCK(lists); + result=_mm_reset(cmdline); + MUTEX_UNLOCK(lists); + MUTEX_UNLOCK(vars); + + return result; +} + +/* If either parameter is -1, the current set value will be retained. */ +int MikMod_SetNumVoices_internal(int music, int sfx) +{ + BOOL resume = 0; + int t, oldchn = 0; + + if((!music)&&(!sfx)) return 1; + _mm_critical = 1; + if(isplaying) { + MikMod_DisableOutput_internal(); + oldchn = md_numchn; + resume = 1; + } + + MikMod_free(sfxinfo); + MikMod_free(md_sample); + md_sample = NULL; + sfxinfo = NULL; + + if(music!=-1) md_sngchn = music; + if(sfx!=-1) md_sfxchn = sfx; + md_numchn = md_sngchn + md_sfxchn; + + LimitHardVoices(md_driver->HardVoiceLimit); + LimitSoftVoices(md_driver->SoftVoiceLimit); + + if(md_driver->SetNumVoices()) { + MikMod_Exit_internal(); + if(_mm_errno) + if(_mm_errorhandler!=NULL) _mm_errorhandler(); + md_numchn = md_softchn = md_hardchn = md_sfxchn = md_sngchn = 0; + return 1; + } + + if(md_sngchn+md_sfxchn) + md_sample=(SAMPLE**)MikMod_calloc(md_sngchn+md_sfxchn,sizeof(SAMPLE*)); + if(md_sfxchn) + sfxinfo = (UBYTE *)MikMod_calloc(md_sfxchn,sizeof(UBYTE)); + + /* make sure the player doesn't start with garbage */ + for(t=oldchn;tPlayStart()) return 1; + isplaying = 1; + } + _mm_critical = 0; + return 0; +} + +MIKMODAPI int MikMod_EnableOutput(void) +{ + int result; + + MUTEX_LOCK(vars); + result=MikMod_EnableOutput_internal(); + MUTEX_UNLOCK(vars); + + return result; +} + +void MikMod_DisableOutput_internal(void) +{ + if(isplaying && md_driver) { + isplaying = 0; + md_driver->PlayStop(); + } +} + +MIKMODAPI void MikMod_DisableOutput(void) +{ + MUTEX_LOCK(vars); + MikMod_DisableOutput_internal(); + MUTEX_UNLOCK(vars); +} + +BOOL MikMod_Active_internal(void) +{ + return isplaying; +} + +MIKMODAPI BOOL MikMod_Active(void) +{ + BOOL result; + + MUTEX_LOCK(vars); + result=MikMod_Active_internal(); + MUTEX_UNLOCK(vars); + + return result; +} + +/* Plays a sound effects sample. Picks a voice from the number of voices + allocated for use as sound effects (loops through voices, skipping all active + criticals). + + Returns the voice that the sound is being played on. */ +static SBYTE Sample_Play_internal(SAMPLE *s,ULONG start,UBYTE flags) +{ + int orig=sfxpool;/* for cases where all channels are critical */ + int c; + + if(!md_sfxchn) return -1; + if(s->volume>64) s->volume = 64; + + /* check the first location after sfxpool */ + do { + if(sfxinfo[sfxpool]&SFX_CRITICAL) { + if(md_driver->VoiceStopped(c=sfxpool+md_sngchn)) { + sfxinfo[sfxpool]=flags; + Voice_Play_internal(c,s,start); + md_driver->VoiceSetVolume(c,s->volume<<2); + Voice_SetPanning_internal(c,s->panning); + md_driver->VoiceSetFrequency(c,s->speed); + sfxpool++; + if(sfxpool>=md_sfxchn) sfxpool=0; + return c; + } + } else { + sfxinfo[sfxpool]=flags; + Voice_Play_internal(c=sfxpool+md_sngchn,s,start); + md_driver->VoiceSetVolume(c,s->volume<<2); + Voice_SetPanning_internal(c,s->panning); + md_driver->VoiceSetFrequency(c,s->speed); + sfxpool++; + if(sfxpool>=md_sfxchn) sfxpool=0; + return c; + } + + sfxpool++; + if(sfxpool>=md_sfxchn) sfxpool = 0; + } while(sfxpool!=orig); + + return -1; +} + +MIKMODAPI SBYTE Sample_Play(SAMPLE *s,ULONG start,UBYTE flags) +{ + SBYTE result; + + MUTEX_LOCK(vars); + result=Sample_Play_internal(s,start,flags); + MUTEX_UNLOCK(vars); + + return result; +} + +MIKMODAPI long MikMod_GetVersion(void) +{ + return LIBMIKMOD_VERSION; +} + +/*========== MT-safe stuff */ + +#ifdef HAVE_PTHREAD +#define INIT_MUTEX(name) \ + pthread_mutex_t _mm_mutex_##name=PTHREAD_MUTEX_INITIALIZER + +#elif defined(__OS2__)||defined(__EMX__) +#define INIT_MUTEX(name) \ + HMTX _mm_mutex_##name + +#elif defined(_WIN32) +#define INIT_MUTEX(name) \ + HANDLE _mm_mutex_##name + +#else +#define INIT_MUTEX(name) \ + void *_mm_mutex_##name = NULL +#endif + +INIT_MUTEX(vars); +INIT_MUTEX(lists); + +MIKMODAPI BOOL MikMod_InitThreads(void) +{ + static int firstcall=1; + static BOOL result = 0; + + if (firstcall) { + firstcall=0; +#ifdef HAVE_PTHREAD + result=1; +#elif defined(__OS2__)||defined(__EMX__) + if(DosCreateMutexSem((PSZ)NULL,&_mm_mutex_lists,0,0) || + DosCreateMutexSem((PSZ)NULL,&_mm_mutex_vars,0,0)) { + _mm_mutex_lists=_mm_mutex_vars=(HMTX)NULL; + result=0; + } else + result=1; +#elif defined(_WIN32) + if((!(_mm_mutex_lists=CreateMutex(NULL,FALSE,TEXT("libmikmod(lists)"))))|| + (!(_mm_mutex_vars=CreateMutex(NULL,FALSE,TEXT("libmikmod(vars)"))))) + result=0; + else + result=1; +#endif + } + return result; +} + +MIKMODAPI void MikMod_Unlock(void) +{ + MUTEX_UNLOCK(lists); + MUTEX_UNLOCK(vars); +} + +MIKMODAPI void MikMod_Lock(void) +{ + MUTEX_LOCK(vars); + MUTEX_LOCK(lists); +} + +/*========== Parameter extraction helper */ + +CHAR *MD_GetAtom(const CHAR *atomname, const CHAR *cmdline, BOOL implicit) +{ + CHAR *ret=NULL; + + if(cmdline) { + const CHAR *buf=strstr(cmdline,atomname); + + if((buf)&&((buf==cmdline)||(*(buf-1)==','))) { + const CHAR *ptr=buf+strlen(atomname); + + if(*ptr=='=') { + for(buf=++ptr;(*ptr)&&((*ptr)!=',');ptr++); + ret=(CHAR *)MikMod_malloc((1+ptr-buf)*sizeof(CHAR)); + if(ret) + strncpy(ret,buf,ptr-buf); + } else if((*ptr==',')||(!*ptr)) { + if(implicit) { + ret=(CHAR *)MikMod_malloc((1+ptr-buf)*sizeof(CHAR)); + if(ret) + strncpy(ret,buf,ptr-buf); + } + } + } + } + return ret; +} + +#if (MIKMOD_UNIX) + +/*========== Posix helper functions */ + +/* Check if the file is a regular or nonexistant file (or a link to a such a + file), and that, should the calling program be setuid, the access rights are + reasonable. Returns 1 if it is safe to rewrite the file, 0 otherwise. + The goal is to prevent a setuid root libmikmod application from overriding + files like /etc/passwd with digital sound... */ +BOOL MD_Access(const CHAR * filename) +{ + struct stat buf; + + if(!stat(filename,&buf)) { + /* not a regular file ? */ + if(!S_ISREG(buf.st_mode)) return 0; + /* more than one hard link to the file ? */ + if(buf.st_nlink>1) return 0; + /* check access rights with the real user and group id */ + if(getuid()==buf.st_uid) { + if(!(buf.st_mode&S_IWUSR)) return 0; + } else if(getgid()==buf.st_gid) { + if(!(buf.st_mode&S_IWGRP)) return 0; + } else + if(!(buf.st_mode&S_IWOTH)) return 0; + } + + return 1; +} + +/* Drop all root privileges we might have */ +int MD_DropPrivileges(void) +{ + if(!geteuid()) { + if(getuid()) { + /* we are setuid root -> drop setuid to become the real user */ + if(setuid(getuid())) return 1; + } else { + /* we are run as root -> drop all and become user 'nobody' */ + struct passwd *nobody; + int uid; + + if(!(nobody=getpwnam("nobody"))) return 1; /* no such user ? */ + uid=nobody->pw_uid; + if (!uid) /* user 'nobody' has root privileges ? weird... */ + return 1; + if (setuid(uid)) return 1; + } + } + return 0; +} + +#endif + +/* ex:set ts=8: */ diff --git a/libs/mikmod/playercode/mdulaw.c b/libs/mikmod/playercode/mdulaw.c new file mode 100644 index 0000000..7cee49b --- /dev/null +++ b/libs/mikmod/playercode/mdulaw.c @@ -0,0 +1,2119 @@ +/* MikMod sound library + (c) 1998, 1999, 2002 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Mu-law routines + +==============================================================================*/ + +/* + The contents of this file are derived from the files libst.h and raw.c from + an old version of the sox (SOund eXchange) package written by Lance Norskog + and Jef Poskanzer. + The following copyright notice applies: + + ** Copyright (C) 1989 by Jef Poskanzer. + ** + ** Permission to use, copy, modify, and distribute this software and its + ** documentation for any purpose and without fee is hereby granted, provided + ** that the above copyright notice appear in all copies and that both that + ** copyright notice and this permission notice appear in supporting + ** documentation. This software is provided "as is" without express or + ** implied warranty. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "mikmod_internals.h" + +static const unsigned char ulaw_comp_table[16384] = { + 0xff, 0xfe, 0xfe, 0xfd, 0xfd, 0xfc, 0xfc, 0xfb, + 0xfb, 0xfa, 0xfa, 0xf9, 0xf9, 0xf8, 0xf8, 0xf7, + 0xf7, 0xf6, 0xf6, 0xf5, 0xf5, 0xf4, 0xf4, 0xf3, + 0xf3, 0xf2, 0xf2, 0xf1, 0xf1, 0xf0, 0xf0, 0xef, + 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xed, + 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xeb, + 0xeb, 0xeb, 0xeb, 0xea, 0xea, 0xea, 0xea, 0xe9, + 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6, 0xe5, + 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, 0xe3, + 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2, 0xe1, + 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, + 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xde, + 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, + 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xda, + 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xd9, + 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd8, + 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd7, + 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd6, + 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd5, + 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd4, + 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd3, + 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd2, + 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd1, + 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd0, + 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xcf, + 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, + 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb, + 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, + 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xca, + 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, + 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xc9, + 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, + 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc8, + 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, + 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc7, + 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, + 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc4, + 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, + 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc3, + 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, + 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc2, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc1, + 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, + 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, + 0x34, 0x34, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, + 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, + 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, + 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, + 0x35, 0x35, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, + 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, + 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, + 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, + 0x37, 0x37, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, + 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, + 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, + 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, + 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, + 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, + 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, + 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, + 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, + 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, + 0x43, 0x43, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, + 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, + 0x45, 0x45, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, + 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, + 0x47, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, + 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, + 0x49, 0x49, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, + 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, + 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, + 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, + 0x4b, 0x4b, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, + 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, + 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, + 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, + 0x4d, 0x4d, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, + 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, + 0x4e, 0x4e, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, + 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, + 0x4f, 0x4f, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, + 0x50, 0x50, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, + 0x51, 0x51, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, + 0x52, 0x52, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, + 0x53, 0x53, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, + 0x54, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, + 0x56, 0x56, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, + 0x57, 0x57, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, + 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, + 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, + 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, + 0x5d, 0x5d, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, + 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x60, 0x61, 0x61, + 0x61, 0x61, 0x62, 0x62, 0x62, 0x62, 0x63, 0x63, + 0x63, 0x63, 0x64, 0x64, 0x64, 0x64, 0x65, 0x65, + 0x65, 0x65, 0x66, 0x66, 0x66, 0x66, 0x67, 0x67, + 0x67, 0x67, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69, + 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, + 0x6b, 0x6b, 0x6c, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, + 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, + 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, 0x72, 0x72, + 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x76, 0x76, + 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a, + 0x7b, 0x7b, 0x7c, 0x7c, 0x7d, 0x7d, 0x7e, 0x7e +}; + +#define st_linear_to_ulaw(x) ulaw_comp_table[(x / 4) & 0x3fff] + +/* convert unsigned linear data from Mixer_WriteBytes() to ulaw */ +void unsignedtoulaw(char *buf, int nsamp) +{ + while (nsamp--) { + register long datum = (long)*((unsigned char *)buf); + + datum ^= 128; /* convert to signed */ + datum <<= 8; + /* round up to 12 bits of data */ + datum += 0x8; /* + 0b1000 */ + datum = st_linear_to_ulaw(datum); + *buf++ = (char)datum; + } +} + +/* ex:set ts=4: */ diff --git a/libs/mikmod/playercode/mloader.c b/libs/mikmod/playercode/mloader.c new file mode 100644 index 0000000..684b8f2 --- /dev/null +++ b/libs/mikmod/playercode/mloader.c @@ -0,0 +1,670 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file + AUTHORS for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + These routines are used to access the available module loaders + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +MREADER *modreader; +MODULE of; + +static MLOADER *firstloader=NULL; + +static MUNPACKER unpackers[] = { + PP20_Unpack, + MMCMP_Unpack, + XPK_Unpack, + S404_Unpack, + NULL +}; + +const UWORD finetune[16] = { + 8363,8413,8463,8529,8581,8651,8723,8757, + 7895,7941,7985,8046,8107,8169,8232,8280 +}; + +MIKMODAPI CHAR* MikMod_InfoLoader(void) +{ + int len=0; + MLOADER *l; + CHAR *list=NULL; + + MUTEX_LOCK(lists); + /* compute size of buffer */ + for(l = firstloader; l; l = l->next) + len += 1 + (l->next ? 1 : 0) + strlen(l->version); + + if(len) + if((list=(CHAR*)MikMod_malloc(len*sizeof(CHAR))) != NULL) { + CHAR *list_end = list; + list[0] = 0; + /* list all registered module loders */ + for(l = firstloader; l; l = l->next) { + list_end += sprintf(list_end, "%s%s", l->version, (l->next) ? "\n" : ""); + } + } + MUTEX_UNLOCK(lists); + return list; +} + +void _mm_registerloader(MLOADER* ldr) +{ + MLOADER *cruise=firstloader; + + if(cruise) { + while(cruise->next) cruise = cruise->next; + cruise->next=ldr; + } else + firstloader=ldr; +} + +MIKMODAPI void MikMod_RegisterLoader(struct MLOADER* ldr) +{ + /* if we try to register an invalid loader, or an already registered loader, + ignore this attempt */ + if ((!ldr)||(ldr->next)) + return; + + MUTEX_LOCK(lists); + _mm_registerloader(ldr); + MUTEX_UNLOCK(lists); +} + +BOOL ReadComment(UWORD len) +{ + if(len) { + int i; + + if(!(of.comment=(CHAR*)MikMod_malloc(len+1))) return 0; + _mm_read_UBYTES(of.comment,len,modreader); + + /* translate IT linefeeds */ + for(i=0;i=0)&&(line[i]==' ');i--) line[i]=0; + for(i=0;ilines) { + if(!(of.comment=(CHAR*)MikMod_malloc(total+1))) { + MikMod_free(storage); + MikMod_free(tempcomment); + return 0; + } + + /* convert message */ + for(line=tempcomment,t=0;tlength) SL_RegisterSample(s,MD_MUSIC,modreader); + + return 1; +} + +/* Creates a CSTR out of a character buffer of 'len' bytes, but strips any + terminating non-printing characters like 0, spaces etc. */ +CHAR *DupStr(const CHAR* s, UWORD len, BOOL strict) +{ + UWORD t; + CHAR *d=NULL; + + /* Scan for last printing char in buffer [includes high ascii up to 254] */ + while(len) { + if(s[len-1]>0x20) break; + len--; + } + + /* Scan forward for possible NULL character */ + if(strict) { + for(t=0;thandle>=0) + MD_SampleUnload(s->handle); + +/* moved samplename freeing to our caller ML_FreeEx(), + * because we are called conditionally. */ +} + +static void ML_XFreeInstrument(INSTRUMENT *i) +{ + MikMod_free(i->insname); +} + +static void ML_FreeEx(MODULE *mf) +{ + UWORD t; + + MikMod_free(mf->songname); + MikMod_free(mf->comment); + + MikMod_free(mf->modtype); + MikMod_free(mf->positions); + MikMod_free(mf->patterns); + MikMod_free(mf->pattrows); + + if(mf->tracks) { + for(t=0;tnumtrk;t++) + MikMod_free(mf->tracks[t]); + MikMod_free(mf->tracks); + } + if(mf->instruments) { + for(t=0;tnumins;t++) + ML_XFreeInstrument(&mf->instruments[t]); + MikMod_free(mf->instruments); + } + if(mf->samples) { + for(t=0;tnumsmp;t++) { + MikMod_free(mf->samples[t].samplename); + if(mf->samples[t].length) ML_XFreeSample(&mf->samples[t]); + } + MikMod_free(mf->samples); + } + memset(mf,0,sizeof(MODULE)); + if(mf!=&of) MikMod_free(mf); +} + +static MODULE *ML_AllocUniMod(void) +{ + return (MODULE *) MikMod_malloc(sizeof(MODULE)); +} + +static BOOL ML_TryUnpack(MREADER *reader,void **out,long *outlen) +{ + int i; + + *out = NULL; + *outlen = 0; + + for(i=0;unpackers[i]!=NULL;++i) { + _mm_rewind(reader); + if(unpackers[i](reader,out,outlen)) return 1; + } + return 0; +} + +static void Player_Free_internal(MODULE *mf) +{ + if(mf) { + Player_Exit_internal(mf); + ML_FreeEx(mf); + } +} + +MIKMODAPI void Player_Free(MODULE *mf) +{ + MUTEX_LOCK(vars); + Player_Free_internal(mf); + MUTEX_UNLOCK(vars); +} + +static CHAR* Player_LoadTitle_internal(MREADER *reader) +{ + MLOADER *l; + CHAR *title; + void *unpk; + long newlen; + + modreader=reader; + _mm_errno = 0; + _mm_critical = 0; + _mm_iobase_setcur(modreader); + + if(ML_TryUnpack(modreader,&unpk,&newlen)) { + if(!(modreader=_mm_new_mem_reader(unpk,newlen))) { + modreader=reader; + MikMod_free(unpk); + return NULL; + } + } + + /* Try to find a loader that recognizes the module */ + for(l=firstloader;l;l=l->next) { + _mm_rewind(modreader); + if(l->Test()) break; + } + + if(l) { + title = l->LoadTitle(); + } + else { + _mm_errno = MMERR_NOT_A_MODULE; + if(_mm_errorhandler) _mm_errorhandler(); + title = NULL; + } + + if (modreader!=reader) { + _mm_delete_mem_reader(modreader); + modreader=reader; + MikMod_free(unpk); + } + return title; +} + +MIKMODAPI CHAR* Player_LoadTitleFP(FILE *fp) +{ + CHAR* result=NULL; + MREADER* reader; + + if(fp && (reader=_mm_new_file_reader(fp)) != NULL) { + MUTEX_LOCK(lists); + result=Player_LoadTitle_internal(reader); + MUTEX_UNLOCK(lists); + _mm_delete_file_reader(reader); + } + return result; +} + +MIKMODAPI CHAR* Player_LoadTitleMem(const char *buffer,int len) +{ + CHAR *result=NULL; + MREADER* reader; + + if (!buffer || len <= 0) return NULL; + if ((reader=_mm_new_mem_reader(buffer,len)) != NULL) + { + MUTEX_LOCK(lists); + result=Player_LoadTitle_internal(reader); + MUTEX_UNLOCK(lists); + _mm_delete_mem_reader(reader); + } + + return result; +} + +MIKMODAPI CHAR* Player_LoadTitleGeneric(MREADER *reader) +{ + CHAR *result=NULL; + + if (reader) { + MUTEX_LOCK(lists); + result=Player_LoadTitle_internal(reader); + MUTEX_UNLOCK(lists); + } + return result; +} + +MIKMODAPI CHAR* Player_LoadTitle(const CHAR* filename) +{ + CHAR* result=NULL; + FILE* fp; + MREADER* reader; + + if((fp=_mm_fopen(filename,"rb")) != NULL) { + if((reader=_mm_new_file_reader(fp)) != NULL) { + MUTEX_LOCK(lists); + result=Player_LoadTitle_internal(reader); + MUTEX_UNLOCK(lists); + _mm_delete_file_reader(reader); + } + _mm_fclose(fp); + } + return result; +} + +/* Loads a module given an reader */ +static MODULE* Player_LoadGeneric_internal(MREADER *reader,int maxchan,BOOL curious) +{ + int t; + MLOADER *l; + BOOL ok; + MODULE *mf; + void *unpk; + long newlen; + + modreader = reader; + _mm_errno = 0; + _mm_critical = 0; + _mm_iobase_setcur(modreader); + + if(ML_TryUnpack(modreader,&unpk,&newlen)) { + if(!(modreader=_mm_new_mem_reader(unpk,newlen))) { + modreader=reader; + MikMod_free(unpk); + return NULL; + } + } + + /* Try to find a loader that recognizes the module */ + for(l=firstloader;l;l=l->next) { + _mm_rewind(modreader); + if(l->Test()) break; + } + + if(!l) { + _mm_errno = MMERR_NOT_A_MODULE; + if(modreader!=reader) { + _mm_delete_mem_reader(modreader); + modreader=reader; + MikMod_free(unpk); + } + if(_mm_errorhandler) _mm_errorhandler(); + _mm_rewind(modreader); + _mm_iobase_revert(modreader); + return NULL; + } + + /* init unitrk routines */ + if(!UniInit()) { + if(modreader!=reader) { + _mm_delete_mem_reader(modreader); + modreader=reader; + MikMod_free(unpk); + } + if(_mm_errorhandler) _mm_errorhandler(); + _mm_rewind(modreader); + _mm_iobase_revert(modreader); + return NULL; + } + + /* init the module structure with vanilla settings */ + memset(&of,0,sizeof(MODULE)); + of.bpmlimit = 33; + of.initvolume = 128; + for (t = 0; t < UF_MAXCHAN; t++) of.chanvol[t] = 64; + for (t = 0; t < UF_MAXCHAN; t++) + of.panning[t] = ((t + 1) & 2) ? PAN_RIGHT : PAN_LEFT; + + /* init module loader and load the header / patterns */ + if (!l->Init || l->Init()) { + _mm_rewind(modreader); + ok = l->Load(curious); + if (ok) { + /* propagate inflags=flags for in-module samples */ + for (t = 0; t < of.numsmp; t++) + if (of.samples[t].inflags == 0) + of.samples[t].inflags = of.samples[t].flags; + } + } else + ok = 0; + + /* free loader and unitrk allocations */ + if (l->Cleanup) l->Cleanup(); + UniCleanup(); + + if(ok) ok = ML_LoadSamples(); + if(ok) ok = ((mf=ML_AllocUniMod()) != NULL); + if(!ok) { + ML_FreeEx(&of); + if(modreader!=reader) { + _mm_delete_mem_reader(modreader); + modreader=reader; + MikMod_free(unpk); + } + if(_mm_errorhandler) _mm_errorhandler(); + _mm_rewind(modreader); + _mm_iobase_revert(modreader); + return NULL; + } + + /* If the module doesn't have any specific panning, create a + MOD-like panning, with the channels half-separated. */ + if (!(of.flags & UF_PANNING)) + for (t = 0; t < of.numchn; t++) + of.panning[t] = ((t + 1) & 2) ? PAN_HALFRIGHT : PAN_HALFLEFT; + + /* Copy the static MODULE contents into the dynamic MODULE struct. */ + memcpy(mf,&of,sizeof(MODULE)); + + if(maxchan>0) { + if(!(mf->flags&UF_NNA)&&(mf->numchnnumchn; + else + if((mf->numvoices)&&(mf->numvoicesnumvoices; + + if(maxchannumchn) mf->flags |= UF_NNA; + + ok = !MikMod_SetNumVoices_internal(maxchan,-1); + } + + if(ok) ok = !SL_LoadSamples(); + if(ok) ok = !Player_Init(mf); + + if(modreader!=reader) { + _mm_delete_mem_reader(modreader); + modreader=reader; + MikMod_free(unpk); + } + _mm_iobase_revert(modreader); + + if(!ok) { + Player_Free_internal(mf); + return NULL; + } + return mf; +} + +MIKMODAPI MODULE* Player_LoadGeneric(MREADER *reader,int maxchan,BOOL curious) +{ + MODULE* result; + + MUTEX_LOCK(vars); + MUTEX_LOCK(lists); + result=Player_LoadGeneric_internal(reader,maxchan,curious); + MUTEX_UNLOCK(lists); + MUTEX_UNLOCK(vars); + + return result; +} + +MIKMODAPI MODULE* Player_LoadMem(const char *buffer,int len,int maxchan,BOOL curious) +{ + MODULE* result=NULL; + MREADER* reader; + + if (!buffer || len <= 0) return NULL; + if ((reader=_mm_new_mem_reader(buffer, len)) != NULL) { + result=Player_LoadGeneric(reader,maxchan,curious); + _mm_delete_mem_reader(reader); + } + return result; +} + +/* Loads a module given a file pointer. + File is loaded from the current file seek position. */ +MIKMODAPI MODULE* Player_LoadFP(FILE* fp,int maxchan,BOOL curious) +{ + MODULE* result=NULL; + struct MREADER* reader; + + if (fp && (reader=_mm_new_file_reader(fp)) != NULL) { + result=Player_LoadGeneric(reader,maxchan,curious); + _mm_delete_file_reader(reader); + } + return result; +} + +/* Open a module via its filename. The loader will initialize the specified + song-player 'player'. */ +MIKMODAPI MODULE* Player_Load(const CHAR* filename,int maxchan,BOOL curious) +{ + FILE *fp; + MODULE *mf=NULL; + + if((fp=_mm_fopen(filename,"rb")) != NULL) { + mf=Player_LoadFP(fp,maxchan,curious); + _mm_fclose(fp); + } + return mf; +} + +/* ex:set ts=4: */ diff --git a/libs/mikmod/playercode/mlreg.c b/libs/mikmod/playercode/mlreg.c new file mode 100644 index 0000000..fd41e86 --- /dev/null +++ b/libs/mikmod/playercode/mlreg.c @@ -0,0 +1,67 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Routine for registering all loaders in libmikmod for the current platform. + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mikmod_internals.h" + +static void MikMod_RegisterAllLoaders_internal(void) +{ + _mm_registerloader(&load_669); + _mm_registerloader(&load_amf); + _mm_registerloader(&load_asy); + _mm_registerloader(&load_dsm); + _mm_registerloader(&load_far); + _mm_registerloader(&load_gdm); +/* _mm_registerloader(&load_gt2);*/ /* load_gt2 isn't complete */ + _mm_registerloader(&load_it); + _mm_registerloader(&load_imf); + _mm_registerloader(&load_mod); + _mm_registerloader(&load_med); + _mm_registerloader(&load_mtm); + _mm_registerloader(&load_okt); + _mm_registerloader(&load_s3m); + _mm_registerloader(&load_stm); + _mm_registerloader(&load_stx); + _mm_registerloader(&load_ult); + _mm_registerloader(&load_umx); + _mm_registerloader(&load_uni); + _mm_registerloader(&load_xm); + + _mm_registerloader(&load_m15); +} + +MIKMODAPI void MikMod_RegisterAllLoaders(void) +{ + MUTEX_LOCK(lists); + MikMod_RegisterAllLoaders_internal(); + MUTEX_UNLOCK(lists); +} +/* ex:set ts=4: */ diff --git a/libs/mikmod/playercode/mlutil.c b/libs/mikmod/playercode/mlutil.c new file mode 100644 index 0000000..21dcb20 --- /dev/null +++ b/libs/mikmod/playercode/mlutil.c @@ -0,0 +1,330 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS + for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Utility functions for the module loader + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +/*========== Shared tracker identifiers */ + +const CHAR *STM_Signatures[STM_NTRACKERS] = { + "!Scream!", + "BMOD2STM", + "WUZAMOD!" +}; + +const CHAR *STM_Version[STM_NTRACKERS] = { + "Screamtracker 2", + "Converted by MOD2STM (STM format)", + "Wuzamod (STM format)" +}; + +/*========== Shared loader variables */ + +SBYTE remap[UF_MAXCHAN]; /* for removing empty channels */ +UBYTE* poslookup=NULL; /* lookup table for pattern jumps after blank + pattern removal */ +UWORD poslookupcnt; +UWORD* origpositions=NULL; + +BOOL filters; /* resonant filters in use */ +UBYTE activemacro; /* active midi macro number for Sxx,xx<80h */ +UBYTE filtermacros[UF_MAXMACRO]; /* midi macro settings */ +FILTER filtersettings[UF_MAXFILTER]; /* computed filter settings */ + +/*========== Linear periods stuff */ + +int* noteindex=NULL; /* remap value for linear period modules */ +static unsigned noteindexcount=0; + +int *AllocLinear(void) +{ + if(of.numsmp>noteindexcount) { + noteindexcount=of.numsmp; + noteindex=(int*)MikMod_realloc(noteindex,noteindexcount*sizeof(int)); + } + return noteindex; +} + +void FreeLinear(void) +{ + MikMod_free(noteindex); + noteindex=NULL; + noteindexcount=0; +} + +int speed_to_finetune(ULONG speed,int sample) +{ + int ctmp=0,tmp,note=1,ft=0; + + speed>>=1; + while((tmp=getfrequency(of.flags,getlinearperiod(note<<1,0)))speed) + tmp=getfrequency(of.flags,getlinearperiod(note<<1,--ft)); + else { + note--; + while(ctmp>4)*10+(inf&0xf)); + else + UniPTEffect(0xd,inf); + break; + case 4: /* Dxy volumeslide */ + UniEffect(UNI_S3MEFFECTD,inf); + break; + case 5: /* Exy toneslide down */ + UniEffect(UNI_S3MEFFECTE,inf); + break; + case 6: /* Fxy toneslide up */ + UniEffect(UNI_S3MEFFECTF,inf); + break; + case 7: /* Gxx Tone portamento, speed xx */ + if (flags & S3MIT_OLDSTYLE) + UniPTEffect(0x3,inf); + else + UniEffect(UNI_ITEFFECTG,inf); + break; + case 8: /* Hxy vibrato */ + if (flags & S3MIT_OLDSTYLE) + UniPTEffect(0x4,inf); + else + UniEffect(UNI_ITEFFECTH,inf); + break; + case 9: /* Ixy tremor, ontime x, offtime y */ + if (flags & S3MIT_OLDSTYLE) + UniEffect(UNI_S3MEFFECTI,inf); + else + UniEffect(UNI_ITEFFECTI,inf); + break; + case 0xa: /* Jxy arpeggio */ + UniPTEffect(0x0,inf); + break; + case 0xb: /* Kxy Dual command H00 & Dxy */ + if (flags & S3MIT_OLDSTYLE) + UniPTEffect(0x4,0); + else + UniEffect(UNI_ITEFFECTH,0); + UniEffect(UNI_S3MEFFECTD,inf); + break; + case 0xc: /* Lxy Dual command G00 & Dxy */ + if (flags & S3MIT_OLDSTYLE) + UniPTEffect(0x3,0); + else + UniEffect(UNI_ITEFFECTG,0); + UniEffect(UNI_S3MEFFECTD,inf); + break; + case 0xd: /* Mxx Set Channel Volume */ + UniEffect(UNI_ITEFFECTM,inf); + break; + case 0xe: /* Nxy Slide Channel Volume */ + UniEffect(UNI_ITEFFECTN,inf); + break; + case 0xf: /* Oxx set sampleoffset xx00h */ + UniPTEffect(0x9,inf); + break; + case 0x10: /* Pxy Slide Panning Commands */ + UniEffect(UNI_ITEFFECTP,inf); + break; + case 0x11: /* Qxy Retrig (+volumeslide) */ + UniWriteByte(UNI_S3MEFFECTQ); + if(inf && !lo && !(flags & S3MIT_OLDSTYLE)) + UniWriteByte(1); + else + UniWriteByte(inf); + break; + case 0x12: /* Rxy tremolo speed x, depth y */ + UniEffect(UNI_S3MEFFECTR,inf); + break; + case 0x13: /* Sxx special commands */ + if (inf>=0xf0) { + /* change resonant filter settings if necessary */ + if((filters)&&((inf&0xf)!=activemacro)) { + activemacro=inf&0xf; + for(inf=0;inf<0x80;inf++) + filtersettings[inf].filter=filtermacros[activemacro]; + } + } else { + /* Scream Tracker does not have samples larger than + 64 Kb, thus doesn't need the SAx effect */ + if ((flags & S3MIT_SCREAM) && ((inf & 0xf0) == 0xa0)) + break; + + UniEffect(UNI_ITEFFECTS0,inf); + } + break; + case 0x14: /* Txx tempo */ + if(inf>=0x20) + UniEffect(UNI_S3MEFFECTT,inf); + else { + if(!(flags & S3MIT_OLDSTYLE)) + /* IT Tempo slide */ + UniEffect(UNI_ITEFFECTT,inf); + } + break; + case 0x15: /* Uxy Fine Vibrato speed x, depth y */ + if(flags & S3MIT_OLDSTYLE) + UniEffect(UNI_S3MEFFECTU,inf); + else + UniEffect(UNI_ITEFFECTU,inf); + break; + case 0x16: /* Vxx Set Global Volume */ + UniEffect(UNI_XMEFFECTG,inf); + break; + case 0x17: /* Wxy Global Volume Slide */ + UniEffect(UNI_ITEFFECTW,inf); + break; + case 0x18: /* Xxx amiga command 8xx */ + if(flags & S3MIT_OLDSTYLE) { + if(inf>128) + UniEffect(UNI_ITEFFECTS0,0x91); /* surround */ + else + UniPTEffect(0x8,(inf==128)?255:(inf<<1)); + } else + UniPTEffect(0x8,inf); + break; + case 0x19: /* Yxy Panbrello speed x, depth y */ + UniEffect(UNI_ITEFFECTY,inf); + break; + case 0x1a: /* Zxx midi/resonant filters */ + if(filtersettings[inf].filter) { + UniWriteByte(UNI_ITEFFECTZ); + UniWriteByte(filtersettings[inf].filter); + UniWriteByte(filtersettings[inf].inf); + } + break; + } + } +} + +/*========== Unitrk stuff */ + +/* Generic effect writing routine */ +void UniEffect(UWORD eff,UWORD dat) +{ + if((!eff)||(eff>=UNI_LAST)) return; + + UniWriteByte(eff); + if(unioperands[eff]==2) + UniWriteWord(dat); + else + UniWriteByte(dat); +} + +/* Appends UNI_PTEFFECTX opcode to the unitrk stream. */ +void UniPTEffect(UBYTE eff, UBYTE dat) +{ +#ifdef MIKMOD_DEBUG + if (eff>=0x10) + fprintf(stderr,"UniPTEffect called with incorrect eff value %d\n",eff); + else +#endif + if((eff)||(dat)||(of.flags&UF_ARPMEM)) UniEffect(UNI_PTEFFECT0+eff,dat); +} + +/* Appends UNI_VOLEFFECT + effect/dat to unistream. */ +void UniVolEffect(UWORD eff,UBYTE dat) +{ + if((eff)||(dat)) { /* don't write empty effect */ + UniWriteByte(UNI_VOLEFFECTS); + UniWriteByte(eff);UniWriteByte(dat); + } +} + +/* ex:set ts=4: */ diff --git a/libs/mikmod/playercode/mplayer.c b/libs/mikmod/playercode/mplayer.c new file mode 100644 index 0000000..e8c46ea --- /dev/null +++ b/libs/mikmod/playercode/mplayer.c @@ -0,0 +1,3429 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS + for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + The Protracker Player Driver + + The protracker driver supports all base Protracker 3.x commands and features. + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#ifdef SRANDOM_IN_MATH_H +#include +#else +#include +#endif + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +extern long int random(void); +#endif + +/* The currently playing module */ +MODULE *pf = NULL; + +#define NUMVOICES(mod) (md_sngchn < (mod)->numvoices ? md_sngchn : (mod)->numvoices) + +#define HIGH_OCTAVE 2 /* number of above-range octaves */ + +static const UWORD oldperiods[OCTAVE*2]={ + 0x6b00, 0x6800, 0x6500, 0x6220, 0x5f50, 0x5c80, + 0x5a00, 0x5740, 0x54d0, 0x5260, 0x5010, 0x4dc0, + 0x4b90, 0x4960, 0x4750, 0x4540, 0x4350, 0x4160, + 0x3f90, 0x3dc0, 0x3c10, 0x3a40, 0x38b0, 0x3700 +}; + +static const UBYTE VibratoTable[32]={ + 0, 24, 49, 74, 97,120,141,161,180,197,212,224,235,244,250,253, + 255,253,250,244,235,224,212,197,180,161,141,120, 97, 74, 49, 24 +}; + +static const UBYTE avibtab[128]={ + 0, 1, 3, 4, 6, 7, 9,10,12,14,15,17,18,20,21,23, + 24,25,27,28,30,31,32,34,35,36,38,39,40,41,42,44, + 45,46,47,48,49,50,51,52,53,54,54,55,56,57,57,58, + 59,59,60,60,61,61,62,62,62,63,63,63,63,63,63,63, + 64,63,63,63,63,63,63,63,62,62,62,61,61,60,60,59, + 59,58,57,57,56,55,54,54,53,52,51,50,49,48,47,46, + 45,44,42,41,40,39,38,36,35,34,32,31,30,28,27,25, + 24,23,21,20,18,17,15,14,12,10, 9, 7, 6, 4, 3, 1 +}; + +/* Triton's linear periods to frequency translation table (for XM modules) */ +static const ULONG lintab[768]={ + 535232,534749,534266,533784,533303,532822,532341,531861, + 531381,530902,530423,529944,529466,528988,528511,528034, + 527558,527082,526607,526131,525657,525183,524709,524236, + 523763,523290,522818,522346,521875,521404,520934,520464, + 519994,519525,519057,518588,518121,517653,517186,516720, + 516253,515788,515322,514858,514393,513929,513465,513002, + 512539,512077,511615,511154,510692,510232,509771,509312, + 508852,508393,507934,507476,507018,506561,506104,505647, + 505191,504735,504280,503825,503371,502917,502463,502010, + 501557,501104,500652,500201,499749,499298,498848,498398, + 497948,497499,497050,496602,496154,495706,495259,494812, + 494366,493920,493474,493029,492585,492140,491696,491253, + 490809,490367,489924,489482,489041,488600,488159,487718, + 487278,486839,486400,485961,485522,485084,484647,484210, + 483773,483336,482900,482465,482029,481595,481160,480726, + 480292,479859,479426,478994,478562,478130,477699,477268, + 476837,476407,475977,475548,475119,474690,474262,473834, + 473407,472979,472553,472126,471701,471275,470850,470425, + 470001,469577,469153,468730,468307,467884,467462,467041, + 466619,466198,465778,465358,464938,464518,464099,463681, + 463262,462844,462427,462010,461593,461177,460760,460345, + 459930,459515,459100,458686,458272,457859,457446,457033, + 456621,456209,455797,455386,454975,454565,454155,453745, + 453336,452927,452518,452110,451702,451294,450887,450481, + 450074,449668,449262,448857,448452,448048,447644,447240, + 446836,446433,446030,445628,445226,444824,444423,444022, + 443622,443221,442821,442422,442023,441624,441226,440828, + 440430,440033,439636,439239,438843,438447,438051,437656, + 437261,436867,436473,436079,435686,435293,434900,434508, + 434116,433724,433333,432942,432551,432161,431771,431382, + 430992,430604,430215,429827,429439,429052,428665,428278, + 427892,427506,427120,426735,426350,425965,425581,425197, + 424813,424430,424047,423665,423283,422901,422519,422138, + 421757,421377,420997,420617,420237,419858,419479,419101, + 418723,418345,417968,417591,417214,416838,416462,416086, + 415711,415336,414961,414586,414212,413839,413465,413092, + 412720,412347,411975,411604,411232,410862,410491,410121, + 409751,409381,409012,408643,408274,407906,407538,407170, + 406803,406436,406069,405703,405337,404971,404606,404241, + 403876,403512,403148,402784,402421,402058,401695,401333, + 400970,400609,400247,399886,399525,399165,398805,398445, + 398086,397727,397368,397009,396651,396293,395936,395579, + 395222,394865,394509,394153,393798,393442,393087,392733, + 392378,392024,391671,391317,390964,390612,390259,389907, + 389556,389204,388853,388502,388152,387802,387452,387102, + 386753,386404,386056,385707,385359,385012,384664,384317, + 383971,383624,383278,382932,382587,382242,381897,381552, + 381208,380864,380521,380177,379834,379492,379149,378807, + 378466,378124,377783,377442,377102,376762,376422,376082, + 375743,375404,375065,374727,374389,374051,373714,373377, + 373040,372703,372367,372031,371695,371360,371025,370690, + 370356,370022,369688,369355,369021,368688,368356,368023, + 367691,367360,367028,366697,366366,366036,365706,365376, + 365046,364717,364388,364059,363731,363403,363075,362747, + 362420,362093,361766,361440,361114,360788,360463,360137, + 359813,359488,359164,358840,358516,358193,357869,357547, + 357224,356902,356580,356258,355937,355616,355295,354974, + 354654,354334,354014,353695,353376,353057,352739,352420, + 352103,351785,351468,351150,350834,350517,350201,349885, + 349569,349254,348939,348624,348310,347995,347682,347368, + 347055,346741,346429,346116,345804,345492,345180,344869, + 344558,344247,343936,343626,343316,343006,342697,342388, + 342079,341770,341462,341154,340846,340539,340231,339924, + 339618,339311,339005,338700,338394,338089,337784,337479, + 337175,336870,336566,336263,335959,335656,335354,335051, + 334749,334447,334145,333844,333542,333242,332941,332641, + 332341,332041,331741,331442,331143,330844,330546,330247, + 329950,329652,329355,329057,328761,328464,328168,327872, + 327576,327280,326985,326690,326395,326101,325807,325513, + 325219,324926,324633,324340,324047,323755,323463,323171, + 322879,322588,322297,322006,321716,321426,321136,320846, + 320557,320267,319978,319690,319401,319113,318825,318538, + 318250,317963,317676,317390,317103,316817,316532,316246, + 315961,315676,315391,315106,314822,314538,314254,313971, + 313688,313405,313122,312839,312557,312275,311994,311712, + 311431,311150,310869,310589,310309,310029,309749,309470, + 309190,308911,308633,308354,308076,307798,307521,307243, + 306966,306689,306412,306136,305860,305584,305308,305033, + 304758,304483,304208,303934,303659,303385,303112,302838, + 302565,302292,302019,301747,301475,301203,300931,300660, + 300388,300117,299847,299576,299306,299036,298766,298497, + 298227,297958,297689,297421,297153,296884,296617,296349, + 296082,295815,295548,295281,295015,294749,294483,294217, + 293952,293686,293421,293157,292892,292628,292364,292100, + 291837,291574,291311,291048,290785,290523,290261,289999, + 289737,289476,289215,288954,288693,288433,288173,287913, + 287653,287393,287134,286875,286616,286358,286099,285841, + 285583,285326,285068,284811,284554,284298,284041,283785, + 283529,283273,283017,282762,282507,282252,281998,281743, + 281489,281235,280981,280728,280475,280222,279969,279716, + 279464,279212,278960,278708,278457,278206,277955,277704, + 277453,277203,276953,276703,276453,276204,275955,275706, + 275457,275209,274960,274712,274465,274217,273970,273722, + 273476,273229,272982,272736,272490,272244,271999,271753, + 271508,271263,271018,270774,270530,270286,270042,269798, + 269555,269312,269069,268826,268583,268341,268099,267857 +}; + +#define LOGFAC 2*16 +static const UWORD logtab[104]={ + LOGFAC*907,LOGFAC*900,LOGFAC*894,LOGFAC*887, + LOGFAC*881,LOGFAC*875,LOGFAC*868,LOGFAC*862, + LOGFAC*856,LOGFAC*850,LOGFAC*844,LOGFAC*838, + LOGFAC*832,LOGFAC*826,LOGFAC*820,LOGFAC*814, + LOGFAC*808,LOGFAC*802,LOGFAC*796,LOGFAC*791, + LOGFAC*785,LOGFAC*779,LOGFAC*774,LOGFAC*768, + LOGFAC*762,LOGFAC*757,LOGFAC*752,LOGFAC*746, + LOGFAC*741,LOGFAC*736,LOGFAC*730,LOGFAC*725, + LOGFAC*720,LOGFAC*715,LOGFAC*709,LOGFAC*704, + LOGFAC*699,LOGFAC*694,LOGFAC*689,LOGFAC*684, + LOGFAC*678,LOGFAC*675,LOGFAC*670,LOGFAC*665, + LOGFAC*660,LOGFAC*655,LOGFAC*651,LOGFAC*646, + LOGFAC*640,LOGFAC*636,LOGFAC*632,LOGFAC*628, + LOGFAC*623,LOGFAC*619,LOGFAC*614,LOGFAC*610, + LOGFAC*604,LOGFAC*601,LOGFAC*597,LOGFAC*592, + LOGFAC*588,LOGFAC*584,LOGFAC*580,LOGFAC*575, + LOGFAC*570,LOGFAC*567,LOGFAC*563,LOGFAC*559, + LOGFAC*555,LOGFAC*551,LOGFAC*547,LOGFAC*543, + LOGFAC*538,LOGFAC*535,LOGFAC*532,LOGFAC*528, + LOGFAC*524,LOGFAC*520,LOGFAC*516,LOGFAC*513, + LOGFAC*508,LOGFAC*505,LOGFAC*502,LOGFAC*498, + LOGFAC*494,LOGFAC*491,LOGFAC*487,LOGFAC*484, + LOGFAC*480,LOGFAC*477,LOGFAC*474,LOGFAC*470, + LOGFAC*467,LOGFAC*463,LOGFAC*460,LOGFAC*457, + LOGFAC*453,LOGFAC*450,LOGFAC*447,LOGFAC*443, + LOGFAC*440,LOGFAC*437,LOGFAC*434,LOGFAC*431 +}; + +static const SBYTE PanbrelloTable[256]={ + 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23, + 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59, + 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60, + 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, + 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26, + 24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2, + 0,- 2,- 3,- 5,- 6,- 8,- 9,-11,-12,-14,-16,-17,-19,-20,-22,-23, + -24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,-43,-44, + -45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,-56,-57,-58,-59, + -59,-60,-60,-61,-61,-62,-62,-62,-63,-63,-63,-64,-64,-64,-64,-64, + -64,-64,-64,-64,-64,-64,-63,-63,-63,-62,-62,-62,-61,-61,-60,-60, + -59,-59,-58,-57,-56,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46, + -45,-44,-43,-42,-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26, + -24,-23,-22,-20,-19,-17,-16,-14,-12,-11,- 9,- 8,- 6,- 5,- 3,- 2 +}; + +/* returns a random value between 0 and ceil-1, ceil must be a power of two */ +static int getrandom(int ceilval) +{ +#if defined(HAVE_SRANDOM) && !defined(_MIKMOD_AMIGA) + return random()&(ceilval-1); +#else + return (rand()*ceilval)/(RAND_MAX+1.0); +#endif +} + +/* New Note Action Scoring System : + -------------------------------- + 1) total-volume (fadevol, chanvol, volume) is the main scorer. + 2) a looping sample is a bonus x2 + 3) a foreground channel is a bonus x4 + 4) an active envelope with keyoff is a handicap -x2 +*/ +static int MP_FindEmptyChannel(MODULE *mod) +{ + MP_VOICE *a; + ULONG t,k,tvol,pp; + + for (t=0;tvoice[t].main.kick==KICK_ABSENT)|| + (mod->voice[t].main.kick==KICK_ENV))&& + Voice_Stopped_internal(t)) + return t; + + tvol=0xffffffUL;t=-1;a=mod->voice; + for (k=0;kmain.s) + return k; + + if ((a->main.kick==KICK_ABSENT)||(a->main.kick==KICK_ENV)) { + pp=a->totalvol<<((a->main.s->flags&SF_LOOP)?1:0); + if ((a->master)&&(a==a->master->slave)) + pp<<=2; + + if (pp8000*7) return -1; + return t; +} + +static SWORD Interpolate(SWORD p,SWORD p1,SWORD p2,SWORD v1,SWORD v2) +{ + if ((p1==p2)||(p==p1)) return v1; + return v1+((SLONG)((p-p1)*(v2-v1))/(p2-p1)); +} + +UWORD getlinearperiod(UWORD note,ULONG fine) +{ + UWORD t; + + t=((20L+2*HIGH_OCTAVE)*OCTAVE+2-note)*32L-(fine>>1); + return t; +} + +static UWORD getlogperiod(UWORD note,ULONG fine) +{ + UWORD n,o; + UWORD p1,p2; + ULONG i; + + n=note%(2*OCTAVE); + o=note/(2*OCTAVE); + i=(n<<2)+(fine>>4); /* n*8 + fine/16 */ + + p1=logtab[i]; + p2=logtab[i+1]; + + return (Interpolate(fine>>4,0,15,p1,p2)>>o); +} + +static UWORD getoldperiod(UWORD note,ULONG speed) +{ + UWORD n,o; + + /* This happens sometimes on badly converted AMF, and old MOD */ + if (!speed) { +#ifdef MIKMOD_DEBUG + fprintf(stderr,"\rmplayer: getoldperiod() called with note=%d, speed=0 !\n",note); +#endif + return 4242; /* <- prevent divide overflow.. (42 hehe) */ + } + + n=note%(2*OCTAVE); + o=note/(2*OCTAVE); + return ((8363L*(ULONG)oldperiods[n])>>o)/speed; +} + +static UWORD GetPeriod(UWORD flags, UWORD note, ULONG speed) +{ + if (flags & UF_XMPERIODS) { + if (flags & UF_LINEAR) + return getlinearperiod(note, speed); + else + return getlogperiod(note, speed); + } else + return getoldperiod(note, speed); +} + +static SWORD InterpolateEnv(SWORD p,ENVPT *a,ENVPT *b) +{ + return (Interpolate(p,a->pos,b->pos,a->val,b->val)); +} + +static SWORD DoPan(SWORD envpan,SWORD pan) +{ + int newpan; + + newpan=pan+(((envpan-PAN_CENTER)*(128-abs(pan-PAN_CENTER)))/128); + + return (newpanPAN_RIGHT?PAN_RIGHT:newpan); +} + +static SWORD StartEnvelope(ENVPR *t,UBYTE flg,UBYTE pts,UBYTE susbeg,UBYTE susend,UBYTE beg,UBYTE end,ENVPT *p,UBYTE keyoff) +{ + t->flg=flg; + t->pts=pts; + t->susbeg=susbeg; + t->susend=susend; + t->beg=beg; + t->end=end; + t->env=p; + t->p=0; + t->a=0; + t->b=((t->flg&EF_SUSTAIN)&&(!(keyoff&KEY_OFF)))?0:1; + + /* Imago Orpheus sometimes stores an extra initial point in the envelope */ + if ((t->pts>=2)&&(t->env[0].pos==t->env[1].pos)) { + t->a++; + t->b++; + } + + /* Fit in the envelope, still */ + if (t->a >= t->pts) + t->a = t->pts - 1; + if (t->b >= t->pts) + t->b = t->pts-1; + + return t->env[t->a].val; +} + +/* This procedure processes all envelope types, include volume, pitch, and + panning. Envelopes are defined by a set of points, each with a magnitude + [relating either to volume, panning position, or pitch modifier] and a tick + position. + + Envelopes work in the following manner: + + (a) Each tick the envelope is moved a point further in its progression. For + an accurate progression, magnitudes between two envelope points are + interpolated. + + (b) When progression reaches a defined point on the envelope, values are + shifted to interpolate between this point and the next, and checks for + loops or envelope end are done. + + Misc: + Sustain loops are loops that are only active as long as the keyoff flag is + clear. When a volume envelope terminates, so does the current fadeout. +*/ +static SWORD ProcessEnvelope(MP_VOICE *aout, ENVPR *t, SWORD v) +{ + if (t->flg & EF_ON) { + UBYTE a, b; /* actual points in the envelope */ + UWORD p; /* the 'tick counter' - real point being played */ + + a = t->a; + b = t->b; + p = t->p; + + /* + * Sustain loop on one point (XM type). + * Not processed if KEYOFF. + * Don't move and don't interpolate when the point is reached + */ + if ((t->flg & EF_SUSTAIN) && t->susbeg == t->susend && + (!(aout->main.keyoff & KEY_OFF) && p == t->env[t->susbeg].pos)) { + v = t->env[t->susbeg].val; + } else { + /* + * All following situations will require interpolation between + * two envelope points. + */ + + /* + * Sustain loop between two points (IT type). + * Not processed if KEYOFF. + */ + /* if we were on a loop point, loop now */ + if ((t->flg & EF_SUSTAIN) && !(aout->main.keyoff & KEY_OFF) && + a >= t->susend) { + a = t->susbeg; + b = (t->susbeg==t->susend)?a:a+1; + p = t->env[a].pos; + v = t->env[a].val; + } else + /* + * Regular loop. + * Be sure to correctly handle single point loops. + */ + if ((t->flg & EF_LOOP) && a >= t->end) { + a = t->beg; + b = t->beg == t->end ? a : a + 1; + p = t->env[a].pos; + v = t->env[a].val; + } else + /* + * Non looping situations. + */ + if (a != b) + v = InterpolateEnv(p, &t->env[a], &t->env[b]); + else + v = t->env[a].val; + + /* + * Start to fade if the volume envelope is finished. + */ + if (p >= t->env[t->pts - 1].pos) { + if (t->flg & EF_VOLENV) { + aout->main.keyoff |= KEY_FADE; + if (!v) + aout->main.fadevol = 0; + } + } else { + p++; + /* did pointer reach point b? */ + if (p >= t->env[b].pos) + a = b++; /* shift points a and b */ + } + t->a = a; + t->b = b; + t->p = p; + } + } + return v; +} + +/* XM linear period to frequency conversion */ +ULONG getfrequency(UWORD flags,ULONG period) +{ + if (flags & UF_LINEAR) { + SLONG shift = ((SLONG)period / 768) - HIGH_OCTAVE; + + if (shift >= 0) + return lintab[period % 768] >> shift; + else + return lintab[period % 768] << (-shift); + } else + return (8363L*1712L)/(period?period:1); +} + +/*========== Protracker effects */ + +static void DoArpeggio(UWORD tick, UWORD flags, MP_CONTROL *a, UBYTE style) +{ + UBYTE note=a->main.note; + + if (a->arpmem) { + switch (style) { + case 0: /* mod style: N, N+x, N+y */ + switch (tick % 3) { + /* case 0: unchanged */ + case 1: + note += (a->arpmem >> 4); + break; + case 2: + note += (a->arpmem & 0xf); + break; + } + break; + case 3: /* okt arpeggio 3: N-x, N, N+y */ + switch (tick % 3) { + case 0: + note -= (a->arpmem >> 4); + break; + /* case 1: unchanged */ + case 2: + note += (a->arpmem & 0xf); + break; + } + break; + case 4: /* okt arpeggio 4: N, N+y, N, N-x */ + switch (tick % 4) { + /* case 0, case 2: unchanged */ + case 1: + note += (a->arpmem & 0xf); + break; + case 3: + note -= (a->arpmem >> 4); + break; + } + break; + case 5: /* okt arpeggio 5: N-x, N+y, N, and nothing at tick 0 */ + if (!tick) + break; + switch (tick % 3) { + /* case 0: unchanged */ + case 1: + note -= (a->arpmem >> 4); + break; + case 2: + note += (a->arpmem & 0xf); + break; + } + break; + } + a->main.period = GetPeriod(flags, (UWORD)note << 1, a->speed); + a->ownper = 1; + } +} + +static int DoPTEffect0(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat = UniGetByte(); + if (!tick) { + if (!dat && (flags & UF_ARPMEM)) + dat=a->arpmem; + else + a->arpmem=dat; + } + if (a->main.period) + DoArpeggio(tick, flags, a, 0); + + return 0; +} + +static int DoPTEffect1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat = UniGetByte(); + if (!tick && dat) + a->slidespeed = (UWORD)dat << 2; + if (a->main.period) + if (tick) + a->tmpperiod -= a->slidespeed; + + return 0; +} + +static int DoPTEffect2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat = UniGetByte(); + if (!tick && dat) + a->slidespeed = (UWORD)dat << 2; + if (a->main.period) + if (tick) + a->tmpperiod += a->slidespeed; + + return 0; +} + +static void DoToneSlide(UWORD tick, MP_CONTROL *a) +{ + if (!a->main.fadevol) + a->main.kick = (a->main.kick == KICK_NOTE)? KICK_NOTE : KICK_KEYOFF; + else + a->main.kick = (a->main.kick == KICK_NOTE)? KICK_ENV : KICK_ABSENT; + + if (tick != 0) { + int dist; + + /* We have to slide a->main.period towards a->wantedperiod, so compute + the difference between those two values */ + dist=a->main.period-a->wantedperiod; + + /* if they are equal or if portamentospeed is too big ...*/ + if (dist == 0 || a->portspeed > abs(dist)) + /* ...make tmpperiod equal tperiod */ + a->tmpperiod=a->main.period=a->wantedperiod; + else if (dist>0) { + a->tmpperiod-=a->portspeed; + a->main.period-=a->portspeed; /* dist>0, slide up */ + } else { + a->tmpperiod+=a->portspeed; + a->main.period+=a->portspeed; /* dist<0, slide down */ + } + } else + a->tmpperiod=a->main.period; + a->ownper = 1; +} + +static int DoPTEffect3(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat=UniGetByte(); + if ((!tick)&&(dat)) a->portspeed=(UWORD)dat<<2; + if (a->main.period) + DoToneSlide(tick, a); + + return 0; +} + +static void DoVibrato(UWORD tick, MP_CONTROL *a) +{ + UBYTE q; + UWORD temp = 0; /* silence warning */ + + if (!tick) + return; + + q=(a->vibpos>>2)&0x1f; + + switch (a->wavecontrol&3) { + case 0: /* sine */ + temp=VibratoTable[q]; + break; + case 1: /* ramp down */ + q<<=3; + if (a->vibpos<0) q=255-q; + temp=q; + break; + case 2: /* square wave */ + temp=255; + break; + case 3: /* random wave */ + temp=getrandom(256); + break; + } + + temp*=a->vibdepth; + temp>>=7;temp<<=2; + + if (a->vibpos>=0) + a->main.period=a->tmpperiod+temp; + else + a->main.period=a->tmpperiod-temp; + a->ownper = 1; + + if (tick != 0) + a->vibpos+=a->vibspd; +} + +static int DoPTEffect4(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat=UniGetByte(); + if (!tick) { + if (dat&0x0f) a->vibdepth=dat&0xf; + if (dat&0xf0) a->vibspd=(dat&0xf0)>>2; + } + if (a->main.period) + DoVibrato(tick, a); + + return 0; +} + +static void DoVolSlide(MP_CONTROL *a, UBYTE dat) +{ + if (dat&0xf) { + a->tmpvolume-=(dat&0x0f); + if (a->tmpvolume<0) + a->tmpvolume=0; + } else { + a->tmpvolume+=(dat>>4); + if (a->tmpvolume>64) + a->tmpvolume=64; + } +} + +static int DoPTEffect5(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat=UniGetByte(); + if (a->main.period) + DoToneSlide(tick, a); + + if (tick) + DoVolSlide(a, dat); + + return 0; +} + +/* DoPTEffect6 after DoPTEffectA */ + +static int DoPTEffect7(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + UBYTE q; + UWORD temp = 0; /* silence warning */ + + dat=UniGetByte(); + if (!tick) { + if (dat&0x0f) a->trmdepth=dat&0xf; + if (dat&0xf0) a->trmspd=(dat&0xf0)>>2; + } + if (a->main.period) { + q=(a->trmpos>>2)&0x1f; + + switch ((a->wavecontrol>>4)&3) { + case 0: /* sine */ + temp=VibratoTable[q]; + break; + case 1: /* ramp down */ + q<<=3; + if (a->trmpos<0) q=255-q; + temp=q; + break; + case 2: /* square wave */ + temp=255; + break; + case 3: /* random wave */ + temp=getrandom(256); + break; + } + temp*=a->trmdepth; + temp>>=6; + + if (a->trmpos>=0) { + a->volume=a->tmpvolume+temp; + if (a->volume>64) a->volume=64; + } else { + a->volume=a->tmpvolume-temp; + if (a->volume<0) a->volume=0; + } + a->ownvol = 1; + + if (tick) + a->trmpos+=a->trmspd; + } + + return 0; +} + +static int DoPTEffect8(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat = UniGetByte(); + if (mod->panflag) + a->main.panning = mod->panning[channel] = dat; + + return 0; +} + +static int DoPTEffect9(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat=UniGetByte(); + if (!tick) { + if (dat) a->soffset=(UWORD)dat<<8; + a->main.start=a->hioffset|a->soffset; + + if ((a->main.s)&&(a->main.start>a->main.s->length)) + a->main.start=a->main.s->flags&(SF_LOOP|SF_BIDI)? + a->main.s->loopstart:a->main.s->length; + } + + return 0; +} + +static int DoPTEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat=UniGetByte(); + if (tick) + DoVolSlide(a, dat); + + return 0; +} + +static int DoPTEffect6(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + if (a->main.period) + DoVibrato(tick, a); + DoPTEffectA(tick, flags, a, mod, channel); + + return 0; +} + +static int DoPTEffectB(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat=UniGetByte(); + + if (tick || mod->patdly2) + return 0; + + /* Vincent Voois uses a nasty trick in "Universal Bolero" */ + if (dat == mod->sngpos && mod->patbrk == mod->patpos) + return 0; + + if (!mod->loop && !mod->patbrk && + (dat < mod->sngpos || + (mod->sngpos == (mod->numpos - 1) && !mod->patbrk) || + (dat == mod->sngpos && (flags & UF_NOWRAP)) ) ) + { + /* if we don't loop, better not to skip the end of the + pattern, after all... so: + mod->patbrk=0; */ + mod->posjmp=3; + } else { + /* if we were fading, adjust... */ + if (mod->sngpos == (mod->numpos-1)) + mod->volume=mod->initvolume>128?128:mod->initvolume; + mod->sngpos=dat; + mod->posjmp=2; + mod->patpos=0; + /* cancel the FT2 pattern loop (E60) bug. + * also see DoEEffects() below for it. */ + if (flags & UF_FT2QUIRKS) mod->patbrk=0; + } + + return 0; +} + +static int DoPTEffectC(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat=UniGetByte(); + if (tick) return 0; + if (dat==(UBYTE)-1) a->anote=dat=0; /* note cut */ + else if (dat>64) dat=64; + a->tmpvolume=dat; + + return 0; +} + +static int DoPTEffectD(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat=UniGetByte(); + if ((tick)||(mod->patdly2)) return 0; + if ((mod->positions[mod->sngpos]!=LAST_PATTERN)&& + (dat>mod->pattrows[mod->positions[mod->sngpos]])) { + dat=mod->pattrows[mod->positions[mod->sngpos]]; + } + mod->patbrk=dat; + if (!mod->posjmp) { + /* don't ask me to explain this code - it makes + backwards.s3m and children.xm (heretic's version) play + correctly, among others. Take that for granted, or write + the page of comments yourself... you might need some + aspirin - Miod */ + if ((mod->sngpos==mod->numpos-1)&&(dat)&& + ((mod->loop) || (mod->positions[mod->sngpos]==(mod->numpat-1) && !(flags&UF_NOWRAP)))) { + mod->sngpos=0; + mod->posjmp=2; + } else + mod->posjmp=3; + } + + return 0; +} + +static void DoEEffects(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, + SWORD channel, UBYTE dat) +{ + UBYTE nib = dat & 0xf; + + switch (dat>>4) { + case 0x0: /* hardware filter toggle, not supported */ + break; + case 0x1: /* fineslide up */ + if (a->main.period) + if (!tick) + a->tmpperiod-=(nib<<2); + break; + case 0x2: /* fineslide dn */ + if (a->main.period) + if (!tick) + a->tmpperiod+=(nib<<2); + break; + case 0x3: /* glissando ctrl */ + a->glissando=nib; + break; + case 0x4: /* set vibrato waveform */ + a->wavecontrol&=0xf0; + a->wavecontrol|=nib; + break; + case 0x5: /* set finetune */ + if (a->main.period) { + if (flags&UF_XMPERIODS) + a->speed=nib+128; + else + a->speed=finetune[nib]; + a->tmpperiod=GetPeriod(flags, (UWORD)a->main.note<<1,a->speed); + } + break; + case 0x6: /* set patternloop */ + if (tick) + break; + if (nib) { /* set reppos or repcnt ? */ + /* set repcnt, so check if repcnt already is set, which means we + are already looping */ + if (a->pat_repcnt) + a->pat_repcnt--; /* already looping, decrease counter */ + else { +#if 0 + /* this would make walker.xm, shipped with Xsoundtracker, + play correctly, but it's better to remain compatible + with FT2 */ + if ((!(flags&UF_NOWRAP))||(a->pat_reppos!=POS_NONE)) +#endif + a->pat_repcnt=nib; /* not yet looping, so set repcnt */ + } + + if (a->pat_repcnt) { /* jump to reppos if repcnt>0 */ + if (a->pat_reppos==POS_NONE) + a->pat_reppos=mod->patpos-1; + if (a->pat_reppos==-1) { + mod->pat_repcrazy=1; + mod->patpos=0; + } else + mod->patpos=a->pat_reppos; + } else a->pat_reppos=POS_NONE; + } else { + a->pat_reppos=mod->patpos-1; /* set reppos - can be (-1) */ + /* emulate the FT2 pattern loop (E60) bug: + * http://milkytracker.org/docs/MilkyTracker.html#fxE6x + * roadblas.xm plays correctly with this. */ + if (flags & UF_FT2QUIRKS) mod->patbrk=mod->patpos; + } + break; + case 0x7: /* set tremolo waveform */ + a->wavecontrol&=0x0f; + a->wavecontrol|=nib<<4; + break; + case 0x8: /* set panning */ + if (mod->panflag) { + if (nib<=8) nib<<=4; + else nib*=17; + a->main.panning=mod->panning[channel]=nib; + } + break; + case 0x9: /* retrig note */ + /* do not retrigger on tick 0, until we are emulating FT2 and effect + data is zero */ + if (!tick && !((flags & UF_FT2QUIRKS) && (!nib))) + break; + /* only retrigger if data nibble > 0, or if tick 0 (FT2 compat) */ + if (nib || !tick) { + if (!a->retrig) { + /* when retrig counter reaches 0, reset counter and restart + the sample */ + if (a->main.period) a->main.kick=KICK_NOTE; + a->retrig=nib; + } + a->retrig--; /* countdown */ + } + break; + case 0xa: /* fine volume slide up */ + if (tick) + break; + a->tmpvolume+=nib; + if (a->tmpvolume>64) a->tmpvolume=64; + break; + case 0xb: /* fine volume slide dn */ + if (tick) + break; + a->tmpvolume-=nib; + if (a->tmpvolume<0)a->tmpvolume=0; + break; + case 0xc: /* cut note */ + /* When tick reaches the cut-note value, turn the volume to + zero (just like on the amiga) */ + if (tick>=nib) + a->tmpvolume=0; /* just turn the volume down */ + break; + case 0xd: /* note delay */ + /* delay the start of the sample until tick==nib */ + if (!tick) + a->main.notedelay=nib; + else if (a->main.notedelay) + a->main.notedelay--; + break; + case 0xe: /* pattern delay */ + if (!tick) + if (!mod->patdly2) + mod->patdly=nib+1; /* only once, when tick=0 */ + break; + case 0xf: /* invert loop, not supported */ + break; + } +} + +static int DoPTEffectE(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + DoEEffects(tick, flags, a, mod, channel, UniGetByte()); + + return 0; +} + +static int DoPTEffectF(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat=UniGetByte(); + if (tick||mod->patdly2) return 0; + if (mod->extspd&&(dat>=mod->bpmlimit)) + mod->bpm=dat; + else + if (dat) { + mod->sngspd=(dat>=mod->bpmlimit)?mod->bpmlimit-1:dat; + mod->vbtick=0; + } + + return 0; +} + +/*========== Scream Tracker effects */ + +static int DoS3MEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE speed; + + speed = UniGetByte(); + + if (tick || mod->patdly2) + return 0; + + if (speed > 128) + speed -= 128; + if (speed) { + mod->sngspd = speed; + mod->vbtick = 0; + } + + return 0; +} + +static void DoS3MVolSlide(UWORD tick, UWORD flags, MP_CONTROL *a, UBYTE inf) +{ + UBYTE lo, hi; + + if (inf) + a->s3mvolslide=inf; + else + inf=a->s3mvolslide; + + lo=inf&0xf; + hi=inf>>4; + + if (!lo) { + if ((tick)||(flags&UF_S3MSLIDES)) a->tmpvolume+=hi; + } else + if (!hi) { + if ((tick)||(flags&UF_S3MSLIDES)) a->tmpvolume-=lo; + } else + if (lo==0xf) { + if (!tick) a->tmpvolume+=(hi?hi:0xf); + } else + if (hi==0xf) { + if (!tick) a->tmpvolume-=(lo?lo:0xf); + } else + return; + + if (a->tmpvolume<0) + a->tmpvolume=0; + else if (a->tmpvolume>64) + a->tmpvolume=64; +} + +static int DoS3MEffectD(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + DoS3MVolSlide(tick, flags, a, UniGetByte()); + + return 1; +} + +static void DoS3MSlideDn(UWORD tick, MP_CONTROL *a, UBYTE inf) +{ + UBYTE hi,lo; + + if (inf) + a->slidespeed=inf; + else + inf=a->slidespeed; + + hi=inf>>4; + lo=inf&0xf; + + if (hi==0xf) { + if (!tick) a->tmpperiod+=(UWORD)lo<<2; + } else + if (hi==0xe) { + if (!tick) a->tmpperiod+=lo; + } else { + if (tick) a->tmpperiod+=(UWORD)inf<<2; + } +} + +static int DoS3MEffectE(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat=UniGetByte(); + if (a->main.period) + DoS3MSlideDn(tick, a,dat); + + return 0; +} + +static void DoS3MSlideUp(UWORD tick, MP_CONTROL *a, UBYTE inf) +{ + UBYTE hi,lo; + + if (inf) a->slidespeed=inf; + else inf=a->slidespeed; + + hi=inf>>4; + lo=inf&0xf; + + if (hi==0xf) { + if (!tick) a->tmpperiod-=(UWORD)lo<<2; + } else + if (hi==0xe) { + if (!tick) a->tmpperiod-=lo; + } else { + if (tick) a->tmpperiod-=(UWORD)inf<<2; + } +} + +static int DoS3MEffectF(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat=UniGetByte(); + if (a->main.period) + DoS3MSlideUp(tick, a,dat); + + return 0; +} + +static int DoS3MEffectI(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE inf, on, off; + + inf = UniGetByte(); + if (inf) + a->s3mtronof = inf; + else { + inf = a->s3mtronof; + if (!inf) + return 0; + } + + if (!tick) + return 0; + + on=(inf>>4)+1; + off=(inf&0xf)+1; + a->s3mtremor%=(on+off); + a->volume=(a->s3mtremortmpvolume:0; + a->ownvol=1; + a->s3mtremor++; + + return 0; +} + +static int DoS3MEffectQ(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE inf; + + inf = UniGetByte(); + if (a->main.period) { + if (inf) { + a->s3mrtgslide=inf>>4; + a->s3mrtgspeed=inf&0xf; + } + + /* only retrigger if low nibble > 0 */ + if (a->s3mrtgspeed>0) { + if (!a->retrig) { + /* when retrig counter reaches 0, reset counter and restart the + sample */ + if (a->main.kick!=KICK_NOTE) a->main.kick=KICK_KEYOFF; + a->retrig=a->s3mrtgspeed; + + if ((tick)||(flags&UF_S3MSLIDES)) { + switch (a->s3mrtgslide) { + case 1: + case 2: + case 3: + case 4: + case 5: + a->tmpvolume-=(1<<(a->s3mrtgslide-1)); + break; + case 6: + a->tmpvolume=(2*a->tmpvolume)/3; + break; + case 7: + a->tmpvolume>>=1; + break; + case 9: + case 0xa: + case 0xb: + case 0xc: + case 0xd: + a->tmpvolume+=(1<<(a->s3mrtgslide-9)); + break; + case 0xe: + a->tmpvolume=(3*a->tmpvolume)>>1; + break; + case 0xf: + a->tmpvolume=a->tmpvolume<<1; + break; + } + if (a->tmpvolume<0) + a->tmpvolume=0; + else if (a->tmpvolume>64) + a->tmpvolume=64; + } + } + a->retrig--; /* countdown */ + } + } + + return 0; +} + +static int DoS3MEffectR(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat, q; + UWORD temp=0; /* silence warning */ + + dat = UniGetByte(); + if (!tick) { + if (dat&0x0f) a->trmdepth=dat&0xf; + if (dat&0xf0) a->trmspd=(dat&0xf0)>>2; + } + + q=(a->trmpos>>2)&0x1f; + + switch ((a->wavecontrol>>4)&3) { + case 0: /* sine */ + temp=VibratoTable[q]; + break; + case 1: /* ramp down */ + q<<=3; + if (a->trmpos<0) q=255-q; + temp=q; + break; + case 2: /* square wave */ + temp=255; + break; + case 3: /* random */ + temp=getrandom(256); + break; + } + + temp*=a->trmdepth; + temp>>=7; + + if (a->trmpos>=0) { + a->volume=a->tmpvolume+temp; + if (a->volume>64) a->volume=64; + } else { + a->volume=a->tmpvolume-temp; + if (a->volume<0) a->volume=0; + } + a->ownvol = 1; + + if (tick) + a->trmpos+=a->trmspd; + + return 0; +} + +static int DoS3MEffectT(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE tempo; + + tempo = UniGetByte(); + + if (tick || mod->patdly2) + return 0; + + mod->bpm = (tempo < 32) ? 32 : tempo; + + return 0; +} + +static int DoS3MEffectU(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat, q; + UWORD temp = 0; /* silence warning */ + + dat = UniGetByte(); + if (!tick) { + if (dat&0x0f) a->vibdepth=dat&0xf; + if (dat&0xf0) a->vibspd=(dat&0xf0)>>2; + } else + if (a->main.period) { + q=(a->vibpos>>2)&0x1f; + + switch (a->wavecontrol&3) { + case 0: /* sine */ + temp=VibratoTable[q]; + break; + case 1: /* ramp down */ + q<<=3; + if (a->vibpos<0) q=255-q; + temp=q; + break; + case 2: /* square wave */ + temp=255; + break; + case 3: /* random */ + temp=getrandom(256); + break; + } + + temp*=a->vibdepth; + temp>>=8; + + if (a->vibpos>=0) + a->main.period=a->tmpperiod+temp; + else + a->main.period=a->tmpperiod-temp; + a->ownper = 1; + + a->vibpos+=a->vibspd; + } + + return 0; +} + +/*========== Envelope helpers */ + +static int DoKeyOff(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + a->main.keyoff|=KEY_OFF; + if ((!(a->main.volflg&EF_ON))||(a->main.volflg&EF_LOOP)) + a->main.keyoff=KEY_KILL; + + return 0; +} + +static int DoKeyFade(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat=UniGetByte(); + if ((tick>=dat)||(tick==mod->sngspd-1)) { + a->main.keyoff=KEY_KILL; + if (!(a->main.volflg&EF_ON)) + a->main.fadevol=0; + } + + return 0; +} + +/*========== Fast Tracker effects */ + +/* DoXMEffect6 after DoXMEffectA */ + +static int DoXMEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE inf, lo, hi; + + inf = UniGetByte(); + if (inf) + a->s3mvolslide = inf; + else + inf = a->s3mvolslide; + + if (tick) { + lo=inf&0xf; + hi=inf>>4; + + if (!hi) { + a->tmpvolume-=lo; + if (a->tmpvolume<0) a->tmpvolume=0; + } else { + a->tmpvolume+=hi; + if (a->tmpvolume>64) a->tmpvolume=64; + } + } + + return 0; +} + +static int DoXMEffect6(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + if (a->main.period) + DoVibrato(tick, a); + + return DoXMEffectA(tick, flags, a, mod, channel); +} + +static int DoXMEffectE1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat=UniGetByte(); + if (!tick) { + if (dat) a->fportupspd=dat; + if (a->main.period) + a->tmpperiod-=(a->fportupspd<<2); + } + + return 0; +} + +static int DoXMEffectE2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat=UniGetByte(); + if (!tick) { + if (dat) a->fportdnspd=dat; + if (a->main.period) + a->tmpperiod+=(a->fportdnspd<<2); + } + + return 0; +} + +static int DoXMEffectEA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat=UniGetByte(); + if (!tick) + if (dat) a->fslideupspd=dat; + a->tmpvolume+=a->fslideupspd; + if (a->tmpvolume>64) a->tmpvolume=64; + + return 0; +} + +static int DoXMEffectEB(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat=UniGetByte(); + if (!tick) + if (dat) a->fslidednspd=dat; + a->tmpvolume-=a->fslidednspd; + if (a->tmpvolume<0) a->tmpvolume=0; + + return 0; +} + +static int DoXMEffectG(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + mod->volume=UniGetByte()<<1; + if (mod->volume>128) mod->volume=128; + + return 0; +} + +static int DoXMEffectH(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE inf; + + inf = UniGetByte(); + + if (tick) { + if (inf) mod->globalslide=inf; + else inf=mod->globalslide; + if (inf & 0xf0) inf&=0xf0; + mod->volume=mod->volume+((inf>>4)-(inf&0xf))*2; + + if (mod->volume<0) + mod->volume=0; + else if (mod->volume>128) + mod->volume=128; + } + + return 0; +} + +static int DoXMEffectL(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat=UniGetByte(); + if ((!tick)&&(a->main.i)) { + UWORD points; + INSTRUMENT *i=a->main.i; + MP_VOICE *aout; + + if ((aout=a->slave) != NULL) { + if (aout->venv.env) { + points=i->volenv[i->volpts-1].pos; + aout->venv.p=aout->venv.env[(dat>points)?points:dat].pos; + } + if (aout->penv.env) { + points=i->panenv[i->panpts-1].pos; + aout->penv.p=aout->penv.env[(dat>points)?points:dat].pos; + } + } + } + + return 0; +} + +static int DoXMEffectP(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE inf, lo, hi; + SWORD pan; + + inf = UniGetByte(); + if (!mod->panflag) + return 0; + + if (inf) + a->pansspd = inf; + else + inf =a->pansspd; + + if (tick) { + lo=inf&0xf; + hi=inf>>4; + + /* slide right has absolute priority */ + if (hi) + lo = 0; + + pan=((a->main.panning==PAN_SURROUND)?PAN_CENTER:a->main.panning)+hi-lo; + a->main.panning=(panPAN_RIGHT?PAN_RIGHT:pan); + } + + return 0; +} + +static int DoXMEffectX1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat = UniGetByte(); + if (dat) + a->ffportupspd = dat; + else + dat = a->ffportupspd; + + if (a->main.period) + if (!tick) { + a->main.period-=dat; + a->tmpperiod-=dat; + a->ownper = 1; + } + + return 0; +} + +static int DoXMEffectX2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat; + + dat = UniGetByte(); + if (dat) + a->ffportdnspd=dat; + else + dat = a->ffportdnspd; + + if (a->main.period) + if (!tick) { + a->main.period+=dat; + a->tmpperiod+=dat; + a->ownper = 1; + } + + return 0; +} + +/*========== Impulse Tracker effects */ + +static void DoITToneSlide(UWORD tick, MP_CONTROL *a, UBYTE dat) +{ + if (dat) + a->portspeed = dat; + + /* if we don't come from another note, ignore the slide and play the note + as is */ + if (!a->oldnote || !a->main.period) + return; + + if ((!tick)&&(a->newsamp)){ + a->main.kick=KICK_NOTE; + a->main.start=-1; + } else + a->main.kick=(a->main.kick==KICK_NOTE)?KICK_ENV:KICK_ABSENT; + + if (tick) { + int dist; + + /* We have to slide a->main.period towards a->wantedperiod, compute the + difference between those two values */ + dist=a->main.period-a->wantedperiod; + + /* if they are equal or if portamentospeed is too big... */ + if ((!dist)||((a->portspeed<<2)>abs(dist))) + /* ... make tmpperiod equal tperiod */ + a->tmpperiod=a->main.period=a->wantedperiod; + else + if (dist>0) { + a->tmpperiod-=a->portspeed<<2; + a->main.period-=a->portspeed<<2; /* dist>0 slide up */ + } else { + a->tmpperiod+=a->portspeed<<2; + a->main.period+=a->portspeed<<2; /* dist<0 slide down */ + } + } else + a->tmpperiod=a->main.period; + a->ownper=1; +} + +static int DoITEffectG(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + DoITToneSlide(tick, a, UniGetByte()); + + return 0; +} + +static void DoITVibrato(UWORD tick, MP_CONTROL *a, UBYTE dat) +{ + UBYTE q; + UWORD temp=0; + + if (!tick) { + if (dat&0x0f) a->vibdepth=dat&0xf; + if (dat&0xf0) a->vibspd=(dat&0xf0)>>2; + } + if (!a->main.period) + return; + + q=(a->vibpos>>2)&0x1f; + + switch (a->wavecontrol&3) { + case 0: /* sine */ + temp=VibratoTable[q]; + break; + case 1: /* square wave */ + temp=255; + break; + case 2: /* ramp down */ + q<<=3; + if (a->vibpos<0) q=255-q; + temp=q; + break; + case 3: /* random */ + temp=getrandom(256); + break; + } + + temp*=a->vibdepth; + temp>>=8; + temp<<=2; + + if (a->vibpos>=0) + a->main.period=a->tmpperiod+temp; + else + a->main.period=a->tmpperiod-temp; + a->ownper=1; + + a->vibpos+=a->vibspd; +} + +static int DoITEffectH(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + DoITVibrato(tick, a, UniGetByte()); + + return 0; +} + +static int DoITEffectI(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE inf, on, off; + + inf = UniGetByte(); + if (inf) + a->s3mtronof = inf; + else { + inf = a->s3mtronof; + if (!inf) + return 0; + } + + on=(inf>>4); + off=(inf&0xf); + + a->s3mtremor%=(on+off); + a->volume=(a->s3mtremortmpvolume:0; + a->ownvol = 1; + a->s3mtremor++; + + return 0; +} + +static int DoITEffectM(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + a->main.chanvol=UniGetByte(); + if (a->main.chanvol>64) + a->main.chanvol=64; + else if (a->main.chanvol<0) + a->main.chanvol=0; + + return 0; +} + +static int DoITEffectN(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE inf, lo, hi; + + inf = UniGetByte(); + + if (inf) + a->chanvolslide = inf; + else + inf = a->chanvolslide; + + lo=inf&0xf; + hi=inf>>4; + + if (!hi) + a->main.chanvol-=lo; + else + if (!lo) { + a->main.chanvol+=hi; + } else + if (hi==0xf) { + if (!tick) a->main.chanvol-=lo; + } else + if (lo==0xf) { + if (!tick) a->main.chanvol+=hi; + } + + if (a->main.chanvol<0) + a->main.chanvol=0; + else if (a->main.chanvol>64) + a->main.chanvol=64; + + return 0; +} + +static int DoITEffectP(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE inf, lo, hi; + SWORD pan; + + inf = UniGetByte(); + if (inf) + a->pansspd = inf; + else + inf = a->pansspd; + + if (!mod->panflag) + return 0; + + lo=inf&0xf; + hi=inf>>4; + + pan=(a->main.panning==PAN_SURROUND)?PAN_CENTER:a->main.panning; + + if (!hi) + pan+=lo<<2; + else + if (!lo) { + pan-=hi<<2; + } else + if (hi==0xf) { + if (!tick) pan+=lo<<2; + } else + if (lo==0xf) { + if (!tick) pan-=hi<<2; + } + a->main.panning= + (panPAN_RIGHT?PAN_RIGHT:pan); + + return 0; +} + +static int DoITEffectT(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE tempo; + SWORD temp; + + tempo = UniGetByte(); + + if (mod->patdly2) + return 0; + + temp = mod->bpm; + if (tempo & 0x10) + temp += (tempo & 0x0f); + else + temp -= tempo; + + mod->bpm=(temp>255)?255:(temp<1?1:temp); + + return 0; +} + +static int DoITEffectU(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat, q; + UWORD temp = 0; /* silence warning */ + + dat = UniGetByte(); + if (!tick) { + if (dat&0x0f) a->vibdepth=dat&0xf; + if (dat&0xf0) a->vibspd=(dat&0xf0)>>2; + } + if (a->main.period) { + q=(a->vibpos>>2)&0x1f; + + switch (a->wavecontrol&3) { + case 0: /* sine */ + temp=VibratoTable[q]; + break; + case 1: /* square wave */ + temp=255; + break; + case 2: /* ramp down */ + q<<=3; + if (a->vibpos<0) q=255-q; + temp=q; + break; + case 3: /* random */ + temp=getrandom(256); + break; + } + + temp*=a->vibdepth; + temp>>=8; + + if (a->vibpos>=0) + a->main.period=a->tmpperiod+temp; + else + a->main.period=a->tmpperiod-temp; + a->ownper = 1; + + a->vibpos+=a->vibspd; + } + + return 0; +} + +static int DoITEffectW(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE inf, lo, hi; + + inf = UniGetByte(); + + if (inf) + mod->globalslide = inf; + else + inf = mod->globalslide; + + lo=inf&0xf; + hi=inf>>4; + + if (!lo) { + if (tick) mod->volume+=hi; + } else + if (!hi) { + if (tick) mod->volume-=lo; + } else + if (lo==0xf) { + if (!tick) mod->volume+=hi; + } else + if (hi==0xf) { + if (!tick) mod->volume-=lo; + } + + if (mod->volume<0) + mod->volume=0; + else if (mod->volume>128) + mod->volume=128; + + return 0; +} + +static int DoITEffectY(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat, q; + SLONG temp = 0; /* silence warning */ + + + dat=UniGetByte(); + if (!tick) { + if (dat&0x0f) a->panbdepth=(dat&0xf); + if (dat&0xf0) a->panbspd=(dat&0xf0)>>4; + } + if (mod->panflag) { + q=a->panbpos; + + switch (a->panbwave) { + case 0: /* sine */ + temp=PanbrelloTable[q]; + break; + case 1: /* square wave */ + temp=(q<0x80)?64:0; + break; + case 2: /* ramp down */ + q<<=3; + temp=q; + break; + case 3: /* random */ + temp=getrandom(256); + break; + } + + temp*=a->panbdepth; + temp=(temp/8)+mod->panning[channel]; + + a->main.panning= + (tempPAN_RIGHT?PAN_RIGHT:temp); + a->panbpos+=a->panbspd; + + } + + return 0; +} + +static void DoNNAEffects(MODULE *, MP_CONTROL *, UBYTE); + +/* Impulse/Scream Tracker Sxx effects. + All Sxx effects share the same memory space. */ +static int DoITEffectS0(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat, inf, c; + + dat = UniGetByte(); + inf=dat&0xf; + c=dat>>4; + + if (!dat) { + c=a->sseffect; + inf=a->ssdata; + } else { + a->sseffect=c; + a->ssdata=inf; + } + + switch (c) { + case SS_GLISSANDO: /* S1x set glissando voice */ + DoEEffects(tick, flags, a, mod, channel, 0x30|inf); + break; + case SS_FINETUNE: /* S2x set finetune */ + DoEEffects(tick, flags, a, mod, channel, 0x50|inf); + break; + case SS_VIBWAVE: /* S3x set vibrato waveform */ + DoEEffects(tick, flags, a, mod, channel, 0x40|inf); + break; + case SS_TREMWAVE: /* S4x set tremolo waveform */ + DoEEffects(tick, flags, a, mod, channel, 0x70|inf); + break; + case SS_PANWAVE: /* S5x panbrello */ + a->panbwave=inf; + break; + case SS_FRAMEDELAY: /* S6x delay x number of frames (patdly) */ + DoEEffects(tick, flags, a, mod, channel, 0xe0|inf); + break; + case SS_S7EFFECTS: /* S7x instrument / NNA commands */ + DoNNAEffects(mod, a, inf); + break; + case SS_PANNING: /* S8x set panning position */ + DoEEffects(tick, flags, a, mod, channel, 0x80 | inf); + break; + case SS_SURROUND: /* S9x set surround sound */ + if (mod->panflag) + a->main.panning = mod->panning[channel] = PAN_SURROUND; + break; + case SS_HIOFFSET: /* SAy set high order sample offset yxx00h */ + if (!tick) { + a->hioffset=inf<<16; + a->main.start=a->hioffset|a->soffset; + + if ((a->main.s)&&(a->main.start>a->main.s->length)) + a->main.start=a->main.s->flags&(SF_LOOP|SF_BIDI)? + a->main.s->loopstart:a->main.s->length; + } + break; + case SS_PATLOOP: /* SBx pattern loop */ + DoEEffects(tick, flags, a, mod, channel, 0x60|inf); + break; + case SS_NOTECUT: /* SCx notecut */ + if (!inf) inf = 1; + DoEEffects(tick, flags, a, mod, channel, 0xC0|inf); + break; + case SS_NOTEDELAY: /* SDx notedelay */ + DoEEffects(tick, flags, a, mod, channel, 0xD0|inf); + break; + case SS_PATDELAY: /* SEx patterndelay */ + DoEEffects(tick, flags, a, mod, channel, 0xE0|inf); + break; + } + + return 0; +} + +/*========== Impulse Tracker Volume/Pan Column effects */ + +/* + * All volume/pan column effects share the same memory space. + */ + +static int DoVolEffects(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE c, inf; + + c = UniGetByte(); + inf = UniGetByte(); + + if ((!c)&&(!inf)) { + c=a->voleffect; + inf=a->voldata; + } else { + a->voleffect=c; + a->voldata=inf; + } + + if (c) + switch (c) { + case VOL_VOLUME: + if (tick) break; + if (inf>64) inf=64; + a->tmpvolume=inf; + break; + case VOL_PANNING: + if (mod->panflag) + a->main.panning=inf; + break; + case VOL_VOLSLIDE: + DoS3MVolSlide(tick, flags, a, inf); + return 1; + case VOL_PITCHSLIDEDN: + if (a->main.period) + DoS3MSlideDn(tick, a, inf); + break; + case VOL_PITCHSLIDEUP: + if (a->main.period) + DoS3MSlideUp(tick, a, inf); + break; + case VOL_PORTAMENTO: + DoITToneSlide(tick, a, inf); + break; + case VOL_VIBRATO: + DoITVibrato(tick, a, inf); + break; + } + + return 0; +} + +/*========== UltraTracker effects */ + +static int DoULTEffect9(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UWORD offset=UniGetWord(); + + if (offset) + a->ultoffset=offset; + + a->main.start=a->ultoffset<<2; + if ((a->main.s)&&(a->main.start>a->main.s->length)) + a->main.start=a->main.s->flags&(SF_LOOP|SF_BIDI)? + a->main.s->loopstart:a->main.s->length; + + return 0; +} + +/*========== OctaMED effects */ + +static int DoMEDSpeed(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UWORD speed=UniGetWord(); + + mod->bpm=speed; + + return 0; +} + +static int DoMEDEffectF1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + DoEEffects(tick, flags, a, mod, channel, 0x90|(mod->sngspd/2)); + + return 0; +} + +static int DoMEDEffectF2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + DoEEffects(tick, flags, a, mod, channel, 0xd0|(mod->sngspd/2)); + + return 0; +} + +static int DoMEDEffectF3(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + DoEEffects(tick, flags, a, mod, channel, 0x90|(mod->sngspd/3)); + + return 0; +} + +/*========== Oktalyzer effects */ + +static int DoOktArp(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UBYTE dat, dat2; + + dat2 = UniGetByte(); /* arpeggio style */ + dat = UniGetByte(); + if (!tick) { + if (!dat && (flags & UF_ARPMEM)) + dat=a->arpmem; + else + a->arpmem=dat; + } + if (a->main.period) + DoArpeggio(tick, flags, a, dat2); + + return 0; +} + +/*========== General player functions */ + +static int DoNothing(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) +{ + UniSkipOpcode(); + + return 0; +} + +typedef int (*effect_func) (UWORD, UWORD, MP_CONTROL *, MODULE *, SWORD); + +static effect_func effects[UNI_LAST] = { + DoNothing, /* 0 */ + DoNothing, /* UNI_NOTE */ + DoNothing, /* UNI_INSTRUMENT */ + DoPTEffect0, /* UNI_PTEFFECT0 */ + DoPTEffect1, /* UNI_PTEFFECT1 */ + DoPTEffect2, /* UNI_PTEFFECT2 */ + DoPTEffect3, /* UNI_PTEFFECT3 */ + DoPTEffect4, /* UNI_PTEFFECT4 */ + DoPTEffect5, /* UNI_PTEFFECT5 */ + DoPTEffect6, /* UNI_PTEFFECT6 */ + DoPTEffect7, /* UNI_PTEFFECT7 */ + DoPTEffect8, /* UNI_PTEFFECT8 */ + DoPTEffect9, /* UNI_PTEFFECT9 */ + DoPTEffectA, /* UNI_PTEFFECTA */ + DoPTEffectB, /* UNI_PTEFFECTB */ + DoPTEffectC, /* UNI_PTEFFECTC */ + DoPTEffectD, /* UNI_PTEFFECTD */ + DoPTEffectE, /* UNI_PTEFFECTE */ + DoPTEffectF, /* UNI_PTEFFECTF */ + DoS3MEffectA, /* UNI_S3MEFFECTA */ + DoS3MEffectD, /* UNI_S3MEFFECTD */ + DoS3MEffectE, /* UNI_S3MEFFECTE */ + DoS3MEffectF, /* UNI_S3MEFFECTF */ + DoS3MEffectI, /* UNI_S3MEFFECTI */ + DoS3MEffectQ, /* UNI_S3MEFFECTQ */ + DoS3MEffectR, /* UNI_S3MEFFECTR */ + DoS3MEffectT, /* UNI_S3MEFFECTT */ + DoS3MEffectU, /* UNI_S3MEFFECTU */ + DoKeyOff, /* UNI_KEYOFF */ + DoKeyFade, /* UNI_KEYFADE */ + DoVolEffects, /* UNI_VOLEFFECTS */ + DoPTEffect4, /* UNI_XMEFFECT4 */ + DoXMEffect6, /* UNI_XMEFFECT6 */ + DoXMEffectA, /* UNI_XMEFFECTA */ + DoXMEffectE1, /* UNI_XMEFFECTE1 */ + DoXMEffectE2, /* UNI_XMEFFECTE2 */ + DoXMEffectEA, /* UNI_XMEFFECTEA */ + DoXMEffectEB, /* UNI_XMEFFECTEB */ + DoXMEffectG, /* UNI_XMEFFECTG */ + DoXMEffectH, /* UNI_XMEFFECTH */ + DoXMEffectL, /* UNI_XMEFFECTL */ + DoXMEffectP, /* UNI_XMEFFECTP */ + DoXMEffectX1, /* UNI_XMEFFECTX1 */ + DoXMEffectX2, /* UNI_XMEFFECTX2 */ + DoITEffectG, /* UNI_ITEFFECTG */ + DoITEffectH, /* UNI_ITEFFECTH */ + DoITEffectI, /* UNI_ITEFFECTI */ + DoITEffectM, /* UNI_ITEFFECTM */ + DoITEffectN, /* UNI_ITEFFECTN */ + DoITEffectP, /* UNI_ITEFFECTP */ + DoITEffectT, /* UNI_ITEFFECTT */ + DoITEffectU, /* UNI_ITEFFECTU */ + DoITEffectW, /* UNI_ITEFFECTW */ + DoITEffectY, /* UNI_ITEFFECTY */ + DoNothing, /* UNI_ITEFFECTZ */ + DoITEffectS0, /* UNI_ITEFFECTS0 */ + DoULTEffect9, /* UNI_ULTEFFECT9 */ + DoMEDSpeed, /* UNI_MEDSPEED */ + DoMEDEffectF1, /* UNI_MEDEFFECTF1 */ + DoMEDEffectF2, /* UNI_MEDEFFECTF2 */ + DoMEDEffectF3, /* UNI_MEDEFFECTF3 */ + DoOktArp, /* UNI_OKTARP */ +}; + +static int pt_playeffects(MODULE *mod, SWORD channel, MP_CONTROL *a) +{ + UWORD tick = mod->vbtick; + UWORD flags = mod->flags; + UBYTE c; + int explicitslides = 0; + effect_func f; + + while((c=UniGetByte()) != 0) { +#if 0 /* this doesn't normally happen unless things go fubar elsewhere */ + if (c >= UNI_LAST) + fprintf(stderr,"fubar'ed opcode %u\n",c); +#endif + f = effects[c]; + if (f != DoNothing) + a->sliding = 0; + explicitslides |= f(tick, flags, a, mod, channel); + } + return explicitslides; +} + +static void DoNNAEffects(MODULE *mod, MP_CONTROL *a, UBYTE dat) +{ + int t; + MP_VOICE *aout; + + dat&=0xf; + aout=(a->slave)?a->slave:NULL; + + switch (dat) { + case 0x0: /* past note cut */ + for (t=0;tvoice[t].master==a) + mod->voice[t].main.fadevol=0; + break; + case 0x1: /* past note off */ + for (t=0;tvoice[t].master==a) { + mod->voice[t].main.keyoff|=KEY_OFF; + if ((!(mod->voice[t].venv.flg & EF_ON))|| + (mod->voice[t].venv.flg & EF_LOOP)) + mod->voice[t].main.keyoff=KEY_KILL; + } + break; + case 0x2: /* past note fade */ + for (t=0;tvoice[t].master==a) + mod->voice[t].main.keyoff|=KEY_FADE; + break; + case 0x3: /* set NNA note cut */ + a->main.nna=(a->main.nna&~NNA_MASK)|NNA_CUT; + break; + case 0x4: /* set NNA note continue */ + a->main.nna=(a->main.nna&~NNA_MASK)|NNA_CONTINUE; + break; + case 0x5: /* set NNA note off */ + a->main.nna=(a->main.nna&~NNA_MASK)|NNA_OFF; + break; + case 0x6: /* set NNA note fade */ + a->main.nna=(a->main.nna&~NNA_MASK)|NNA_FADE; + break; + case 0x7: /* disable volume envelope */ + if (aout) + aout->main.volflg&=~EF_ON; + break; + case 0x8: /* enable volume envelope */ + if (aout) + aout->main.volflg|=EF_ON; + break; + case 0x9: /* disable panning envelope */ + if (aout) + aout->main.panflg&=~EF_ON; + break; + case 0xa: /* enable panning envelope */ + if (aout) + aout->main.panflg|=EF_ON; + break; + case 0xb: /* disable pitch envelope */ + if (aout) + aout->main.pitflg&=~EF_ON; + break; + case 0xc: /* enable pitch envelope */ + if (aout) + aout->main.pitflg|=EF_ON; + break; + } +} + +static void pt_UpdateVoices(MODULE *mod, int max_volume) +{ + SWORD envpan,envvol,envpit,channel; + UWORD playperiod; + SLONG vibval,vibdpt; + ULONG tmpvol; + + MP_VOICE *aout; + INSTRUMENT *i; + SAMPLE *s; + + mod->totalchn=mod->realchn=0; + for (channel=0;channelvoice[channel]; + i=aout->main.i; + s=aout->main.s; + + if (!s || !s->length) continue; + + if (aout->main.period<40) + aout->main.period=40; + else if (aout->main.period>50000) + aout->main.period=50000; + + if ((aout->main.kick==KICK_NOTE)||(aout->main.kick==KICK_KEYOFF)) { + Voice_Play_internal(channel,s,(aout->main.start==-1)? + ((s->flags&SF_UST_LOOP)?s->loopstart:0):aout->main.start); + aout->main.fadevol=32768; + aout->aswppos=0; + } + + envvol = 256; + envpan = PAN_CENTER; + envpit = 32; + if (i && ((aout->main.kick==KICK_NOTE)||(aout->main.kick==KICK_ENV))) { + if (aout->main.volflg & EF_ON) + envvol = StartEnvelope(&aout->venv,aout->main.volflg, + i->volpts,i->volsusbeg,i->volsusend, + i->volbeg,i->volend,i->volenv,aout->main.keyoff); + if (aout->main.panflg & EF_ON) + envpan = StartEnvelope(&aout->penv,aout->main.panflg, + i->panpts,i->pansusbeg,i->pansusend, + i->panbeg,i->panend,i->panenv,aout->main.keyoff); + if (aout->main.pitflg & EF_ON) + envpit = StartEnvelope(&aout->cenv,aout->main.pitflg, + i->pitpts,i->pitsusbeg,i->pitsusend, + i->pitbeg,i->pitend,i->pitenv,aout->main.keyoff); + + if (aout->cenv.flg & EF_ON) + aout->masterperiod=GetPeriod(mod->flags, + (UWORD)aout->main.note<<1, aout->master->speed); + } else { + if (aout->main.volflg & EF_ON) + envvol = ProcessEnvelope(aout,&aout->venv,256); + if (aout->main.panflg & EF_ON) + envpan = ProcessEnvelope(aout,&aout->penv,PAN_CENTER); + if (aout->main.pitflg & EF_ON) + envpit = ProcessEnvelope(aout,&aout->cenv,32); + } + if (aout->main.kick == KICK_NOTE) { + aout->main.kick_flag = 1; + } + aout->main.kick=KICK_ABSENT; + + tmpvol = aout->main.fadevol; /* max 32768 */ + tmpvol *= aout->main.chanvol; /* * max 64 */ + tmpvol *= aout->main.outvolume; /* * max 256 */ + tmpvol /= (256 * 64); /* tmpvol is max 32768 again */ + aout->totalvol = tmpvol >> 2; /* used to determine samplevolume */ + tmpvol *= envvol; /* * max 256 */ + tmpvol *= mod->volume; /* * max 128 */ + tmpvol /= (128 * 256 * 128); + + /* fade out */ + if (mod->sngpos>=mod->numpos) + tmpvol=0; + else + tmpvol=(tmpvol*max_volume)/128; + + if ((aout->masterchn!=-1)&& mod->control[aout->masterchn].muted) + Voice_SetVolume_internal(channel,0); + else { + Voice_SetVolume_internal(channel,tmpvol); + if ((tmpvol)&&(aout->master)&&(aout->master->slave==aout)) + mod->realchn++; + mod->totalchn++; + } + + if (aout->main.panning==PAN_SURROUND) + Voice_SetPanning_internal(channel,PAN_SURROUND); + else + if ((mod->panflag)&&(aout->penv.flg & EF_ON)) + Voice_SetPanning_internal(channel, + DoPan(envpan,aout->main.panning)); + else + Voice_SetPanning_internal(channel,aout->main.panning); + + if (aout->main.period && s->vibdepth) + switch (s->vibtype) { + case 0: + vibval=avibtab[s->avibpos&127]; + if (aout->avibpos & 0x80) vibval=-vibval; + break; + case 1: + vibval=64; + if (aout->avibpos & 0x80) vibval=-vibval; + break; + case 2: + vibval=63-(((aout->avibpos+128)&255)>>1); + break; + default: + vibval=(((aout->avibpos+128)&255)>>1)-64; + break; + } + else + vibval=0; + + if (s->vibflags & AV_IT) { + if ((aout->aswppos>>8)vibdepth) { + aout->aswppos += s->vibsweep; + vibdpt=aout->aswppos; + } else + vibdpt=s->vibdepth<<8; + vibval=(vibval*vibdpt)>>16; + if (aout->mflag) { + if (!(mod->flags&UF_LINEAR)) vibval>>=1; + aout->main.period-=vibval; + } + } else { + /* do XM style auto-vibrato */ + if (!(aout->main.keyoff & KEY_OFF)) { + if (aout->aswpposvibsweep) { + vibdpt=(aout->aswppos*s->vibdepth)/s->vibsweep; + aout->aswppos++; + } else + vibdpt=s->vibdepth; + } else { + /* keyoff -> depth becomes 0 if final depth wasn't reached or + stays at final level if depth WAS reached */ + if (aout->aswppos>=s->vibsweep) + vibdpt=s->vibdepth; + else + vibdpt=0; + } + vibval=(vibval*vibdpt)>>8; + aout->main.period-=vibval; + } + + /* update vibrato position */ + aout->avibpos=(aout->avibpos+s->vibrate)&0xff; + + /* process pitch envelope */ + playperiod=aout->main.period; + + if ((aout->main.pitflg&EF_ON)&&(envpit!=32)) { + long p1; + + envpit-=32; + if ((aout->main.note<<1)+envpit<=0) envpit=-(aout->main.note<<1); + + p1=GetPeriod(mod->flags, ((UWORD)aout->main.note<<1)+envpit, + aout->master->speed)-aout->masterperiod; + if (p1>0) { + if ((UWORD)(playperiod+p1)<=playperiod) { + p1=0; + aout->main.keyoff|=KEY_OFF; + } + } else if (p1<0) { + if ((UWORD)(playperiod+p1)>=playperiod) { + p1=0; + aout->main.keyoff|=KEY_OFF; + } + } + playperiod+=p1; + } + + if (!aout->main.fadevol) { /* check for a dead note (fadevol=0) */ + Voice_Stop_internal(channel); + mod->totalchn--; + if ((tmpvol)&&(aout->master)&&(aout->master->slave==aout)) + mod->realchn--; + } else { + Voice_SetFrequency_internal(channel, + getfrequency(mod->flags,playperiod)); + + /* if keyfade, start substracting fadeoutspeed from fadevol: */ + if ((i)&&(aout->main.keyoff&KEY_FADE)) { + if (aout->main.fadevol>=i->volfade) + aout->main.fadevol-=i->volfade; + else + aout->main.fadevol=0; + } + } + + md_bpm=mod->bpm+mod->relspd; + if (md_bpm<32) + md_bpm=32; + else if ((!(mod->flags&UF_HIGHBPM)) && md_bpm>255) + md_bpm=255; + } +} + +/* Handles new notes or instruments */ +static void pt_Notes(MODULE *mod) +{ + SWORD channel; + MP_CONTROL *a; + UBYTE c,inst; + int tr,funky; /* funky is set to indicate note or instrument change */ + + for (channel=0;channelnumchn;channel++) { + a=&mod->control[channel]; + + if (mod->sngpos>=mod->numpos) { + tr=mod->numtrk; + mod->numrow=0; + } else { + tr=mod->patterns[(mod->positions[mod->sngpos]*mod->numchn)+channel]; + mod->numrow=mod->pattrows[mod->positions[mod->sngpos]]; + } + + a->row=(trnumtrk)?UniFindRow(mod->tracks[tr],mod->patpos):NULL; + a->newsamp=0; + if (!mod->vbtick) a->main.notedelay=0; + + if (!a->row) continue; + UniSetRow(a->row); + funky=0; + + while((c=UniGetByte()) != 0) + switch (c) { + case UNI_NOTE: + funky|=1; + a->oldnote=a->anote,a->anote=UniGetByte(); + a->main.kick =KICK_NOTE; + a->main.start=-1; + a->sliding=0; + + /* retrig tremolo and vibrato waves ? */ + if (!(a->wavecontrol & 0x80)) a->trmpos=0; + if (!(a->wavecontrol & 0x08)) a->vibpos=0; + if (!a->panbwave) a->panbpos=0; + break; + case UNI_INSTRUMENT: + inst=UniGetByte(); + if (inst>=mod->numins) break; /* safety valve */ + funky|=2; + a->main.i=(mod->flags & UF_INST)?&mod->instruments[inst]:NULL; + a->retrig=0; + a->s3mtremor=0; + a->ultoffset=0; + a->main.sample=inst; + break; + default: + UniSkipOpcode(); + break; + } + + if (funky) { + INSTRUMENT *i; + SAMPLE *s; + + if ((i=a->main.i) != NULL) { + if (i->samplenumber[a->anote] >= mod->numsmp) continue; + s=&mod->samples[i->samplenumber[a->anote]]; + a->main.note=i->samplenote[a->anote]; + } else { + a->main.note=a->anote; + s=&mod->samples[a->main.sample]; + } + + if (a->main.s!=s) { + a->main.s=s; + a->newsamp=a->main.period; + } + + /* channel or instrument determined panning ? */ + a->main.panning=mod->panning[channel]; + if (s->flags & SF_OWNPAN) + a->main.panning=s->panning; + else if ((i)&&(i->flags & IF_OWNPAN)) + a->main.panning=i->panning; + + a->main.handle=s->handle; + a->speed=s->speed; + + if (i) { + if ((mod->panflag)&&(i->flags & IF_PITCHPAN) + &&(a->main.panning!=PAN_SURROUND)){ + a->main.panning+= + ((a->anote-i->pitpancenter)*i->pitpansep)/8; + if (a->main.panningmain.panning=PAN_LEFT; + else if (a->main.panning>PAN_RIGHT) + a->main.panning=PAN_RIGHT; + } + a->main.pitflg=i->pitflg; + a->main.volflg=i->volflg; + a->main.panflg=i->panflg; + a->main.nna=i->nnatype; + a->dca=i->dca; + a->dct=i->dct; + } else { + a->main.pitflg=a->main.volflg=a->main.panflg=0; + a->main.nna=a->dca=0; + a->dct=DCT_OFF; + } + + if (funky&2) /* instrument change */ { + /* IT random volume variations: 0:8 bit fixed, and one bit for + sign. */ + a->volume=a->tmpvolume=s->volume; + if ((s)&&(i)) { + if (i->rvolvar) { + a->volume=a->tmpvolume=s->volume+ + ((s->volume*((SLONG)i->rvolvar*(SLONG)getrandom(512) + ))/25600); + if (a->volume<0) + a->volume=a->tmpvolume=0; + else if (a->volume>64) + a->volume=a->tmpvolume=64; + } + if ((mod->panflag)&&(a->main.panning!=PAN_SURROUND)) { + a->main.panning+=((a->main.panning*((SLONG)i->rpanvar* + (SLONG)getrandom(512)))/25600); + if (a->main.panningmain.panning=PAN_LEFT; + else if (a->main.panning>PAN_RIGHT) + a->main.panning=PAN_RIGHT; + } + } + } + + a->wantedperiod=a->tmpperiod= + GetPeriod(mod->flags, (UWORD)a->main.note<<1,a->speed); + a->main.keyoff=KEY_KICK; + } + } +} + +/* Handles effects */ +static void pt_EffectsPass1(MODULE *mod) +{ + SWORD channel; + MP_CONTROL *a; + MP_VOICE *aout; + int explicitslides; + + for (channel=0;channelnumchn;channel++) { + a=&mod->control[channel]; + + if ((aout=a->slave) != NULL) { + a->main.fadevol=aout->main.fadevol; + a->main.period=aout->main.period; + if (a->main.kick==KICK_KEYOFF) + a->main.keyoff=aout->main.keyoff; + } + + if (!a->row) continue; + UniSetRow(a->row); + + a->ownper=a->ownvol=0; + explicitslides = pt_playeffects(mod, channel, a); + + /* continue volume slide if necessary for XM and IT */ + if (mod->flags&UF_BGSLIDES) { + if (!explicitslides && a->sliding) + DoS3MVolSlide(mod->vbtick, mod->flags, a, 0); + else if (a->tmpvolume) + a->sliding = explicitslides; + } + + if (!a->ownper) + a->main.period=a->tmpperiod; + if (!a->ownvol) + a->volume=a->tmpvolume; + + if (a->main.s) { + if (a->main.i) + a->main.outvolume= + (a->volume*a->main.s->globvol*a->main.i->globvol)>>10; + else + a->main.outvolume=(a->volume*a->main.s->globvol)>>4; + if (a->main.outvolume>256) + a->main.outvolume=256; + else if (a->main.outvolume<0) + a->main.outvolume=0; + } + } +} + +/* NNA management */ +static void pt_NNA(MODULE *mod) +{ + SWORD channel; + MP_CONTROL *a; + + for (channel=0;channelnumchn;channel++) { + a=&mod->control[channel]; + + if (a->main.kick==KICK_NOTE) { + BOOL kill=0; + + if (a->slave) { + MP_VOICE *aout; + + aout=a->slave; + if (aout->main.nna & NNA_MASK) { + /* Make sure the old MP_VOICE channel knows it has no + master now ! */ + a->slave=NULL; + /* assume the channel is taken by NNA */ + aout->mflag=0; + + switch (aout->main.nna) { + case NNA_CONTINUE: /* continue note, do nothing */ + break; + case NNA_OFF: /* note off */ + aout->main.keyoff|=KEY_OFF; + if ((!(aout->main.volflg & EF_ON))|| + (aout->main.volflg & EF_LOOP)) + aout->main.keyoff=KEY_KILL; + break; + case NNA_FADE: + aout->main.keyoff |= KEY_FADE; + break; + } + } + } + + if (a->dct!=DCT_OFF) { + int t; + + for (t=0;tvoice[t].masterchn==channel)&& + (a->main.sample==mod->voice[t].main.sample)) { + kill=0; + switch (a->dct) { + case DCT_NOTE: + if (a->main.note==mod->voice[t].main.note) + kill=1; + break; + case DCT_SAMPLE: + if (a->main.handle==mod->voice[t].main.handle) + kill=1; + break; + case DCT_INST: + kill=1; + break; + } + if (kill) + switch (a->dca) { + case DCA_CUT: + mod->voice[t].main.fadevol=0; + break; + case DCA_OFF: + mod->voice[t].main.keyoff|=KEY_OFF; + if ((!(mod->voice[t].main.volflg&EF_ON))|| + (mod->voice[t].main.volflg&EF_LOOP)) + mod->voice[t].main.keyoff=KEY_KILL; + break; + case DCA_FADE: + mod->voice[t].main.keyoff|=KEY_FADE; + break; + } + } + } + } /* if (a->main.kick==KICK_NOTE) */ + } +} + +/* Setup module and NNA voices */ +static void pt_SetupVoices(MODULE *mod) +{ + SWORD channel; + MP_CONTROL *a; + MP_VOICE *aout; + + for (channel=0;channelnumchn;channel++) { + a=&mod->control[channel]; + + if (a->main.notedelay) continue; + if (a->main.kick==KICK_NOTE) { + /* if no channel was cut above, find an empty or quiet channel + here */ + if (mod->flags&UF_NNA) { + if (!a->slave) { + int newchn; + + if ((newchn=MP_FindEmptyChannel(mod))!=-1) + a->slave=&mod->voice[a->slavechn=newchn]; + } + } else + a->slave=&mod->voice[a->slavechn=channel]; + + /* assign parts of MP_VOICE only done for a KICK_NOTE */ + if ((aout=a->slave) != NULL) { + if (aout->mflag && aout->master) aout->master->slave=NULL; + aout->master=a; + a->slave=aout; + aout->masterchn=channel; + aout->mflag=1; + } + } else + aout=a->slave; + + if (aout) + aout->main=a->main; + a->main.kick=KICK_ABSENT; + } +} + +/* second effect pass */ +static void pt_EffectsPass2(MODULE *mod) +{ + SWORD channel; + MP_CONTROL *a; + UBYTE c; + + for (channel=0;channelnumchn;channel++) { + a=&mod->control[channel]; + + if (!a->row) continue; + UniSetRow(a->row); + + while((c=UniGetByte()) != 0) + if (c==UNI_ITEFFECTS0) { + c=UniGetByte(); + if ((c>>4)==SS_S7EFFECTS) + DoNNAEffects(mod, a, c&0xf); + } else + UniSkipOpcode(); + } +} + +void Player_HandleTick(void) +{ + SWORD channel; + int max_volume; + +#if 0 + /* don't handle the very first ticks, this allows the other hardware to + settle down so we don't loose any starting notes */ + if (isfirst) { + isfirst--; + return; + } +#endif + + if ((!pf)||(pf->forbid)||(pf->sngpos>=pf->numpos)) return; + + /* update time counter (sngtime is in milliseconds (in fact 2^-10)) */ + pf->sngremainder+=(1<<9)*5; /* thus 2.5*(1<<10), since fps=0.4xtempo */ + pf->sngtime+=pf->sngremainder/pf->bpm; + pf->sngremainder%=pf->bpm; + + if (++pf->vbtick>=pf->sngspd) { + if (pf->pat_repcrazy) + pf->pat_repcrazy=0; /* play 2 times row 0 */ + else + pf->patpos++; + pf->vbtick=0; + + /* process pattern-delay. pf->patdly2 is the counter and pf->patdly is + the command memory. */ + if (pf->patdly) + pf->patdly2=pf->patdly,pf->patdly=0; + if (pf->patdly2) { + /* patterndelay active */ + if (--pf->patdly2) + /* so turn back pf->patpos by 1 */ + if (pf->patpos) pf->patpos--; + } + + /* do we have to get a new patternpointer ? (when pf->patpos reaches the + pattern size, or when a patternbreak is active) */ + if (((pf->patpos>=pf->numrow)&&(pf->numrow>0))&&(!pf->posjmp)) + pf->posjmp=3; + + if (pf->posjmp) { + pf->patpos=pf->numrow?(pf->patbrk%pf->numrow):0; + pf->pat_repcrazy=0; + pf->sngpos+=(pf->posjmp-2); + for (channel=0;channelnumchn;channel++) + pf->control[channel].pat_reppos=-1; + + pf->patbrk=pf->posjmp=0; + + if (pf->sngpos<0) pf->sngpos=(SWORD)(pf->numpos-1); + + /* handle the "---" (end of song) pattern since it can occur + *inside* the module in some formats */ + if ((pf->sngpos>=pf->numpos)|| + (pf->positions[pf->sngpos]==LAST_PATTERN)) { + if (!pf->wrap) return; + if (!(pf->sngpos=pf->reppos)) { + pf->volume=pf->initvolume>128?128:pf->initvolume; + if(pf->initspeed!=0) + pf->sngspd=pf->initspeed<32?pf->initspeed:32; + else + pf->sngspd=6; + pf->bpm=pf->inittempo<32?32:pf->inittempo; + } + } + } + + if (!pf->patdly2) + pt_Notes(pf); + } + + /* Fade global volume if enabled and we're playing the last pattern */ + if (((pf->sngpos==pf->numpos-1)|| + (pf->positions[pf->sngpos+1]==LAST_PATTERN))&& + (pf->fadeout)) + max_volume=pf->numrow?((pf->numrow-pf->patpos)*128)/pf->numrow:0; + else + max_volume=128; + + pt_EffectsPass1(pf); + if (pf->flags&UF_NNA) + pt_NNA(pf); + pt_SetupVoices(pf); + pt_EffectsPass2(pf); + + /* now set up the actual hardware channel playback information */ + pt_UpdateVoices(pf, max_volume); +} + +static void Player_Init_internal(MODULE* mod) +{ + int t; + + for (t=0;tnumchn;t++) { + mod->control[t].main.chanvol=mod->chanvol[t]; + mod->control[t].main.panning=mod->panning[t]; + } + + mod->sngtime=0; + mod->sngremainder=0; + + mod->pat_repcrazy=0; + mod->sngpos=0; + if(mod->initspeed!=0) + mod->sngspd=mod->initspeed<32?mod->initspeed:32; + else + mod->sngspd=6; + mod->volume=mod->initvolume>128?128:mod->initvolume; + + mod->vbtick=mod->sngspd; + mod->patdly=0; + mod->patdly2=0; + mod->bpm=mod->inittempo<32?32:mod->inittempo; + mod->realchn=0; + + mod->patpos=0; + mod->posjmp=2; /* make sure the player fetches the first note */ + mod->numrow=-1; + mod->patbrk=0; +} + +int Player_Init(MODULE* mod) +{ + mod->extspd=1; + mod->panflag=1; + mod->wrap=0; + mod->loop=1; + mod->fadeout=0; + + mod->relspd=0; + + /* make sure the player doesn't start with garbage */ + if (!(mod->control=(MP_CONTROL*)MikMod_calloc(mod->numchn,sizeof(MP_CONTROL)))) + return 1; + if (!(mod->voice=(MP_VOICE*)MikMod_calloc(md_sngchn,sizeof(MP_VOICE)))) + return 1; + + /* mod->numvoices was used during loading to clamp md_sngchn. + After loading it's used to remember how big mod->voice is. + */ + mod->numvoices = md_sngchn; + + Player_Init_internal(mod); + return 0; +} + +void Player_Exit_internal(MODULE* mod) +{ + if (!mod) + return; + + /* Stop playback if necessary */ + if (mod==pf) { + Player_Stop_internal(); + pf=NULL; + } + + MikMod_free(mod->control); + MikMod_free(mod->voice); + mod->control=NULL; + mod->voice=NULL; +} + +void Player_Exit(MODULE* mod) +{ + MUTEX_LOCK(vars); + Player_Exit_internal(mod); + MUTEX_UNLOCK(vars); +} + +MIKMODAPI void Player_SetVolume(SWORD volume) +{ + MUTEX_LOCK(vars); + if (pf) { + pf->volume=(volume<0)?0:(volume>128)?128:volume; + pf->initvolume=pf->volume; + } + MUTEX_UNLOCK(vars); +} + +MIKMODAPI MODULE* Player_GetModule(void) +{ + MODULE* result; + + MUTEX_LOCK(vars); + result=pf; + MUTEX_UNLOCK(vars); + + return result; +} + +MIKMODAPI void Player_Start(MODULE *mod) +{ + int t; + + if (!mod) + return; + + if (!MikMod_Active()) + MikMod_EnableOutput(); + + mod->forbid=0; + + MUTEX_LOCK(vars); + if (pf!=mod) { + /* new song is being started, so completely stop out the old one. */ + if (pf) pf->forbid=1; + for (t=0;tforbid=1; + pf=NULL; +} + +MIKMODAPI void Player_Stop(void) +{ + MUTEX_LOCK(vars); + Player_Stop_internal(); + MUTEX_UNLOCK(vars); +} + +MIKMODAPI BOOL Player_Active(void) +{ + BOOL result=0; + + MUTEX_LOCK(vars); + if (pf) + result=(!(pf->sngpos>=pf->numpos)); + MUTEX_UNLOCK(vars); + + return result; +} + +MIKMODAPI void Player_NextPosition(void) +{ + MUTEX_LOCK(vars); + if (pf) { + int t; + + pf->forbid=1; + pf->posjmp=3; + pf->patbrk=0; + pf->vbtick=pf->sngspd; + + for (t=0;tvoice[t].main.i=NULL; + pf->voice[t].main.s=NULL; + } + for (t=0;tnumchn;t++) { + pf->control[t].main.i=NULL; + pf->control[t].main.s=NULL; + } + pf->forbid=0; + } + MUTEX_UNLOCK(vars); +} + +MIKMODAPI void Player_PrevPosition(void) +{ + MUTEX_LOCK(vars); + if (pf) { + int t; + + pf->forbid=1; + pf->posjmp=1; + pf->patbrk=0; + pf->vbtick=pf->sngspd; + + for (t=0;tvoice[t].main.i=NULL; + pf->voice[t].main.s=NULL; + } + for (t=0;tnumchn;t++) { + pf->control[t].main.i=NULL; + pf->control[t].main.s=NULL; + } + pf->forbid=0; + } + MUTEX_UNLOCK(vars); +} + +MIKMODAPI void Player_SetPosition(UWORD pos) +{ + MUTEX_LOCK(vars); + if (pf) { + int t; + + pf->forbid=1; + if (pos>=pf->numpos) pos=pf->numpos; + pf->posjmp=2; + pf->patbrk=0; + pf->sngpos=pos; + pf->vbtick=pf->sngspd; + + for (t=0;tvoice[t].main.i=NULL; + pf->voice[t].main.s=NULL; + } + for (t=0;tnumchn;t++) { + pf->control[t].main.i=NULL; + pf->control[t].main.s=NULL; + } + pf->forbid=0; + + if (!pos) + Player_Init_internal(pf); + } + MUTEX_UNLOCK(vars); +} + +static void Player_Unmute_internal(SLONG arg1,va_list ap) +{ + SLONG t,arg2,arg3=0; + + if (pf) { + switch (arg1) { + case MUTE_INCLUSIVE: + if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| + (arg2>arg3)||(arg3>=pf->numchn)) + return; + for (;arg2numchn && arg2<=arg3;arg2++) + pf->control[arg2].muted=0; + break; + case MUTE_EXCLUSIVE: + if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| + (arg2>arg3)||(arg3>=pf->numchn)) + return; + for (t=0;tnumchn;t++) { + if ((t>=arg2) && (t<=arg3)) + continue; + pf->control[t].muted=0; + } + break; + default: + if (arg1numchn) pf->control[arg1].muted=0; + break; + } + } +} + +MIKMODAPI void Player_Unmute(SLONG arg1, ...) +{ + va_list args; + + va_start(args,arg1); + MUTEX_LOCK(vars); + Player_Unmute_internal(arg1,args); + MUTEX_UNLOCK(vars); + va_end(args); +} + +static void Player_Mute_internal(SLONG arg1,va_list ap) +{ + SLONG t,arg2,arg3=0; + + if (pf) { + switch (arg1) { + case MUTE_INCLUSIVE: + if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| + (arg2>arg3)||(arg3>=pf->numchn)) + return; + for (;arg2numchn && arg2<=arg3;arg2++) + pf->control[arg2].muted=1; + break; + case MUTE_EXCLUSIVE: + if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| + (arg2>arg3)||(arg3>=pf->numchn)) + return; + for (t=0;tnumchn;t++) { + if ((t>=arg2) && (t<=arg3)) + continue; + pf->control[t].muted=1; + } + break; + default: + if (arg1numchn) + pf->control[arg1].muted=1; + break; + } + } +} + +MIKMODAPI void Player_Mute(SLONG arg1,...) +{ + va_list args; + + va_start(args,arg1); + MUTEX_LOCK(vars); + Player_Mute_internal(arg1,args); + MUTEX_UNLOCK(vars); + va_end(args); +} + +static void Player_ToggleMute_internal(SLONG arg1,va_list ap) +{ + SLONG arg2,arg3=0; + ULONG t; + + if (pf) { + switch (arg1) { + case MUTE_INCLUSIVE: + if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| + (arg2>arg3)||(arg3>=pf->numchn)) + return; + for (;arg2numchn && arg2<=arg3;arg2++) + pf->control[arg2].muted=1-pf->control[arg2].muted; + break; + case MUTE_EXCLUSIVE: + if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| + (arg2>arg3)||(arg3>=pf->numchn)) + return; + for (t=0;tnumchn;t++) { + if ((t>=arg2) && (t<=arg3)) + continue; + pf->control[t].muted=1-pf->control[t].muted; + } + break; + default: + if (arg1numchn) + pf->control[arg1].muted=1-pf->control[arg1].muted; + break; + } + } +} + +MIKMODAPI void Player_ToggleMute(SLONG arg1,...) +{ + va_list args; + + va_start(args,arg1); + MUTEX_LOCK(vars); + Player_ToggleMute_internal(arg1,args); + MUTEX_UNLOCK(vars); + va_end(args); +} + +MIKMODAPI BOOL Player_Muted(UBYTE chan) +{ + BOOL result=1; + + MUTEX_LOCK(vars); + if (pf) + result=(channumchn)?pf->control[chan].muted:1; + MUTEX_UNLOCK(vars); + + return result; +} + +MIKMODAPI int Player_GetChannelVoice(UBYTE chan) +{ + int result=0; + + MUTEX_LOCK(vars); + if (pf) + result=(channumchn)?pf->control[chan].slavechn:-1; + MUTEX_UNLOCK(vars); + + return result; +} + +MIKMODAPI UWORD Player_GetChannelPeriod(UBYTE chan) +{ + UWORD result=0; + + MUTEX_LOCK(vars); + if (pf) + result=(channumchn)?pf->control[chan].main.period:0; + MUTEX_UNLOCK(vars); + + return result; +} + +BOOL Player_Paused_internal(void) +{ + return pf?pf->forbid:1; +} + +MIKMODAPI BOOL Player_Paused(void) +{ + BOOL result; + + MUTEX_LOCK(vars); + result=Player_Paused_internal(); + MUTEX_UNLOCK(vars); + + return result; +} + +MIKMODAPI void Player_TogglePause(void) +{ + MUTEX_LOCK(vars); + if (pf) + pf->forbid=1-pf->forbid; + MUTEX_UNLOCK(vars); +} + +MIKMODAPI void Player_SetSpeed(UWORD speed) +{ + MUTEX_LOCK(vars); + if (pf) + pf->sngspd=speed?(speed<32?speed:32):1; + MUTEX_UNLOCK(vars); +} + +MIKMODAPI void Player_SetTempo(UWORD tempo) +{ + if (tempo<32) tempo=32; + MUTEX_LOCK(vars); + if (pf) { + if ((!(pf->flags&UF_HIGHBPM))&&(tempo>255)) tempo=255; + pf->bpm=tempo; + } + MUTEX_UNLOCK(vars); +} + +MIKMODAPI int Player_QueryVoices(UWORD numvoices, VOICEINFO *vinfo) +{ + int i; + + if (numvoices > md_sngchn) + numvoices = md_sngchn; + + MUTEX_LOCK(vars); + if (pf) + for (i = 0; i < md_sngchn; i++) { + vinfo [i].i = pf->voice[i].main.i; + vinfo [i].s = pf->voice[i].main.s; + vinfo [i].panning = pf->voice [i].main.panning; + vinfo [i].volume = pf->voice [i].main.chanvol; + vinfo [i].period = pf->voice [i].main.period; + vinfo [i].kick = pf->voice [i].main.kick_flag; + pf->voice [i].main.kick_flag = 0; + } + MUTEX_UNLOCK(vars); + + return numvoices; +} + +/* Get current module order */ +MIKMODAPI int Player_GetOrder(void) +{ + int ret; + MUTEX_LOCK(vars); + ret = pf ? pf->sngpos :0; /* pf->positions[pf->sngpos ? pf->sngpos-1 : 0]: 0; */ + MUTEX_UNLOCK(vars); + return ret; +} + +/* Get current module row */ +MIKMODAPI int Player_GetRow(void) +{ + int ret; + MUTEX_LOCK(vars); + ret = pf ? pf->patpos : 0; + MUTEX_UNLOCK(vars); + return ret; +} + +/* ex:set ts=4: */ diff --git a/libs/mikmod/playercode/munitrk.c b/libs/mikmod/playercode/munitrk.c new file mode 100644 index 0000000..349038c --- /dev/null +++ b/libs/mikmod/playercode/munitrk.c @@ -0,0 +1,303 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS + for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + All routines dealing with the manipulation of UNITRK streams + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mikmod_internals.h" + +#include + +/* Unibuffer chunk size */ +#define BUFPAGE 128 + +const UWORD unioperands[UNI_LAST] = { + 0, /* not used */ + 1, /* UNI_NOTE */ + 1, /* UNI_INSTRUMENT */ + 1, /* UNI_PTEFFECT0 */ + 1, /* UNI_PTEFFECT1 */ + 1, /* UNI_PTEFFECT2 */ + 1, /* UNI_PTEFFECT3 */ + 1, /* UNI_PTEFFECT4 */ + 1, /* UNI_PTEFFECT5 */ + 1, /* UNI_PTEFFECT6 */ + 1, /* UNI_PTEFFECT7 */ + 1, /* UNI_PTEFFECT8 */ + 1, /* UNI_PTEFFECT9 */ + 1, /* UNI_PTEFFECTA */ + 1, /* UNI_PTEFFECTB */ + 1, /* UNI_PTEFFECTC */ + 1, /* UNI_PTEFFECTD */ + 1, /* UNI_PTEFFECTE */ + 1, /* UNI_PTEFFECTF */ + 1, /* UNI_S3MEFFECTA */ + 1, /* UNI_S3MEFFECTD */ + 1, /* UNI_S3MEFFECTE */ + 1, /* UNI_S3MEFFECTF */ + 1, /* UNI_S3MEFFECTI */ + 1, /* UNI_S3MEFFECTQ */ + 1, /* UNI_S3MEFFECTR */ + 1, /* UNI_S3MEFFECTT */ + 1, /* UNI_S3MEFFECTU */ + 0, /* UNI_KEYOFF */ + 1, /* UNI_KEYFADE */ + 2, /* UNI_VOLEFFECTS */ + 1, /* UNI_XMEFFECT4 */ + 1, /* UNI_XMEFFECT6 */ + 1, /* UNI_XMEFFECTA */ + 1, /* UNI_XMEFFECTE1 */ + 1, /* UNI_XMEFFECTE2 */ + 1, /* UNI_XMEFFECTEA */ + 1, /* UNI_XMEFFECTEB */ + 1, /* UNI_XMEFFECTG */ + 1, /* UNI_XMEFFECTH */ + 1, /* UNI_XMEFFECTL */ + 1, /* UNI_XMEFFECTP */ + 1, /* UNI_XMEFFECTX1 */ + 1, /* UNI_XMEFFECTX2 */ + 1, /* UNI_ITEFFECTG */ + 1, /* UNI_ITEFFECTH */ + 1, /* UNI_ITEFFECTI */ + 1, /* UNI_ITEFFECTM */ + 1, /* UNI_ITEFFECTN */ + 1, /* UNI_ITEFFECTP */ + 1, /* UNI_ITEFFECTT */ + 1, /* UNI_ITEFFECTU */ + 1, /* UNI_ITEFFECTW */ + 1, /* UNI_ITEFFECTY */ + 2, /* UNI_ITEFFECTZ */ + 1, /* UNI_ITEFFECTS0 */ + 2, /* UNI_ULTEFFECT9 */ + 2, /* UNI_MEDSPEED */ + 0, /* UNI_MEDEFFECTF1 */ + 0, /* UNI_MEDEFFECTF2 */ + 0, /* UNI_MEDEFFECTF3 */ + 2, /* UNI_OKTARP */ +}; + +/* Sparse description of the internal module format + ------------------------------------------------ + + A UNITRK stream is an array of bytes representing a single track of a pattern. +It's made up of 'repeat/length' bytes, opcodes and operands (sort of a assembly +language): + +rrrlllll +[REP/LEN][OPCODE][OPERAND][OPCODE][OPERAND] [REP/LEN][OPCODE][OPERAND].. +^ ^ ^ +|-------ROWS 0 - 0+REP of a track---------| |-------ROWS xx - xx+REP of a track... + + The rep/len byte contains the number of bytes in the current row, _including_ +the length byte itself (So the LENGTH byte of row 0 in the previous example +would have a value of 5). This makes it easy to search through a stream for a +particular row. A track is concluded by a 0-value length byte. + + The upper 3 bits of the rep/len byte contain the number of times -1 this row +is repeated for this track. (so a value of 7 means this row is repeated 8 times) + + Opcodes can range from 1 to 255 but currently only opcodes 1 to 62 are being +used. Each opcode can have a different number of operands. You can find the +number of operands to a particular opcode by using the opcode as an index into +the 'unioperands' table. + +*/ + +/*========== Reading routines */ + +static UBYTE *rowstart; /* startadress of a row */ +static UBYTE *rowend; /* endaddress of a row (exclusive) */ +static UBYTE *rowpc; /* current unimod(tm) programcounter */ + +static UBYTE lastbyte; /* for UniSkipOpcode() */ + +void UniSetRow(UBYTE* t) +{ + rowstart = t; + rowpc = rowstart; + rowend = t?rowstart+(*(rowpc++)&0x1f):t; +} + +UBYTE UniGetByte(void) +{ + return lastbyte = (rowpc end of track.. */ + l = (c>>5)+1; /* extract repeat value */ + if(l>row) break; /* reached wanted row? -> return pointer */ + row -= l; /* haven't reached row yet.. update row */ + t += c&0x1f; /* point t to the next row */ + } + return t; +} + +/*========== Writing routines */ + +static UBYTE *unibuf; /* pointer to the temporary unitrk buffer */ +static UWORD unimax; /* buffer size */ + +static UWORD unipc; /* buffer cursor */ +static UWORD unitt; /* current row index */ +static UWORD lastp; /* previous row index */ + +/* Resets index-pointers to create a new track. */ +void UniReset(void) +{ + unitt = 0; /* reset index to rep/len byte */ + unipc = 1; /* first opcode will be written to index 1 */ + lastp = 0; /* no previous row yet */ + unibuf[0] = 0; /* clear rep/len byte */ +} + +/* Expands the buffer */ +static BOOL UniExpand(int wanted) +{ + if ((unipc+wanted)>=unimax) { + UBYTE *newbuf; + + /* Expand the buffer by BUFPAGE bytes */ + newbuf=(UBYTE*)MikMod_realloc(unibuf,(unimax+BUFPAGE)*sizeof(UBYTE)); + + /* Check if MikMod_realloc succeeded */ + if(newbuf) { + unibuf = newbuf; + unimax+=BUFPAGE; + return 1; + } else + return 0; + } + return 1; +} + +/* Appends one byte of data to the current row of a track. */ +void UniWriteByte(UBYTE data) +{ + if (UniExpand(1)) + /* write byte to current position and update */ + unibuf[unipc++]=data; +} + +void UniWriteWord(UWORD data) +{ + if (UniExpand(2)) { + unibuf[unipc++]=data>>8; + unibuf[unipc++]=data&0xff; + } +} + +static BOOL MyCmp(const UBYTE* a,const UBYTE* b,UWORD l) +{ + UWORD t; + + for(t=0;t>5)+1; /* repeat of previous row */ + l = (unibuf[lastp]&0x1f); /* length of previous row */ + + len = unipc-unitt; /* length of current row */ + + /* Now, check if the previous and the current row are identical.. when they + are, just increase the repeat field of the previous row */ + if(n<8 && len==l && MyCmp(&unibuf[lastp+1],&unibuf[unitt+1],len-1)) { + unibuf[lastp]+=0x20; + unipc = unitt+1; + } else { + if (UniExpand(unitt-unipc)) { + /* current and previous row aren't equal... update the pointers */ + unibuf[unitt] = len; + lastp = unitt; + unitt = unipc++; + } + } +} + +/* Terminates the current unitrk stream and returns a pointer to a copy of the + stream. */ +UBYTE* UniDup(void) +{ + void *d; + + if (!UniExpand(unipc-unitt)) return NULL; + unibuf[unitt] = 0; + + if(!(d=MikMod_malloc(unipc))) return NULL; + memcpy(d,unibuf,unipc); + + return (UBYTE *)d; +} + +BOOL UniInit(void) +{ + unimax = BUFPAGE; + + if(!(unibuf=(UBYTE*)MikMod_malloc(unimax*sizeof(UBYTE)))) return 0; + return 1; +} + +void UniCleanup(void) +{ + MikMod_free(unibuf); + unibuf = NULL; +} + +/* ex:set ts=4: */ diff --git a/libs/mikmod/playercode/mwav.c b/libs/mikmod/playercode/mwav.c new file mode 100644 index 0000000..6f2394f --- /dev/null +++ b/libs/mikmod/playercode/mwav.c @@ -0,0 +1,388 @@ +/* MikMod sound library + (c) 2004, Raphael Assenat + (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS + for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + WAV sample loader + +==============================================================================*/ + +/* + FIXME: Stereo .WAV files are not yet supported as samples. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include + +#include "mikmod_internals.h" + +#ifdef SUNOS +extern int fprintf(FILE *, const char *, ...); +#endif + +static void extract_channel(const char *src, char *dst, int num_chan, int num_samples, int samp_size, int channel); + + +typedef struct WAV { + CHAR rID[4]; + ULONG rLen; + CHAR wID[4]; + CHAR fID[4]; + ULONG fLen; + UWORD wFormatTag; + UWORD nChannels; + ULONG nSamplesPerSec; + ULONG nAvgBytesPerSec; + UWORD nBlockAlign; + UWORD nFormatSpecific; +} WAV; + +static BOOL isWaveFile(MREADER* reader) +{ + WAV wh; + + _mm_fseek(reader, SEEK_SET, 0); + + /* read wav header */ + _mm_read_string(wh.rID,4,reader); + wh.rLen = _mm_read_I_ULONG(reader); + _mm_read_string(wh.wID,4,reader); + + /* check for correct header */ + if(_mm_eof(reader)|| memcmp(wh.rID,"RIFF",4) || memcmp(wh.wID,"WAVE",4)) { + return 0; + } + return 1; +} + +static SAMPLE* Sample_LoadGeneric_internal_wav(MREADER* reader) +{ + SAMPLE *si=NULL; + WAV wh; + BOOL have_fmt=0; + + _mm_fseek(reader, SEEK_SET, 0); + + /* read wav header */ + _mm_read_string(wh.rID,4,reader); + wh.rLen = _mm_read_I_ULONG(reader); + _mm_read_string(wh.wID,4,reader); + + /* check for correct header */ + if(_mm_eof(reader)|| memcmp(wh.rID,"RIFF",4) || memcmp(wh.wID,"WAVE",4)) { + _mm_errno = MMERR_UNKNOWN_WAVE_TYPE; + return NULL; + } + + /* scan all RIFF blocks until we find the sample data */ + for(;;) { + CHAR dID[4]; + ULONG len,start; + + _mm_read_string(dID,4,reader); + len = _mm_read_I_ULONG(reader); + /* truncated file ? */ + if (_mm_eof(reader)) { + _mm_errno=MMERR_UNKNOWN_WAVE_TYPE; + return NULL; + } + start = _mm_ftell(reader); + + /* sample format block + should be present only once and before a data block */ + if(!memcmp(dID,"fmt ",4)) { + wh.wFormatTag = _mm_read_I_UWORD(reader); + wh.nChannels = _mm_read_I_UWORD(reader); + wh.nSamplesPerSec = _mm_read_I_ULONG(reader); + wh.nAvgBytesPerSec = _mm_read_I_ULONG(reader); + wh.nBlockAlign = _mm_read_I_UWORD(reader); + wh.nFormatSpecific = _mm_read_I_UWORD(reader); + +#ifdef MIKMOD_DEBUG + fprintf(stderr,"\rwavloader : wFormatTag=%04x blockalign=%04x nFormatSpc=%04x\n", + wh.wFormatTag,wh.nBlockAlign,wh.nFormatSpecific); +#endif + + if((have_fmt)||(wh.nChannels>1)) { + _mm_errno=MMERR_UNKNOWN_WAVE_TYPE; + return NULL; + } + have_fmt=1; + } else + /* sample data block + should be present only once and after a format block */ + if(!memcmp(dID,"data",4)) { + if(!have_fmt) { + _mm_errno=MMERR_UNKNOWN_WAVE_TYPE; + return NULL; + } + if(!(si=(SAMPLE*)MikMod_malloc(sizeof(SAMPLE)))) return NULL; + si->speed = wh.nSamplesPerSec/wh.nChannels; + si->volume = 64; + si->length = len; + if(wh.nBlockAlign == 2) { + si->flags = SF_16BITS | SF_SIGNED; + si->length >>= 1; + } + si->inflags = si->flags; + SL_RegisterSample(si,MD_SNDFX,reader); + SL_LoadSamples(); + + /* skip any other remaining blocks - so in case of repeated sample + fragments, we'll return the first anyway instead of an error */ + break; + } + /* onto next block */ + _mm_fseek(reader,start+len,SEEK_SET); + if (_mm_eof(reader)) + break; + } + + return si; +} + +static SAMPLE* Sample_LoadRawGeneric_internal(MREADER* reader, ULONG rate, ULONG channel, ULONG flags) +{ + SAMPLE *si; + long len; + int samp_size=1; + + if(!(si=(SAMPLE*)MikMod_malloc(sizeof(SAMPLE)))) return NULL; + + /* length */ + _mm_fseek(reader, 0, SEEK_END); + len = _mm_ftell(reader); + + si->panning = PAN_CENTER; + si->speed = rate/1; + si->volume = 64; + si->length = len; + si->loopstart=0; + si->loopend = len; + si->susbegin = 0; + si->susend = 0; + si->inflags = si->flags = flags; + if (si->flags & SF_16BITS) { + si->length >>= 1; + si->loopstart >>= 1; + si->loopend >>= 1; + samp_size = 2; + } + + if (si->flags & SF_STEREO) + { + char *data, *channel_data; + int num_samp = si->length/samp_size/2; + MREADER *chn_reader; + + data = (char*)MikMod_malloc(si->length); + if (!data) { MikMod_free(si); return NULL; } + + channel_data = (char*)MikMod_malloc(si->length/2); + if (!channel_data) { MikMod_free(data); MikMod_free(si); return NULL; } + + /* load the raw samples completely, and fully extract the + * requested channel. Create a memory reader pointing to + * the channel data. */ + _mm_fseek(reader, 0, SEEK_SET); + reader->Read(reader, data, si->length); + + extract_channel(data, channel_data, 2, num_samp, samp_size, channel); + chn_reader = _mm_new_mem_reader(channel_data, num_samp * samp_size); + if (!chn_reader) { + MikMod_free(channel_data); + MikMod_free(data); + MikMod_free(si); + return NULL; + } + + /* half of the samples were in the other channel */ + si->loopstart=0; + si->length=num_samp; + si->loopend=num_samp; + + SL_RegisterSample(si, MD_SNDFX, chn_reader); + SL_LoadSamples(); + + _mm_delete_mem_reader(chn_reader); + MikMod_free(channel_data); + MikMod_free(data); + + return si; + } + + _mm_fseek(reader, 0, SEEK_SET); + SL_RegisterSample(si, MD_SNDFX, reader); + SL_LoadSamples(); + + return si; +} + +static SAMPLE* Sample_LoadGeneric_internal(MREADER* reader, const char *options) +{ + if (isWaveFile(reader)) { + return Sample_LoadGeneric_internal_wav(reader); + } + + return NULL; +} + +MIKMODAPI SAMPLE* Sample_LoadRawGeneric(MREADER* reader, ULONG rate, ULONG channel, ULONG flags) +{ + SAMPLE* result; + + MUTEX_LOCK(vars); + result = Sample_LoadRawGeneric_internal(reader, rate, channel, flags); + MUTEX_UNLOCK(vars); + + return result; +} + +MIKMODAPI SAMPLE *Sample_LoadRawMem(const char *buf, int len, ULONG rate, ULONG channel, ULONG flags) +{ + SAMPLE *result=NULL; + MREADER *reader; + + if (!buf || len <= 0) return NULL; + if ((reader=_mm_new_mem_reader(buf, len)) != NULL) { + result=Sample_LoadRawGeneric(reader, rate, channel, flags); + _mm_delete_mem_reader(reader); + } + return result; +} + +MIKMODAPI SAMPLE* Sample_LoadRawFP(FILE *fp, ULONG rate, ULONG channel, ULONG flags) +{ + SAMPLE* result=NULL; + MREADER* reader; + + if(fp && (reader=_mm_new_file_reader(fp)) != NULL) { + result=Sample_LoadRawGeneric(reader, rate, channel, flags); + _mm_delete_file_reader(reader); + } + return result; +} + +MIKMODAPI SAMPLE* Sample_LoadRaw(const CHAR* filename, ULONG rate, ULONG channel, ULONG flags) +{ + FILE *fp; + SAMPLE *si=NULL; + + if(!(md_mode & DMODE_SOFT_SNDFX)) return NULL; + if((fp=_mm_fopen(filename,"rb")) != NULL) { + si = Sample_LoadRawFP(fp, rate, channel, flags); + _mm_fclose(fp); + } + return si; +} + +MIKMODAPI SAMPLE* Sample_LoadGeneric(MREADER* reader) +{ + SAMPLE* result; + + MUTEX_LOCK(vars); + result=Sample_LoadGeneric_internal(reader, NULL); + MUTEX_UNLOCK(vars); + + return result; +} + +MIKMODAPI SAMPLE *Sample_LoadMem(const char *buf, int len) +{ + SAMPLE* result=NULL; + MREADER* reader; + + if (!buf || len <= 0) return NULL; + if ((reader=_mm_new_mem_reader(buf, len)) != NULL) { + result=Sample_LoadGeneric(reader); + _mm_delete_mem_reader(reader); + } + return result; +} + +MIKMODAPI SAMPLE* Sample_LoadFP(FILE *fp) +{ + SAMPLE* result=NULL; + MREADER* reader; + + if(fp && (reader=_mm_new_file_reader(fp)) != NULL) { + result=Sample_LoadGeneric(reader); + _mm_delete_file_reader(reader); + } + return result; +} + +MIKMODAPI SAMPLE* Sample_Load(const CHAR* filename) +{ + FILE *fp; + SAMPLE *si=NULL; + + if(!(md_mode & DMODE_SOFT_SNDFX)) return NULL; + if((fp=_mm_fopen(filename,"rb")) != NULL) { + si = Sample_LoadFP(fp); + _mm_fclose(fp); + } + return si; +} + +MIKMODAPI void Sample_Free(SAMPLE* si) +{ + if(si) { + if (si->onfree) si->onfree(si->ctx); + MD_SampleUnload(si->handle); + MikMod_free(si); + } +} + +void Sample_Free_internal(SAMPLE *si) +{ + MUTEX_LOCK(vars); + Sample_Free(si); + MUTEX_UNLOCK(vars); +} + +static void extract_channel(const char *src, char *dst, int num_chan, int num_samples, int samp_size, int channel) +{ + int i; +#ifdef MIKMOD_DEBUG + fprintf(stderr,"Extract channel: %p %p, num_chan=%d, num_samples=%d, samp_size=%d, channel=%d\n", + src,dst,num_chan,num_samples,samp_size,channel); +#endif + src += channel * samp_size; + + while (num_samples--) + { + for (i=0; i 0 */ + /* C C# D D# E F F# G G# A A# B */ + 0x6b0,0x650,0x5f4,0x5a0,0x54c,0x500,0x4b8,0x474,0x434,0x3f8,0x3c0,0x38a, + 0x358,0x328,0x2fa,0x2d0,0x2a6,0x280,0x25c,0x23a,0x21a,0x1fc,0x1e0,0x1c5, + 0x1ac,0x194,0x17d,0x168,0x153,0x140,0x12e,0x11d,0x10d,0x0fe,0x0f0,0x0e2, + 0x0d6,0x0ca,0x0be,0x0b4,0x0aa,0x0a0,0x097,0x08f,0x087,0x07f,0x078,0x071, + 0x06b,0x065,0x05f,0x05a,0x055,0x050,0x04b,0x047,0x043,0x03f,0x03c,0x038, + 0x035,0x032,0x02f,0x02d,0x02a,0x028,0x025,0x023,0x021,0x01f,0x01e,0x01c, + 0x01b,0x019,0x018,0x016,0x015,0x014,0x013,0x012,0x011,0x010,0x00f,0x00e +}; + +/* ex:set ts=4: */ + diff --git a/libs/mikmod/playercode/sloader.c b/libs/mikmod/playercode/sloader.c new file mode 100644 index 0000000..30e9675 --- /dev/null +++ b/libs/mikmod/playercode/sloader.c @@ -0,0 +1,522 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS + for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Routines for loading samples. The sample loader utilizes the routines + provided by the "registered" sample loader. + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "mikmod_internals.h" + +static int sl_rlength; +static SWORD sl_old; +static SWORD *sl_buffer=NULL; +static SAMPLOAD *musiclist=NULL,*sndfxlist=NULL; + +/* size of the loader buffer in words */ +#define SLBUFSIZE 2048 + +/* IT-Compressed status structure */ +typedef struct ITPACK { + UWORD bits; /* current number of bits */ + UWORD bufbits; /* bits in buffer */ + SWORD last; /* last output */ + UBYTE buf; /* bit buffer */ +} ITPACK; + +BOOL SL_Init(SAMPLOAD* s) +{ + if(!sl_buffer) + if(!(sl_buffer=(SWORD*)MikMod_malloc(SLBUFSIZE*sizeof(SWORD)))) return 0; + + sl_rlength = s->length; + if(s->infmt & SF_16BITS) sl_rlength>>=1; + sl_old = 0; + + return 1; +} + +void SL_Exit(SAMPLOAD *s) +{ + if(sl_rlength>0) _mm_fseek(s->reader,sl_rlength,SEEK_CUR); + + MikMod_free(sl_buffer); + sl_buffer=NULL; +} + +/* unpack a 8bit IT packed sample */ +static int read_itcompr8(ITPACK* status,MREADER *reader,SWORD *out,UWORD count,UWORD* incnt) +{ + SWORD *dest=out,*end=out+count; + UWORD x,y,needbits,havebits,new_count=0; + UWORD bits = status->bits; + UWORD bufbits = status->bufbits; + SBYTE last = status->last; + UBYTE buf = status->buf; + + while (dest>=y; + bufbits-=y; + needbits-=y; + havebits+=y; + } + if (new_count) { + new_count = 0; + if (++x >= bits) + x++; + bits = x; + continue; + } + if (bits<7) { + if (x==(1<<(bits-1))) { + new_count = 1; + continue; + } + } + else if (bits<9) { + y = (0xff >> (9-bits)) - 4; + if ((x>y)&&(x<=y+8)) { + if ((x-=y)>=bits) + x++; + bits = x; + continue; + } + } + else if (bits<10) { + if (x>=0x100) { + bits=x-0x100+1; + continue; + } + } else { + /* error in compressed data... */ + _mm_errno=MMERR_ITPACK_INVALID_DATA; + return 0; + } + + if (bits<8) /* extend sign */ + x = ((SBYTE)(x <<(8-bits))) >> (8-bits); + *(dest++)= (last+=x) << 8; /* convert to 16 bit */ + } + status->bits = bits; + status->bufbits = bufbits; + status->last = last; + status->buf = buf; + return (dest-out); +} + +/* unpack a 16bit IT packed sample */ +static int read_itcompr16(ITPACK *status,MREADER *reader,SWORD *out,UWORD count,UWORD* incnt) +{ + SWORD *dest=out,*end=out+count; + SLONG x,y,needbits,havebits,new_count=0; + UWORD bits = status->bits; + UWORD bufbits = status->bufbits; + SWORD last = status->last; + UBYTE buf = status->buf; + + while (dest>=y; + bufbits-=(UWORD)y; + needbits-=(UWORD)y; + havebits+=(UWORD)y; + } + if (new_count) { + new_count = 0; + if (++x >= bits) + x++; + bits = (UWORD)x; + continue; + } + if (bits<7) { + if (x==(1<<(bits-1))) { + new_count=1; + continue; + } + } + else if (bits<17) { + y=(0xffff>>(17-bits))-8; + if ((x>y)&&(x<=y+16)) { + if ((x-=y)>=bits) + x++; + bits = (UWORD)x; + continue; + } + } + else if (bits<18) { + if (x>=0x10000) { + bits=(UWORD)(x-0x10000+1); + continue; + } + } else { + /* error in compressed data... */ + _mm_errno=MMERR_ITPACK_INVALID_DATA; + return 0; + } + + if (bits<16) /* extend sign */ + x = ((SWORD)(x<<(16-bits)))>>(16-bits); + *(dest++)=(last+=x); + } + status->bits = bits; + status->bufbits = bufbits; + status->last = last; + status->buf = buf; + return (dest-out); +} + +static int SL_LoadInternal(void* buffer,UWORD infmt,UWORD outfmt,int scalefactor,ULONG length,MREADER* reader,BOOL dither) +{ + SBYTE *bptr = (SBYTE*)buffer; + SWORD *wptr = (SWORD*)buffer; + int stodo,t,u; + + int result,c_block=0; /* compression bytes until next block */ + ITPACK status; + UWORD incnt = 0; + + status.buf = 0; + status.last = 0; + status.bufbits = 0; + status.bits = 0; + + while(length) { + stodo=(lengthRead(reader,sl_buffer,sizeof(SBYTE)*stodo); + src = (SBYTE*)sl_buffer; + dest = sl_buffer; + src += stodo;dest += stodo; + + for(t=0;t>1); + length-=2; + } + stodo = idx; + } + } + + if(outfmt & SF_16BITS) { + for(t=0;t>8; + } + } + return 0; +} + +int SL_Load(void* buffer,SAMPLOAD *smp,ULONG length) +{ + return SL_LoadInternal(buffer,smp->infmt,smp->outfmt,smp->scalefactor, + length,smp->reader,0); +} + +/* Registers a sample for loading when SL_LoadSamples() is called. */ +SAMPLOAD* SL_RegisterSample(SAMPLE* s,int type,MREADER* reader) +{ + SAMPLOAD *news,**samplist,*cruise; + + if(type==MD_MUSIC) { + samplist = &musiclist; + cruise = musiclist; + } else + if (type==MD_SNDFX) { + samplist = &sndfxlist; + cruise = sndfxlist; + } else + return NULL; + + /* Allocate and add structure to the END of the list */ + if(!(news=(SAMPLOAD*)MikMod_malloc(sizeof(SAMPLOAD)))) return NULL; + + if(cruise) { + while(cruise->next) cruise=cruise->next; + cruise->next = news; + } else + *samplist = news; + + news->infmt = s->flags & SF_FORMATMASK; + news->outfmt = news->infmt; + news->reader = reader; + news->sample = s; + news->length = s->length; + news->loopstart = s->loopstart; + news->loopend = s->loopend; + + return news; +} + +static void FreeSampleList(SAMPLOAD* s) +{ + SAMPLOAD *old; + + while(s) { + old = s; + s = s->next; + MikMod_free(old); + } +} + +/* Returns the total amount of memory required by the samplelist queue. */ +static ULONG SampleTotal(SAMPLOAD* samplist,int type) +{ + int total = 0; + + while(samplist) { + samplist->sample->flags= + (samplist->sample->flags&~SF_FORMATMASK)|samplist->outfmt; + total += MD_SampleLength(type,samplist->sample); + samplist=samplist->next; + } + + return total; +} + +static ULONG RealSpeed(SAMPLOAD *s) +{ + return(s->sample->speed/(s->scalefactor?s->scalefactor:1)); +} + +static int DitherSamples(SAMPLOAD* samplist,int type) +{ + SAMPLOAD *c2smp=NULL; + ULONG maxsize, speed; + SAMPLOAD *s; + + if(!samplist) return 0; + + if((maxsize=MD_SampleSpace(type)*1024) != 0) + while(SampleTotal(samplist,type)>maxsize) { + /* First Pass - check for any 16 bit samples */ + s = samplist; + while(s) { + if(s->outfmt & SF_16BITS) { + SL_Sample16to8(s); + break; + } + s=s->next; + } + /* Second pass (if no 16bits found above) is to take the sample with + the highest speed and dither it by half. */ + if(!s) { + s = samplist; + speed = 0; + while(s) { + if((s->sample->length) && (RealSpeed(s)>speed)) { + speed=RealSpeed(s); + c2smp=s; + } + s=s->next; + } + if (c2smp) + SL_HalveSample(c2smp,2); + } + } + + /* Samples dithered, now load them ! */ + s = samplist; + while(s) { + /* sample has to be loaded ? -> increase number of samples, allocate + memory and load sample. */ + if(s->sample->length) { + if(s->sample->seekpos) + _mm_fseek(s->reader, s->sample->seekpos, SEEK_SET); + + /* Call the sample load routine of the driver module. It has to + return a 'handle' (>=0) that identifies the sample. */ + s->sample->handle = MD_SampleLoad(s, type); + s->sample->flags = (s->sample->flags & ~SF_FORMATMASK) | s->outfmt; + if(s->sample->handle<0) { + FreeSampleList(samplist); + if(_mm_errorhandler) _mm_errorhandler(); + return 1; + } + } + s = s->next; + } + + FreeSampleList(samplist); + return 0; +} + +int SL_LoadSamples(void) +{ + int rc; + + _mm_critical = 0; + + if((!musiclist)&&(!sndfxlist)) return 0; + rc=DitherSamples(musiclist,MD_MUSIC)||DitherSamples(sndfxlist,MD_SNDFX); + musiclist=sndfxlist=NULL; + + return rc; +} + +void SL_Sample16to8(SAMPLOAD* s) +{ + s->outfmt &= ~SF_16BITS; + s->sample->flags = (s->sample->flags&~SF_FORMATMASK) | s->outfmt; +} + +void SL_Sample8to16(SAMPLOAD* s) +{ + s->outfmt |= SF_16BITS; + s->sample->flags = (s->sample->flags&~SF_FORMATMASK) | s->outfmt; +} + +void SL_SampleSigned(SAMPLOAD* s) +{ + s->outfmt |= SF_SIGNED; + s->sample->flags = (s->sample->flags&~SF_FORMATMASK) | s->outfmt; +} + +void SL_SampleUnsigned(SAMPLOAD* s) +{ + s->outfmt &= ~SF_SIGNED; + s->sample->flags = (s->sample->flags&~SF_FORMATMASK) | s->outfmt; +} + +void SL_HalveSample(SAMPLOAD* s,int factor) +{ + s->scalefactor=factor>0?factor:2; + + s->sample->divfactor = s->scalefactor; + s->sample->length = s->length / s->scalefactor; + s->sample->loopstart = s->loopstart / s->scalefactor; + s->sample->loopend = s->loopend / s->scalefactor; +} + +/* ex:set ts=4: */ diff --git a/libs/mikmod/playercode/virtch.c b/libs/mikmod/playercode/virtch.c new file mode 100644 index 0000000..9342978 --- /dev/null +++ b/libs/mikmod/playercode/virtch.c @@ -0,0 +1,1351 @@ +/* MikMod sound library + (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file + AUTHORS for complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +/*============================================================================== + + $Id$ + + Sample mixing routines, using a 32 bits mixing buffer. + +==============================================================================*/ + +/* + + Optional features include: + (a) 4-step reverb (for 16 bit output only) + (b) Interpolation of sample data during mixing + (c) Dolby Surround Sound +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +#include "mikmod_internals.h" + +/* + Constant definitions + ==================== + + BITSHIFT + Controls the maximum volume of the sound output. All data is shifted + right by BITSHIFT after being mixed. Higher values result in quieter + sound and less chance of distortion. + + REVERBERATION + Controls the duration of the reverb. Larger values represent a shorter + reverb loop. Smaller values extend the reverb but can result in more of + an echo-ish sound. + +*/ + +#define BITSHIFT 9 +#define REVERBERATION 110000L + +#define FRACBITS 11 +#define FRACMASK ((1L< sample has to be restarted */ + UBYTE active; /* =1 -> sample is playing */ + UWORD flags; /* 16/8 bits looping/one-shot */ + SWORD handle; /* identifies the sample */ + ULONG start; /* start index */ + ULONG size; /* samplesize */ + ULONG reppos; /* loop start */ + ULONG repend; /* loop end */ + ULONG frq; /* current frequency */ + int vol; /* current volume */ + int pan; /* current panning position */ + + int rampvol; + int lvolsel,rvolsel; /* Volume factor in range 0-255 */ + int oldlvol,oldrvol; + + SLONGLONG current; /* current index in the sample */ + SLONGLONG increment; /* increment value */ +} VINFO; + +static SWORD **Samples; +static VINFO *vinf=NULL,*vnf; +static long tickleft,samplesthatfit,vc_memory=0; +static int vc_softchn; +static SLONGLONG idxsize,idxlpos,idxlend; +static SLONG *vc_tickbuf=NULL; +static UWORD vc_mode; + +/* Reverb control variables */ + +static int RVc1, RVc2, RVc3, RVc4, RVc5, RVc6, RVc7, RVc8; +static ULONG RVRindex; + +/* For Mono or Left Channel */ +static SLONG *RVbufL1=NULL,*RVbufL2=NULL,*RVbufL3=NULL,*RVbufL4=NULL, + *RVbufL5=NULL,*RVbufL6=NULL,*RVbufL7=NULL,*RVbufL8=NULL; + +/* For Stereo only (Right Channel) */ +static SLONG *RVbufR1=NULL,*RVbufR2=NULL,*RVbufR3=NULL,*RVbufR4=NULL, + *RVbufR5=NULL,*RVbufR6=NULL,*RVbufR7=NULL,*RVbufR8=NULL; + +#ifdef NATIVE_64BIT_INT +#define NATIVE SLONGLONG +#else +#define NATIVE SLONG +#endif + +#if defined HAVE_SSE2 || defined HAVE_ALTIVEC + +# if !defined(NATIVE_64BIT_INT) +static SINTPTR_T MixSIMDMonoNormal(const SWORD* srce,SLONG* dest,SINTPTR_T idx,SINTPTR_T increment,SINTPTR_T todo) +{ + /* TODO: */ + SWORD sample; + SLONG lvolsel = vnf->lvolsel; + + while(todo--) { + sample = srce[idx >> FRACBITS]; + idx += increment; + + *dest++ += lvolsel * sample; + } + return idx; +} +# endif /* !NATIVE_64BIT_INT */ + +static SINTPTR_T MixSIMDStereoNormal(const SWORD* srce,SLONG* dest,SINTPTR_T idx,SINTPTR_T increment,SINTPTR_T todo) +{ + SWORD vol[8] = {vnf->lvolsel, vnf->rvolsel}; + SWORD sample; + SLONG remain = todo; + + /* Dest can be misaligned */ + while(!IS_ALIGNED_16(dest)) { + sample=srce[idx >> FRACBITS]; + idx += increment; + *dest++ += vol[0] * sample; + *dest++ += vol[1] * sample; + todo--; + if(!todo) return idx; + } + + /* Srce is always aligned */ + +#if defined HAVE_SSE2 + remain = todo&3; + { + __m128i v0 = _mm_set_epi16(0, vol[1], + 0, vol[0], + 0, vol[1], + 0, vol[0]); + for(todo>>=2;todo; todo--) + { + SWORD s0 = srce[idx >> FRACBITS]; + SWORD s1 = srce[(idx += increment) >> FRACBITS]; + SWORD s2 = srce[(idx += increment) >> FRACBITS]; + SWORD s3 = srce[(idx += increment) >> FRACBITS]; + __m128i v1 = _mm_set_epi16(0, s1, 0, s1, 0, s0, 0, s0); + __m128i v2 = _mm_set_epi16(0, s3, 0, s3, 0, s2, 0, s2); + __m128i v3 = _mm_load_si128((__m128i*)(dest+0)); + __m128i v4 = _mm_load_si128((__m128i*)(dest+4)); + _mm_store_si128((__m128i*)(dest+0), _mm_add_epi32(v3, _mm_madd_epi16(v0, v1))); + _mm_store_si128((__m128i*)(dest+4), _mm_add_epi32(v4, _mm_madd_epi16(v0, v2))); + dest+=8; + idx += increment; + } + } + +#elif defined HAVE_ALTIVEC + remain = todo&3; + { + SWORD s[8]; + vector signed short r0 = vec_ld(0, vol); + vector signed short v0 = vec_perm(r0, r0, (vector unsigned char)(0, 1, /* l */ + 0, 1, /* l */ + 2, 3, /* r */ + 2, 1, /* r */ + 0, 1, /* l */ + 0, 1, /* l */ + 2, 3, /* r */ + 2, 3 /* r */ + )); + + for(todo>>=2;todo; todo--) + { + vector short int r1; + vector signed short v1, v2; + vector signed int v3, v4, v5, v6; + + /* Load constants */ + s[0] = srce[idx >> FRACBITS]; + s[1] = srce[(idx += increment) >> FRACBITS]; + s[2] = srce[(idx += increment) >> FRACBITS]; + s[3] = srce[(idx += increment) >> FRACBITS]; + s[4] = 0; + + r1 = vec_ld(0, s); + v1 = vec_perm(r1, r1, (vector unsigned char) + (0*2, 0*2+1, /* s0 */ + 4*2, 4*2+1, /* 0 */ + 0*2, 0*2+1, /* s0 */ + 4*2, 4*2+1, /* 0 */ + 1*2, 1*2+1, /* s1 */ + 4*2, 4*2+1, /* 0 */ + 1*2, 1*2+1, /* s1 */ + 4*2, 4*2+1 /* 0 */ + ) ); + v2 = vec_perm(r1, r1, (vector unsigned char) + (2*2, 2*2+1, /* s2 */ + 4*2, 4*2+1, /* 0 */ + 2*2, 2*2+1, /* s2 */ + 4*2, 4*2+1, /* 0 */ + 3*2, 3*2+1, /* s3 */ + 4*2, 4*2+1, /* 0 */ + 3*2, 3*2+1, /* s3 */ + 4*2, 4*2+1 /* 0 */ + ) ); + + v3 = vec_ld(0, dest); + v4 = vec_ld(0, dest + 4); + v5 = vec_mule(v0, v1); + v6 = vec_mule(v0, v2); + + vec_st(vec_add(v3, v5), 0, dest); + vec_st(vec_add(v4, v6), 0x10, dest); + + dest+=8; + idx += increment; + } + } +#endif /* HAVE_ALTIVEC */ + + /* Remaining bits */ + while(remain--) { + sample=srce[idx >> FRACBITS]; + idx += increment; + + *dest++ += vol[0] * sample; + *dest++ += vol[1] * sample; + } + return idx; +} +#endif + +/*========== 32 bit sample mixers - only for 32 bit platforms */ +#ifndef NATIVE_64BIT_INT + +static SLONG Mix32MonoNormal(const SWORD* srce,SLONG* dest,SLONG idx,SLONG increment,SLONG todo) +{ +#if defined HAVE_ALTIVEC || defined HAVE_SSE2 + if (md_mode & DMODE_SIMDMIXER) { + return MixSIMDMonoNormal(srce, dest, idx, increment, todo); + } + else +#endif + { + SWORD sample; + SLONG lvolsel = vnf->lvolsel; + + while(todo--) { + sample = srce[idx >> FRACBITS]; + idx += increment; + + *dest++ += lvolsel * sample; + } + } + return idx; +} + +/* FIXME: This mixer should works also on 64-bit platform */ +/* Hint : changes SLONG / SLONGLONG mess with intptr */ +static SLONG Mix32StereoNormal(const SWORD* srce,SLONG* dest,SLONG idx,SLONG increment,SLONG todo) +{ +#if defined HAVE_ALTIVEC || defined HAVE_SSE2 + if (md_mode & DMODE_SIMDMIXER) { + return MixSIMDStereoNormal(srce, dest, idx, increment, todo); + } + else +#endif + { + SWORD sample; + SLONG lvolsel = vnf->lvolsel; + SLONG rvolsel = vnf->rvolsel; + + while(todo--) { + sample=srce[idx >> FRACBITS]; + idx += increment; + + *dest++ += lvolsel * sample; + *dest++ += rvolsel * sample; + } + } + return idx; +} + +static SLONG Mix32SurroundNormal(const SWORD* srce,SLONG* dest,SLONG idx,SLONG increment,SLONG todo) +{ + SWORD sample; + SLONG lvolsel = vnf->lvolsel; + SLONG rvolsel = vnf->rvolsel; + + if (lvolsel>=rvolsel) { + while(todo--) { + sample = srce[idx >> FRACBITS]; + idx += increment; + + *dest++ += lvolsel*sample; + *dest++ -= lvolsel*sample; + } + } + else { + while(todo--) { + sample = srce[idx >> FRACBITS]; + idx += increment; + + *dest++ -= rvolsel*sample; + *dest++ += rvolsel*sample; + } + } + return idx; +} + +static SLONG Mix32MonoInterp(const SWORD* srce,SLONG* dest,SLONG idx,SLONG increment,SLONG todo) +{ + SLONG sample; + SLONG lvolsel = vnf->lvolsel; + SLONG rampvol = vnf->rampvol; + + if (rampvol) { + SLONG oldlvol = vnf->oldlvol - lvolsel; + while(todo--) { + sample=(SLONG)srce[idx>>FRACBITS]+ + ((SLONG)(srce[(idx>>FRACBITS)+1]-srce[idx>>FRACBITS]) + *(idx&FRACMASK)>>FRACBITS); + idx += increment; + + *dest++ += ((lvolsel << CLICK_SHIFT) + oldlvol * rampvol) + * sample >> CLICK_SHIFT; + if (!--rampvol) + break; + } + vnf->rampvol = rampvol; + if (todo < 0) + return idx; + } + + while(todo--) { + sample=(SLONG)srce[idx>>FRACBITS]+ + ((SLONG)(srce[(idx>>FRACBITS)+1]-srce[idx>>FRACBITS]) + *(idx&FRACMASK)>>FRACBITS); + idx += increment; + + *dest++ += lvolsel * sample; + } + return idx; +} + +static SLONG Mix32StereoInterp(const SWORD* srce,SLONG* dest,SLONG idx,SLONG increment,SLONG todo) +{ + SLONG sample; + SLONG lvolsel = vnf->lvolsel; + SLONG rvolsel = vnf->rvolsel; + SLONG rampvol = vnf->rampvol; + + if (rampvol) { + SLONG oldlvol = vnf->oldlvol - lvolsel; + SLONG oldrvol = vnf->oldrvol - rvolsel; + while(todo--) { + sample=(SLONG)srce[idx>>FRACBITS]+ + ((SLONG)(srce[(idx>>FRACBITS)+1]-srce[idx>>FRACBITS]) + *(idx&FRACMASK)>>FRACBITS); + idx += increment; + + *dest++ += ((lvolsel << CLICK_SHIFT) + oldlvol * rampvol) + * sample >> CLICK_SHIFT; + *dest++ += ((rvolsel << CLICK_SHIFT) + oldrvol * rampvol) + * sample >> CLICK_SHIFT; + if (!--rampvol) + break; + } + vnf->rampvol = rampvol; + if (todo < 0) + return idx; + } + + while(todo--) { + sample=(SLONG)srce[idx>>FRACBITS]+ + ((SLONG)(srce[(idx>>FRACBITS)+1]-srce[idx>>FRACBITS]) + *(idx&FRACMASK)>>FRACBITS); + idx += increment; + + *dest++ += lvolsel * sample; + *dest++ += rvolsel * sample; + } + return idx; +} + +static SLONG Mix32SurroundInterp(const SWORD* srce,SLONG* dest,SLONG idx,SLONG increment,SLONG todo) +{ + SLONG sample; + SLONG lvolsel = vnf->lvolsel; + SLONG rvolsel = vnf->rvolsel; + SLONG rampvol = vnf->rampvol; + SLONG oldvol, vol; + + if (lvolsel >= rvolsel) { + vol = lvolsel; + oldvol = vnf->oldlvol; + } else { + vol = rvolsel; + oldvol = vnf->oldrvol; + } + + if (rampvol) { + oldvol -= vol; + while(todo--) { + sample=(SLONG)srce[idx>>FRACBITS]+ + ((SLONG)(srce[(idx>>FRACBITS)+1]-srce[idx>>FRACBITS]) + *(idx&FRACMASK)>>FRACBITS); + idx += increment; + + sample=((vol << CLICK_SHIFT) + oldvol * rampvol) + * sample >> CLICK_SHIFT; + *dest++ += sample; + *dest++ -= sample; + + if (!--rampvol) + break; + } + vnf->rampvol = rampvol; + if (todo < 0) + return idx; + } + + while(todo--) { + sample=(SLONG)srce[idx>>FRACBITS]+ + ((SLONG)(srce[(idx>>FRACBITS)+1]-srce[idx>>FRACBITS]) + *(idx&FRACMASK)>>FRACBITS); + idx += increment; + + *dest++ += vol*sample; + *dest++ -= vol*sample; + } + return idx; +} +#endif + +/*========== 64 bit sample mixers - all platforms */ + +static SLONGLONG MixMonoNormal(const SWORD* srce,SLONG* dest,SLONGLONG idx,SLONGLONG increment,SLONG todo) +{ + SWORD sample; + SLONG lvolsel = vnf->lvolsel; + + while(todo--) { + sample = srce[idx >> FRACBITS]; + idx += increment; + + *dest++ += lvolsel * sample; + } + return idx; +} + +static SLONGLONG MixStereoNormal(const SWORD* srce,SLONG* dest,SLONGLONG idx,SLONGLONG increment,SLONG todo) +{ + SWORD sample; + SLONG lvolsel = vnf->lvolsel; + SLONG rvolsel = vnf->rvolsel; + + while(todo--) { + sample=srce[idx >> FRACBITS]; + idx += increment; + + *dest++ += lvolsel * sample; + *dest++ += rvolsel * sample; + } + return idx; +} + +static SLONGLONG MixSurroundNormal(const SWORD* srce,SLONG* dest,SLONGLONG idx,SLONGLONG increment,SLONG todo) +{ + SWORD sample; + SLONG lvolsel = vnf->lvolsel; + SLONG rvolsel = vnf->rvolsel; + + if(vnf->lvolsel>=vnf->rvolsel) { + while(todo--) { + sample = srce[idx >> FRACBITS]; + idx += increment; + + *dest++ += lvolsel*sample; + *dest++ -= lvolsel*sample; + } + } + else { + while(todo--) { + sample = srce[idx >> FRACBITS]; + idx += increment; + + *dest++ -= rvolsel*sample; + *dest++ += rvolsel*sample; + } + } + return idx; +} + +static SLONGLONG MixMonoInterp(const SWORD* srce,SLONG* dest,SLONGLONG idx,SLONGLONG increment,SLONG todo) +{ + SLONG sample; + SLONG lvolsel = vnf->lvolsel; + SLONG rampvol = vnf->rampvol; + + if (rampvol) { + SLONG oldlvol = vnf->oldlvol - lvolsel; + while(todo--) { + sample=(SLONG)srce[idx>>FRACBITS]+ + ((SLONG)(srce[(idx>>FRACBITS)+1]-srce[idx>>FRACBITS]) + *(idx&FRACMASK)>>FRACBITS); + idx += increment; + + *dest++ += ((lvolsel << CLICK_SHIFT) + oldlvol * rampvol) + * sample >> CLICK_SHIFT; + if (!--rampvol) + break; + } + vnf->rampvol = rampvol; + if (todo < 0) + return idx; + } + + while(todo--) { + sample=(SLONG)srce[idx>>FRACBITS]+ + ((SLONG)(srce[(idx>>FRACBITS)+1]-srce[idx>>FRACBITS]) + *(idx&FRACMASK)>>FRACBITS); + idx += increment; + + *dest++ += lvolsel * sample; + } + return idx; +} + +static SLONGLONG MixStereoInterp(const SWORD* srce,SLONG* dest,SLONGLONG idx,SLONGLONG increment,SLONG todo) +{ + SLONG sample; + SLONG lvolsel = vnf->lvolsel; + SLONG rvolsel = vnf->rvolsel; + SLONG rampvol = vnf->rampvol; + + if (rampvol) { + SLONG oldlvol = vnf->oldlvol - lvolsel; + SLONG oldrvol = vnf->oldrvol - rvolsel; + while(todo--) { + sample=(SLONG)srce[idx>>FRACBITS]+ + ((SLONG)(srce[(idx>>FRACBITS)+1]-srce[idx>>FRACBITS]) + *(idx&FRACMASK)>>FRACBITS); + idx += increment; + + *dest++ +=((lvolsel << CLICK_SHIFT) + oldlvol * rampvol) + * sample >> CLICK_SHIFT; + *dest++ +=((rvolsel << CLICK_SHIFT) + oldrvol * rampvol) + * sample >> CLICK_SHIFT; + if (!--rampvol) + break; + } + vnf->rampvol = rampvol; + if (todo < 0) + return idx; + } + + while(todo--) { + sample=(SLONG)srce[idx>>FRACBITS]+ + ((SLONG)(srce[(idx>>FRACBITS)+1]-srce[idx>>FRACBITS]) + *(idx&FRACMASK)>>FRACBITS); + idx += increment; + + *dest++ += lvolsel * sample; + *dest++ += rvolsel * sample; + } + return idx; +} + +static SLONGLONG MixSurroundInterp(const SWORD* srce,SLONG* dest,SLONGLONG idx,SLONGLONG increment,SLONG todo) +{ + SLONG sample; + SLONG lvolsel = vnf->lvolsel; + SLONG rvolsel = vnf->rvolsel; + SLONG rampvol = vnf->rampvol; + SLONG oldvol, vol; + + if (lvolsel >= rvolsel) { + vol = lvolsel; + oldvol = vnf->oldlvol; + } else { + vol = rvolsel; + oldvol = vnf->oldrvol; + } + + if (rampvol) { + oldvol -= vol; + while(todo--) { + sample=(SLONG)srce[idx>>FRACBITS]+ + ((SLONG)(srce[(idx>>FRACBITS)+1]-srce[idx>>FRACBITS]) + *(idx&FRACMASK)>>FRACBITS); + idx += increment; + + sample=((vol << CLICK_SHIFT) + oldvol * rampvol) + * sample >> CLICK_SHIFT; + *dest++ += sample; + *dest++ -= sample; + if (!--rampvol) + break; + } + vnf->rampvol = rampvol; + if (todo < 0) + return idx; + } + + while(todo--) { + sample=(SLONG)srce[idx>>FRACBITS]+ + ((SLONG)(srce[(idx>>FRACBITS)+1]-srce[idx>>FRACBITS]) + *(idx&FRACMASK)>>FRACBITS); + idx += increment; + + *dest++ += vol*sample; + *dest++ -= vol*sample; + } + return idx; +} + +static void (*MixReverb)(SLONG* srce,NATIVE count); + +/* Reverb macros */ +#define COMPUTE_LOC(n) loc##n = RVRindex % RVc##n +#define COMPUTE_LECHO(n) RVbufL##n [loc##n ]=speedup+((ReverbPct*RVbufL##n [loc##n ])>>7) +#define COMPUTE_RECHO(n) RVbufR##n [loc##n ]=speedup+((ReverbPct*RVbufR##n [loc##n ])>>7) + +static void MixReverb_Normal(SLONG* srce,NATIVE count) +{ + unsigned int speedup; + int ReverbPct; + unsigned int loc1,loc2,loc3,loc4; + unsigned int loc5,loc6,loc7,loc8; + + ReverbPct=58+(md_reverb<<2); + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + while(count--) { + /* Compute the left channel echo buffers */ + speedup = *srce >> 3; + + COMPUTE_LECHO(1); COMPUTE_LECHO(2); COMPUTE_LECHO(3); COMPUTE_LECHO(4); + COMPUTE_LECHO(5); COMPUTE_LECHO(6); COMPUTE_LECHO(7); COMPUTE_LECHO(8); + + /* Prepare to compute actual finalized data */ + RVRindex++; + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + /* left channel */ + *srce++ +=RVbufL1[loc1]-RVbufL2[loc2]+RVbufL3[loc3]-RVbufL4[loc4]+ + RVbufL5[loc5]-RVbufL6[loc6]+RVbufL7[loc7]-RVbufL8[loc8]; + } +} + +static void MixReverb_Stereo(SLONG* srce,NATIVE count) +{ + unsigned int speedup; + int ReverbPct; + unsigned int loc1, loc2, loc3, loc4; + unsigned int loc5, loc6, loc7, loc8; + + ReverbPct = 92+(md_reverb<<1); + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + while(count--) { + /* Compute the left channel echo buffers */ + speedup = *srce >> 3; + + COMPUTE_LECHO(1); COMPUTE_LECHO(2); COMPUTE_LECHO(3); COMPUTE_LECHO(4); + COMPUTE_LECHO(5); COMPUTE_LECHO(6); COMPUTE_LECHO(7); COMPUTE_LECHO(8); + + /* Compute the right channel echo buffers */ + speedup = srce[1] >> 3; + + COMPUTE_RECHO(1); COMPUTE_RECHO(2); COMPUTE_RECHO(3); COMPUTE_RECHO(4); + COMPUTE_RECHO(5); COMPUTE_RECHO(6); COMPUTE_RECHO(7); COMPUTE_RECHO(8); + + /* Prepare to compute actual finalized data */ + RVRindex++; + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + /* left channel then right channel */ + *srce++ +=RVbufL1[loc1]-RVbufL2[loc2]+RVbufL3[loc3]-RVbufL4[loc4]+ + RVbufL5[loc5]-RVbufL6[loc6]+RVbufL7[loc7]-RVbufL8[loc8]; + + *srce++ +=RVbufR1[loc1]-RVbufR2[loc2]+RVbufR3[loc3]-RVbufR4[loc4]+ + RVbufR5[loc5]-RVbufR6[loc6]+RVbufR7[loc7]-RVbufR8[loc8]; + } +} + +static void (*MixLowPass)(SLONG* srce,NATIVE count); + +static int nLeftNR, nRightNR; + +static void MixLowPass_Stereo(SLONG* srce,NATIVE count) +{ + int n1 = nLeftNR, n2 = nRightNR; + SLONG *pnr = srce; + int nr=count; + for (; nr; nr--) + { + int vnr = pnr[0] >> 1; + pnr[0] = vnr + n1; + n1 = vnr; + vnr = pnr[1] >> 1; + pnr[1] = vnr + n2; + n2 = vnr; + pnr += 2; + } + nLeftNR = n1; + nRightNR = n2; +} + +static void MixLowPass_Normal(SLONG* srce,NATIVE count) +{ + int n1 = nLeftNR; + SLONG *pnr = srce; + int nr=count; + for (; nr; nr--) + { + int vnr = pnr[0] >> 1; + pnr[0] = vnr + n1; + n1 = vnr; + pnr ++; + } + nLeftNR = n1; +} + +/* shifting fudge factor for FP scaling, should be 0 < FP_SHIFT < BITSHIFT */ +#define FP_SHIFT 4 + +/* Mixing macros */ +#define EXTRACT_SAMPLE_FP(var,size) var=(*srce++>>(BITSHIFT-size)) * ((1.0f / 32768.0f) / (1 << size)) +#define CHECK_SAMPLE_FP(var,bound) var=(var>bound)?bound:(var<-bound)?-bound:var +#define PUT_SAMPLE_FP(var) *dste++=var + +static void Mix32ToFP(float* dste,const SLONG *srce,NATIVE count) +{ + float x1,x2,x3,x4; + int remain; + + remain=count&3; + for(count>>=2;count;count--) { + EXTRACT_SAMPLE_FP(x1,FP_SHIFT); EXTRACT_SAMPLE_FP(x2,FP_SHIFT); + EXTRACT_SAMPLE_FP(x3,FP_SHIFT); EXTRACT_SAMPLE_FP(x4,FP_SHIFT); + + CHECK_SAMPLE_FP(x1,1.0f); CHECK_SAMPLE_FP(x2,1.0f); + CHECK_SAMPLE_FP(x3,1.0f); CHECK_SAMPLE_FP(x4,1.0f); + + PUT_SAMPLE_FP(x1); PUT_SAMPLE_FP(x2); + PUT_SAMPLE_FP(x3); PUT_SAMPLE_FP(x4); + } + while(remain--) { + EXTRACT_SAMPLE_FP(x1,FP_SHIFT); + CHECK_SAMPLE_FP(x1,1.0f); + PUT_SAMPLE_FP(x1); + } +} + + +/* Mixing macros */ +#define EXTRACT_SAMPLE(var,size) var=*srce++>>(BITSHIFT+16-size) +#define CHECK_SAMPLE(var,bound) var=(var>=bound)?bound-1:(var<-bound)?-bound:var +#define PUT_SAMPLE(var) *dste++=var + +static void Mix32To16(SWORD* dste,const SLONG *srce,NATIVE count) +{ + SLONG x1,x2,x3,x4; + int remain; + + remain=count&3; + for(count>>=2;count;count--) { + EXTRACT_SAMPLE(x1,16); EXTRACT_SAMPLE(x2,16); + EXTRACT_SAMPLE(x3,16); EXTRACT_SAMPLE(x4,16); + + CHECK_SAMPLE(x1,32768); CHECK_SAMPLE(x2,32768); + CHECK_SAMPLE(x3,32768); CHECK_SAMPLE(x4,32768); + + PUT_SAMPLE(x1); PUT_SAMPLE(x2); PUT_SAMPLE(x3); PUT_SAMPLE(x4); + } + while(remain--) { + EXTRACT_SAMPLE(x1,16); + CHECK_SAMPLE(x1,32768); + PUT_SAMPLE(x1); + } +} + +static void Mix32To8(SBYTE* dste,const SLONG *srce,NATIVE count) +{ + SWORD x1,x2,x3,x4; + int remain; + + remain=count&3; + for(count>>=2;count;count--) { + EXTRACT_SAMPLE(x1,8); EXTRACT_SAMPLE(x2,8); + EXTRACT_SAMPLE(x3,8); EXTRACT_SAMPLE(x4,8); + + CHECK_SAMPLE(x1,128); CHECK_SAMPLE(x2,128); + CHECK_SAMPLE(x3,128); CHECK_SAMPLE(x4,128); + + PUT_SAMPLE(x1+128); PUT_SAMPLE(x2+128); + PUT_SAMPLE(x3+128); PUT_SAMPLE(x4+128); + } + while(remain--) { + EXTRACT_SAMPLE(x1,8); + CHECK_SAMPLE(x1,128); + PUT_SAMPLE(x1+128); + } +} + +#if defined HAVE_ALTIVEC || defined HAVE_SSE2 + +/* Mix 32bit input to floating point. 32 samples per iteration */ +/* PC: ?, Mac OK */ +static void Mix32ToFP_SIMD(float* dste,const SLONG* srce,NATIVE count) +{ + const float k = ((1.0f / 32768.0f) / (1 << FP_SHIFT)); + int remain=count; + simd_m128 x1, x2, xk; + + while(!IS_ALIGNED_16(dste) || !IS_ALIGNED_16(srce)) + { + float xf; + EXTRACT_SAMPLE_FP(xf,FP_SHIFT); + CHECK_SAMPLE_FP(xf,1.0f); + PUT_SAMPLE_FP(xf); + count--; + if (!count) return; + } + + remain = count&7; + + xk = LOAD_PS1_SIMD(&k); /* Scale factor */ + + for(count>>=3;count;count--) { + EXTRACT_SAMPLE_SIMD_F(srce, x1, FP_SHIFT, xk); /* Load 4 samples */ + EXTRACT_SAMPLE_SIMD_F(srce+4, x2, FP_SHIFT, xk); /* Load 4 samples */ + PUT_SAMPLE_SIMD_F(dste, x1); /* Store 4 samples */ + PUT_SAMPLE_SIMD_F(dste+4, x2); /* Store 4 samples */ + srce+=8; + dste+=8; + } + + if (remain&4) { + EXTRACT_SAMPLE_SIMD_F(srce, x1, FP_SHIFT, xk); /* Load 4 samples */ + PUT_SAMPLE_SIMD_F(dste, x1); /* Store 4 samples */ + srce+=4; + dste+=4; + remain &= 3; + } + + while(remain--) { + float xf; + EXTRACT_SAMPLE_FP(xf,FP_SHIFT); + CHECK_SAMPLE_FP(xf,1.0f); + PUT_SAMPLE_FP(xf); + } +} + +/* PC: Ok, Mac Ok */ +static void Mix32To16_SIMD(SWORD* dste,const SLONG* srce,NATIVE count) +{ + int remain = count; + + while(!IS_ALIGNED_16(dste) || !IS_ALIGNED_16(srce)) + { + SLONG x1; + EXTRACT_SAMPLE(x1,16); + CHECK_SAMPLE(x1,32768); + PUT_SAMPLE(x1); + count--; + if (!count) return; + } + + remain = count&7; + + for(count>>=3;count;count--) + { + simd_m128i x1,x2; + EXTRACT_SAMPLE_SIMD_16(srce, x1); /* Load 4 samples */ + EXTRACT_SAMPLE_SIMD_16(srce+4, x2); /* Load 4 samples */ + PUT_SAMPLE_SIMD_W(dste, x1, x2); /* Store 8 samples */ + srce+=8; + dste+=8; + } + + if (remain) + Mix32To16(dste, srce, remain); +} + +/* Mix 32bit input to 8bit. 128 samples per iteration */ +/* PC:OK, Mac: Ok */ +static void Mix32To8_SIMD(SBYTE* dste,const SLONG* srce,NATIVE count) +{ + int remain=count; + + while(!IS_ALIGNED_16(dste) || !IS_ALIGNED_16(srce)) + { + SWORD x1; + EXTRACT_SAMPLE(x1,8); + CHECK_SAMPLE(x1,128); + PUT_SAMPLE(x1+128); + count--; + if (!count) return; + } + + remain = count&15; + + for(count>>=4;count;count--) { + simd_m128i x1,x2,x3,x4; + EXTRACT_SAMPLE_SIMD_8(srce, x1); /* Load 4 samples */ + EXTRACT_SAMPLE_SIMD_8(srce+4, x2); /* Load 4 samples */ + EXTRACT_SAMPLE_SIMD_8(srce+8, x3); /* Load 4 samples */ + EXTRACT_SAMPLE_SIMD_8(srce+12, x4); /* Load 4 samples */ + PUT_SAMPLE_SIMD_B(dste, x1, x2, x3, x4); /* Store 16 samples */ + srce+=16; + dste+=16; + } + + if (remain) + Mix32To8(dste, srce, remain); +} + +#endif + + +static void AddChannel(SLONG* ptr,NATIVE todo) +{ + SLONGLONG end,done; + SWORD *s; + + if(!(s=Samples[vnf->handle])) { + vnf->current = vnf->active = 0; + return; + } + + /* update the 'current' index so the sample loops, or stops playing if it + reached the end of the sample */ + while(todo>0) { + SLONGLONG endpos; + + if(vnf->flags & SF_REVERSE) { + /* The sample is playing in reverse */ + if((vnf->flags&SF_LOOP)&&(vnf->currentflags & SF_BIDI) { + /* sample is doing bidirectional loops, so 'bounce' the + current index against the idxlpos */ + vnf->current = idxlpos+(idxlpos-vnf->current); + vnf->flags &= ~SF_REVERSE; + vnf->increment = -vnf->increment; + } else + /* normal backwards looping, so set the current position to + loopend index */ + vnf->current=idxlend-(idxlpos-vnf->current); + } else { + /* the sample is not looping, so check if it reached index 0 */ + if(vnf->current < 0) { + /* playing index reached 0, so stop playing this sample */ + vnf->current = vnf->active = 0; + break; + } + } + } else { + /* The sample is playing forward */ + if((vnf->flags & SF_LOOP) && + (vnf->current >= idxlend)) { + /* the sample is looping, check the loopend index */ + if(vnf->flags & SF_BIDI) { + /* sample is doing bidirectional loops, so 'bounce' the + current index against the idxlend */ + vnf->flags |= SF_REVERSE; + vnf->increment = -vnf->increment; + vnf->current = idxlend-(vnf->current-idxlend); + } else + /* normal backwards looping, so set the current position + to loopend index */ + vnf->current=idxlpos+(vnf->current-idxlend); + } else { + /* sample is not looping, so check if it reached the last + position */ + if(vnf->current >= idxsize) { + /* yes, so stop playing this sample */ + vnf->current = vnf->active = 0; + break; + } + } + } + + end=(vnf->flags&SF_REVERSE)?(vnf->flags&SF_LOOP)?idxlpos:0: + (vnf->flags&SF_LOOP)?idxlend:idxsize; + + /* if the sample is not blocked... */ + if((end==vnf->current)||(!vnf->increment)) + done=0; + else { + done=MIN((end-vnf->current)/vnf->increment+1,todo); + if(done<0) done=0; + } + + if(!done) { + vnf->active = 0; + break; + } + + endpos=vnf->current+done*vnf->increment; + + if(vnf->vol) { +#ifndef NATIVE_64BIT_INT + /* use the 32 bit mixers as often as we can (they're much faster) */ + if((vnf->current<0x7fffffff)&&(endpos<0x7fffffff)) { + if((md_mode & DMODE_INTERP)) { + if(vc_mode & DMODE_STEREO) { + if((vnf->pan==PAN_SURROUND)&&(md_mode&DMODE_SURROUND)) + vnf->current=Mix32SurroundInterp + (s,ptr,vnf->current,vnf->increment,done); + else + vnf->current=Mix32StereoInterp + (s,ptr,vnf->current,vnf->increment,done); + } else + vnf->current=Mix32MonoInterp + (s,ptr,vnf->current,vnf->increment,done); + } else if(vc_mode & DMODE_STEREO) { + if((vnf->pan==PAN_SURROUND)&&(md_mode&DMODE_SURROUND)) + vnf->current=Mix32SurroundNormal + (s,ptr,vnf->current,vnf->increment,done); + else + { +#if defined HAVE_ALTIVEC || defined HAVE_SSE2 + if (md_mode & DMODE_SIMDMIXER) + vnf->current=MixSIMDStereoNormal + (s,ptr,vnf->current,vnf->increment,done); + else +#endif + vnf->current=Mix32StereoNormal + (s,ptr,vnf->current,vnf->increment,done); + } + } else + vnf->current=Mix32MonoNormal + (s,ptr,vnf->current,vnf->increment,done); + } + else +#endif + { + if((md_mode & DMODE_INTERP)) { + if(vc_mode & DMODE_STEREO) { + if((vnf->pan==PAN_SURROUND)&&(md_mode&DMODE_SURROUND)) + vnf->current=MixSurroundInterp + (s,ptr,vnf->current,vnf->increment,done); + else + vnf->current=MixStereoInterp + (s,ptr,vnf->current,vnf->increment,done); + } else + vnf->current=MixMonoInterp + (s,ptr,vnf->current,vnf->increment,done); + } else if(vc_mode & DMODE_STEREO) { + if((vnf->pan==PAN_SURROUND)&&(md_mode&DMODE_SURROUND)) + vnf->current=MixSurroundNormal + (s,ptr,vnf->current,vnf->increment,done); + else + { +#if defined HAVE_ALTIVEC || defined HAVE_SSE2 + if (md_mode & DMODE_SIMDMIXER) + vnf->current=MixSIMDStereoNormal + (s,ptr,vnf->current,vnf->increment,done); + else +#endif + vnf->current=MixStereoNormal + (s,ptr,vnf->current,vnf->increment,done); + } + } else + vnf->current=MixMonoNormal + (s,ptr,vnf->current,vnf->increment,done); + } + } else + /* update sample position */ + vnf->current=endpos; + + todo-=done; + ptr +=(vc_mode & DMODE_STEREO)?(done<<1):done; + } +} + +#ifdef NO_HQMIXER +#define VC_SetupPointers() do{}while(0) +#define VC1_Init VC_Init +#define VC1_Exit VC_Exit +#define VC1_PlayStart VC_PlayStart +#define VC1_PlayStop VC_PlayStop +#define VC1_SampleLength VC_SampleLength +#define VC1_SampleSpace VC_SampleSpace +#define VC1_SampleLoad VC_SampleLoad +#define VC1_SampleUnload VC_SampleUnload +#define VC1_SetNumVoices VC_SetNumVoices +#define VC1_SilenceBytes VC_SilenceBytes +#define VC1_VoicePlay VC_VoicePlay +#define VC1_VoiceStop VC_VoiceStop +#define VC1_VoiceGetFrequency VC_VoiceGetFrequency +#define VC1_VoiceGetPanning VC_VoiceGetPanning +#define VC1_VoiceGetPosition VC_VoiceGetPosition +#define VC1_VoiceGetVolume VC_VoiceGetVolume +#define VC1_VoiceRealVolume VC_VoiceRealVolume +#define VC1_VoiceSetFrequency VC_VoiceSetFrequency +#define VC1_VoiceSetPanning VC_VoiceSetPanning +#define VC1_VoiceSetVolume VC_VoiceSetVolume +#define VC1_VoiceStopped VC_VoiceStopped +#define VC1_WriteBytes VC_WriteBytes +#define VC1_WriteSamples VC_WriteSamples +#endif + +#define _IN_VIRTCH_ +#include "virtch_common.c" +#undef _IN_VIRTCH_ + +void VC1_WriteSamples(SBYTE* buf,ULONG todo) +{ + int left,portion=0,count; + SBYTE *buffer; + int t, pan, vol; + + while(todo) { + if(!tickleft) { + if(vc_mode & DMODE_SOFT_MUSIC) md_player(); + tickleft=(md_mixfreq*125L)/(md_bpm*50L); + } + left = MIN(tickleft, todo); + buffer = buf; + tickleft -= left; + todo -= left; + buf += samples2bytes(left); + + while(left) { + portion = MIN(left, samplesthatfit); + count = (vc_mode & DMODE_STEREO)?(portion<<1):portion; + memset(vc_tickbuf, 0, count<<2); + for(t=0;tkick) { + vnf->current=((SLONGLONG)vnf->start)<kick =0; + vnf->active =1; + } + + if(!vnf->frq) vnf->active = 0; + + if(vnf->active) { + vnf->increment=((SLONGLONG)(vnf->frq<flags&SF_REVERSE) vnf->increment=-vnf->increment; + vol = vnf->vol; pan = vnf->pan; + + vnf->oldlvol=vnf->lvolsel;vnf->oldrvol=vnf->rvolsel; + if(vc_mode & DMODE_STEREO) { + if(pan != PAN_SURROUND) { + vnf->lvolsel=(vol*(PAN_RIGHT-pan))>>8; + vnf->rvolsel=(vol*pan)>>8; + } else + vnf->lvolsel=vnf->rvolsel=vol/2; + } else + vnf->lvolsel=vol; + + idxsize = (vnf->size)? ((SLONGLONG)vnf->size << FRACBITS)-1 : 0; + idxlend = (vnf->repend)? ((SLONGLONG)vnf->repend << FRACBITS)-1 : 0; + idxlpos = (SLONGLONG)vnf->reppos << FRACBITS; + AddChannel(vc_tickbuf, portion); + } + } + + if(md_mode & DMODE_NOISEREDUCTION) { + MixLowPass(vc_tickbuf, portion); + } + + if(md_reverb) { + if(md_reverb>15) md_reverb=15; + MixReverb(vc_tickbuf, portion); + } + + if (vc_callback) { + vc_callback((unsigned char*)vc_tickbuf, portion); + } + +#if defined HAVE_ALTIVEC || defined HAVE_SSE2 + if (md_mode & DMODE_SIMDMIXER) + { + if(vc_mode & DMODE_FLOAT) + Mix32ToFP_SIMD((float*) buffer, vc_tickbuf, count); + else if(vc_mode & DMODE_16BITS) + Mix32To16_SIMD((SWORD*) buffer, vc_tickbuf, count); + else + Mix32To8_SIMD((SBYTE*) buffer, vc_tickbuf, count); + } + else +#endif + { + if(vc_mode & DMODE_FLOAT) + Mix32ToFP((float*) buffer, vc_tickbuf, count); + else if(vc_mode & DMODE_16BITS) + Mix32To16((SWORD*) buffer, vc_tickbuf, count); + else + Mix32To8((SBYTE*) buffer, vc_tickbuf, count); + } + buffer += samples2bytes(portion); + left -= portion; + } + } +} + +int VC1_Init(void) +{ +#ifndef NO_HQMIXER + VC_SetupPointers(); + + if (md_mode&DMODE_HQMIXER) + return VC2_Init(); +#endif + + if(!(Samples=(SWORD**)MikMod_amalloc(MAXSAMPLEHANDLES*sizeof(SWORD*)))) { + _mm_errno = MMERR_INITIALIZING_MIXER; + return 1; + } + if(!vc_tickbuf) { + if(!(vc_tickbuf=(SLONG*)MikMod_amalloc((TICKLSIZE+32)*sizeof(SLONG)))) { + _mm_errno = MMERR_INITIALIZING_MIXER; + return 1; + } + } + + MixReverb=(md_mode&DMODE_STEREO)?MixReverb_Stereo:MixReverb_Normal; + MixLowPass=(md_mode&DMODE_STEREO)?MixLowPass_Stereo:MixLowPass_Normal; + vc_mode = md_mode; + return 0; +} + +int VC1_PlayStart(void) +{ + samplesthatfit=TICKLSIZE; + if(vc_mode & DMODE_STEREO) samplesthatfit >>= 1; + tickleft = 0; + + RVc1 = (5000L * md_mixfreq) / REVERBERATION; + RVc2 = (5078L * md_mixfreq) / REVERBERATION; + RVc3 = (5313L * md_mixfreq) / REVERBERATION; + RVc4 = (5703L * md_mixfreq) / REVERBERATION; + RVc5 = (6250L * md_mixfreq) / REVERBERATION; + RVc6 = (6953L * md_mixfreq) / REVERBERATION; + RVc7 = (7813L * md_mixfreq) / REVERBERATION; + RVc8 = (8828L * md_mixfreq) / REVERBERATION; + + if(!(RVbufL1=(SLONG*)MikMod_calloc((RVc1+1),sizeof(SLONG)))) return 1; + if(!(RVbufL2=(SLONG*)MikMod_calloc((RVc2+1),sizeof(SLONG)))) return 1; + if(!(RVbufL3=(SLONG*)MikMod_calloc((RVc3+1),sizeof(SLONG)))) return 1; + if(!(RVbufL4=(SLONG*)MikMod_calloc((RVc4+1),sizeof(SLONG)))) return 1; + if(!(RVbufL5=(SLONG*)MikMod_calloc((RVc5+1),sizeof(SLONG)))) return 1; + if(!(RVbufL6=(SLONG*)MikMod_calloc((RVc6+1),sizeof(SLONG)))) return 1; + if(!(RVbufL7=(SLONG*)MikMod_calloc((RVc7+1),sizeof(SLONG)))) return 1; + if(!(RVbufL8=(SLONG*)MikMod_calloc((RVc8+1),sizeof(SLONG)))) return 1; + + /* allocate reverb buffers for the right channel if in stereo mode only. */ + if (vc_mode & DMODE_STEREO) { + if(!(RVbufR1=(SLONG*)MikMod_calloc((RVc1+1),sizeof(SLONG)))) return 1; + if(!(RVbufR2=(SLONG*)MikMod_calloc((RVc2+1),sizeof(SLONG)))) return 1; + if(!(RVbufR3=(SLONG*)MikMod_calloc((RVc3+1),sizeof(SLONG)))) return 1; + if(!(RVbufR4=(SLONG*)MikMod_calloc((RVc4+1),sizeof(SLONG)))) return 1; + if(!(RVbufR5=(SLONG*)MikMod_calloc((RVc5+1),sizeof(SLONG)))) return 1; + if(!(RVbufR6=(SLONG*)MikMod_calloc((RVc6+1),sizeof(SLONG)))) return 1; + if(!(RVbufR7=(SLONG*)MikMod_calloc((RVc7+1),sizeof(SLONG)))) return 1; + if(!(RVbufR8=(SLONG*)MikMod_calloc((RVc8+1),sizeof(SLONG)))) return 1; + } + + RVRindex = 0; + return 0; +} + +void VC1_PlayStop(void) +{ + MikMod_free(RVbufL1); + MikMod_free(RVbufL2); + MikMod_free(RVbufL3); + MikMod_free(RVbufL4); + MikMod_free(RVbufL5); + MikMod_free(RVbufL6); + MikMod_free(RVbufL7); + MikMod_free(RVbufL8); + RVbufL1=RVbufL2=RVbufL3=RVbufL4=RVbufL5=RVbufL6=RVbufL7=RVbufL8=NULL; + MikMod_free(RVbufR1); + MikMod_free(RVbufR2); + MikMod_free(RVbufR3); + MikMod_free(RVbufR4); + MikMod_free(RVbufR5); + MikMod_free(RVbufR6); + MikMod_free(RVbufR7); + MikMod_free(RVbufR8); + RVbufR1=RVbufR2=RVbufR3=RVbufR4=RVbufR5=RVbufR6=RVbufR7=RVbufR8=NULL; +} + +int VC1_SetNumVoices(void) +{ + int t; + + if(!(vc_softchn=md_softchn)) return 0; + + MikMod_free(vinf); + if(!(vinf=(VINFO*)MikMod_calloc(vc_softchn,sizeof(VINFO)))) return 1; + + for(t=0;t +#endif +#include + +#include "mikmod_internals.h" + +/* + Constant Definitions + ==================== + + MAXVOL_FACTOR (was BITSHIFT in virtch.c) + Controls the maximum volume of the output data. All mixed data is + divided by this number after mixing, so larger numbers result in + quieter mixing. Smaller numbers will increase the likeliness of + distortion on loud modules. + + REVERBERATION + Larger numbers result in shorter reverb duration. Longer reverb + durations can cause unwanted static and make the reverb sound more + like a crappy echo. + + SAMPLING_SHIFT + Specified the shift multiplier which controls by how much the mixing + rate is multiplied while mixing. Higher values can improve quality by + smoothing the sound and reducing pops and clicks. Note, this is a shift + value, so a value of 2 becomes a mixing-rate multiplier of 4, and a + value of 3 = 8, etc. + + FRACBITS + The number of bits per integer devoted to the fractional part of the + number. Generally, this number should not be changed for any reason. + + !!! IMPORTANT !!! All values below MUST ALWAYS be greater than 0 + +*/ + +#define BITSHIFT 9 +#define MAXVOL_FACTOR (1< sample has to be restarted */ + UBYTE active; /* =1 -> sample is playing */ + UWORD flags; /* 16/8 bits looping/one-shot */ + SWORD handle; /* identifies the sample */ + ULONG start; /* start index */ + ULONG size; /* samplesize */ + ULONG reppos; /* loop start */ + ULONG repend; /* loop end */ + ULONG frq; /* current frequency */ + int vol; /* current volume */ + int pan; /* current panning position */ + + int click; + int rampvol; + SLONG lastvalL,lastvalR; + int lvolsel,rvolsel; /* Volume factor in range 0-255 */ + int oldlvol,oldrvol; + + SLONGLONG current; /* current index in the sample */ + SLONGLONG increment; /* increment value */ +} VINFO; + +static SWORD **Samples; +static VINFO *vinf=NULL,*vnf; +static long tickleft,samplesthatfit,vc_memory=0; +static int vc_softchn; +static SLONGLONG idxsize,idxlpos,idxlend; +static SLONG *vc_tickbuf=NULL; +static UWORD vc_mode; + +#ifdef _MSC_VER +/* Weird bug in compiler */ /* FIXME is this still needed? */ +typedef void (*MikMod_callback_t)(unsigned char *data, size_t len); +#endif + +/* Reverb control variables */ + +static int RVc1, RVc2, RVc3, RVc4, RVc5, RVc6, RVc7, RVc8; +static ULONG RVRindex; + +/* For Mono or Left Channel */ +static SLONG *RVbufL1=NULL,*RVbufL2=NULL,*RVbufL3=NULL,*RVbufL4=NULL, + *RVbufL5=NULL,*RVbufL6=NULL,*RVbufL7=NULL,*RVbufL8=NULL; + +/* For Stereo only (Right Channel) */ +static SLONG *RVbufR1=NULL,*RVbufR2=NULL,*RVbufR3=NULL,*RVbufR4=NULL, + *RVbufR5=NULL,*RVbufR6=NULL,*RVbufR7=NULL,*RVbufR8=NULL; + +#ifdef NATIVE_64BIT_INT +#define NATIVE SLONGLONG +#else +#define NATIVE SLONG +#endif + +/*========== 32 bit sample mixers - only for 32 bit platforms */ +#ifndef NATIVE_64BIT_INT + +static SLONG Mix32MonoNormal(const SWORD* const srce,SLONG* dest,SLONG idx,SLONG increment,SLONG todo) +{ + SWORD sample=0; + SLONG i,f; + + while(todo--) { + i=idx>>FRACBITS,f=idx&FRACMASK; + sample=(SWORD)( (((SLONG)(srce[i]*(FRACMASK+1L-f)) + + ((SLONG)srce[i+1]*f)) >> FRACBITS)); + idx+=increment; + + if(vnf->rampvol) { + *dest++ += (long)( + ( ( (SLONG)(vnf->oldlvol*vnf->rampvol) + + (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) ) * + (SLONG)sample ) >> CLICK_SHIFT ); + vnf->rampvol--; + } else + if(vnf->click) { + *dest++ += (long)( + ( ( ((SLONG)vnf->lvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONG)sample ) + + (vnf->lastvalL*vnf->click) ) >> CLICK_SHIFT ); + vnf->click--; + } else + *dest++ +=vnf->lvolsel*sample; + } + vnf->lastvalL=vnf->lvolsel * sample; + + return idx; +} + +static SLONG Mix32StereoNormal(const SWORD* const srce,SLONG* dest,SLONG idx,SLONG increment,ULONG todo) +{ + SWORD sample=0; + SLONG i,f; + + while(todo--) { + i=idx>>FRACBITS,f=idx&FRACMASK; + sample=(SWORD)(((((SLONG)srce[i]*(FRACMASK+1L-f)) + + ((SLONG)srce[i+1] * f)) >> FRACBITS)); + idx += increment; + + if(vnf->rampvol) { + *dest++ += (long)( + ( ( ((SLONG)vnf->oldlvol*vnf->rampvol) + + (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) + ) * (SLONG)sample ) >> CLICK_SHIFT ); + *dest++ += (long)( + ( ( ((SLONG)vnf->oldrvol*vnf->rampvol) + + (vnf->rvolsel*(CLICK_BUFFER-vnf->rampvol)) + ) * (SLONG)sample ) >> CLICK_SHIFT ); + vnf->rampvol--; + } else + if(vnf->click) { + *dest++ += (long)( + ( ( (SLONG)(vnf->lvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONG)sample ) + (vnf->lastvalL * vnf->click) ) + >> CLICK_SHIFT ); + *dest++ += (long)( + ( ( ((SLONG)vnf->rvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONG)sample ) + (vnf->lastvalR * vnf->click) ) + >> CLICK_SHIFT ); + vnf->click--; + } else { + *dest++ +=vnf->lvolsel*sample; + *dest++ +=vnf->rvolsel*sample; + } + } + vnf->lastvalL=vnf->lvolsel*sample; + vnf->lastvalR=vnf->rvolsel*sample; + + return idx; +} + +static SLONG Mix32StereoSurround(const SWORD* const srce,SLONG* dest,SLONG idx,SLONG increment,ULONG todo) +{ + SWORD sample=0; + long whoop; + SLONG i, f; + + while(todo--) { + i=idx>>FRACBITS,f=idx&FRACMASK; + sample=(SWORD)(((((SLONG)srce[i]*(FRACMASK+1L-f)) + + ((SLONG)srce[i+1]*f)) >> FRACBITS)); + idx+=increment; + + if(vnf->rampvol) { + whoop=(long)( + ( ( (SLONG)(vnf->oldlvol*vnf->rampvol) + + (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) ) * + (SLONG)sample) >> CLICK_SHIFT ); + *dest++ +=whoop; + *dest++ -=whoop; + vnf->rampvol--; + } else + if(vnf->click) { + whoop = (long)( + ( ( ((SLONG)vnf->lvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONG)sample) + + (vnf->lastvalL * vnf->click) ) >> CLICK_SHIFT ); + *dest++ +=whoop; + *dest++ -=whoop; + vnf->click--; + } else { + *dest++ +=vnf->lvolsel*sample; + *dest++ -=vnf->lvolsel*sample; + } + } + vnf->lastvalL=vnf->lvolsel*sample; + vnf->lastvalR=vnf->lvolsel*sample; + + return idx; +} +#endif + +/*========== 64 bit mixers */ + +static SLONGLONG MixMonoNormal(const SWORD* const srce,SLONG* dest,SLONGLONG idx,SLONGLONG increment,SLONG todo) +{ + SWORD sample=0; + SLONGLONG i,f; + + while(todo--) { + i=idx>>FRACBITS,f=idx&FRACMASK; + sample=(SWORD)((((SLONGLONG)(srce[i]*(FRACMASK+1L-f)) + + ((SLONGLONG)srce[i+1]*f)) >> FRACBITS)); + idx+=increment; + + if(vnf->rampvol) { + *dest++ += (long)( + ( ( (SLONGLONG)(vnf->oldlvol*vnf->rampvol) + + (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) ) * + (SLONGLONG)sample ) >> CLICK_SHIFT ); + vnf->rampvol--; + } else + if(vnf->click) { + *dest++ += (long)( + ( ( ((SLONGLONG)vnf->lvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONGLONG)sample ) + + (vnf->lastvalL*vnf->click) ) >> CLICK_SHIFT ); + vnf->click--; + } else + *dest++ +=vnf->lvolsel*sample; + } + vnf->lastvalL=vnf->lvolsel * sample; + + return idx; +} + +/* Slowest part... */ + +#if defined HAVE_SSE2 || defined HAVE_ALTIVEC + +static __inline SWORD GetSample(const SWORD* const srce, SLONGLONG idx) +{ + SLONGLONG i=idx>>FRACBITS; + SLONGLONG f=idx&FRACMASK; + return (SWORD)(((((SLONGLONG)srce[i]*(FRACMASK+1L-f)) + + ((SLONGLONG)srce[i+1] * f)) >> FRACBITS)); +} + +static SLONGLONG MixSIMDStereoNormal(const SWORD* const srce,SLONG* dest,SLONGLONG idx,SLONGLONG increment,ULONG todo) +{ + SWORD vol[8] = {vnf->lvolsel, vnf->rvolsel}; + SWORD sample=0; + SLONG remain = todo; + + /* Dest can be misaligned */ + while(!IS_ALIGNED_16(dest)) { + sample=srce[idx >> FRACBITS]; + idx += increment; + *dest++ += vol[0] * sample; + *dest++ += vol[1] * sample; + todo--; + if(!todo) goto end; + } + + /* Srce is always aligned */ + +#if defined HAVE_SSE2 + remain = todo&3; + { + __m128i v0 = _mm_set_epi16(0, vol[1], + 0, vol[0], + 0, vol[1], + 0, vol[0]); + for(todo>>=2;todo; todo--) + { + SWORD s0 = GetSample(srce, idx); + SWORD s1 = GetSample(srce, idx += increment); + SWORD s2 = GetSample(srce, idx += increment); + SWORD s3 = GetSample(srce, idx += increment); + __m128i v1 = _mm_set_epi16(0, s1, 0, s1, 0, s0, 0, s0); + __m128i v2 = _mm_set_epi16(0, s3, 0, s3, 0, s2, 0, s2); + __m128i v3 = _mm_load_si128((__m128i*)(dest+0)); + __m128i v4 = _mm_load_si128((__m128i*)(dest+4)); + _mm_store_si128((__m128i*)(dest+0), _mm_add_epi32(v3, _mm_madd_epi16(v0, v1))); + _mm_store_si128((__m128i*)(dest+4), _mm_add_epi32(v4, _mm_madd_epi16(v0, v2))); + dest+=8; + idx += increment; + } + } + +#elif defined HAVE_ALTIVEC + remain = todo&3; + { + SWORD s[8]; + vector signed short r0 = vec_ld(0, vol); + vector signed short v0 = vec_perm(r0, r0, (vector unsigned char)(0, 1, /* l */ + 0, 1, /* l */ + 2, 3, /* r */ + 2, 1, /* r */ + 0, 1, /* l */ + 0, 1, /* l */ + 2, 3, /* r */ + 2, 3 /* r */ + )); + + for(todo>>=2;todo; todo--) + { + vector short int r1; + vector signed short v1, v2; + vector signed int v3, v4, v5, v6; + + /* Load constants */ + s[0] = GetSample(srce, idx); + s[1] = GetSample(srce, idx += increment); + s[2] = GetSample(srce, idx += increment); + s[3] = GetSample(srce, idx += increment); + s[4] = 0; + + r1 = vec_ld(0, s); + v1 = vec_perm(r1, r1, (vector unsigned char) + (0*2, 0*2+1, /* s0 */ + 4*2, 4*2+1, /* 0 */ + 0*2, 0*2+1, /* s0 */ + 4*2, 4*2+1, /* 0 */ + 1*2, 1*2+1, /* s1 */ + 4*2, 4*2+1, /* 0 */ + 1*2, 1*2+1, /* s1 */ + 4*2, 4*2+1 /* 0 */ + ) ); + v2 = vec_perm(r1, r1, (vector unsigned char) + (2*2, 2*2+1, /* s2 */ + 4*2, 4*2+1, /* 0 */ + 2*2, 2*2+1, /* s2 */ + 4*2, 4*2+1, /* 0 */ + 3*2, 3*2+1, /* s3 */ + 4*2, 4*2+1, /* 0 */ + 3*2, 3*2+1, /* s3 */ + 4*2, 4*2+1 /* 0 */ + ) ); + + v3 = vec_ld(0, dest); + v4 = vec_ld(0x10, dest); + v5 = vec_mule(v0, v1); + v6 = vec_mule(v0, v2); + + vec_st(vec_add(v3, v5), 0, dest); + vec_st(vec_add(v4, v6), 0x10, dest); + + dest+=8; + idx += increment; + } + } +#endif /* HAVE_ALTIVEC */ + + /* Remaining bits */ + while(remain--) { + sample=GetSample(srce, idx); + idx+= increment; + *dest++ += vol[0] * sample; + *dest++ += vol[1] * sample; + } +end: + vnf->lastvalL=vnf->lvolsel*sample; + vnf->lastvalR=vnf->rvolsel*sample; + return idx; +} + +static SLONGLONG MixStereoNormal(const SWORD* const srce,SLONG* dest,SLONGLONG idx,SLONGLONG increment,ULONG todo) +{ + SWORD sample=0; + SLONGLONG i,f; + + if (vnf->rampvol) + while(todo) { + todo--; + i=idx>>FRACBITS,f=idx&FRACMASK; + sample=(SWORD)(((((SLONGLONG)srce[i]*(FRACMASK+1L-f)) + + ((SLONGLONG)srce[i+1] * f)) >> FRACBITS)); + idx += increment; + + *dest++ += (long)( + ( ( ((SLONGLONG)vnf->oldlvol*vnf->rampvol) + + (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) + ) * (SLONGLONG)sample ) >> CLICK_SHIFT ); + *dest++ += (long)( + ( ( ((SLONGLONG)vnf->oldrvol*vnf->rampvol) + + (vnf->rvolsel*(CLICK_BUFFER-vnf->rampvol)) + ) * (SLONGLONG)sample ) >> CLICK_SHIFT ); + vnf->rampvol--; + + if (!vnf->rampvol) + break; + } + + if (vnf->click) + while(todo) { + todo--; + i=idx>>FRACBITS,f=idx&FRACMASK; + sample=(SWORD)(((((SLONGLONG)srce[i]*(FRACMASK+1L-f)) + + ((SLONGLONG)srce[i+1] * f)) >> FRACBITS)); + idx += increment; + + *dest++ += (long)( + ( ( (SLONGLONG)(vnf->lvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONGLONG)sample ) + (vnf->lastvalL * vnf->click) ) + >> CLICK_SHIFT ); + + *dest++ += (long)( + ( ( ((SLONGLONG)vnf->rvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONGLONG)sample ) + (vnf->lastvalR * vnf->click) ) + >> CLICK_SHIFT ); + vnf->click--; + + if (!vnf->click) + break; + } + + if (todo) + { + if (md_mode & DMODE_SIMDMIXER) { + return MixSIMDStereoNormal(srce, dest, idx, increment, todo); + } + while(todo) + { + i=idx>>FRACBITS, + f=idx&FRACMASK; + sample=(SWORD)(((((SLONGLONG)srce[i]*(FRACMASK+1L-f)) + + ((SLONGLONG)srce[i+1] * f)) >> FRACBITS)); + idx += increment; + + *dest++ +=vnf->lvolsel*sample; + *dest++ +=vnf->rvolsel*sample; + todo--; + } + } + vnf->lastvalL=vnf->lvolsel*sample; + vnf->lastvalR=vnf->rvolsel*sample; + + return idx; +} + +#else /* HAVE_SSE2 || HAVE_ALTIVEC */ +static SLONGLONG MixStereoNormal(const SWORD* const srce,SLONG* dest,SLONGLONG idx,SLONGLONG increment,ULONG todo) +{ + SWORD sample=0; + SLONGLONG i,f; + + while(todo--) { + i=idx>>FRACBITS,f=idx&FRACMASK; + sample=(SWORD)(((((SLONGLONG)srce[i]*(FRACMASK+1L-f)) + + ((SLONGLONG)srce[i+1] * f)) >> FRACBITS)); + idx += increment; + + if(vnf->rampvol) { + *dest++ += (long)( + ( ( ((SLONGLONG)vnf->oldlvol*vnf->rampvol) + + (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) + ) * (SLONGLONG)sample ) >> CLICK_SHIFT ); + *dest++ += (long)( + ( ( ((SLONGLONG)vnf->oldrvol*vnf->rampvol) + + (vnf->rvolsel*(CLICK_BUFFER-vnf->rampvol)) + ) * (SLONGLONG)sample ) >> CLICK_SHIFT ); + vnf->rampvol--; + } else + if(vnf->click) { + *dest++ += (long)( + ( ( (SLONGLONG)(vnf->lvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONGLONG)sample ) + (vnf->lastvalL * vnf->click) ) + >> CLICK_SHIFT ); + *dest++ += (long)( + ( ( ((SLONGLONG)vnf->rvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONGLONG)sample ) + (vnf->lastvalR * vnf->click) ) + >> CLICK_SHIFT ); + vnf->click--; + } else { + *dest++ +=vnf->lvolsel*sample; + *dest++ +=vnf->rvolsel*sample; + } + } + vnf->lastvalL=vnf->lvolsel*sample; + vnf->lastvalR=vnf->rvolsel*sample; + + return idx; +} +#endif /* HAVE_SSE2 || HAVE_ALTIVEC */ + + +static SLONGLONG MixStereoSurround(const SWORD* srce,SLONG* dest,SLONGLONG idx,SLONGLONG increment,ULONG todo) +{ + SWORD sample=0; + long whoop; + SLONGLONG i, f; + + while(todo--) { + i=idx>>FRACBITS,f=idx&FRACMASK; + sample=(SWORD)(((((SLONGLONG)srce[i]*(FRACMASK+1L-f)) + + ((SLONGLONG)srce[i+1]*f)) >> FRACBITS)); + idx+=increment; + + if(vnf->rampvol) { + whoop=(long)( + ( ( (SLONGLONG)(vnf->oldlvol*vnf->rampvol) + + (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) ) * + (SLONGLONG)sample) >> CLICK_SHIFT ); + *dest++ +=whoop; + *dest++ -=whoop; + vnf->rampvol--; + } else + if(vnf->click) { + whoop = (long)( + ( ( ((SLONGLONG)vnf->lvolsel*(CLICK_BUFFER-vnf->click)) * + (SLONGLONG)sample) + + (vnf->lastvalL * vnf->click) ) >> CLICK_SHIFT ); + *dest++ +=whoop; + *dest++ -=whoop; + vnf->click--; + } else { + *dest++ +=vnf->lvolsel*sample; + *dest++ -=vnf->lvolsel*sample; + } + } + vnf->lastvalL=vnf->lvolsel*sample; + vnf->lastvalR=vnf->lvolsel*sample; + + return idx; +} + +static void(*Mix32toFP)(float* dste,const SLONG *srce,NATIVE count); +static void(*Mix32to16)(SWORD* dste,const SLONG *srce,NATIVE count); +static void(*Mix32to8)(SBYTE* dste,const SLONG *srce,NATIVE count); +static void(*MixReverb)(SLONG *srce,NATIVE count); + +/* Reverb macros */ +#define COMPUTE_LOC(n) loc##n = RVRindex % RVc##n +#define COMPUTE_LECHO(n) RVbufL##n [loc##n ]=speedup+((ReverbPct*RVbufL##n [loc##n ])>>7) +#define COMPUTE_RECHO(n) RVbufR##n [loc##n ]=speedup+((ReverbPct*RVbufR##n [loc##n ])>>7) + +static void MixReverb_Normal(SLONG *srce,NATIVE count) +{ + NATIVE speedup; + int ReverbPct; + unsigned int loc1,loc2,loc3,loc4,loc5,loc6,loc7,loc8; + + ReverbPct=58+(md_reverb*4); + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + while(count--) { + /* Compute the left channel echo buffers */ + speedup = *srce >> 3; + + COMPUTE_LECHO(1); COMPUTE_LECHO(2); COMPUTE_LECHO(3); COMPUTE_LECHO(4); + COMPUTE_LECHO(5); COMPUTE_LECHO(6); COMPUTE_LECHO(7); COMPUTE_LECHO(8); + + /* Prepare to compute actual finalized data */ + RVRindex++; + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + /* left channel */ + *srce++ +=RVbufL1[loc1]-RVbufL2[loc2]+RVbufL3[loc3]-RVbufL4[loc4]+ + RVbufL5[loc5]-RVbufL6[loc6]+RVbufL7[loc7]-RVbufL8[loc8]; + } +} + +static void MixReverb_Stereo(SLONG *srce,NATIVE count) +{ + NATIVE speedup; + int ReverbPct; + unsigned int loc1,loc2,loc3,loc4,loc5,loc6,loc7,loc8; + + ReverbPct=58+(md_reverb*4); + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + while(count--) { + /* Compute the left channel echo buffers */ + speedup = *srce >> 3; + + COMPUTE_LECHO(1); COMPUTE_LECHO(2); COMPUTE_LECHO(3); COMPUTE_LECHO(4); + COMPUTE_LECHO(5); COMPUTE_LECHO(6); COMPUTE_LECHO(7); COMPUTE_LECHO(8); + + /* Compute the right channel echo buffers */ + speedup = srce[1] >> 3; + + COMPUTE_RECHO(1); COMPUTE_RECHO(2); COMPUTE_RECHO(3); COMPUTE_RECHO(4); + COMPUTE_RECHO(5); COMPUTE_RECHO(6); COMPUTE_RECHO(7); COMPUTE_RECHO(8); + + /* Prepare to compute actual finalized data */ + RVRindex++; + + COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4); + COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8); + + /* left channel */ + *srce++ +=RVbufL1[loc1]-RVbufL2[loc2]+RVbufL3[loc3]-RVbufL4[loc4]+ + RVbufL5[loc5]-RVbufL6[loc6]+RVbufL7[loc7]-RVbufL8[loc8]; + + /* right channel */ + *srce++ +=RVbufR1[loc1]-RVbufR2[loc2]+RVbufR3[loc3]-RVbufR4[loc4]+ + RVbufR5[loc5]-RVbufR6[loc6]+RVbufR7[loc7]-RVbufR8[loc8]; + } +} + +static void (*MixLowPass)(SLONG* srce,NATIVE count); + +static int nLeftNR, nRightNR; + +static void MixLowPass_Stereo(SLONG* srce,NATIVE count) +{ + int n1 = nLeftNR, n2 = nRightNR; + SLONG *pnr = srce; + int nr=count; + for (; nr; nr--) + { + int vnr = pnr[0] >> 1; + pnr[0] = vnr + n1; + n1 = vnr; + vnr = pnr[1] >> 1; + pnr[1] = vnr + n2; + n2 = vnr; + pnr += 2; + } + nLeftNR = n1; + nRightNR = n2; +} + +static void MixLowPass_Normal(SLONG* srce,NATIVE count) +{ + int n1 = nLeftNR; + SLONG *pnr = srce; + int nr=count; + for (; nr; nr--) + { + int vnr = pnr[0] >> 1; + pnr[0] = vnr + n1; + n1 = vnr; + pnr ++; + } + nLeftNR = n1; +} + +/* Mixing macros */ +#define EXTRACT_SAMPLE_FP(var,attenuation) var=*srce++*((1.0f / 32768.0f) / (MAXVOL_FACTOR*attenuation)) +#define CHECK_SAMPLE_FP(var,bound) var=(var>bound)?bound:(var<-bound)?-bound:var + +static void Mix32ToFP_Normal(float* dste,const SLONG *srce,NATIVE count) +{ + float x1,x2,tmpx; + int i; + + for(count/=SAMPLING_FACTOR;count;count--) { + tmpx=0.0f; + + for(i=SAMPLING_FACTOR/2;i;i--) { + EXTRACT_SAMPLE_FP(x1,1.0f); EXTRACT_SAMPLE_FP(x2,1.0f); + + CHECK_SAMPLE_FP(x1,1.0f); CHECK_SAMPLE_FP(x2,1.0f); + + tmpx+=x1+x2; + } + *dste++ =tmpx*(1.0f/SAMPLING_FACTOR); + } +} + +static void Mix32ToFP_Stereo(float* dste,const SLONG *srce,NATIVE count) +{ + float x1,x2,x3,x4,tmpx,tmpy; + int i; + + for(count/=SAMPLING_FACTOR;count;count--) { + tmpx=tmpy=0.0f; + + for(i=SAMPLING_FACTOR/2;i;i--) { + EXTRACT_SAMPLE_FP(x1,1.0f); EXTRACT_SAMPLE_FP(x2,1.0f); + EXTRACT_SAMPLE_FP(x3,1.0f); EXTRACT_SAMPLE_FP(x4,1.0f); + + CHECK_SAMPLE_FP(x1,1.0f); CHECK_SAMPLE_FP(x2,1.0f); + CHECK_SAMPLE_FP(x3,1.0f); CHECK_SAMPLE_FP(x4,1.0f); + + tmpx+=x1+x3; + tmpy+=x2+x4; + } + *dste++ =tmpx*(1.0f/SAMPLING_FACTOR); + *dste++ =tmpy*(1.0f/SAMPLING_FACTOR); + } +} + +/* Mixing macros */ +#define EXTRACT_SAMPLE(var,attenuation) var=*srce++/(MAXVOL_FACTOR*attenuation) +#define CHECK_SAMPLE(var,bound) var=(var>=bound)?bound-1:(var<-bound)?-bound:var + +static void Mix32To16_Normal(SWORD* dste,const SLONG *srce,NATIVE count) +{ + NATIVE x1,x2,tmpx; + int i; + + for(count/=SAMPLING_FACTOR;count;count--) { + tmpx=0; + + for(i=SAMPLING_FACTOR/2;i;i--) { + EXTRACT_SAMPLE(x1,1); EXTRACT_SAMPLE(x2,1); + + CHECK_SAMPLE(x1,32768); CHECK_SAMPLE(x2,32768); + + tmpx+=x1+x2; + } + *dste++ =(SWORD)(tmpx/SAMPLING_FACTOR); + } +} + + +static void Mix32To16_Stereo(SWORD* dste,const SLONG *srce,NATIVE count) +{ + NATIVE x1,x2,x3,x4,tmpx,tmpy; + int i; + + for(count/=SAMPLING_FACTOR;count;count--) { + tmpx=tmpy=0; + + for(i=SAMPLING_FACTOR/2;i;i--) { + EXTRACT_SAMPLE(x1,1); EXTRACT_SAMPLE(x2,1); + EXTRACT_SAMPLE(x3,1); EXTRACT_SAMPLE(x4,1); + + CHECK_SAMPLE(x1,32768); CHECK_SAMPLE(x2,32768); + CHECK_SAMPLE(x3,32768); CHECK_SAMPLE(x4,32768); + + tmpx+=x1+x3; + tmpy+=x2+x4; + } + *dste++ =(SWORD)(tmpx/SAMPLING_FACTOR); + *dste++ =(SWORD)(tmpy/SAMPLING_FACTOR); + } +} + +static void Mix32To8_Normal(SBYTE* dste,const SLONG *srce,NATIVE count) +{ + NATIVE x1,x2,tmpx; + int i; + + for(count/=SAMPLING_FACTOR;count;count--) { + tmpx = 0; + + for(i=SAMPLING_FACTOR/2;i;i--) { + EXTRACT_SAMPLE(x1,256); EXTRACT_SAMPLE(x2,256); + + CHECK_SAMPLE(x1,128); CHECK_SAMPLE(x2,128); + + tmpx+=x1+x2; + } + *dste++ = (SBYTE)((tmpx/SAMPLING_FACTOR)+128); + } +} + +static void Mix32To8_Stereo(SBYTE* dste,const SLONG *srce,NATIVE count) +{ + NATIVE x1,x2,x3,x4,tmpx,tmpy; + int i; + + for(count/=SAMPLING_FACTOR;count;count--) { + tmpx=tmpy=0; + + for(i=SAMPLING_FACTOR/2;i;i--) { + EXTRACT_SAMPLE(x1,256); EXTRACT_SAMPLE(x2,256); + EXTRACT_SAMPLE(x3,256); EXTRACT_SAMPLE(x4,256); + + CHECK_SAMPLE(x1,128); CHECK_SAMPLE(x2,128); + CHECK_SAMPLE(x3,128); CHECK_SAMPLE(x4,128); + + tmpx+=x1+x3; + tmpy+=x2+x4; + } + *dste++ =(SBYTE)((tmpx/SAMPLING_FACTOR)+128); + *dste++ =(SBYTE)((tmpy/SAMPLING_FACTOR)+128); + } +} + +#if defined HAVE_SSE2 +#define SHIFT_MIX_TO_16 (BITSHIFT + 16 - 16) +/* TEST: Ok */ +static void Mix32To16_Stereo_SIMD_4Tap(SWORD* dste, const SLONG* srce, NATIVE count) +{ + int remain = count; + + /* Check unaligned dste buffer. srce is always aligned. */ + while(!IS_ALIGNED_16(dste)) + { + Mix32To16_Stereo(dste, srce, SAMPLING_FACTOR); + dste+=2; + srce+=8; + count--; + if(!count) return; + } + + /* dste and srce aligned. srce is always aligned. */ + remain = count & 15; + /* count / 2 for 1 sample */ + + for(count>>=4;count;count--) + { + /* Load 32bit sample. 1st average */ + __m128i v0 = _mm_add_epi32( + _mm_srai_epi32(_mm_loadu_si128((__m128i const *)(srce+0)), SHIFT_MIX_TO_16), + _mm_srai_epi32(_mm_loadu_si128((__m128i const *)(srce+4)), SHIFT_MIX_TO_16) + ); /* v0: s0.l+s2.l | s0.r+s2.r | s1.l+s3.l | s1.r+s3.r */ + + /* 2nd average (s0.l+s2.l+s1.l+s3.l / 4, s0.r+s2.r+s1.r+s3.r / 4). Upper 64bit is unused (1 stereo sample) */ + __m128i v1 = _mm_srai_epi32(_mm_add_epi32(v0, mm_hiqq(v0)), 2); + /* v1: s0.l+s2.l / 4 | s0.r+s2.r / 4 | s1.l+s3.l+s0.l+s2.l / 4 | s1.r+s3.r+s0.r+s2.r / 4 */ + + __m128i v2 = _mm_add_epi32( + _mm_srai_epi32(_mm_loadu_si128((__m128i const *)(srce+8)), SHIFT_MIX_TO_16), + _mm_srai_epi32(_mm_loadu_si128((__m128i const *)(srce+12)), SHIFT_MIX_TO_16) + ); /* v2: s4.l+s6.l | s4.r+s6.r | s5.l+s7.l | s5.r+s7.r */ + + __m128i v3 = _mm_srai_epi32(_mm_add_epi32(v2, mm_hiqq(v2)), 2); /* Upper 64bit is unused */ + /* v3: s4.l+s6.l /4 | s4.r+s6.r / 4| s5.l+s7.l+s4.l+s6.l / 4 | s5.r+s7.r+s4.r+s6.l / 4 */ + + /* pack two stereo samples in one */ + __m128i v4 = _mm_unpacklo_epi64(v1, v3); /* v4 = avg(s0,s1,s2,s3) | avg(s4,s5,s6,s7) */ + + __m128i v6; + + /* Load 32bit sample. 1st average (s0.l+s2.l, s0.r+s2.r, s1.l+s3.l, s1.r+s3.r) */ + v0 = _mm_add_epi32( + _mm_srai_epi32(_mm_loadu_si128((__m128i const *)(srce+16)), SHIFT_MIX_TO_16), + _mm_srai_epi32(_mm_loadu_si128((__m128i const *)(srce+20)), SHIFT_MIX_TO_16) + ); /* 128bit = 2 stereo samples */ + + /* 2nd average (s0.l+s2.l+s1.l+s3.l / 4, s0.r+s2.r+s1.r+s3.r / 4). Upper 64bit is unused (1 stereo sample) */ + v1 = _mm_srai_epi32(_mm_add_epi32(v0, mm_hiqq(v0)), 2); + + v2 = _mm_add_epi32( + _mm_srai_epi32(_mm_loadu_si128((__m128i const *)(srce+24)), SHIFT_MIX_TO_16), + _mm_srai_epi32(_mm_loadu_si128((__m128i const *)(srce+28)), SHIFT_MIX_TO_16) + ); + + v3 = _mm_srai_epi32(_mm_add_epi32(v2, mm_hiqq(v2)), 2); /* Upper 64bit is unused */ + + /* pack two stereo samples in one */ + v6 = _mm_unpacklo_epi64(v1, v3); /* v6 = avg(s8,s9,s10,s11) | avg(s12,s13,s14,s15) */ + + _mm_store_si128((__m128i*)dste, _mm_packs_epi32(v4, v6)); /* 4 interpolated stereo sample 32bit to 4 */ + + dste+=8; + srce+=32; /* 32 = 4 * 8 */ + } + + /* FIXME: THIS PART WRITES PAST DST !! */ + if (remain) + { + Mix32To16_Stereo(dste, srce, remain); + } +} + +#elif defined HAVE_ALTIVEC +#define SHIFT_MIX_TO_16 vec_splat_u32(BITSHIFT + 16 - 16) +/* TEST: Ok */ +static void Mix32To16_Stereo_SIMD_4Tap(SWORD* dste, const SLONG* srce, NATIVE count) +{ + int remain = count; + + /* Check unaligned dste buffer. srce is always aligned. */ + while(!IS_ALIGNED_16(dste)) + { + Mix32To16_Stereo(dste, srce, SAMPLING_FACTOR); + dste+=2; + srce+=8; + count--; + if(!count) return; + } + + /* dste and srce aligned. srce is always aligned. */ + remain = count & 15; + for(count>>=4;count;count--) + { + /* Load 32bit sample. 1st average (s0.l+s2.l, s0.r+s2.r, s1.l+s3.l, s1.r+s3.r) */ + vector signed int v0 = vec_add( + vec_sra(vec_ld(0, srce), SHIFT_MIX_TO_16), /* 128bit = 2 stereo samples */ + vec_sra(vec_ld(0x10, srce), SHIFT_MIX_TO_16) + ); /* 128bit = 2 stereo samples */ + + /* 2nd average (s0.l+s2.l+s1.l+s3.l / 4, s0.r+s2.r+s1.r+s3.r / 4). Upper 64bit is unused (1 stereo sample) */ + vector signed int v1 = vec_sra(vec_add(v0, vec_hiqq(v0)), vec_splat_u32(2)); + + vector signed int v2 = vec_add( + vec_sra(vec_ld(0x20, srce), SHIFT_MIX_TO_16), + vec_sra(vec_ld(0x30, srce), SHIFT_MIX_TO_16) + ); + + vector signed int v3 = vec_sra(vec_add(v2, vec_hiqq(v2)), vec_splat_u32(2)); /* Upper 64bit is unused */ + + /* pack two stereo samples in one */ + vector signed int v6, v4 = vec_unpacklo(v1, v3); /* v4 = lo64(v1) | lo64(v3) */ + + /* Load 32bit sample. 1st average (s0.l+s2.l, s0.r+s2.r, s1.l+s3.l, s1.r+s3.r) */ + v0 = vec_add( + vec_sra(vec_ld(0x40, srce), SHIFT_MIX_TO_16), /* 128bit = 2 stereo samples */ + vec_sra(vec_ld(0x50, srce), SHIFT_MIX_TO_16) + ); /* 128bit = 2 stereo samples */ + + /* 2nd average (s0.l+s2.l+s1.l+s3.l / 4, s0.r+s2.r+s1.r+s3.r / 4). Upper 64bit is unused (1 stereo sample) */ + v1 = vec_sra(vec_add(v0, vec_hiqq(v0)), vec_splat_u32(2)); + + v2 = vec_add( + vec_sra(vec_ld(0x60, srce), SHIFT_MIX_TO_16), + vec_sra(vec_ld(0x70, srce), SHIFT_MIX_TO_16) + ); + + v3 = vec_sra(vec_add(v2, vec_hiqq(v2)), vec_splat_u32(2)); /* Upper 64bit is unused */ + + /* pack two stereo samples in one */ + v6 = vec_unpacklo(v1, v3); + + vec_st(vec_packs(v4, v6), 0, dste); /* 4 interpolated stereo sample 32bit to 4 interpolated stereo sample 16bit + saturation */ + + dste+=8; + srce+=32; /* 32 = 4 * 8 */ + } + + if (remain) + { + Mix32To16_Stereo(dste, srce, remain); + } +} + +#endif + + +static void AddChannel(SLONG* ptr,NATIVE todo) +{ + SLONGLONG end,done; + SWORD *s; + + if(!(s=Samples[vnf->handle])) { + vnf->current = vnf->active = 0; + vnf->lastvalL = vnf->lastvalR = 0; + return; + } + + /* update the 'current' index so the sample loops, or stops playing if it + reached the end of the sample */ + while(todo>0) { + SLONGLONG endpos; + + if(vnf->flags & SF_REVERSE) { + /* The sample is playing in reverse */ + if((vnf->flags&SF_LOOP)&&(vnf->currentflags & SF_BIDI) { + /* sample is doing bidirectional loops, so 'bounce' the + current index against the idxlpos */ + vnf->current = idxlpos+(idxlpos-vnf->current); + vnf->flags &= ~SF_REVERSE; + vnf->increment = -vnf->increment; + } else + /* normal backwards looping, so set the current position to + loopend index */ + vnf->current=idxlend-(idxlpos-vnf->current); + } else { + /* the sample is not looping, so check if it reached index 0 */ + if(vnf->current < 0) { + /* playing index reached 0, so stop playing this sample */ + vnf->current = vnf->active = 0; + break; + } + } + } else { + /* The sample is playing forward */ + if((vnf->flags & SF_LOOP) && + (vnf->current >= idxlend)) { + /* the sample is looping, check the loopend index */ + if(vnf->flags & SF_BIDI) { + /* sample is doing bidirectional loops, so 'bounce' the + current index against the idxlend */ + vnf->flags |= SF_REVERSE; + vnf->increment = -vnf->increment; + vnf->current = idxlend-(vnf->current-idxlend); + } else + /* normal backwards looping, so set the current position + to loopend index */ + vnf->current=idxlpos+(vnf->current-idxlend); + } else { + /* sample is not looping, so check if it reached the last + position */ + if(vnf->current >= idxsize) { + /* yes, so stop playing this sample */ + vnf->current = vnf->active = 0; + break; + } + } + } + + end=(vnf->flags&SF_REVERSE)?(vnf->flags&SF_LOOP)?idxlpos:0: + (vnf->flags&SF_LOOP)?idxlend:idxsize; + + /* if the sample is not blocked... */ + if((end==vnf->current)||(!vnf->increment)) + done=0; + else { + done=MIN((end-vnf->current)/vnf->increment+1,todo); + if(done<0) done=0; + } + + if(!done) { + vnf->active = 0; + break; + } + + endpos=vnf->current+done*vnf->increment; + + if(vnf->vol || vnf->rampvol) { +#ifndef NATIVE_64BIT_INT + /* use the 32 bit mixers as often as we can (they're much faster) */ + if((vnf->current<0x7fffffff)&&(endpos<0x7fffffff)) { + if(vc_mode & DMODE_STEREO) { + if((vnf->pan==PAN_SURROUND)&&(vc_mode&DMODE_SURROUND)) + vnf->current=(SLONGLONG)Mix32StereoSurround + (s,ptr,vnf->current,vnf->increment,done); + else + vnf->current=Mix32StereoNormal + (s,ptr,vnf->current,vnf->increment,done); + } else + vnf->current=Mix32MonoNormal + (s,ptr,vnf->current,vnf->increment,done); + } + else +#endif + { + if(vc_mode & DMODE_STEREO) { + if((vnf->pan==PAN_SURROUND)&&(vc_mode&DMODE_SURROUND)) + vnf->current=MixStereoSurround + (s,ptr,vnf->current,vnf->increment,done); + else + vnf->current=MixStereoNormal + (s,ptr,vnf->current,vnf->increment,done); + } else + vnf->current=MixMonoNormal + (s,ptr,vnf->current,vnf->increment,done); + } + } else { + vnf->lastvalL = vnf->lastvalR = 0; + /* update sample position */ + vnf->current=endpos; + } + + todo -= done; + ptr += (vc_mode & DMODE_STEREO)?(done<<1):done; + } +} + +#define _IN_VIRTCH_ + +#define VC1_SilenceBytes VC2_SilenceBytes +#define VC1_WriteSamples VC2_WriteSamples +#define VC1_WriteBytes VC2_WriteBytes +#define VC1_Exit VC2_Exit +#define VC1_VoiceSetVolume VC2_VoiceSetVolume +#define VC1_VoiceGetVolume VC2_VoiceGetVolume +#define VC1_VoiceSetPanning VC2_VoiceSetPanning +#define VC1_VoiceGetPanning VC2_VoiceGetPanning +#define VC1_VoiceSetFrequency VC2_VoiceSetFrequency +#define VC1_VoiceGetFrequency VC2_VoiceGetFrequency +#define VC1_VoicePlay VC2_VoicePlay +#define VC1_VoiceStop VC2_VoiceStop +#define VC1_VoiceStopped VC2_VoiceStopped +#define VC1_VoiceGetPosition VC2_VoiceGetPosition +#define VC1_SampleUnload VC2_SampleUnload +#define VC1_SampleLoad VC2_SampleLoad +#define VC1_SampleSpace VC2_SampleSpace +#define VC1_SampleLength VC2_SampleLength +#define VC1_VoiceRealVolume VC2_VoiceRealVolume + +#include "virtch_common.c" +#undef _IN_VIRTCH_ + +void VC2_WriteSamples(SBYTE* buf,ULONG todo) +{ + int left,portion=0; + SBYTE *buffer; + int t,pan,vol; + + todo*=SAMPLING_FACTOR; + + while(todo) { + if(!tickleft) { + if(vc_mode & DMODE_SOFT_MUSIC) md_player(); + tickleft=(md_mixfreq*125L*SAMPLING_FACTOR)/(md_bpm*50L); + tickleft&=~(SAMPLING_FACTOR-1); + } + left = MIN(tickleft, todo); + buffer = buf; + tickleft -= left; + todo -= left; + buf += samples2bytes(left)/SAMPLING_FACTOR; + + while(left) { + portion = MIN(left, samplesthatfit); + memset(vc_tickbuf,0,portion<<((vc_mode&DMODE_STEREO)?3:2)); + for(t=0;tkick) { + vnf->current=((SLONGLONG)(vnf->start))<kick = 0; + vnf->active = 1; + vnf->click = CLICK_BUFFER; + vnf->rampvol = 0; + } + + if(!vnf->frq) vnf->active = 0; + + if(vnf->active) { + vnf->increment=((SLONGLONG)(vnf->frq)<<(FRACBITS-SAMPLING_SHIFT)) + /md_mixfreq; + if(vnf->flags&SF_REVERSE) vnf->increment=-vnf->increment; + vol = vnf->vol; pan = vnf->pan; + + vnf->oldlvol=vnf->lvolsel;vnf->oldrvol=vnf->rvolsel; + if(vc_mode & DMODE_STEREO) { + if(pan!=PAN_SURROUND) { + vnf->lvolsel=(vol*(PAN_RIGHT-pan))>>8; + vnf->rvolsel=(vol*pan)>>8; + } else { + vnf->lvolsel=vnf->rvolsel=(vol * 256L) / 480; + } + } else + vnf->lvolsel=vol; + + idxsize=(vnf->size)?((SLONGLONG)(vnf->size)<repend)?((SLONGLONG)(vnf->repend)<reppos)<15) md_reverb=15; + MixReverb(vc_tickbuf,portion); + } + + if (vc_callback) { + vc_callback((unsigned char*)vc_tickbuf, portion); + } + + if(vc_mode & DMODE_FLOAT) + Mix32toFP((float*)buffer,vc_tickbuf,portion); + else if(vc_mode & DMODE_16BITS) + Mix32to16((SWORD*)buffer,vc_tickbuf,portion); + else + Mix32to8((SBYTE*)buffer,vc_tickbuf,portion); + + buffer += samples2bytes(portion) / SAMPLING_FACTOR; + left -= portion; + } + } +} + +int VC2_Init(void) +{ + VC_SetupPointers(); + + if (!(md_mode&DMODE_HQMIXER)) + return VC1_Init(); + + if(!(Samples=(SWORD**)MikMod_amalloc(MAXSAMPLEHANDLES*sizeof(SWORD*)))) { + _mm_errno = MMERR_INITIALIZING_MIXER; + return 1; + } + if(!vc_tickbuf) { + if(!(vc_tickbuf=(SLONG*)MikMod_amalloc((TICKLSIZE+32)*sizeof(SLONG)))) { + _mm_errno = MMERR_INITIALIZING_MIXER; + return 1; + } + } + + if(md_mode & DMODE_STEREO) { + Mix32toFP = Mix32ToFP_Stereo; +#if ((defined HAVE_ALTIVEC || defined HAVE_SSE2) && (SAMPLING_FACTOR == 4)) + if (md_mode & DMODE_SIMDMIXER) + Mix32to16 = Mix32To16_Stereo_SIMD_4Tap; + else +#endif + Mix32to16 = Mix32To16_Stereo; + Mix32to8 = Mix32To8_Stereo; + MixReverb = MixReverb_Stereo; + MixLowPass = MixLowPass_Stereo; + } else { + Mix32toFP = Mix32ToFP_Normal; + Mix32to16 = Mix32To16_Normal; + Mix32to8 = Mix32To8_Normal; + MixReverb = MixReverb_Normal; + MixLowPass = MixLowPass_Normal; + } + + md_mode |= DMODE_INTERP; + vc_mode = md_mode; + return 0; +} + +int VC2_PlayStart(void) +{ + md_mode|=DMODE_INTERP; + + samplesthatfit = TICKLSIZE; + if(vc_mode & DMODE_STEREO) samplesthatfit >>= 1; + tickleft = 0; + + RVc1 = (5000L * md_mixfreq) / (REVERBERATION * 10); + RVc2 = (5078L * md_mixfreq) / (REVERBERATION * 10); + RVc3 = (5313L * md_mixfreq) / (REVERBERATION * 10); + RVc4 = (5703L * md_mixfreq) / (REVERBERATION * 10); + RVc5 = (6250L * md_mixfreq) / (REVERBERATION * 10); + RVc6 = (6953L * md_mixfreq) / (REVERBERATION * 10); + RVc7 = (7813L * md_mixfreq) / (REVERBERATION * 10); + RVc8 = (8828L * md_mixfreq) / (REVERBERATION * 10); + + if(!(RVbufL1=(SLONG*)MikMod_calloc((RVc1+1),sizeof(SLONG)))) return 1; + if(!(RVbufL2=(SLONG*)MikMod_calloc((RVc2+1),sizeof(SLONG)))) return 1; + if(!(RVbufL3=(SLONG*)MikMod_calloc((RVc3+1),sizeof(SLONG)))) return 1; + if(!(RVbufL4=(SLONG*)MikMod_calloc((RVc4+1),sizeof(SLONG)))) return 1; + if(!(RVbufL5=(SLONG*)MikMod_calloc((RVc5+1),sizeof(SLONG)))) return 1; + if(!(RVbufL6=(SLONG*)MikMod_calloc((RVc6+1),sizeof(SLONG)))) return 1; + if(!(RVbufL7=(SLONG*)MikMod_calloc((RVc7+1),sizeof(SLONG)))) return 1; + if(!(RVbufL8=(SLONG*)MikMod_calloc((RVc8+1),sizeof(SLONG)))) return 1; + + /* allocate reverb buffers for the right channel if in stereo mode only. */ + if (vc_mode & DMODE_STEREO) { + if(!(RVbufR1=(SLONG*)MikMod_calloc((RVc1+1),sizeof(SLONG)))) return 1; + if(!(RVbufR2=(SLONG*)MikMod_calloc((RVc2+1),sizeof(SLONG)))) return 1; + if(!(RVbufR3=(SLONG*)MikMod_calloc((RVc3+1),sizeof(SLONG)))) return 1; + if(!(RVbufR4=(SLONG*)MikMod_calloc((RVc4+1),sizeof(SLONG)))) return 1; + if(!(RVbufR5=(SLONG*)MikMod_calloc((RVc5+1),sizeof(SLONG)))) return 1; + if(!(RVbufR6=(SLONG*)MikMod_calloc((RVc6+1),sizeof(SLONG)))) return 1; + if(!(RVbufR7=(SLONG*)MikMod_calloc((RVc7+1),sizeof(SLONG)))) return 1; + if(!(RVbufR8=(SLONG*)MikMod_calloc((RVc8+1),sizeof(SLONG)))) return 1; + } + + RVRindex = 0; + return 0; +} + +void VC2_PlayStop(void) +{ + MikMod_free(RVbufL1); + MikMod_free(RVbufL2); + MikMod_free(RVbufL3); + MikMod_free(RVbufL4); + MikMod_free(RVbufL5); + MikMod_free(RVbufL6); + MikMod_free(RVbufL7); + MikMod_free(RVbufL8); + MikMod_free(RVbufR1); + MikMod_free(RVbufR2); + MikMod_free(RVbufR3); + MikMod_free(RVbufR4); + MikMod_free(RVbufR5); + MikMod_free(RVbufR6); + MikMod_free(RVbufR7); + MikMod_free(RVbufR8); + + RVbufL1=RVbufL2=RVbufL3=RVbufL4=RVbufL5=RVbufL6=RVbufL7=RVbufL8=NULL; + RVbufR1=RVbufR2=RVbufR3=RVbufR4=RVbufR5=RVbufR6=RVbufR7=RVbufR8=NULL; +} + +int VC2_SetNumVoices(void) +{ + int t; + + md_mode|=DMODE_INTERP; + + if(!(vc_softchn=md_softchn)) return 0; + + MikMod_free(vinf); + if(!(vinf=(VINFO*)MikMod_calloc(vc_softchn,sizeof(VINFO)))) return 1; + + for(t=0;t>= 2; + else if(vc_mode & DMODE_16BITS) bytes >>= 1; + if(vc_mode & DMODE_STEREO) bytes >>= 1; + return bytes; +} + +/* Fill the buffer with 'todo' bytes of silence (it depends on the mixing mode + how the buffer is filled) */ +ULONG VC1_SilenceBytes(SBYTE* buf,ULONG todo) +{ + todo=samples2bytes(bytes2samples(todo)); + + /* clear the buffer to zero (16 bits signed) or 0x80 (8 bits unsigned) */ + if(vc_mode &(DMODE_16BITS|DMODE_FLOAT)) + memset(buf,0,todo); + else + memset(buf,0x80,todo); + + return todo; +} + +void VC1_WriteSamples(SBYTE*,ULONG); + +/* Writes 'todo' mixed SBYTES (!!) to 'buf'. It returns the number of SBYTES + actually written to 'buf' (which is rounded to number of samples that fit + into 'todo' bytes). */ +ULONG VC1_WriteBytes(SBYTE* buf,ULONG todo) +{ + if(!vc_softchn) + return VC1_SilenceBytes(buf,todo); + + todo = bytes2samples(todo); + VC1_WriteSamples(buf,todo); + + return samples2bytes(todo); +} + +void VC1_Exit(void) +{ + MikMod_free(vinf); + MikMod_afree(vc_tickbuf); + MikMod_afree(Samples); + + vc_tickbuf = NULL; + vinf = NULL; + Samples = NULL; + + VC_SetupPointers(); +} + +UWORD VC1_VoiceGetVolume(UBYTE voice) +{ + return vinf[voice].vol; +} + +ULONG VC1_VoiceGetPanning(UBYTE voice) +{ + return vinf[voice].pan; +} + +void VC1_VoiceSetFrequency(UBYTE voice,ULONG frq) +{ + vinf[voice].frq=frq; +} + +ULONG VC1_VoiceGetFrequency(UBYTE voice) +{ + return vinf[voice].frq; +} + +void VC1_VoicePlay(UBYTE voice,SWORD handle,ULONG start,ULONG size,ULONG reppos,ULONG repend,UWORD flags) +{ + vinf[voice].flags = flags; + vinf[voice].handle = handle; + vinf[voice].start = start; + vinf[voice].size = size; + vinf[voice].reppos = reppos; + vinf[voice].repend = repend; + vinf[voice].kick = 1; +} + +void VC1_VoiceStop(UBYTE voice) +{ + vinf[voice].active = 0; +} + +BOOL VC1_VoiceStopped(UBYTE voice) +{ + return(vinf[voice].active==0); +} + +SLONG VC1_VoiceGetPosition(UBYTE voice) +{ + return (SLONG)(vinf[voice].current>>FRACBITS); +} + +void VC1_VoiceSetVolume(UBYTE voice,UWORD vol) +{ + /* protect against clicks if volume variation is too high */ + if(abs((int)vinf[voice].vol-(int)vol)>32) + vinf[voice].rampvol=CLICK_BUFFER; + vinf[voice].vol=vol; +} + +void VC1_VoiceSetPanning(UBYTE voice,ULONG pan) +{ + /* protect against clicks if panning variation is too high */ + if(abs((int)vinf[voice].pan-(int)pan)>48) + vinf[voice].rampvol=CLICK_BUFFER; + vinf[voice].pan=pan; +} + +/*========== External mixer interface */ + +void VC1_SampleUnload(SWORD handle) +{ + if (Samples && (handle < MAXSAMPLEHANDLES)) { + MikMod_afree(Samples[handle]); + Samples[handle]=NULL; + } +} + +SWORD VC1_SampleLoad(struct SAMPLOAD* sload,int type) +{ + SAMPLE *s = sload->sample; + int handle; + ULONG t, length,loopstart,loopend,looplen; + + if(type==MD_HARDWARE) return -1; + + /* Find empty slot to put sample address in */ + for(handle=0;handleloopend > s->length) + s->loopend = s->length; + if (s->loopstart >= s->loopend) + s->flags &= ~SF_LOOP; + + length = s->length; + loopstart = s->loopstart; + loopend = s->loopend; + + SL_SampleSigned(sload); + SL_Sample8to16(sload); + + if(!(Samples[handle]=(SWORD*)MikMod_amalloc((length+20)<<1))) { + _mm_errno = MMERR_SAMPLE_TOO_BIG; + return -1; + } + + /* read sample into buffer */ + if (SL_Load(Samples[handle],sload,length)) + return -1; + + /* Unclick sample */ + if(s->flags & SF_LOOP) { + looplen = loopend - loopstart;/* handle short samples */ + if(s->flags & SF_BIDI) + for(t=0;t<16 && tlength*((s->flags&SF_16BITS)?2:1))+16; +} + +ULONG VC1_VoiceRealVolume(UBYTE voice) +{ + ULONG i,s,size; + int k,j; + SWORD *smp; + SLONG t; + + t = (SLONG)(vinf[voice].current>>FRACBITS); + if(!vinf[voice].active) return 0; + + s = vinf[voice].handle; + size = vinf[voice].size; + + i=64; t-=64; k=0; j=0; + if(i>size) i = size; + if(t<0) t = 0; + if(t+i > size) t = size-i; + + i &= ~1; /* make sure it's EVEN. */ + + smp = &Samples[s][t]; + for(;i;i--,smp++) { + if(k<*smp) k = *smp; + if(j>*smp) j = *smp; + } + return abs(k-j); +} + +#endif /* _VIRTCH_COMMON_ */ + +#endif /* _IN_VIRTCH_ */ + +/* ex:set ts=4: */ diff --git a/libs/mikmod/posix/memcmp.c b/libs/mikmod/posix/memcmp.c new file mode 100644 index 0000000..7f41705 --- /dev/null +++ b/libs/mikmod/posix/memcmp.c @@ -0,0 +1,22 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +int memcmp(const void *__s1, const void *__s2, size_t __n) +{ + const char *scan1, *scan2; + size_t n; + + scan1 = __s1; + scan2 = __s2; + for (n = __n; n > 0; n--) + if (*scan1 == *scan2) { + scan1++; + scan2++; + } else + return *scan1 - *scan2; + + return 0; +} diff --git a/libs/mikmod/posix/strcasecmp.c b/libs/mikmod/posix/strcasecmp.c new file mode 100644 index 0000000..5b9935f --- /dev/null +++ b/libs/mikmod/posix/strcasecmp.c @@ -0,0 +1,23 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mikmod_internals.h" +#include "mikmod_ctype.h" + +int _mm_strcasecmp(const char *__s1, const char *__s2) +{ + const char *p1 = __s1; + const char *p2 = __s2; + char c1, c2; + + if (p1 == p2) return 0; + + do { + c1 = mik_tolower(*p1++); + c2 = mik_tolower(*p2++); + if (c1 == '\0') break; + } while (c1 == c2); + + return (int)(c1 - c2); +} diff --git a/libs/mikmod/posix/strstr.c b/libs/mikmod/posix/strstr.c new file mode 100644 index 0000000..82e1fd4 --- /dev/null +++ b/libs/mikmod/posix/strstr.c @@ -0,0 +1,19 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +char *strstr(const char *haystack, const char *needle) +{ + const char *scan; + size_t len; + char firstc; + + firstc = *needle; + len = strlen(needle); + for (scan = haystack; *scan != firstc || strncmp(scan, needle, len); ) + if (!*scan++) + return NULL; + return (char *)scan; +} -- 1.7.10.4