diff --git a/project/jni/timidity/AUTHORS b/project/jni/timidity/AUTHORS new file mode 100644 index 000000000..7db3afa01 --- /dev/null +++ b/project/jni/timidity/AUTHORS @@ -0,0 +1,8 @@ +* Tuukka Toivonen + wrote TiMidity and maintained it until version v0.2i. + +* Torbjörn Andersson + code cleanup and import it to the SDL_sound library as MIDI decoder. + +* Konstantin Korikov + make a separate library from SDL_sound MIDI decoder. diff --git a/project/jni/timidity/Android.mk b/project/jni/timidity/Android.mk new file mode 100644 index 000000000..757f967cb --- /dev/null +++ b/project/jni/timidity/Android.mk @@ -0,0 +1,24 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := timidity + +APP_SUBDIRS := $(patsubst $(LOCAL_PATH)/%, %, $(shell find $(LOCAL_PATH)/src -type d)) + +LOCAL_CFLAGS := -O3 $(foreach D, $(APP_SUBDIRS), -I$(LOCAL_PATH)/$(D)) \ + -I$(LOCAL_PATH)/include -DHAVE_CONFIG_H + + +LOCAL_CPP_EXTENSION := .cpp + +LOCAL_SRC_FILES := $(foreach F, $(APP_SUBDIRS), $(addprefix $(F)/,$(notdir $(wildcard $(LOCAL_PATH)/$(F)/*.cpp)))) +LOCAL_SRC_FILES += $(foreach F, $(APP_SUBDIRS), $(addprefix $(F)/,$(notdir $(wildcard $(LOCAL_PATH)/$(F)/*.c)))) + +LOCAL_SHARED_LIBRARIES := + +LOCAL_STATIC_LIBRARIES := + +LOCAL_LDLIBS := + +include $(BUILD_SHARED_LIBRARY) diff --git a/project/jni/timidity/CHANGES b/project/jni/timidity/CHANGES new file mode 100644 index 000000000..9b3fce39e --- /dev/null +++ b/project/jni/timidity/CHANGES @@ -0,0 +1,39 @@ +This version of TiMidity should contain all the fixes from the +November 15 2004 SDL_sound Subversion snapshot. In addition, I've made some +changes of my own, e.g.: + +* Replacing SDL types and endian-handling with owns. + +* File access to the config file and instruments is done through + stdio functions. File access to MIDI files is done through abstract + input stream. Implemented functions to create input stream from + file name, from stdio file pointer, from memory, from callback functions. + +* Renamed interface functions + Timidity_Init -> mid_init + Timidity_Init_NoConfig -> mid_init_no_config + Timidity_SetVolume -> mid_song_set_volume + Timidity_PlaySome -> mid_song_read_wave + Timidity_LoadDLS -> mid_dlspatches_load + Timidity_FreeDLS -> mid_dlspatches_free + Timidity_LoadDLSSong -> mid_song_load_dls + Timidity_LoadSong -> mid_song_load + Timidity_Start -> mid_song_start + Timidity_Seek -> mid_song_seek + Timidity_GetSongLength -> mid_song_get_total_time + Timidity_FreeSong -> mid_song_free + +* Most structures and macro definition made hidden and placed in + timidity_internal.h. + +* Results of mid_song_read_wave (Timidity_PlaySome) not + depends with internal sample buffer size. + +* mid_init can accept timidity config file name. + +* Added functions: mid_song_get_time, mid_song_get_meta. + +* Added examples/tests midi2raw.c and playmidi.c. + +-- +Konstantin Korikov diff --git a/project/jni/timidity/COPYING b/project/jni/timidity/COPYING new file mode 100644 index 000000000..10abb20c3 --- /dev/null +++ b/project/jni/timidity/COPYING @@ -0,0 +1,513 @@ +Please note that the included source from TiMidity, is also licensed + under the following terms (GNU LGPL), but can also be used separately + under the GNU GPL, or the Perl Artistic License. Those licensing + terms are not reprinted here, but can be found on the web easily. + + +------------------- + + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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/project/jni/timidity/ChangeLog b/project/jni/timidity/ChangeLog new file mode 100644 index 000000000..c513f878c --- /dev/null +++ b/project/jni/timidity/ChangeLog @@ -0,0 +1,53 @@ +2004-11-23 Konstantin Korikov + + * configure.ac: Changed version to 0.1.0. + + * NEWS: NEWS updated. + + * libtimidity.spec.in: Spec updated. + +2004-11-22 Konstantin Korikov + + * src/timidity.h.in, tests/midi2raw.c, tests/playmidi.c, + src/readmidi.c: Replaced MID_SONG_TITLE to MID_SONG_TEXT. + + * README: README updated. + + * tests/playmidi.c: Added ao_close() call. + + * src/playmidi.c: Fixed mid_song_seek, seeking to high positions. + + * configure.ac, libtimidity.pc.in, libtimidity.spec.in, + Makefile.am: Added pkg-config metadata file. + +2004-11-21 Konstantin Korikov + + * libtimidity.spec.in: Call ldconfig in post and postun scriplets. + + * CHANGES, COPYING, ChangeLog, src/Makefile.am, src/common.c, + src/common.h, src/dls1.h, src/dls2.h, src/instrum.c, + src/instrum.h, src/instrum_dls.c, src/instrum_dls.h, + src/mix.c, src/mix.h, src/options.h, src/output.c, + src/output.h, src/playmidi.c, src/playmidi.h, src/readmidi.c, + src/readmidi.h, src/resample.c, src/resample.h, src/stream.c, + src/tables.c, src/tables.h, src/timidity.c, src/timidity.h.in, + src/timidity_internal.h, tests/Makefile.am, tests/ame002.mid, + tests/midi2raw.c, tests/playmidi.c, tests/runtest.sh: Imported + sources. + + * CHANGES, COPYING, ChangeLog, src/Makefile.am, src/common.c, + src/common.h, src/dls1.h, src/dls2.h, src/instrum.c, + src/instrum.h, src/instrum_dls.c, src/instrum_dls.h, + src/mix.c, src/mix.h, src/options.h, src/output.c, + src/output.h, src/playmidi.c, src/playmidi.h, src/readmidi.c, + src/readmidi.h, src/resample.c, src/resample.h, src/stream.c, + src/tables.c, src/tables.h, src/timidity.c, src/timidity.h.in, + src/timidity_internal.h, tests/Makefile.am, tests/ame002.mid, + tests/midi2raw.c, tests/playmidi.c, tests/runtest.sh: New file. + + * AUTHORS, INSTALL, Makefile.am, NEWS, README, README.timidity, + TODO, configure.ac, libtimidity.spec.in: Imported sources. + + * AUTHORS, INSTALL, Makefile.am, NEWS, README, README.timidity, + TODO, configure.ac, libtimidity.spec.in: New file. + diff --git a/project/jni/timidity/INSTALL b/project/jni/timidity/INSTALL new file mode 100644 index 000000000..54caf7c19 --- /dev/null +++ b/project/jni/timidity/INSTALL @@ -0,0 +1,229 @@ +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software +Foundation, Inc. + + This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. (Caching is +disabled by default to prevent problems with accidental use of stale +cache files.) + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You only need +`configure.ac' if you want to change it or regenerate `configure' using +a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not support the `VPATH' +variable, you have to compile the package for one architecture at a +time in the source code directory. After you have installed the +package for one architecture, use `make distclean' before reconfiguring +for another architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the `--target=TYPE' option to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +will cause the specified gcc to be used as the C compiler (unless it is +overridden in the site shell script). + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/project/jni/timidity/NEWS b/project/jni/timidity/NEWS new file mode 100644 index 000000000..0ac8cf35c --- /dev/null +++ b/project/jni/timidity/NEWS @@ -0,0 +1,3 @@ +2004-11-23 - 0.1.0 released + + First release. diff --git a/project/jni/timidity/README b/project/jni/timidity/README new file mode 100644 index 000000000..9e4d2f10d --- /dev/null +++ b/project/jni/timidity/README @@ -0,0 +1,12 @@ +libTiMidity -- MIDI to WAVE converter library. + +This library is based on the TiMidity decoder from SDL_sound library. +Purpose to create this library is to avoid unnecessary dependences. +SDL_sound requires SDL and some other libraries, that not needed to +process MIDI files. In addition libTiMidity provides more suitable +API to work with MIDI songs, it enables to specify full path to the +timidity configuration file, and have function to retrieve meta data +from MIDI song. + +If you have propositions regarding development of this library please +mail to me diff --git a/project/jni/timidity/README.timidity b/project/jni/timidity/README.timidity new file mode 100644 index 000000000..266ce9367 --- /dev/null +++ b/project/jni/timidity/README.timidity @@ -0,0 +1,85 @@ +---------------------------------*-text-*--------------------------------- + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +-------------------------------------------------------------------------- + + This is the README file for TiMidity v0.2i + + TiMidity is a MIDI to WAVE converter that uses Gravis +Ultrasound(*)-compatible patch files to generate digital audio data +from General MIDI files. The audio data can be played through any +sound device or stored on disk. On a fast machine, music can be +played in real time. TiMidity runs under Linux, FreeBSD, HP-UX, SunOS, and +Win32, and porting to other systems with gcc should be easy. + + TiMidity Features: + + * 32 or more dynamically allocated fully independent voices + * Compatibility with GUS patch files + * Output to 16- or 8-bit PCM or uLaw audio device, file, or + stdout at any sampling rate + * Optional interactive mode with real-time status display + under ncurses and SLang terminal control libraries. Also + a user friendly motif interface since version 0.2h + * Support for transparent loading of compressed MIDI files and + patch files + + * Support for the following MIDI events: + - Program change + - Key pressure + - Channel main volume + - Tempo + - Panning + - Damper pedal (Sustain) + - Pitch wheel + - Pitch wheel sensitivity + - Change drum set + +* The file "CHANGES" contains a brief list of what has changed since + the previous version. Please check it if you've been using a TiMidity + version older than 0.2d. You may have to modify your configuration + files slightly -- look at the included "gravis.cfg" for examples. + +* Installation instructions are in the file "INSTALL". + +* Instructions for use can be found in the manual page "timidity.man", + preformatted in "timidity.txt", and in comments in the configuration + files. + +* The GNU General Public License can, as always, be found in the file + "COPYING". + +* TiMidity requires sampled instruments (patches) to play MIDI files. You + should get the file "timidity-lib-0.1.tar.gz" and unpack it in the same + directory where you unpacked the source code archive. You'll want more + patches later -- read the file "FAQ" for pointers. + +* The latest version of TiMidity can be found on the TiMidity Home Page, + URL http://www.clinet.fi/~toivonen/timidity/ + + An alternative URL for Motif version is http://www.loria.fr/~pagel + +* If you have any comments or suggestions, please email me! + + + Tuukka Toivonen + +[(*) Any Registered Trademarks used anywhere in the documentation or +source code for TiMidity are acknowledged as belonging to their +respective owners.] diff --git a/project/jni/timidity/TODO b/project/jni/timidity/TODO new file mode 100644 index 000000000..59f8f0fe2 --- /dev/null +++ b/project/jni/timidity/TODO @@ -0,0 +1,2 @@ +* Need documentation +* Error handling API (mid_errno, mid_strerror) diff --git a/project/jni/timidity/config.h b/project/jni/timidity/config.h new file mode 100644 index 000000000..6d59befdd --- /dev/null +++ b/project/jni/timidity/config.h @@ -0,0 +1,93 @@ +/* config.h. Generated by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Debug mode */ +/* #undef DEBUG */ + +/* Debug chatter */ +/* #undef DEBUG_CHATTER */ + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the `dlopen' function. */ +/* #undef HAVE_DLOPEN */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MATH_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* Define to 1 if you have the `pow' function. */ +#define HAVE_POW 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strchr' function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Name of package */ +#define PACKAGE "libtimidity" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "0.1.0" + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to rpl_malloc if the replacement function should be used. */ +//#define malloc rpl_malloc + +/* Define to `unsigned' if does not define. */ +/* #undef size_t */ diff --git a/project/jni/timidity/include/timidity.h b/project/jni/timidity/include/timidity.h new file mode 100644 index 000000000..6d8d3cac9 --- /dev/null +++ b/project/jni/timidity/include/timidity.h @@ -0,0 +1,206 @@ +/* + + libTiMidity -- MIDI to WAVE converter library + Copyright (C) 1995 Tuukka Toivonen + Copyright (C) 2004 Konstantin Korikov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef TIMIDITY_H +#define TIMIDITY_H + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define LIBTIMIDITY_VERSION_MAJOR 0L +#define LIBTIMIDITY_VERSION_MINOR 1L +#define LIBTIMIDITY_PATCHLEVEL 0L + +#define LIBTIMIDITY_VERSION \ + ((LIBTIMIDITY_VERSION_MAJOR<<16)| \ + (LIBTIMIDITY_VERSION_MINOR<< 8)| \ + (LIBTIMIDITY_PATCHLEVEL)) + +/* Audio format flags (defaults to LSB byte order) + */ +#define MID_AUDIO_U8 0x0008 /* Unsigned 8-bit samples */ +#define MID_AUDIO_S8 0x8008 /* Signed 8-bit samples */ +#define MID_AUDIO_U16LSB 0x0010 /* Unsigned 16-bit samples */ +#define MID_AUDIO_S16LSB 0x8010 /* Signed 16-bit samples */ +#define MID_AUDIO_U16MSB 0x1010 /* As above, but big-endian byte order */ +#define MID_AUDIO_S16MSB 0x9010 /* As above, but big-endian byte order */ +#define MID_AUDIO_U16 MID_AUDIO_U16LSB +#define MID_AUDIO_S16 MID_AUDIO_S16LSB + +/* Core Library Types + */ + typedef unsigned char uint8; + typedef signed char sint8; + typedef unsigned short uint16; + typedef signed short sint16; + typedef unsigned int uint32; + typedef signed int sint32; + + typedef size_t (*MidIStreamReadFunc) (void *ctx, void *ptr, size_t size, + size_t nmemb); + typedef int (*MidIStreamCloseFunc) (void *ctx); + + typedef struct _MidIStream MidIStream; + typedef struct _MidDLSPatches MidDLSPatches; + typedef struct _MidSong MidSong; + + typedef struct _MidSongOptions MidSongOptions; + struct _MidSongOptions + { + sint32 rate; /* DSP frequency -- samples per second */ + uint16 format; /* Audio data format */ + uint8 channels; /* Number of channels: 1 mono, 2 stereo */ + uint16 buffer_size; /* Sample buffer size in samples */ + }; + + typedef enum + { + MID_SONG_TEXT = 0, + MID_SONG_COPYRIGHT = 1 + } MidSongMetaId; + + +/* Core Library Functions + * ====================== + */ + +/* Initialize the library. If config_file is NULL + * search for configuratin file in default directories + */ + extern int mid_init (char *config_file); + +/* Initialize the library without reading any + * configuratin file + */ + extern int mid_init_no_config (void); + +/* Shutdown the library + */ + extern void mid_exit (void); + + +/* Input Stream Functions + * ====================== + */ + +/* Create input stream from a file name + */ + extern MidIStream *mid_istream_open_file (const char *file); + +/* Create input stream from a file pointer + */ + extern MidIStream *mid_istream_open_fp (FILE * fp, int autoclose); + +/* Create input stream from memory + */ + extern MidIStream *mid_istream_open_mem (void *mem, size_t size, + int autofree); + +/* Create custom input stream + */ + extern MidIStream *mid_istream_open_callbacks (MidIStreamReadFunc read, + MidIStreamCloseFunc close, + void *context); + +/* Read data from input stream + */ + extern size_t mid_istream_read (MidIStream * stream, void *ptr, size_t size, + size_t nmemb); + +/* Skip data from input stream + */ + extern void mid_istream_skip (MidIStream * stream, size_t len); + +/* Close and destroy input stream + */ + extern int mid_istream_close (MidIStream * stream); + + +/* DLS Pathes Functions + * ==================== + */ + +/* Load DLS patches + */ + extern MidDLSPatches *mid_dlspatches_load (MidIStream * stream); + +/* Destroy DLS patches + */ + extern void mid_dlspatches_free (MidDLSPatches * patches); + + +/* MIDI Song Functions + * =================== + */ + +/* Load MIDI song + */ + extern MidSong *mid_song_load (MidIStream * stream, + MidSongOptions * options); + +/* Load MIDI song with specified DLS pathes + */ + extern MidSong *mid_song_load_dls (MidIStream * stream, + MidDLSPatches * patches, + MidSongOptions * options); + +/* Set song amplification value + */ + extern void mid_song_set_volume (MidSong * song, int volume); + +/* Seek song to the start position and initialize conversion + */ + extern void mid_song_start (MidSong * song); + +/* Read WAVE data + */ + extern size_t mid_song_read_wave (MidSong * song, void *ptr, size_t size); + +/* Seek song to specified offset in millseconds + */ + extern void mid_song_seek (MidSong * song, uint32 ms); + +/* Get total song time in millseconds + */ + extern uint32 mid_song_get_total_time (MidSong * song); + +/* Get current song time in millseconds + */ + extern uint32 mid_song_get_time (MidSong * song); + +/* Get song meta data. Return NULL if no meta data found + */ + extern char *mid_song_get_meta (MidSong * song, MidSongMetaId what); + +/* Destroy song + */ + extern void mid_song_free (MidSong * song); + +#ifdef __cplusplus +} +#endif +#endif /* TIMIDITY_H */ diff --git a/project/jni/timidity/src/common.c b/project/jni/timidity/src/common.c new file mode 100644 index 000000000..3a6ce55cd --- /dev/null +++ b/project/jni/timidity/src/common.c @@ -0,0 +1,137 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + common.c + + */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +/* I guess "rb" should be right for any libc */ +#define OPEN_MODE "rb" + +#include "timidity.h" +#include "timidity_internal.h" +#include "options.h" +#include "common.h" + +/* The paths in this list will be tried whenever we're reading a file */ +static PathList *pathlist = NULL; /* This is a linked list */ + +/* This is meant to find and open files for reading */ +FILE *open_file(char *name) +{ + FILE *fp; + + if (!name || !(*name)) + { + DEBUG_MSG("Attempted to open nameless file.\n"); + return 0; + } + + /* First try the given name */ + + DEBUG_MSG("Trying to open %s\n", name); + if ((fp = fopen(name, OPEN_MODE))) + return fp; + + if (name[0] != PATH_SEP) + { + char current_filename[1024]; + PathList *plp = pathlist; + int l; + + while (plp) /* Try along the path then */ + { + *current_filename = 0; + l = strlen(plp->path); + if(l) + { + strcpy(current_filename, plp->path); + if(current_filename[l - 1] != PATH_SEP) + { + current_filename[l] = PATH_SEP; + current_filename[l + 1] = '\0'; + } + } + strcat(current_filename, name); + DEBUG_MSG("Trying to open %s\n", current_filename); + if ((fp = fopen(current_filename, OPEN_MODE))) + return fp; + plp = plp->next; + } + } + + /* Nothing could be opened. */ + DEBUG_MSG("Could not open %s\n", name); + return 0; +} + +/* This'll allocate memory or die. */ +void *safe_malloc(size_t count) +{ + void *p; + + p = malloc(count); + if (p == NULL) + DEBUG_MSG("Sorry. Couldn't malloc %d bytes.\n", count); + + return p; +} + +/* This adds a directory to the path list */ +void add_to_pathlist(char *s) +{ + PathList *plp = safe_malloc(sizeof(PathList)); + + if (plp == NULL) + return; + + plp->path = safe_malloc(strlen(s) + 1); + if (plp->path == NULL) + { + free(plp); + return; + } + + strcpy(plp->path, s); + plp->next = pathlist; + pathlist = plp; +} + +void free_pathlist(void) +{ + PathList *plp = pathlist; + PathList *next; + + while (plp) + { + next = plp->next; + free(plp->path); + free(plp); + plp = next; + } + pathlist = NULL; +} diff --git a/project/jni/timidity/src/common.h b/project/jni/timidity/src/common.h new file mode 100644 index 000000000..3d8efcab9 --- /dev/null +++ b/project/jni/timidity/src/common.h @@ -0,0 +1,32 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + + common.h +*/ + +typedef struct { + char *path; + void *next; +} PathList; + +extern FILE *open_file(char *name); +extern void add_to_pathlist(char *s); +extern void *safe_malloc(size_t count); +extern void free_pathlist(void); diff --git a/project/jni/timidity/src/dls1.h b/project/jni/timidity/src/dls1.h new file mode 100644 index 000000000..abc2075a5 --- /dev/null +++ b/project/jni/timidity/src/dls1.h @@ -0,0 +1,266 @@ +/*==========================================================================; +// +// dls1.h +// +// +// Description: +// +// Interface defines and structures for the Instrument Collection Form +// RIFF DLS. +// +// +// Written by Sonic Foundry 1996. Released for public use. +// +//=========================================================================*/ + +#ifndef _INC_DLS1 +#define _INC_DLS1 + +/*////////////////////////////////////////////////////////////////////////// +// +// +// Layout of an instrument collection: +// +// +// RIFF [] 'DLS ' [dlid,colh,INSTLIST,WAVEPOOL,INFOLIST] +// +// INSTLIST +// LIST [] 'lins' +// LIST [] 'ins ' [dlid,insh,RGNLIST,ARTLIST,INFOLIST] +// LIST [] 'ins ' [dlid,insh,RGNLIST,ARTLIST,INFOLIST] +// LIST [] 'ins ' [dlid,insh,RGNLIST,ARTLIST,INFOLIST] +// +// RGNLIST +// LIST [] 'lrgn' +// LIST [] 'rgn ' [rgnh,wsmp,wlnk,ARTLIST] +// LIST [] 'rgn ' [rgnh,wsmp,wlnk,ARTLIST] +// LIST [] 'rgn ' [rgnh,wsmp,wlnk,ARTLIST] +// +// ARTLIST +// LIST [] 'lart' +// 'art1' level 1 Articulation connection graph +// 'art2' level 2 Articulation connection graph +// '3rd1' Possible 3rd party articulation structure 1 +// '3rd2' Possible 3rd party articulation structure 2 .... and so on +// +// WAVEPOOL +// ptbl [] [pool table] +// LIST [] 'wvpl' +// [path], +// [path], +// LIST [] 'wave' [dlid,RIFFWAVE] +// LIST [] 'wave' [dlid,RIFFWAVE] +// LIST [] 'wave' [dlid,RIFFWAVE] +// LIST [] 'wave' [dlid,RIFFWAVE] +// LIST [] 'wave' [dlid,RIFFWAVE] +// +// INFOLIST +// LIST [] 'INFO' +// 'icmt' 'One of those crazy comments.' +// 'icop' 'Copyright (C) 1996 Sonic Foundry' +// +/////////////////////////////////////////////////////////////////////////*/ + + +/*///////////////////////////////////////////////////////////////////////// +// FOURCC's used in the DLS file +/////////////////////////////////////////////////////////////////////////*/ + +#define FOURCC_DLS mmioFOURCC('D','L','S',' ') +#define FOURCC_DLID mmioFOURCC('d','l','i','d') +#define FOURCC_COLH mmioFOURCC('c','o','l','h') +#define FOURCC_WVPL mmioFOURCC('w','v','p','l') +#define FOURCC_PTBL mmioFOURCC('p','t','b','l') +#define FOURCC_PATH mmioFOURCC('p','a','t','h') +#define FOURCC_wave mmioFOURCC('w','a','v','e') +#define FOURCC_LINS mmioFOURCC('l','i','n','s') +#define FOURCC_INS mmioFOURCC('i','n','s',' ') +#define FOURCC_INSH mmioFOURCC('i','n','s','h') +#define FOURCC_LRGN mmioFOURCC('l','r','g','n') +#define FOURCC_RGN mmioFOURCC('r','g','n',' ') +#define FOURCC_RGNH mmioFOURCC('r','g','n','h') +#define FOURCC_LART mmioFOURCC('l','a','r','t') +#define FOURCC_ART1 mmioFOURCC('a','r','t','1') +#define FOURCC_WLNK mmioFOURCC('w','l','n','k') +#define FOURCC_WSMP mmioFOURCC('w','s','m','p') +#define FOURCC_VERS mmioFOURCC('v','e','r','s') + +/*///////////////////////////////////////////////////////////////////////// +// Articulation connection graph definitions +/////////////////////////////////////////////////////////////////////////*/ + +/* Generic Sources */ +#define CONN_SRC_NONE 0x0000 +#define CONN_SRC_LFO 0x0001 +#define CONN_SRC_KEYONVELOCITY 0x0002 +#define CONN_SRC_KEYNUMBER 0x0003 +#define CONN_SRC_EG1 0x0004 +#define CONN_SRC_EG2 0x0005 +#define CONN_SRC_PITCHWHEEL 0x0006 + +/* Midi Controllers 0-127 */ +#define CONN_SRC_CC1 0x0081 +#define CONN_SRC_CC7 0x0087 +#define CONN_SRC_CC10 0x008a +#define CONN_SRC_CC11 0x008b + +/* Generic Destinations */ +#define CONN_DST_NONE 0x0000 +#define CONN_DST_ATTENUATION 0x0001 +#define CONN_DST_PITCH 0x0003 +#define CONN_DST_PAN 0x0004 + +/* LFO Destinations */ +#define CONN_DST_LFO_FREQUENCY 0x0104 +#define CONN_DST_LFO_STARTDELAY 0x0105 + +/* EG1 Destinations */ +#define CONN_DST_EG1_ATTACKTIME 0x0206 +#define CONN_DST_EG1_DECAYTIME 0x0207 +#define CONN_DST_EG1_RELEASETIME 0x0209 +#define CONN_DST_EG1_SUSTAINLEVEL 0x020a + +/* EG2 Destinations */ +#define CONN_DST_EG2_ATTACKTIME 0x030a +#define CONN_DST_EG2_DECAYTIME 0x030b +#define CONN_DST_EG2_RELEASETIME 0x030d +#define CONN_DST_EG2_SUSTAINLEVEL 0x030e + +#define CONN_TRN_NONE 0x0000 +#define CONN_TRN_CONCAVE 0x0001 + +typedef struct _DLSID { + ULONG ulData1; + USHORT usData2; + USHORT usData3; + BYTE abData4[8]; +} DLSID, FAR *LPDLSID; + +typedef struct _DLSVERSION { + DWORD dwVersionMS; + DWORD dwVersionLS; +} DLSVERSION, FAR *LPDLSVERSION; + + +typedef struct _CONNECTION { + USHORT usSource; + USHORT usControl; + USHORT usDestination; + USHORT usTransform; + LONG lScale; +} CONNECTION, FAR *LPCONNECTION; + + +/* Level 1 Articulation Data */ + +typedef struct _CONNECTIONLIST { + ULONG cbSize; /* size of the connection list structure */ + ULONG cConnections; /* count of connections in the list */ +} CONNECTIONLIST, FAR *LPCONNECTIONLIST; + + + +/*///////////////////////////////////////////////////////////////////////// +// Generic type defines for regions and instruments +/////////////////////////////////////////////////////////////////////////*/ + +typedef struct _RGNRANGE { + USHORT usLow; + USHORT usHigh; +} RGNRANGE, FAR * LPRGNRANGE; + +#define F_INSTRUMENT_DRUMS 0x80000000 + +typedef struct _MIDILOCALE { + ULONG ulBank; + ULONG ulInstrument; +} MIDILOCALE, FAR *LPMIDILOCALE; + +/*///////////////////////////////////////////////////////////////////////// +// Header structures found in an DLS file for collection, instruments, and +// regions. +/////////////////////////////////////////////////////////////////////////*/ + +#define F_RGN_OPTION_SELFNONEXCLUSIVE 0x0001 + +typedef struct _RGNHEADER { + RGNRANGE RangeKey; /* Key range */ + RGNRANGE RangeVelocity; /* Velocity Range */ + USHORT fusOptions; /* Synthesis options for this range */ + USHORT usKeyGroup; /* Key grouping for non simultaneous play */ + /* 0 = no group, 1 up is group */ + /* for Level 1 only groups 1-15 are allowed */ +} RGNHEADER, FAR *LPRGNHEADER; + +typedef struct _INSTHEADER { + ULONG cRegions; /* Count of regions in this instrument */ + MIDILOCALE Locale; /* Intended MIDI locale of this instrument */ +} INSTHEADER, FAR *LPINSTHEADER; + +typedef struct _DLSHEADER { + ULONG cInstruments; /* Count of instruments in the collection */ +} DLSHEADER, FAR *LPDLSHEADER; + +/*//////////////////////////////////////////////////////////////////////////// +// definitions for the Wave link structure +////////////////////////////////////////////////////////////////////////////*/ + +/* **** For level 1 only WAVELINK_CHANNEL_MONO is valid **** */ +/* ulChannel allows for up to 32 channels of audio with each bit position */ +/* specifiying a channel of playback */ + +#define WAVELINK_CHANNEL_LEFT 0x0001l +#define WAVELINK_CHANNEL_RIGHT 0x0002l + +#define F_WAVELINK_PHASE_MASTER 0x0001 + +typedef struct _WAVELINK { /* any paths or links are stored right after struct */ + USHORT fusOptions; /* options flags for this wave */ + USHORT usPhaseGroup; /* Phase grouping for locking channels */ + ULONG ulChannel; /* channel placement */ + ULONG ulTableIndex; /* index into the wave pool table, 0 based */ +} WAVELINK, FAR *LPWAVELINK; + +#define POOL_CUE_NULL 0xffffffffl + +typedef struct _POOLCUE { + ULONG ulOffset; /* Offset to the entry in the list */ +} POOLCUE, FAR *LPPOOLCUE; + +typedef struct _POOLTABLE { + ULONG cbSize; /* size of the pool table structure */ + ULONG cCues; /* count of cues in the list */ +} POOLTABLE, FAR *LPPOOLTABLE; + +/*//////////////////////////////////////////////////////////////////////////// +// Structures for the "wsmp" chunk +////////////////////////////////////////////////////////////////////////////*/ + +#define F_WSMP_NO_TRUNCATION 0x0001l +#define F_WSMP_NO_COMPRESSION 0x0002l + + +typedef struct _rwsmp { + ULONG cbSize; + USHORT usUnityNote; /* MIDI Unity Playback Note */ + SHORT sFineTune; /* Fine Tune in log tuning */ + LONG lAttenuation; /* Overall Attenuation to be applied to data */ + ULONG fulOptions; /* Flag options */ + ULONG cSampleLoops; /* Count of Sample loops, 0 loops is one shot */ +} WSMPL, FAR *LPWSMPL; + + +/* This loop type is a normal forward playing loop which is continually */ +/* played until the envelope reaches an off threshold in the release */ +/* portion of the volume envelope */ + +#define WLOOP_TYPE_FORWARD 0 + +typedef struct _rloop { + ULONG cbSize; + ULONG ulType; /* Loop Type */ + ULONG ulStart; /* Start of loop in samples */ + ULONG ulLength; /* Length of loop in samples */ +} WLOOP, FAR *LPWLOOP; + +#endif /*_INC_DLS1 */ diff --git a/project/jni/timidity/src/dls2.h b/project/jni/timidity/src/dls2.h new file mode 100644 index 000000000..30cec23a2 --- /dev/null +++ b/project/jni/timidity/src/dls2.h @@ -0,0 +1,130 @@ +/* + + dls2.h + + Description: + + Interface defines and structures for the DLS2 extensions of DLS. + + + Written by Microsoft 1998. Released for public use. + +*/ + +#ifndef _INC_DLS2 +#define _INC_DLS2 + +/* + FOURCC's used in the DLS2 file, in addition to DLS1 chunks +*/ + +#define FOURCC_RGN2 mmioFOURCC('r','g','n','2') +#define FOURCC_LAR2 mmioFOURCC('l','a','r','2') +#define FOURCC_ART2 mmioFOURCC('a','r','t','2') +#define FOURCC_CDL mmioFOURCC('c','d','l',' ') +#define FOURCC_DLID mmioFOURCC('d','l','i','d') + +/* + Articulation connection graph definitions. These are in addition to + the definitions in the DLS1 header. +*/ + +/* Generic Sources (in addition to DLS1 sources. */ +#define CONN_SRC_POLYPRESSURE 0x0007 /* Polyphonic Pressure */ +#define CONN_SRC_CHANNELPRESSURE 0x0008 /* Channel Pressure */ +#define CONN_SRC_VIBRATO 0x0009 /* Vibrato LFO */ +#define CONN_SRC_MONOPRESSURE 0x000a /* MIDI Mono pressure */ + + +/* Midi Controllers */ +#define CONN_SRC_CC91 0x00db /* Reverb Send */ +#define CONN_SRC_CC93 0x00dd /* Chorus Send */ + + +/* Generic Destinations */ +#define CONN_DST_GAIN 0x0001 /* Same as CONN_DST_ ATTENUATION, but more appropriate terminology. */ +#define CONN_DST_KEYNUMBER 0x0005 /* Key Number Generator */ + +/* Audio Channel Output Destinations */ +#define CONN_DST_LEFT 0x0010 /* Left Channel Send */ +#define CONN_DST_RIGHT 0x0011 /* Right Channel Send */ +#define CONN_DST_CENTER 0x0012 /* Center Channel Send */ +#define CONN_DST_LEFTREAR 0x0013 /* Left Rear Channel Send */ +#define CONN_DST_RIGHTREAR 0x0014 /* Right Rear Channel Send */ +#define CONN_DST_LFE_CHANNEL 0x0015 /* LFE Channel Send */ +#define CONN_DST_CHORUS 0x0080 /* Chorus Send */ +#define CONN_DST_REVERB 0x0081 /* Reverb Send */ + +/* Vibrato LFO Destinations */ +#define CONN_DST_VIB_FREQUENCY 0x0114 /* Vibrato Frequency */ +#define CONN_DST_VIB_STARTDELAY 0x0115 /* Vibrato Start Delay */ + +/* EG1 Destinations */ +#define CONN_DST_EG1_DELAYTIME 0x020B /* EG1 Delay Time */ +#define CONN_DST_EG1_HOLDTIME 0x020C /* EG1 Hold Time */ +#define CONN_DST_EG1_SHUTDOWNTIME 0x020D /* EG1 Shutdown Time */ + + +/* EG2 Destinations */ +#define CONN_DST_EG2_DELAYTIME 0x030F /* EG2 Delay Time */ +#define CONN_DST_EG2_HOLDTIME 0x0310 /* EG2 Hold Time */ + + +/* Filter Destinations */ +#define CONN_DST_FILTER_CUTOFF 0x0500 /* Filter Cutoff Frequency */ +#define CONN_DST_FILTER_Q 0x0501 /* Filter Resonance */ + + +/* Transforms */ +#define CONN_TRN_CONVEX 0x0002 /* Convex Transform */ +#define CONN_TRN_SWITCH 0x0003 /* Switch Transform */ + + +/* Conditional chunk operators */ + #define DLS_CDL_AND 0x0001 /* X = X & Y */ + #define DLS_CDL_OR 0x0002 /* X = X | Y */ + #define DLS_CDL_XOR 0x0003 /* X = X ^ Y */ + #define DLS_CDL_ADD 0x0004 /* X = X + Y */ + #define DLS_CDL_SUBTRACT 0x0005 /* X = X - Y */ + #define DLS_CDL_MULTIPLY 0x0006 /* X = X * Y */ + #define DLS_CDL_DIVIDE 0x0007 /* X = X / Y */ + #define DLS_CDL_LOGICAL_AND 0x0008 /* X = X && Y */ + #define DLS_CDL_LOGICAL_OR 0x0009 /* X = X || Y */ + #define DLS_CDL_LT 0x000A /* X = (X < Y) */ + #define DLS_CDL_LE 0x000B /* X = (X <= Y) */ + #define DLS_CDL_GT 0x000C /* X = (X > Y) */ + #define DLS_CDL_GE 0x000D /* X = (X >= Y) */ + #define DLS_CDL_EQ 0x000E /* X = (X == Y) */ + #define DLS_CDL_NOT 0x000F /* X = !X */ + #define DLS_CDL_CONST 0x0010 /* 32-bit constant */ + #define DLS_CDL_QUERY 0x0011 /* 32-bit value returned from query */ + #define DLS_CDL_QUERYSUPPORTED 0x0012 /* Test to see if query is supported by synth */ + +/* + Loop and release +*/ + +#define WLOOP_TYPE_RELEASE 1 + +/* + WaveLink chunk +*/ + +#define F_WAVELINK_MULTICHANNEL 0x0002 + + +/* + DLSID queries for +*/ + +DEFINE_GUID(DLSID_GMInHardware, 0x178f2f24, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12); +DEFINE_GUID(DLSID_GSInHardware, 0x178f2f25, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12); +DEFINE_GUID(DLSID_XGInHardware, 0x178f2f26, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12); +DEFINE_GUID(DLSID_SupportsDLS1, 0x178f2f27, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12); +DEFINE_GUID(DLSID_SupportsDLS2, 0xf14599e5, 0x4689, 0x11d2, 0xaf, 0xa6, 0x0, 0xaa, 0x0, 0x24, 0xd8, 0xb6); +DEFINE_GUID(DLSID_SampleMemorySize, 0x178f2f28, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12); +DEFINE_GUID(DLSID_ManufacturersID, 0xb03e1181, 0x8095, 0x11d2, 0xa1, 0xef, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8); +DEFINE_GUID(DLSID_ProductID, 0xb03e1182, 0x8095, 0x11d2, 0xa1, 0xef, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8); +DEFINE_GUID(DLSID_SamplePlaybackRate, 0x2a91f713, 0xa4bf, 0x11d2, 0xbb, 0xdf, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8); + +#endif /* _INC_DLS2 */ diff --git a/project/jni/timidity/src/instrum.c b/project/jni/timidity/src/instrum.c new file mode 100644 index 000000000..93eb9bf27 --- /dev/null +++ b/project/jni/timidity/src/instrum.c @@ -0,0 +1,619 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + instrum.c + + Code to load and unload GUS-compatible instrument patches. + +*/ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include "timidity.h" +#include "timidity_internal.h" +#include "options.h" +#include "common.h" +#include "instrum.h" +#include "instrum_dls.h" +#include "resample.h" +#include "tables.h" + +static void free_instrument(MidInstrument *ip) +{ + MidSample *sp; + int i; + if (!ip) return; + for (i=0; isamples; i++) + { + sp=&(ip->sample[i]); + free(sp->data); + } + free(ip->sample); + free(ip); +} + +static void free_bank(MidSong *song, int dr, int b) +{ + int i; + MidToneBank *bank=((dr) ? song->drumset[b] : song->tonebank[b]); + for (i=0; i<128; i++) + if (bank->instrument[i]) + { + /* Not that this could ever happen, of course */ + if (bank->instrument[i] != MAGIC_LOAD_INSTRUMENT) + free_instrument(bank->instrument[i]); + bank->instrument[i]=0; + } +} + +static sint32 convert_envelope_rate(MidSong *song, uint8 rate) +{ + sint32 r; + + r = 3 - ((rate >> 6) & 0x3); + r *= 3; + r = (sint32) (rate & 0x3f) << r; /* 6.9 fixed point */ + + /* 15.15 fixed point. */ + r = ((r * 44100) / song->rate) * song->control_ratio; + +#ifdef FAST_DECAY + return r << 10; +#else + return r << 9; +#endif +} + +static sint32 convert_envelope_offset(uint8 offset) +{ + /* This is not too good... Can anyone tell me what these values mean? + Are they GUS-style "exponential" volumes? And what does that mean? */ + + /* 15.15 fixed point */ + return offset << (7+15); +} + +static sint32 convert_tremolo_sweep(MidSong *song, uint8 sweep) +{ + if (!sweep) + return 0; + + return + ((song->control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / + (song->rate * sweep); +} + +static sint32 convert_vibrato_sweep(MidSong *song, uint8 sweep, + sint32 vib_control_ratio) +{ + if (!sweep) + return 0; + + return + (sint32) (FSCALE((double) (vib_control_ratio) * SWEEP_TUNING, SWEEP_SHIFT) + / (double)(song->rate * sweep)); + + /* this was overflowing with seashore.pat + + ((vib_control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / + (song->rate * sweep); */ +} + +static sint32 convert_tremolo_rate(MidSong *song, uint8 rate) +{ + return + ((SINE_CYCLE_LENGTH * song->control_ratio * rate) << RATE_SHIFT) / + (TREMOLO_RATE_TUNING * song->rate); +} + +static sint32 convert_vibrato_rate(MidSong *song, uint8 rate) +{ + /* Return a suitable vibrato_control_ratio value */ + return + (VIBRATO_RATE_TUNING * song->rate) / + (rate * 2 * MID_VIBRATO_SAMPLE_INCREMENTS); +} + +static void reverse_data(sint16 *sp, sint32 ls, sint32 le) +{ + sint16 s, *ep=sp+le; + sp+=ls; + le-=ls; + le/=2; + while (le--) + { + s=*sp; + *sp++=*ep; + *ep--=s; + } +} + +/* + If panning or note_to_use != -1, it will be used for all samples, + instead of the sample-specific values in the instrument file. + + For note_to_use, any value <0 or >127 will be forced to 0. + + For other parameters, 1 means yes, 0 means no, other values are + undefined. + + TODO: do reverse loops right */ +static MidInstrument *load_instrument(MidSong *song, char *name, int percussion, + int panning, int amp, int note_to_use, + int strip_loop, int strip_envelope, + int strip_tail) +{ + MidInstrument *ip; + MidSample *sp; + FILE *fp; + char tmp[1024]; + int i,j,noluck=0; + static char *patch_ext[] = PATCH_EXT_LIST; + + if (!name) return 0; + + /* Open patch file */ + if ((fp=open_file(name)) == NULL) + { + noluck=1; + /* Try with various extensions */ + for (i=0; patch_ext[i]; i++) + { + if (strlen(name)+strlen(patch_ext[i])<1024) + { + strcpy(tmp, name); + strcat(tmp, patch_ext[i]); + if ((fp=open_file(tmp)) != NULL) + { + noluck=0; + break; + } + } + } + } + + if (noluck) + { + DEBUG_MSG("Instrument `%s' can't be found.\n", name); + return 0; + } + + DEBUG_MSG("Loading instrument %s\n", tmp); + + /* Read some headers and do cursory sanity checks. There are loads + of magic offsets. This could be rewritten... */ + + if ((239 != fread(tmp, 1, 239, fp)) || + (memcmp(tmp, "GF1PATCH110\0ID#000002", 22) && + memcmp(tmp, "GF1PATCH100\0ID#000002", 22))) /* don't know what the + differences are */ + { + DEBUG_MSG("%s: not an instrument\n", name); + return 0; + } + + if (tmp[82] != 1 && tmp[82] != 0) /* instruments. To some patch makers, + 0 means 1 */ + { + DEBUG_MSG("Can't handle patches with %d instruments\n", tmp[82]); + return 0; + } + + if (tmp[151] != 1 && tmp[151] != 0) /* layers. What's a layer? */ + { + DEBUG_MSG("Can't handle instruments with %d layers\n", tmp[151]); + return 0; + } + + ip=safe_malloc(sizeof(MidInstrument)); + ip->samples = tmp[198]; + ip->sample = safe_malloc(sizeof(MidSample) * ip->samples); + for (i=0; isamples; i++) + { + + uint8 fractions; + sint32 tmplong; + uint16 tmpshort; + uint8 tmpchar; + +#define READ_CHAR(thing) \ + if (1 != fread(&tmpchar, 1, 1, fp)) goto fail; \ + thing = tmpchar; +#define READ_SHORT(thing) \ + if (1 != fread(&tmpshort, 2, 1, fp)) goto fail; \ + thing = SWAPLE16(tmpshort); +#define READ_LONG(thing) \ + if (1 != fread(&tmplong, 4, 1, fp)) goto fail; \ + thing = SWAPLE32(tmplong); + + fseek(fp, 7, SEEK_CUR); /* Skip the wave name */ + + if (1 != fread(&fractions, 1, 1, fp)) + { + fail: + DEBUG_MSG("Error reading sample %d\n", i); + for (j=0; jsample[j].data); + free(ip->sample); + free(ip); + return 0; + } + + sp=&(ip->sample[i]); + + READ_LONG(sp->data_length); + READ_LONG(sp->loop_start); + READ_LONG(sp->loop_end); + READ_SHORT(sp->sample_rate); + READ_LONG(sp->low_freq); + READ_LONG(sp->high_freq); + READ_LONG(sp->root_freq); + sp->low_vel = 0; + sp->high_vel = 127; + fseek(fp, 2, SEEK_CUR); /* Why have a "root frequency" and then + * "tuning"?? */ + + READ_CHAR(tmp[0]); + + if (panning==-1) + sp->panning = (tmp[0] * 8 + 4) & 0x7f; + else + sp->panning=(uint8)(panning & 0x7F); + + /* envelope, tremolo, and vibrato */ + if (18 != fread(tmp, 1, 18, fp)) goto fail; + + if (!tmp[13] || !tmp[14]) + { + sp->tremolo_sweep_increment= + sp->tremolo_phase_increment=sp->tremolo_depth=0; + DEBUG_MSG(" * no tremolo\n"); + } + else + { + sp->tremolo_sweep_increment=convert_tremolo_sweep(song, tmp[12]); + sp->tremolo_phase_increment=convert_tremolo_rate(song, tmp[13]); + sp->tremolo_depth=tmp[14]; + DEBUG_MSG(" * tremolo: sweep %d, phase %d, depth %d\n", + sp->tremolo_sweep_increment, sp->tremolo_phase_increment, + sp->tremolo_depth); + } + + if (!tmp[16] || !tmp[17]) + { + sp->vibrato_sweep_increment= + sp->vibrato_control_ratio=sp->vibrato_depth=0; + DEBUG_MSG(" * no vibrato\n"); + } + else + { + sp->vibrato_control_ratio=convert_vibrato_rate(song, tmp[16]); + sp->vibrato_sweep_increment= + convert_vibrato_sweep(song, tmp[15], sp->vibrato_control_ratio); + sp->vibrato_depth=tmp[17]; + DEBUG_MSG(" * vibrato: sweep %d, ctl %d, depth %d\n", + sp->vibrato_sweep_increment, sp->vibrato_control_ratio, + sp->vibrato_depth); + + } + + READ_CHAR(sp->modes); + + fseek(fp, 40, SEEK_CUR); /* skip the useless scale frequency, scale + factor (what's it mean?), and reserved + space */ + + /* Mark this as a fixed-pitch instrument if such a deed is desired. */ + if (note_to_use!=-1) + sp->note_to_use=(uint8)(note_to_use); + else + sp->note_to_use=0; + + /* seashore.pat in the Midia patch set has no Sustain. I don't + understand why, and fixing it by adding the Sustain flag to + all looped patches probably breaks something else. We do it + anyway. */ + + if (sp->modes & MODES_LOOPING) + sp->modes |= MODES_SUSTAIN; + + /* Strip any loops and envelopes we're permitted to */ + if ((strip_loop==1) && + (sp->modes & (MODES_SUSTAIN | MODES_LOOPING | + MODES_PINGPONG | MODES_REVERSE))) + { + DEBUG_MSG(" - Removing loop and/or sustain\n"); + sp->modes &=~(MODES_SUSTAIN | MODES_LOOPING | + MODES_PINGPONG | MODES_REVERSE); + } + + if (strip_envelope==1) + { + if (sp->modes & MODES_ENVELOPE) + DEBUG_MSG(" - Removing envelope\n"); + sp->modes &= ~MODES_ENVELOPE; + } + else if (strip_envelope != 0) + { + /* Have to make a guess. */ + if (!(sp->modes & (MODES_LOOPING | MODES_PINGPONG | MODES_REVERSE))) + { + /* No loop? Then what's there to sustain? No envelope needed + either... */ + sp->modes &= ~(MODES_SUSTAIN|MODES_ENVELOPE); + DEBUG_MSG(" - No loop, removing sustain and envelope\n"); + } + else if (!memcmp(tmp, "??????", 6) || tmp[11] >= 100) + { + /* Envelope rates all maxed out? Envelope end at a high "offset"? + That's a weird envelope. Take it out. */ + sp->modes &= ~MODES_ENVELOPE; + DEBUG_MSG(" - Weirdness, removing envelope\n"); + } + else if (!(sp->modes & MODES_SUSTAIN)) + { + /* No sustain? Then no envelope. I don't know if this is + justified, but patches without sustain usually don't need the + envelope either... at least the Gravis ones. They're mostly + drums. I think. */ + sp->modes &= ~MODES_ENVELOPE; + DEBUG_MSG(" - No sustain, removing envelope\n"); + } + } + + for (j=0; j<6; j++) + { + sp->envelope_rate[j]= + convert_envelope_rate(song, tmp[j]); + sp->envelope_offset[j]= + convert_envelope_offset(tmp[6+j]); + } + + /* Then read the sample data */ + sp->data = safe_malloc(sp->data_length); + if (1 != fread(sp->data, sp->data_length, 1, fp)) + goto fail; + + if (!(sp->modes & MODES_16BIT)) /* convert to 16-bit data */ + { + sint32 i=sp->data_length; + uint8 *cp=(uint8 *)(sp->data); + uint16 *tmp,*new; + tmp=new=safe_malloc(sp->data_length*2); + while (i--) + *tmp++ = (uint16)(*cp++) << 8; + cp=(uint8 *)(sp->data); + sp->data = (sample_t *)new; + free(cp); + sp->data_length *= 2; + sp->loop_start *= 2; + sp->loop_end *= 2; + } +#ifndef LITTLE_ENDIAN + else + /* convert to machine byte order */ + { + sint32 i=sp->data_length/2; + sint16 *tmp=(sint16 *)sp->data,s; + while (i--) + { + s=SWAPLE16(*tmp); + *tmp++=s; + } + } +#endif + + if (sp->modes & MODES_UNSIGNED) /* convert to signed data */ + { + sint32 i=sp->data_length/2; + sint16 *tmp=(sint16 *)sp->data; + while (i--) + *tmp++ ^= 0x8000; + } + + /* Reverse reverse loops and pass them off as normal loops */ + if (sp->modes & MODES_REVERSE) + { + sint32 t; + /* The GUS apparently plays reverse loops by reversing the + whole sample. We do the same because the GUS does not SUCK. */ + + DEBUG_MSG("Reverse loop in %s\n", name); + reverse_data((sint16 *)sp->data, 0, sp->data_length/2); + + t=sp->loop_start; + sp->loop_start=sp->data_length - sp->loop_end; + sp->loop_end=sp->data_length - t; + + sp->modes &= ~MODES_REVERSE; + sp->modes |= MODES_LOOPING; /* just in case */ + } + +#ifdef ADJUST_SAMPLE_VOLUMES + if (amp!=-1) + sp->volume=(float)((amp) / 100.0); + else + { + /* Try to determine a volume scaling factor for the sample. + This is a very crude adjustment, but things sound more + balanced with it. Still, this should be a runtime option. */ + sint32 i=sp->data_length/2; + sint16 maxamp=0,a; + sint16 *tmp=(sint16 *)sp->data; + while (i--) + { + a=*tmp++; + if (a<0) a=-a; + if (a>maxamp) + maxamp=a; + } + sp->volume=(float)(32768.0 / maxamp); + DEBUG_MSG(" * volume comp: %f\n", sp->volume); + } +#else + if (amp!=-1) + sp->volume=(double)(amp) / 100.0; + else + sp->volume=1.0; +#endif + + sp->data_length /= 2; /* These are in bytes. Convert into samples. */ + sp->loop_start /= 2; + sp->loop_end /= 2; + + /* Then fractional samples */ + sp->data_length <<= FRACTION_BITS; + sp->loop_start <<= FRACTION_BITS; + sp->loop_end <<= FRACTION_BITS; + + /* Adjust for fractional loop points. This is a guess. Does anyone + know what "fractions" really stands for? */ + sp->loop_start |= + (fractions & 0x0F) << (FRACTION_BITS-4); + sp->loop_end |= + ((fractions>>4) & 0x0F) << (FRACTION_BITS-4); + + /* If this instrument will always be played on the same note, + and it's not looped, we can resample it now. */ + if (sp->note_to_use && !(sp->modes & MODES_LOOPING)) + pre_resample(song, sp); + + if (strip_tail==1) + { + /* Let's not really, just say we did. */ + DEBUG_MSG(" - Stripping tail\n"); + sp->data_length = sp->loop_end; + } + } + + fclose(fp); + return ip; +} + +static int fill_bank(MidSong *song, int dr, int b) +{ + int i, errors=0; + MidToneBank *bank=((dr) ? song->drumset[b] : song->tonebank[b]); + if (!bank) + { + DEBUG_MSG("Huh. Tried to load instruments in non-existent %s %d\n", + (dr) ? "drumset" : "tone bank", b); + return 0; + } + for (i=0; i<128; i++) + { + if (bank->instrument[i]==MAGIC_LOAD_INSTRUMENT) + { + bank->instrument[i]=load_instrument_dls(song, dr, b, i); + if (bank->instrument[i]) + { + continue; + } + if (!(bank->tone[i].name)) + { + DEBUG_MSG("No instrument mapped to %s %d, program %d%s\n", + (dr)? "drum set" : "tone bank", b, i, + (b!=0) ? "" : " - this instrument will not be heard"); + if (b!=0) + { + /* Mark the corresponding instrument in the default + bank / drumset for loading (if it isn't already) */ + if (!dr) + { + if (!(song->tonebank[0]->instrument[i])) + song->tonebank[0]->instrument[i] = + MAGIC_LOAD_INSTRUMENT; + } + else + { + if (!(song->drumset[0]->instrument[i])) + song->drumset[0]->instrument[i] = + MAGIC_LOAD_INSTRUMENT; + } + } + bank->instrument[i] = 0; + errors++; + } + else if (!(bank->instrument[i] = + load_instrument(song, + bank->tone[i].name, + (dr) ? 1 : 0, + bank->tone[i].pan, + bank->tone[i].amp, + (bank->tone[i].note!=-1) ? + bank->tone[i].note : + ((dr) ? i : -1), + (bank->tone[i].strip_loop!=-1) ? + bank->tone[i].strip_loop : + ((dr) ? 1 : -1), + (bank->tone[i].strip_envelope != -1) ? + bank->tone[i].strip_envelope : + ((dr) ? 1 : -1), + bank->tone[i].strip_tail ))) + { + DEBUG_MSG("Couldn't load instrument %s (%s %d, program %d)\n", + bank->tone[i].name, + (dr)? "drum set" : "tone bank", b, i); + errors++; + } + } + } + return errors; +} + +int load_missing_instruments(MidSong *song) +{ + int i=128,errors=0; + while (i--) + { + if (song->tonebank[i]) + errors+=fill_bank(song,0,i); + if (song->drumset[i]) + errors+=fill_bank(song,1,i); + } + return errors; +} + +void free_instruments(MidSong *song) +{ + int i=128; + while(i--) + { + if (song->tonebank[i]) + free_bank(song, 0, i); + if (song->drumset[i]) + free_bank(song, 1, i); + } +} + +int set_default_instrument(MidSong *song, char *name) +{ + MidInstrument *ip; + if (!(ip=load_instrument(song, name, 0, -1, -1, -1, 0, 0, 0))) + return -1; + song->default_instrument = ip; + song->default_program = SPECIAL_PROGRAM; + return 0; +} diff --git a/project/jni/timidity/src/instrum.h b/project/jni/timidity/src/instrum.h new file mode 100644 index 000000000..95be874f6 --- /dev/null +++ b/project/jni/timidity/src/instrum.h @@ -0,0 +1,41 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + instrum.h + + */ + +/* Bits in modes: */ +#define MODES_16BIT (1<<0) +#define MODES_UNSIGNED (1<<1) +#define MODES_LOOPING (1<<2) +#define MODES_PINGPONG (1<<3) +#define MODES_REVERSE (1<<4) +#define MODES_SUSTAIN (1<<5) +#define MODES_ENVELOPE (1<<6) + +/* A hack to delay instrument loading until after reading the + entire MIDI file. */ +#define MAGIC_LOAD_INSTRUMENT ((MidInstrument *) (-1)) + +#define SPECIAL_PROGRAM -1 + +extern int load_missing_instruments(MidSong *song); +extern void free_instruments(MidSong *song); +extern int set_default_instrument(MidSong *song, char *name); diff --git a/project/jni/timidity/src/instrum_dls.c b/project/jni/timidity/src/instrum_dls.c new file mode 100644 index 000000000..8aa48e59f --- /dev/null +++ b/project/jni/timidity/src/instrum_dls.c @@ -0,0 +1,1196 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + instrum.h + + */ + +#include +#include +#include + +#include "timidity.h" +#include "timidity_internal.h" +#include "options.h" +#include "instrum.h" +#include "tables.h" +#include "common.h" + +/*-------------------------------------------------------------------------*/ +/* * * * * * * * * * * * * * * * * load_riff.h * * * * * * * * * * * * * * */ +/*-------------------------------------------------------------------------*/ +typedef struct _RIFF_Chunk { + uint32 magic; + uint32 length; + uint32 subtype; + uint8 *data; + struct _RIFF_Chunk *child; + struct _RIFF_Chunk *next; +} RIFF_Chunk; + +extern RIFF_Chunk* LoadRIFF(MidIStream *stream); +extern void FreeRIFF(RIFF_Chunk *chunk); +extern void PrintRIFF(RIFF_Chunk *chunk, int level); +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*-------------------------------------------------------------------------*/ +/* * * * * * * * * * * * * * * * * load_riff.c * * * * * * * * * * * * * * */ +/*-------------------------------------------------------------------------*/ +#define RIFF 0x46464952 /* "RIFF" */ +#define LIST 0x5453494c /* "LIST" */ + +static RIFF_Chunk *AllocRIFFChunk() +{ + RIFF_Chunk *chunk = (RIFF_Chunk *)safe_malloc(sizeof(*chunk)); + if ( !chunk ) return NULL; + memset(chunk, 0, sizeof(*chunk)); + return chunk; +} + +static void FreeRIFFChunk(RIFF_Chunk *chunk) +{ + if ( chunk->child ) { + FreeRIFFChunk(chunk->child); + } + if ( chunk->next ) { + FreeRIFFChunk(chunk->next); + } + free(chunk); +} + +static int ChunkHasSubType(uint32 magic) +{ + static uint32 chunk_list[] = { + RIFF, LIST + }; + int i; + for ( i = 0; i < (sizeof(chunk_list) / sizeof(uint32)); ++i ) { + if ( magic == chunk_list[i] ) { + return 1; + } + } + return 0; +} + +static int ChunkHasSubChunks(uint32 magic) +{ + static uint32 chunk_list[] = { + RIFF, LIST + }; + int i; + for ( i = 0; i < (sizeof(chunk_list) / sizeof(uint32)); ++i ) { + if ( magic == chunk_list[i] ) { + return 1; + } + } + return 0; +} + +static void LoadSubChunks(RIFF_Chunk *chunk, uint8 *data, uint32 left) +{ + uint8 *subchunkData; + uint32 subchunkDataLen; + + while ( left > 8 ) { + RIFF_Chunk *child = AllocRIFFChunk(); + RIFF_Chunk *next, *prev = NULL; + for ( next = chunk->child; next; next = next->next ) { + prev = next; + } + if ( prev ) { + prev->next = child; + } else { + chunk->child = child; + } + + child->magic = (data[0] << 0) | + (data[1] << 8) | + (data[2] << 16) | + (data[3] << 24); + data += 4; + left -= 4; + child->length = (data[0] << 0) | + (data[1] << 8) | + (data[2] << 16) | + (data[3] << 24); + data += 4; + left -= 4; + child->data = data; + + if ( child->length > left ) { + child->length = left; + } + + subchunkData = child->data; + subchunkDataLen = child->length; + if ( ChunkHasSubType(child->magic) && subchunkDataLen >= 4 ) { + child->subtype = (subchunkData[0] << 0) | + (subchunkData[1] << 8) | + (subchunkData[2] << 16) | + (subchunkData[3] << 24); + subchunkData += 4; + subchunkDataLen -= 4; + } + if ( ChunkHasSubChunks(child->magic) ) { + LoadSubChunks(child, subchunkData, subchunkDataLen); + } + + data += child->length; + left -= child->length; + } +} + +RIFF_Chunk *LoadRIFF(MidIStream *stream) +{ + RIFF_Chunk *chunk; + uint8 *subchunkData; + uint32 subchunkDataLen; + uint32 tmpUint32; + + /* Allocate the chunk structure */ + chunk = AllocRIFFChunk(); + + /* Make sure the file is in RIFF format */ + mid_istream_read(stream, &tmpUint32, sizeof(uint32), 1); + chunk->magic = SWAPLE32(tmpUint32); + mid_istream_read(stream, &tmpUint32, sizeof(uint32), 1); + chunk->length = SWAPLE32(tmpUint32); + if ( chunk->magic != RIFF ) { + DEBUG_MSG("Not a RIFF file\n"); + FreeRIFFChunk(chunk); + return NULL; + } + chunk->data = (uint8 *)malloc(chunk->length); + if ( chunk->data == NULL ) { + DEBUG_MSG("Out of memory\n"); + FreeRIFFChunk(chunk); + return NULL; + } + if ( mid_istream_read(stream, chunk->data, chunk->length, 1) != 1 ) { + DEBUG_MSG("IO error\n"); + FreeRIFF(chunk); + return NULL; + } + subchunkData = chunk->data; + subchunkDataLen = chunk->length; + if ( ChunkHasSubType(chunk->magic) && subchunkDataLen >= 4 ) { + chunk->subtype = (subchunkData[0] << 0) | + (subchunkData[1] << 8) | + (subchunkData[2] << 16) | + (subchunkData[3] << 24); + subchunkData += 4; + subchunkDataLen -= 4; + } + if ( ChunkHasSubChunks(chunk->magic) ) { + LoadSubChunks(chunk, subchunkData, subchunkDataLen); + } + return chunk; +} + +void FreeRIFF(RIFF_Chunk *chunk) +{ + free(chunk->data); + FreeRIFFChunk(chunk); +} + +void PrintRIFF(RIFF_Chunk *chunk, int level) +{ + static char prefix[128]; + + if ( level == sizeof(prefix)-1 ) { + return; + } + if ( level > 0 ) { + prefix[(level-1)*2] = ' '; + prefix[(level-1)*2+1] = ' '; + } + prefix[level*2] = '\0'; + printf("%sChunk: %c%c%c%c (%d bytes)", prefix, + ((chunk->magic >> 0) & 0xFF), + ((chunk->magic >> 8) & 0xFF), + ((chunk->magic >> 16) & 0xFF), + ((chunk->magic >> 24) & 0xFF), chunk->length); + if ( chunk->subtype ) { + printf(" subtype: %c%c%c%c", + ((chunk->subtype >> 0) & 0xFF), + ((chunk->subtype >> 8) & 0xFF), + ((chunk->subtype >> 16) & 0xFF), + ((chunk->subtype >> 24) & 0xFF)); + } + printf("\n"); + if ( chunk->child ) { + printf("%s{\n", prefix); + PrintRIFF(chunk->child, level + 1); + printf("%s}\n", prefix); + } + if ( chunk->next ) { + PrintRIFF(chunk->next, level); + } + if ( level > 0 ) { + prefix[(level-1)*2] = '\0'; + } +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*-------------------------------------------------------------------------*/ +/* * * * * * * * * * * * * * * * * load_dls.h * * * * * * * * * * * * * * */ +/*-------------------------------------------------------------------------*/ +/* This code is based on the DLS spec version 1.1, available at: + http://www.midi.org/about-midi/dls/dlsspec.shtml +*/ + +/* Some typedefs so the public dls headers don't need to be modified */ +#define FAR +typedef uint8 BYTE; +typedef sint16 SHORT; +typedef uint16 USHORT; +typedef uint16 WORD; +typedef sint32 LONG; +typedef uint32 ULONG; +typedef uint32 DWORD; +#define mmioFOURCC(A, B, C, D) \ + (((A) << 0) | ((B) << 8) | ((C) << 16) | ((D) << 24)) +#define DEFINE_GUID(A, B, C, E, F, G, H, I, J, K, L, M) + +#include "dls1.h" +#include "dls2.h" + +typedef struct _WaveFMT { + WORD wFormatTag; + WORD wChannels; + DWORD dwSamplesPerSec; + DWORD dwAvgBytesPerSec; + WORD wBlockAlign; + WORD wBitsPerSample; +} WaveFMT; + +typedef struct _DLS_Wave { + WaveFMT *format; + uint8 *data; + uint32 length; + WSMPL *wsmp; + WLOOP *wsmp_loop; +} DLS_Wave; + +typedef struct _DLS_Region { + RGNHEADER *header; + WAVELINK *wlnk; + WSMPL *wsmp; + WLOOP *wsmp_loop; + CONNECTIONLIST *art; + CONNECTION *artList; +} DLS_Region; + +typedef struct _DLS_Instrument { + const char *name; + INSTHEADER *header; + DLS_Region *regions; + CONNECTIONLIST *art; + CONNECTION *artList; +} DLS_Instrument; + +struct _MidDLSPatches { + struct _RIFF_Chunk *chunk; + + uint32 cInstruments; + DLS_Instrument *instruments; + + POOLTABLE *ptbl; + POOLCUE *ptblList; + DLS_Wave *waveList; + + const char *name; + const char *artist; + const char *copyright; + const char *comments; +}; + +extern MidDLSPatches* mid_dlspatches_load(MidIStream *stream); +extern void mid_dlspatches_free(MidDLSPatches *chunk); +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*-------------------------------------------------------------------------*/ +/* * * * * * * * * * * * * * * * * load_dls.c * * * * * * * * * * * * * * */ +/*-------------------------------------------------------------------------*/ + +#define FOURCC_LIST 0x5453494c /* "LIST" */ +#define FOURCC_FMT 0x20746D66 /* "fmt " */ +#define FOURCC_DATA 0x61746164 /* "data" */ +#define FOURCC_INFO mmioFOURCC('I','N','F','O') +#define FOURCC_IARL mmioFOURCC('I','A','R','L') +#define FOURCC_IART mmioFOURCC('I','A','R','T') +#define FOURCC_ICMS mmioFOURCC('I','C','M','S') +#define FOURCC_ICMT mmioFOURCC('I','C','M','T') +#define FOURCC_ICOP mmioFOURCC('I','C','O','P') +#define FOURCC_ICRD mmioFOURCC('I','C','R','D') +#define FOURCC_IENG mmioFOURCC('I','E','N','G') +#define FOURCC_IGNR mmioFOURCC('I','G','N','R') +#define FOURCC_IKEY mmioFOURCC('I','K','E','Y') +#define FOURCC_IMED mmioFOURCC('I','M','E','D') +#define FOURCC_INAM mmioFOURCC('I','N','A','M') +#define FOURCC_IPRD mmioFOURCC('I','P','R','D') +#define FOURCC_ISBJ mmioFOURCC('I','S','B','J') +#define FOURCC_ISFT mmioFOURCC('I','S','F','T') +#define FOURCC_ISRC mmioFOURCC('I','S','R','C') +#define FOURCC_ISRF mmioFOURCC('I','S','R','F') +#define FOURCC_ITCH mmioFOURCC('I','T','C','H') + + +static void FreeRegions(DLS_Instrument *instrument) +{ + if ( instrument->regions ) { + free(instrument->regions); + } +} + +static void AllocRegions(DLS_Instrument *instrument) +{ + int datalen = (instrument->header->cRegions * sizeof(DLS_Region)); + FreeRegions(instrument); + instrument->regions = (DLS_Region *)malloc(datalen); + if ( instrument->regions ) { + memset(instrument->regions, 0, datalen); + } +} + +static void FreeInstruments(MidDLSPatches *data) +{ + if ( data->instruments ) { + uint32 i; + for ( i = 0; i < data->cInstruments; ++i ) { + FreeRegions(&data->instruments[i]); + } + free(data->instruments); + } +} + +static void AllocInstruments(MidDLSPatches *data) +{ + int datalen = (data->cInstruments * sizeof(DLS_Instrument)); + FreeInstruments(data); + data->instruments = (DLS_Instrument *)malloc(datalen); + if ( data->instruments ) { + memset(data->instruments, 0, datalen); + } +} + +static void FreeWaveList(MidDLSPatches *data) +{ + if ( data->waveList ) { + free(data->waveList); + } +} + +static void AllocWaveList(MidDLSPatches *data) +{ + int datalen = (data->ptbl->cCues * sizeof(DLS_Wave)); + FreeWaveList(data); + data->waveList = (DLS_Wave *)malloc(datalen); + if ( data->waveList ) { + memset(data->waveList, 0, datalen); + } +} + +static void Parse_colh(MidDLSPatches *data, RIFF_Chunk *chunk) +{ + data->cInstruments = SWAPLE32(*(uint32 *)chunk->data); + AllocInstruments(data); +} + +static void Parse_insh(MidDLSPatches *data, RIFF_Chunk *chunk, DLS_Instrument *instrument) +{ + INSTHEADER *header = (INSTHEADER *)chunk->data; + header->cRegions = SWAPLE32(header->cRegions); + header->Locale.ulBank = SWAPLE32(header->Locale.ulBank); + header->Locale.ulInstrument = SWAPLE32(header->Locale.ulInstrument); + instrument->header = header; + AllocRegions(instrument); +} + +static void Parse_rgnh(MidDLSPatches *data, RIFF_Chunk *chunk, DLS_Region *region) +{ + RGNHEADER *header = (RGNHEADER *)chunk->data; + header->RangeKey.usLow = SWAPLE16(header->RangeKey.usLow); + header->RangeKey.usHigh = SWAPLE16(header->RangeKey.usHigh); + header->RangeVelocity.usLow = SWAPLE16(header->RangeVelocity.usLow); + header->RangeVelocity.usHigh = SWAPLE16(header->RangeVelocity.usHigh); + header->fusOptions = SWAPLE16(header->fusOptions); + header->usKeyGroup = SWAPLE16(header->usKeyGroup); + region->header = header; +} + +static void Parse_wlnk(MidDLSPatches *data, RIFF_Chunk *chunk, DLS_Region *region) +{ + WAVELINK *wlnk = (WAVELINK *)chunk->data; + wlnk->fusOptions = SWAPLE16(wlnk->fusOptions); + wlnk->usPhaseGroup = SWAPLE16(wlnk->usPhaseGroup); + wlnk->ulChannel = SWAPLE16(wlnk->ulChannel); + wlnk->ulTableIndex = SWAPLE16(wlnk->ulTableIndex); + region->wlnk = wlnk; +} + +static void Parse_wsmp(MidDLSPatches *data, RIFF_Chunk *chunk, WSMPL **wsmp_ptr, WLOOP **wsmp_loop_ptr) +{ + uint32 i; + WSMPL *wsmp = (WSMPL *)chunk->data; + WLOOP *loop; + wsmp->cbSize = SWAPLE32(wsmp->cbSize); + wsmp->usUnityNote = SWAPLE16(wsmp->usUnityNote); + wsmp->sFineTune = SWAPLE16(wsmp->sFineTune); + wsmp->lAttenuation = SWAPLE32(wsmp->lAttenuation); + wsmp->fulOptions = SWAPLE32(wsmp->fulOptions); + wsmp->cSampleLoops = SWAPLE32(wsmp->cSampleLoops); + loop = (WLOOP *)((uint8 *)chunk->data + wsmp->cbSize); + *wsmp_ptr = wsmp; + *wsmp_loop_ptr = loop; + for ( i = 0; i < wsmp->cSampleLoops; ++i ) { + loop->cbSize = SWAPLE32(loop->cbSize); + loop->ulType = SWAPLE32(loop->ulType); + loop->ulStart = SWAPLE32(loop->ulStart); + loop->ulLength = SWAPLE32(loop->ulLength); + ++loop; + } +} + +static void Parse_art(MidDLSPatches *data, RIFF_Chunk *chunk, CONNECTIONLIST **art_ptr, CONNECTION **artList_ptr) +{ + uint32 i; + CONNECTIONLIST *art = (CONNECTIONLIST *)chunk->data; + CONNECTION *artList; + art->cbSize = SWAPLE32(art->cbSize); + art->cConnections = SWAPLE32(art->cConnections); + artList = (CONNECTION *)((uint8 *)chunk->data + art->cbSize); + *art_ptr = art; + *artList_ptr = artList; + for ( i = 0; i < art->cConnections; ++i ) { + artList->usSource = SWAPLE16(artList->usSource); + artList->usControl = SWAPLE16(artList->usControl); + artList->usDestination = SWAPLE16(artList->usDestination); + artList->usTransform = SWAPLE16(artList->usTransform); + artList->lScale = SWAPLE32(artList->lScale); + ++artList; + } +} + +static void Parse_lart(MidDLSPatches *data, RIFF_Chunk *chunk, CONNECTIONLIST **conn_ptr, CONNECTION **connList_ptr) +{ + /* FIXME: This only supports one set of connections */ + for ( chunk = chunk->child; chunk; chunk = chunk->next ) { + uint32 magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic; + switch(magic) { + case FOURCC_ART1: + case FOURCC_ART2: + Parse_art(data, chunk, conn_ptr, connList_ptr); + return; + } + } +} + +static void Parse_rgn(MidDLSPatches *data, RIFF_Chunk *chunk, DLS_Region *region) +{ + for ( chunk = chunk->child; chunk; chunk = chunk->next ) { + uint32 magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic; + switch(magic) { + case FOURCC_RGNH: + Parse_rgnh(data, chunk, region); + break; + case FOURCC_WLNK: + Parse_wlnk(data, chunk, region); + break; + case FOURCC_WSMP: + Parse_wsmp(data, chunk, ®ion->wsmp, ®ion->wsmp_loop); + break; + case FOURCC_LART: + case FOURCC_LAR2: + Parse_lart(data, chunk, ®ion->art, ®ion->artList); + break; + } + } +} + +static void Parse_lrgn(MidDLSPatches *data, RIFF_Chunk *chunk, DLS_Instrument *instrument) +{ + uint32 region = 0; + for ( chunk = chunk->child; chunk; chunk = chunk->next ) { + uint32 magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic; + switch(magic) { + case FOURCC_RGN: + case FOURCC_RGN2: + if ( region < instrument->header->cRegions ) { + Parse_rgn(data, chunk, &instrument->regions[region++]); + } + break; + } + } +} + +static void Parse_INFO_INS(MidDLSPatches *data, RIFF_Chunk *chunk, DLS_Instrument *instrument) +{ + for ( chunk = chunk->child; chunk; chunk = chunk->next ) { + uint32 magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic; + switch(magic) { + case FOURCC_INAM: /* Name */ + instrument->name = chunk->data; + break; + } + } +} + +static void Parse_ins(MidDLSPatches *data, RIFF_Chunk *chunk, DLS_Instrument *instrument) +{ + for ( chunk = chunk->child; chunk; chunk = chunk->next ) { + uint32 magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic; + switch(magic) { + case FOURCC_INSH: + Parse_insh(data, chunk, instrument); + break; + case FOURCC_LRGN: + Parse_lrgn(data, chunk, instrument); + break; + case FOURCC_LART: + case FOURCC_LAR2: + Parse_lart(data, chunk, &instrument->art, &instrument->artList); + break; + case FOURCC_INFO: + Parse_INFO_INS(data, chunk, instrument); + break; + } + } +} + +static void Parse_lins(MidDLSPatches *data, RIFF_Chunk *chunk) +{ + uint32 instrument = 0; + for ( chunk = chunk->child; chunk; chunk = chunk->next ) { + uint32 magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic; + switch(magic) { + case FOURCC_INS: + if ( instrument < data->cInstruments ) { + Parse_ins(data, chunk, &data->instruments[instrument++]); + } + break; + } + } +} + +static void Parse_ptbl(MidDLSPatches *data, RIFF_Chunk *chunk) +{ + uint32 i; + POOLTABLE *ptbl = (POOLTABLE *)chunk->data; + ptbl->cbSize = SWAPLE32(ptbl->cbSize); + ptbl->cCues = SWAPLE32(ptbl->cCues); + data->ptbl = ptbl; + data->ptblList = (POOLCUE *)((uint8 *)chunk->data + ptbl->cbSize); + for ( i = 0; i < ptbl->cCues; ++i ) { + data->ptblList[i].ulOffset = SWAPLE32(data->ptblList[i].ulOffset); + } + AllocWaveList(data); +} + +static void Parse_fmt(MidDLSPatches *data, RIFF_Chunk *chunk, DLS_Wave *wave) +{ + WaveFMT *fmt = (WaveFMT *)chunk->data; + fmt->wFormatTag = SWAPLE16(fmt->wFormatTag); + fmt->wChannels = SWAPLE16(fmt->wChannels); + fmt->dwSamplesPerSec = SWAPLE32(fmt->dwSamplesPerSec); + fmt->dwAvgBytesPerSec = SWAPLE32(fmt->dwAvgBytesPerSec); + fmt->wBlockAlign = SWAPLE16(fmt->wBlockAlign); + fmt->wBitsPerSample = SWAPLE16(fmt->wBitsPerSample); + wave->format = fmt; +} + +static void Parse_data(MidDLSPatches *data, RIFF_Chunk *chunk, DLS_Wave *wave) +{ + wave->data = chunk->data; + wave->length = chunk->length; +} + +static void Parse_wave(MidDLSPatches *data, RIFF_Chunk *chunk, DLS_Wave *wave) +{ + for ( chunk = chunk->child; chunk; chunk = chunk->next ) { + uint32 magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic; + switch(magic) { + case FOURCC_FMT: + Parse_fmt(data, chunk, wave); + break; + case FOURCC_DATA: + Parse_data(data, chunk, wave); + break; + case FOURCC_WSMP: + Parse_wsmp(data, chunk, &wave->wsmp, &wave->wsmp_loop); + break; + } + } +} + +static void Parse_wvpl(MidDLSPatches *data, RIFF_Chunk *chunk) +{ + uint32 wave = 0; + for ( chunk = chunk->child; chunk; chunk = chunk->next ) { + uint32 magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic; + switch(magic) { + case FOURCC_wave: + if ( wave < data->ptbl->cCues ) { + Parse_wave(data, chunk, &data->waveList[wave++]); + } + break; + } + } +} + +static void Parse_INFO_DLS(MidDLSPatches *data, RIFF_Chunk *chunk) +{ + for ( chunk = chunk->child; chunk; chunk = chunk->next ) { + uint32 magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic; + switch(magic) { + case FOURCC_IARL: /* Archival Location */ + break; + case FOURCC_IART: /* Artist */ + data->artist = chunk->data; + break; + case FOURCC_ICMS: /* Commisioned */ + break; + case FOURCC_ICMT: /* Comments */ + data->comments = chunk->data; + break; + case FOURCC_ICOP: /* Copyright */ + data->copyright = chunk->data; + break; + case FOURCC_ICRD: /* Creation Date */ + break; + case FOURCC_IENG: /* Engineer */ + break; + case FOURCC_IGNR: /* Genre */ + break; + case FOURCC_IKEY: /* Keywords */ + break; + case FOURCC_IMED: /* Medium */ + break; + case FOURCC_INAM: /* Name */ + data->name = chunk->data; + break; + case FOURCC_IPRD: /* Product */ + break; + case FOURCC_ISBJ: /* Subject */ + break; + case FOURCC_ISFT: /* Software */ + break; + case FOURCC_ISRC: /* Source */ + break; + case FOURCC_ISRF: /* Source Form */ + break; + case FOURCC_ITCH: /* Technician */ + break; + } + } +} + +MidDLSPatches *mid_dlspatches_load(MidIStream *stream) +{ + RIFF_Chunk *chunk; + MidDLSPatches *data = (MidDLSPatches *)safe_malloc(sizeof(*data)); + if ( !data ) return NULL; + memset(data, 0, sizeof(*data)); + + data->chunk = LoadRIFF(stream); + if ( !data->chunk ) { + mid_dlspatches_free(data); + return NULL; + } + + for ( chunk = data->chunk->child; chunk; chunk = chunk->next ) { + uint32 magic = (chunk->magic == FOURCC_LIST) ? chunk->subtype : chunk->magic; + switch(magic) { + case FOURCC_COLH: + Parse_colh(data, chunk); + break; + case FOURCC_LINS: + Parse_lins(data, chunk); + break; + case FOURCC_PTBL: + Parse_ptbl(data, chunk); + break; + case FOURCC_WVPL: + Parse_wvpl(data, chunk); + break; + case FOURCC_INFO: + Parse_INFO_DLS(data, chunk); + break; + } + } + return data; +} + +void mid_dlspatches_free(MidDLSPatches *data) +{ + if ( data->chunk ) { + FreeRIFF(data->chunk); + } + FreeInstruments(data); + FreeWaveList(data); + free(data); +} + +static const char *SourceToString(USHORT usSource) +{ + switch(usSource) { + case CONN_SRC_NONE: + return "NONE"; + case CONN_SRC_LFO: + return "LFO"; + case CONN_SRC_KEYONVELOCITY: + return "KEYONVELOCITY"; + case CONN_SRC_KEYNUMBER: + return "KEYNUMBER"; + case CONN_SRC_EG1: + return "EG1"; + case CONN_SRC_EG2: + return "EG2"; + case CONN_SRC_PITCHWHEEL: + return "PITCHWHEEL"; + case CONN_SRC_CC1: + return "CC1"; + case CONN_SRC_CC7: + return "CC7"; + case CONN_SRC_CC10: + return "CC10"; + case CONN_SRC_CC11: + return "CC11"; + case CONN_SRC_POLYPRESSURE: + return "POLYPRESSURE"; + case CONN_SRC_CHANNELPRESSURE: + return "CHANNELPRESSURE"; + case CONN_SRC_VIBRATO: + return "VIBRATO"; + case CONN_SRC_MONOPRESSURE: + return "MONOPRESSURE"; + case CONN_SRC_CC91: + return "CC91"; + case CONN_SRC_CC93: + return "CC93"; + default: + return "UNKNOWN"; + } +} + +static const char *TransformToString(USHORT usTransform) +{ + switch (usTransform) { + case CONN_TRN_NONE: + return "NONE"; + case CONN_TRN_CONCAVE: + return "CONCAVE"; + case CONN_TRN_CONVEX: + return "CONVEX"; + case CONN_TRN_SWITCH: + return "SWITCH"; + default: + return "UNKNOWN"; + } +} + +static const char *DestinationToString(USHORT usDestination) +{ + switch (usDestination) { + case CONN_DST_NONE: + return "NONE"; + case CONN_DST_ATTENUATION: + return "ATTENUATION"; + case CONN_DST_PITCH: + return "PITCH"; + case CONN_DST_PAN: + return "PAN"; + case CONN_DST_LFO_FREQUENCY: + return "LFO_FREQUENCY"; + case CONN_DST_LFO_STARTDELAY: + return "LFO_STARTDELAY"; + case CONN_DST_EG1_ATTACKTIME: + return "EG1_ATTACKTIME"; + case CONN_DST_EG1_DECAYTIME: + return "EG1_DECAYTIME"; + case CONN_DST_EG1_RELEASETIME: + return "EG1_RELEASETIME"; + case CONN_DST_EG1_SUSTAINLEVEL: + return "EG1_SUSTAINLEVEL"; + case CONN_DST_EG2_ATTACKTIME: + return "EG2_ATTACKTIME"; + case CONN_DST_EG2_DECAYTIME: + return "EG2_DECAYTIME"; + case CONN_DST_EG2_RELEASETIME: + return "EG2_RELEASETIME"; + case CONN_DST_EG2_SUSTAINLEVEL: + return "EG2_SUSTAINLEVEL"; + case CONN_DST_KEYNUMBER: + return "KEYNUMBER"; + case CONN_DST_LEFT: + return "LEFT"; + case CONN_DST_RIGHT: + return "RIGHT"; + case CONN_DST_CENTER: + return "CENTER"; + case CONN_DST_LEFTREAR: + return "LEFTREAR"; + case CONN_DST_RIGHTREAR: + return "RIGHTREAR"; + case CONN_DST_LFE_CHANNEL: + return "LFE_CHANNEL"; + case CONN_DST_CHORUS: + return "CHORUS"; + case CONN_DST_REVERB: + return "REVERB"; + case CONN_DST_VIB_FREQUENCY: + return "VIB_FREQUENCY"; + case CONN_DST_VIB_STARTDELAY: + return "VIB_STARTDELAY"; + case CONN_DST_EG1_DELAYTIME: + return "EG1_DELAYTIME"; + case CONN_DST_EG1_HOLDTIME: + return "EG1_HOLDTIME"; + case CONN_DST_EG1_SHUTDOWNTIME: + return "EG1_SHUTDOWNTIME"; + case CONN_DST_EG2_DELAYTIME: + return "EG2_DELAYTIME"; + case CONN_DST_EG2_HOLDTIME: + return "EG2_HOLDTIME"; + case CONN_DST_FILTER_CUTOFF: + return "FILTER_CUTOFF"; + case CONN_DST_FILTER_Q: + return "FILTER_Q"; + default: + return "UNKOWN"; + } +} + +static void PrintArt(const char *type, CONNECTIONLIST *art, CONNECTION *artList) +{ + uint32 i; + printf("%s Connections:\n", type); + for ( i = 0; i < art->cConnections; ++i ) { + printf(" Source: %s, Control: %s, Destination: %s, Transform: %s, Scale: %d\n", + SourceToString(artList[i].usSource), + SourceToString(artList[i].usControl), + DestinationToString(artList[i].usDestination), + TransformToString(artList[i].usTransform), + artList[i].lScale); + } +} + +static void PrintWave(DLS_Wave *wave, uint32 index) +{ + WaveFMT *format = wave->format; + if ( format ) { + printf(" Wave %u: Format: %hu, %hu channels, %u Hz, %hu bits (length = %u)\n", index, format->wFormatTag, format->wChannels, format->dwSamplesPerSec, format->wBitsPerSample, wave->length); + } + if ( wave->wsmp ) { + uint32 i; + printf(" wsmp->usUnityNote = %hu\n", wave->wsmp->usUnityNote); + printf(" wsmp->sFineTune = %hd\n", wave->wsmp->sFineTune); + printf(" wsmp->lAttenuation = %d\n", wave->wsmp->lAttenuation); + printf(" wsmp->fulOptions = 0x%8.8x\n", wave->wsmp->fulOptions); + printf(" wsmp->cSampleLoops = %u\n", wave->wsmp->cSampleLoops); + for ( i = 0; i < wave->wsmp->cSampleLoops; ++i ) { + WLOOP *loop = &wave->wsmp_loop[i]; + printf(" Loop %u:\n", i); + printf(" ulStart = %u\n", loop->ulStart); + printf(" ulLength = %u\n", loop->ulLength); + } + } +} + +static void PrintRegion(DLS_Region *region, uint32 index) +{ + printf(" Region %u:\n", index); + if ( region->header ) { + printf(" RangeKey = { %hu - %hu }\n", region->header->RangeKey.usLow, region->header->RangeKey.usHigh); + printf(" RangeVelocity = { %hu - %hu }\n", region->header->RangeVelocity.usLow, region->header->RangeVelocity.usHigh); + printf(" fusOptions = 0x%4.4hx\n", region->header->fusOptions); + printf(" usKeyGroup = %hu\n", region->header->usKeyGroup); + } + if ( region->wlnk ) { + printf(" wlnk->fusOptions = 0x%4.4hx\n", region->wlnk->fusOptions); + printf(" wlnk->usPhaseGroup = %hu\n", region->wlnk->usPhaseGroup); + printf(" wlnk->ulChannel = %u\n", region->wlnk->ulChannel); + printf(" wlnk->ulTableIndex = %u\n", region->wlnk->ulTableIndex); + } + if ( region->wsmp ) { + uint32 i; + printf(" wsmp->usUnityNote = %hu\n", region->wsmp->usUnityNote); + printf(" wsmp->sFineTune = %hd\n", region->wsmp->sFineTune); + printf(" wsmp->lAttenuation = %d\n", region->wsmp->lAttenuation); + printf(" wsmp->fulOptions = 0x%8.8x\n", region->wsmp->fulOptions); + printf(" wsmp->cSampleLoops = %u\n", region->wsmp->cSampleLoops); + for ( i = 0; i < region->wsmp->cSampleLoops; ++i ) { + WLOOP *loop = ®ion->wsmp_loop[i]; + printf(" Loop %u:\n", i); + printf(" ulStart = %u\n", loop->ulStart); + printf(" ulLength = %u\n", loop->ulLength); + } + } + if ( region->art && region->art->cConnections > 0 ) { + PrintArt("Region", region->art, region->artList); + } +} + +static void PrintInstrument(DLS_Instrument *instrument, uint32 index) +{ + printf("Instrument %u:\n", index); + if ( instrument->name ) { + printf(" Name: %s\n", instrument->name); + } + if ( instrument->header ) { + uint32 i; + printf(" ulBank = 0x%8.8x\n", instrument->header->Locale.ulBank); + printf(" ulInstrument = %u\n", instrument->header->Locale.ulInstrument); + printf(" Regions: %u\n", instrument->header->cRegions); + for ( i = 0; i < instrument->header->cRegions; ++i ) { + PrintRegion(&instrument->regions[i], i); + } + } + if ( instrument->art && instrument->art->cConnections > 0 ) { + PrintArt("Instrument", instrument->art, instrument->artList); + } +}; + +void PrintDLS(MidDLSPatches *data) +{ + printf("DLS Data:\n"); + printf("cInstruments = %u\n", data->cInstruments); + if ( data->instruments ) { + uint32 i; + for ( i = 0; i < data->cInstruments; ++i ) { + PrintInstrument(&data->instruments[i], i); + } + } + if ( data->ptbl && data->ptbl->cCues > 0 ) { + uint32 i; + printf("Cues: "); + for ( i = 0; i < data->ptbl->cCues; ++i ) { + if ( i > 0 ) { + printf(", "); + } + printf("%u", data->ptblList[i].ulOffset); + } + printf("\n"); + } + if ( data->waveList ) { + uint32 i; + printf("Waves:\n"); + for ( i = 0; i < data->ptbl->cCues; ++i ) { + PrintWave(&data->waveList[i], i); + } + } + if ( data->name ) { + printf("Name: %s\n", data->name); + } + if ( data->artist ) { + printf("Artist: %s\n", data->artist); + } + if ( data->copyright ) { + printf("Copyright: %s\n", data->copyright); + } + if ( data->comments ) { + printf("Comments: %s\n", data->comments); + } +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*-------------------------------------------------------------------------*/ +/* * * * * * * * * * * * * * * * * instrum_dls.c * * * * * * * * * * * * * */ +/*-------------------------------------------------------------------------*/ + +/* convert timecents to sec */ +static double to_msec(int timecent) +{ + if (timecent == 0x80000000 || timecent == 0) + return 0.0; + return 1000.0 * pow(2.0, (double)(timecent / 65536) / 1200.0); +} + +/* convert decipercent to {0..1} */ +static double to_normalized_percent(int decipercent) +{ + return ((double)(decipercent / 65536)) / 1000.0; +} + +/* convert from 8bit value to fractional offset (15.15) */ +static sint32 to_offset(int offset) +{ + return (sint32)offset << (7+15); +} + +/* calculate ramp rate in fractional unit; + * diff = 8bit, time = msec + */ +static sint32 calc_rate(MidSong *song, int diff, int sample_rate, double msec) +{ + double rate; + + if(msec < 6) + msec = 6; + if(diff == 0) + diff = 255; + diff <<= (7+15); + rate = ((double)diff / song->rate) * song->control_ratio * 1000.0 / msec; + return (sint32)rate; +} + +static int load_connection(ULONG cConnections, CONNECTION *artList, USHORT destination) +{ + ULONG i; + int value = 0; + for (i = 0; i < cConnections; ++i) { + CONNECTION *conn = &artList[i]; + if(conn->usDestination == destination) { + // The formula for the destination is: + // usDestination = usDestination + usTransform(usSource * (usControl * lScale)) + // Since we are only handling source/control of NONE and identity + // transform, this simplifies to: usDestination = usDestination + lScale + if (conn->usSource == CONN_SRC_NONE && + conn->usControl == CONN_SRC_NONE && + conn->usTransform == CONN_TRN_NONE) + value += conn->lScale; + } + } + return value; +} + +static void load_region_dls(MidSong *song, MidSample *sample, DLS_Instrument *ins, uint32 index) +{ + DLS_Region *rgn = &ins->regions[index]; + DLS_Wave *wave = &song->patches->waveList[rgn->wlnk->ulTableIndex]; + + sample->low_freq = freq_table[rgn->header->RangeKey.usLow]; + sample->high_freq = freq_table[rgn->header->RangeKey.usHigh]; + sample->root_freq = freq_table[rgn->wsmp->usUnityNote]; + sample->low_vel = rgn->header->RangeVelocity.usLow; + sample->high_vel = rgn->header->RangeVelocity.usHigh; + + sample->modes = MODES_16BIT; + sample->sample_rate = wave->format->dwSamplesPerSec; + sample->data_length = wave->length / 2; + sample->data = (sample_t *)safe_malloc(wave->length); + memcpy(sample->data, wave->data, wave->length); + if (rgn->wsmp->cSampleLoops) { + sample->modes |= (MODES_LOOPING|MODES_SUSTAIN); + sample->loop_start = rgn->wsmp_loop->ulStart / 2; + sample->loop_end = sample->loop_start + (rgn->wsmp_loop->ulLength / 2); + } + sample->volume = 1.0f; + + if (sample->modes & MODES_SUSTAIN) { + int value; + double attack, hold, decay, release; int sustain; + CONNECTIONLIST *art = NULL; + CONNECTION *artList = NULL; + + if (ins->art && ins->art->cConnections > 0 && ins->artList) { + art = ins->art; + artList = ins->artList; + } else { + art = rgn->art; + artList = rgn->artList; + } + + value = load_connection(art->cConnections, artList, CONN_DST_EG1_ATTACKTIME); + attack = to_msec(value); + value = load_connection(art->cConnections, artList, CONN_DST_EG1_HOLDTIME); + hold = to_msec(value); + value = load_connection(art->cConnections, artList, CONN_DST_EG1_DECAYTIME); + decay = to_msec(value); + value = load_connection(art->cConnections, artList, CONN_DST_EG1_RELEASETIME); + release = to_msec(value); + value = load_connection(art->cConnections, artList, CONN_DST_EG1_SUSTAINLEVEL); + sustain = (int)((1.0 - to_normalized_percent(value)) * 250.0); + value = load_connection(art->cConnections, artList, CONN_DST_PAN); + sample->panning = (int)((0.5 + to_normalized_percent(value)) * 127.0); + +/* +printf("%d, Rate=%d LV=%d HV=%d Low=%d Hi=%d Root=%d Pan=%d Attack=%f Hold=%f Sustain=%d Decay=%f Release=%f\n", index, sample->sample_rate, rgn->header->RangeVelocity.usLow, rgn->header->RangeVelocity.usHigh, sample->low_freq, sample->high_freq, sample->root_freq, sample->panning, attack, hold, sustain, decay, release); +*/ + + sample->envelope_offset[0] = to_offset(255); + sample->envelope_rate[0] = calc_rate(song, 255, sample->sample_rate, attack); + + sample->envelope_offset[1] = to_offset(250); + sample->envelope_rate[1] = calc_rate(song, 5, sample->sample_rate, hold); + + sample->envelope_offset[2] = to_offset(sustain); + sample->envelope_rate[2] = calc_rate(song, 255 - sustain, sample->sample_rate, decay); + + sample->envelope_offset[3] = to_offset(0); + sample->envelope_rate[3] = calc_rate(song, 5 + sustain, sample->sample_rate, release); + + sample->envelope_offset[4] = to_offset(0); + sample->envelope_rate[4] = to_offset(1); + + sample->envelope_offset[5] = to_offset(0); + sample->envelope_rate[5] = to_offset(1); + + sample->modes |= MODES_ENVELOPE; + } + + sample->data_length <<= FRACTION_BITS; + sample->loop_start <<= FRACTION_BITS; + sample->loop_end <<= FRACTION_BITS; +} + +MidInstrument *load_instrument_dls(MidSong *song, int drum, int bank, int instrument) +{ + MidInstrument *inst; + uint32 i; + DLS_Instrument *dls_ins; + + if (!song->patches) + return(NULL); + + drum = drum ? 0x80000000 : 0; + for (i = 0; i < song->patches->cInstruments; ++i) { + dls_ins = &song->patches->instruments[i]; + if ((dls_ins->header->Locale.ulBank & 0x80000000) == drum && + ((dls_ins->header->Locale.ulBank >> 8) & 0xFF) == bank && + dls_ins->header->Locale.ulInstrument == instrument) + break; + } + if (i == song->patches->cInstruments && !bank) { + for (i = 0; i < song->patches->cInstruments; ++i) { + dls_ins = &song->patches->instruments[i]; + if ((dls_ins->header->Locale.ulBank & 0x80000000) == drum && + dls_ins->header->Locale.ulInstrument == instrument) + break; + } + } + if (i == song->patches->cInstruments) { + DEBUG_MSG("Couldn't find %s instrument %d in bank %d\n", drum ? "drum" : "melodic", instrument, bank); + return(NULL); + } + + inst = (MidInstrument *)safe_malloc(sizeof(*inst)); + inst->samples = dls_ins->header->cRegions; + inst->sample = (MidSample *)safe_malloc(inst->samples * sizeof(*inst->sample)); + memset(inst->sample, 0, inst->samples * sizeof(*inst->sample)); +/* +printf("Found %s instrument %d in bank %d named %s with %d regions\n", drum ? "drum" : "melodic", instrument, bank, dls_ins->name, inst->samples); +*/ + for (i = 0; i < dls_ins->header->cRegions; ++i) { + load_region_dls(song, &inst->sample[i], dls_ins, i); + } + return(inst); +} diff --git a/project/jni/timidity/src/instrum_dls.h b/project/jni/timidity/src/instrum_dls.h new file mode 100644 index 000000000..60220b428 --- /dev/null +++ b/project/jni/timidity/src/instrum_dls.h @@ -0,0 +1,24 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + instrum.h + + */ + +extern MidInstrument *load_instrument_dls(MidSong *song, int drum, int bank, int instrument); diff --git a/project/jni/timidity/src/mix.c b/project/jni/timidity/src/mix.c new file mode 100644 index 000000000..1a120f76e --- /dev/null +++ b/project/jni/timidity/src/mix.c @@ -0,0 +1,569 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + Suddenly, you realize that this program is free software; you get + an overwhelming urge to redistribute it and/or modify it under the + terms of the GNU 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 General Public License for more details. + + You should have received another copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + I bet they'll be amazed. + + mix.c */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include "timidity.h" +#include "timidity_internal.h" +#include "options.h" +#include "instrum.h" +#include "playmidi.h" +#include "output.h" +#include "tables.h" +#include "resample.h" +#include "mix.h" + +/* Returns 1 if envelope runs out */ +int recompute_envelope(MidSong *song, int v) +{ + int stage; + + stage = song->voice[v].envelope_stage; + + if (stage>5) + { + /* Envelope ran out. */ + song->voice[v].status = VOICE_FREE; + return 1; + } + + if (song->voice[v].sample->modes & MODES_ENVELOPE) + { + if (song->voice[v].status==VOICE_ON || song->voice[v].status==VOICE_SUSTAINED) + { + if (stage>2) + { + /* Freeze envelope until note turns off. Trumpets want this. */ + song->voice[v].envelope_increment=0; + return 0; + } + } + } + song->voice[v].envelope_stage=stage+1; + + if (song->voice[v].envelope_volume==song->voice[v].sample->envelope_offset[stage]) + return recompute_envelope(song, v); + song->voice[v].envelope_target = song->voice[v].sample->envelope_offset[stage]; + song->voice[v].envelope_increment = song->voice[v].sample->envelope_rate[stage]; + if (song->voice[v].envelope_target < song->voice[v].envelope_volume) + song->voice[v].envelope_increment = -song->voice[v].envelope_increment; + return 0; +} + +void apply_envelope_to_amp(MidSong *song, int v) +{ + float lamp = song->voice[v].left_amp, ramp; + sint32 la,ra; + if (song->voice[v].panned == PANNED_MYSTERY) + { + ramp = song->voice[v].right_amp; + if (song->voice[v].tremolo_phase_increment) + { + lamp *= song->voice[v].tremolo_volume; + ramp *= song->voice[v].tremolo_volume; + } + if (song->voice[v].sample->modes & MODES_ENVELOPE) + { + lamp *= (float)vol_table[song->voice[v].envelope_volume>>23]; + ramp *= (float)vol_table[song->voice[v].envelope_volume>>23]; + } + + la = (sint32)FSCALE(lamp,AMP_BITS); + + if (la>MAX_AMP_VALUE) + la=MAX_AMP_VALUE; + + ra = (sint32)FSCALE(ramp,AMP_BITS); + if (ra>MAX_AMP_VALUE) + ra=MAX_AMP_VALUE; + + song->voice[v].left_mix = la; + song->voice[v].right_mix = ra; + } + else + { + if (song->voice[v].tremolo_phase_increment) + lamp *= song->voice[v].tremolo_volume; + if (song->voice[v].sample->modes & MODES_ENVELOPE) + lamp *= (float)vol_table[song->voice[v].envelope_volume>>23]; + + la = (sint32)FSCALE(lamp,AMP_BITS); + + if (la>MAX_AMP_VALUE) + la=MAX_AMP_VALUE; + + song->voice[v].left_mix = la; + } +} + +static int update_envelope(MidSong *song, int v) +{ + song->voice[v].envelope_volume += song->voice[v].envelope_increment; + /* Why is there no ^^ operator?? */ + if (((song->voice[v].envelope_increment < 0) && + (song->voice[v].envelope_volume <= song->voice[v].envelope_target)) || + ((song->voice[v].envelope_increment > 0) && + (song->voice[v].envelope_volume >= song->voice[v].envelope_target))) + { + song->voice[v].envelope_volume = song->voice[v].envelope_target; + if (recompute_envelope(song, v)) + return 1; + } + return 0; +} + +static void update_tremolo(MidSong *song, int v) +{ + sint32 depth = song->voice[v].sample->tremolo_depth << 7; + + if (song->voice[v].tremolo_sweep) + { + /* Update sweep position */ + + song->voice[v].tremolo_sweep_position += song->voice[v].tremolo_sweep; + if (song->voice[v].tremolo_sweep_position >= (1 << SWEEP_SHIFT)) + song->voice[v].tremolo_sweep=0; /* Swept to max amplitude */ + else + { + /* Need to adjust depth */ + depth *= song->voice[v].tremolo_sweep_position; + depth >>= SWEEP_SHIFT; + } + } + + song->voice[v].tremolo_phase += song->voice[v].tremolo_phase_increment; + + /* if (song->voice[v].tremolo_phase >= (SINE_CYCLE_LENGTH<voice[v].tremolo_phase -= SINE_CYCLE_LENGTH<voice[v].tremolo_volume = (float) + (1.0 - FSCALENEG((sine(song->voice[v].tremolo_phase >> RATE_SHIFT) + 1.0) + * depth * TREMOLO_AMPLITUDE_TUNING, + 17)); + + /* I'm not sure about the +1.0 there -- it makes tremoloed voices' + volumes on average the lower the higher the tremolo amplitude. */ +} + +/* Returns 1 if the note died */ +static int update_signal(MidSong *song, int v) +{ + if (song->voice[v].envelope_increment && update_envelope(song, v)) + return 1; + + if (song->voice[v].tremolo_phase_increment) + update_tremolo(song, v); + + apply_envelope_to_amp(song, v); + return 0; +} + +#define MIXATION(a) *lp++ += (a)*s; + +static void mix_mystery_signal(MidSong *song, sample_t *sp, sint32 *lp, int v, + int count) +{ + MidVoice *vp = song->voice + v; + final_volume_t + left=vp->left_mix, + right=vp->right_mix; + int cc; + sample_t s; + + if (!(cc = vp->control_counter)) + { + cc = song->control_ratio; + if (update_signal(song, v)) + return; /* Envelope ran out */ + left = vp->left_mix; + right = vp->right_mix; + } + + while (count) + if (cc < count) + { + count -= cc; + while (cc--) + { + s = *sp++; + MIXATION(left); + MIXATION(right); + } + cc = song->control_ratio; + if (update_signal(song, v)) + return; /* Envelope ran out */ + left = vp->left_mix; + right = vp->right_mix; + } + else + { + vp->control_counter = cc - count; + while (count--) + { + s = *sp++; + MIXATION(left); + MIXATION(right); + } + return; + } +} + +static void mix_center_signal(MidSong *song, sample_t *sp, sint32 *lp, int v, + int count) +{ + MidVoice *vp = song->voice + v; + final_volume_t + left=vp->left_mix; + int cc; + sample_t s; + + if (!(cc = vp->control_counter)) + { + cc = song->control_ratio; + if (update_signal(song, v)) + return; /* Envelope ran out */ + left = vp->left_mix; + } + + while (count) + if (cc < count) + { + count -= cc; + while (cc--) + { + s = *sp++; + MIXATION(left); + MIXATION(left); + } + cc = song->control_ratio; + if (update_signal(song, v)) + return; /* Envelope ran out */ + left = vp->left_mix; + } + else + { + vp->control_counter = cc - count; + while (count--) + { + s = *sp++; + MIXATION(left); + MIXATION(left); + } + return; + } +} + +static void mix_single_signal(MidSong *song, sample_t *sp, sint32 *lp, int v, + int count) +{ + MidVoice *vp = song->voice + v; + final_volume_t + left=vp->left_mix; + int cc; + sample_t s; + + if (!(cc = vp->control_counter)) + { + cc = song->control_ratio; + if (update_signal(song, v)) + return; /* Envelope ran out */ + left = vp->left_mix; + } + + while (count) + if (cc < count) + { + count -= cc; + while (cc--) + { + s = *sp++; + MIXATION(left); + lp++; + } + cc = song->control_ratio; + if (update_signal(song, v)) + return; /* Envelope ran out */ + left = vp->left_mix; + } + else + { + vp->control_counter = cc - count; + while (count--) + { + s = *sp++; + MIXATION(left); + lp++; + } + return; + } +} + +static void mix_mono_signal(MidSong *song, sample_t *sp, sint32 *lp, int v, + int count) +{ + MidVoice *vp = song->voice + v; + final_volume_t + left=vp->left_mix; + int cc; + sample_t s; + + if (!(cc = vp->control_counter)) + { + cc = song->control_ratio; + if (update_signal(song, v)) + return; /* Envelope ran out */ + left = vp->left_mix; + } + + while (count) + if (cc < count) + { + count -= cc; + while (cc--) + { + s = *sp++; + MIXATION(left); + } + cc = song->control_ratio; + if (update_signal(song, v)) + return; /* Envelope ran out */ + left = vp->left_mix; + } + else + { + vp->control_counter = cc - count; + while (count--) + { + s = *sp++; + MIXATION(left); + } + return; + } +} + +static void mix_mystery(MidSong *song, sample_t *sp, sint32 *lp, int v, int count) +{ + final_volume_t + left = song->voice[v].left_mix, + right = song->voice[v].right_mix; + sample_t s; + + while (count--) + { + s = *sp++; + MIXATION(left); + MIXATION(right); + } +} + +static void mix_center(MidSong *song, sample_t *sp, sint32 *lp, int v, int count) +{ + final_volume_t + left = song->voice[v].left_mix; + sample_t s; + + while (count--) + { + s = *sp++; + MIXATION(left); + MIXATION(left); + } +} + +static void mix_single(MidSong *song, sample_t *sp, sint32 *lp, int v, int count) +{ + final_volume_t + left = song->voice[v].left_mix; + sample_t s; + + while (count--) + { + s = *sp++; + MIXATION(left); + lp++; + } +} + +static void mix_mono(MidSong *song, sample_t *sp, sint32 *lp, int v, int count) +{ + final_volume_t + left = song->voice[v].left_mix; + sample_t s; + + while (count--) + { + s = *sp++; + MIXATION(left); + } +} + +/* Ramp a note out in c samples */ +static void ramp_out(MidSong *song, sample_t *sp, sint32 *lp, int v, sint32 c) +{ + + /* should be final_volume_t, but uint8 gives trouble. */ + sint32 left, right, li, ri; + + sample_t s=0; /* silly warning about uninitialized s */ + + /* Fix by James Caldwell */ + if ( c == 0 ) c = 1; + + left=song->voice[v].left_mix; + li=-(left/c); + if (!li) li=-1; + + /* printf("Ramping out: left=%d, c=%d, li=%d\n", left, c, li); */ + + if (!(song->encoding & PE_MONO)) + { + if (song->voice[v].panned==PANNED_MYSTERY) + { + right=song->voice[v].right_mix; + ri=-(right/c); + while (c--) + { + left += li; + if (left<0) + left=0; + right += ri; + if (right<0) + right=0; + s=*sp++; + MIXATION(left); + MIXATION(right); + } + } + else if (song->voice[v].panned==PANNED_CENTER) + { + while (c--) + { + left += li; + if (left<0) + return; + s=*sp++; + MIXATION(left); + MIXATION(left); + } + } + else if (song->voice[v].panned==PANNED_LEFT) + { + while (c--) + { + left += li; + if (left<0) + return; + s=*sp++; + MIXATION(left); + lp++; + } + } + else if (song->voice[v].panned==PANNED_RIGHT) + { + while (c--) + { + left += li; + if (left<0) + return; + s=*sp++; + lp++; + MIXATION(left); + } + } + } + else + { + /* Mono output. */ + while (c--) + { + left += li; + if (left<0) + return; + s=*sp++; + MIXATION(left); + } + } +} + + +/**************** interface function ******************/ + +void mix_voice(MidSong *song, sint32 *buf, int v, sint32 c) +{ + MidVoice *vp = song->voice + v; + sample_t *sp; + if (vp->status==VOICE_DIE) + { + if (c>=MAX_DIE_TIME) + c=MAX_DIE_TIME; + sp=resample_voice(song, v, &c); + ramp_out(song, sp, buf, v, c); + vp->status=VOICE_FREE; + } + else + { + sp=resample_voice(song, v, &c); + if (song->encoding & PE_MONO) + { + /* Mono output. */ + if (vp->envelope_increment || vp->tremolo_phase_increment) + mix_mono_signal(song, sp, buf, v, c); + else + mix_mono(song, sp, buf, v, c); + } + else + { + if (vp->panned == PANNED_MYSTERY) + { + if (vp->envelope_increment || vp->tremolo_phase_increment) + mix_mystery_signal(song, sp, buf, v, c); + else + mix_mystery(song, sp, buf, v, c); + } + else if (vp->panned == PANNED_CENTER) + { + if (vp->envelope_increment || vp->tremolo_phase_increment) + mix_center_signal(song, sp, buf, v, c); + else + mix_center(song, sp, buf, v, c); + } + else + { + /* It's either full left or full right. In either case, + every other sample is 0. Just get the offset right: */ + if (vp->panned == PANNED_RIGHT) buf++; + + if (vp->envelope_increment || vp->tremolo_phase_increment) + mix_single_signal(song, sp, buf, v, c); + else + mix_single(song, sp, buf, v, c); + } + } + } +} diff --git a/project/jni/timidity/src/mix.h b/project/jni/timidity/src/mix.h new file mode 100644 index 000000000..83a99b697 --- /dev/null +++ b/project/jni/timidity/src/mix.h @@ -0,0 +1,27 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + In case you haven't heard, this program is free software; + you can redistribute it and/or modify it under the terms of the + GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + mix.h + +*/ + +extern void mix_voice(MidSong *song, sint32 *buf, int v, sint32 c); +extern int recompute_envelope(MidSong *song, int v); +extern void apply_envelope_to_amp(MidSong *song, int v); diff --git a/project/jni/timidity/src/options.h b/project/jni/timidity/src/options.h new file mode 100644 index 000000000..4fa2fb7c9 --- /dev/null +++ b/project/jni/timidity/src/options.h @@ -0,0 +1,113 @@ +/* + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* When a patch file can't be opened, one of these extensions is + appended to the filename and the open is tried again. + */ +#define PATCH_EXT_LIST { ".pat", 0 } + +/* Acoustic Grand Piano seems to be the usual default instrument. */ +#define DEFAULT_PROGRAM 0 + +/* 9 here is MIDI channel 10, which is the standard percussion channel. + Some files (notably C:\WINDOWS\CANYON.MID) think that 16 is one too. + On the other hand, some files know that 16 is not a drum channel and + try to play music on it. This is now a runtime option, so this isn't + a critical choice anymore. */ +#define DEFAULT_DRUMCHANNELS ((1<<9) | (1<<15)) + +/* In percent. */ +#define DEFAULT_AMPLIFICATION 70 + +/* Default polyphony */ +#define DEFAULT_VOICES 32 + +/* 1000 here will give a control ratio of 22:1 with 22 kHz output. + Higher CONTROLS_PER_SECOND values allow more accurate rendering + of envelopes and tremolo. The cost is CPU time. */ +#define CONTROLS_PER_SECOND 1000 + +/* Make envelopes twice as fast. Saves ~20% CPU time (notes decay + faster) and sounds more like a GUS. There is now a command line + option to toggle this as well. */ +#define FAST_DECAY + +/* How many bits to use for the fractional part of sample positions. + This affects tonal accuracy. The entire position counter must fit + in 32 bits, so with FRACTION_BITS equal to 12, the maximum size of + a sample is 1048576 samples (2 megabytes in memory). The GUS gets + by with just 9 bits and a little help from its friends... + "The GUS does not SUCK!!!" -- a happy user :) */ +#define FRACTION_BITS 12 + +/* For some reason the sample volume is always set to maximum in all + patch files. Define this for a crude adjustment that may help + equalize instrument volumes. */ +#define ADJUST_SAMPLE_VOLUMES + +/* The number of samples to use for ramping out a dying note. Affects + click removal. */ +#define MAX_DIE_TIME 20 + +/**************************************************************************/ +/* Anything below this shouldn't need to be changed unless you're porting + to a new machine with other than 32-bit, big-endian words. */ +/**************************************************************************/ + +/* change FRACTION_BITS above, not these */ +#define INTEGER_MASK (0xFFFFFFFF << FRACTION_BITS) +#define FRACTION_MASK (~ INTEGER_MASK) + +/* This is enforced by some computations that must fit in an int */ +#define MAX_CONTROL_RATIO 255 + +#define MAX_AMPLIFICATION 800 + +/* The TiMidity configuration file */ +#define CONFIG_FILE "timidity.cfg" + +/* These affect general volume */ +#define GUARD_BITS 3 +#define AMP_BITS (15-GUARD_BITS) + +#define MAX_AMP_VALUE ((1<<(AMP_BITS+1))-1) + +#define FSCALE(a,b) (float)((a) * (double)(1<<(b))) +#define FSCALENEG(a,b) (float)((a) * (1.0L / (double)(1<<(b)))) + +/* Vibrato and tremolo Choices of the Day */ +#define SWEEP_TUNING 38 +#define VIBRATO_AMPLITUDE_TUNING 1.0L +#define VIBRATO_RATE_TUNING 38 +#define TREMOLO_AMPLITUDE_TUNING 1.0L +#define TREMOLO_RATE_TUNING 38 + +#define SWEEP_SHIFT 16 +#define RATE_SHIFT 5 + +#ifndef PI + #define PI 3.14159265358979323846 +#endif + +/* The path separator (D.M.) */ +#ifdef WIN32 +# define PATH_SEP '\\' +#else +# define PATH_SEP '/' +#endif diff --git a/project/jni/timidity/src/output.c b/project/jni/timidity/src/output.c new file mode 100644 index 000000000..72704234b --- /dev/null +++ b/project/jni/timidity/src/output.c @@ -0,0 +1,113 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + output.c + + Audio output (to file / device) functions. +*/ + +#if HAVE_CONFIG_H +# include +#endif + +#include "timidity.h" +#include "timidity_internal.h" +#include "options.h" +#include "output.h" + +/*****************************************************************/ +/* Some functions to convert signed 32-bit data to other formats */ + +void s32tos8(void *dp, sint32 *lp, sint32 c) +{ + sint8 *cp=(sint8 *)(dp); + sint32 l; + while (c--) + { + l=(*lp++)>>(32-8-GUARD_BITS); + if (l>127) l=127; + else if (l<-128) l=-128; + *cp++ = (sint8) (l); + } +} + +void s32tou8(void *dp, sint32 *lp, sint32 c) +{ + uint8 *cp=(uint8 *)(dp); + sint32 l; + while (c--) + { + l=(*lp++)>>(32-8-GUARD_BITS); + if (l>127) l=127; + else if (l<-128) l=-128; + *cp++ = 0x80 ^ ((uint8) l); + } +} + +void s32tos16(void *dp, sint32 *lp, sint32 c) +{ + sint16 *sp=(sint16 *)(dp); + sint32 l; + while (c--) + { + l=(*lp++)>>(32-16-GUARD_BITS); + if (l > 32767) l=32767; + else if (l<-32768) l=-32768; + *sp++ = (sint16)(l); + } +} + +void s32tou16(void *dp, sint32 *lp, sint32 c) +{ + uint16 *sp=(uint16 *)(dp); + sint32 l; + while (c--) + { + l=(*lp++)>>(32-16-GUARD_BITS); + if (l > 32767) l=32767; + else if (l<-32768) l=-32768; + *sp++ = 0x8000 ^ (uint16)(l); + } +} + +void s32tos16x(void *dp, sint32 *lp, sint32 c) +{ + sint16 *sp=(sint16 *)(dp); + sint32 l; + while (c--) + { + l=(*lp++)>>(32-16-GUARD_BITS); + if (l > 32767) l=32767; + else if (l<-32768) l=-32768; + *sp++ = XCHG_SHORT((sint16)(l)); + } +} + +void s32tou16x(void *dp, sint32 *lp, sint32 c) +{ + uint16 *sp=(uint16 *)(dp); + sint32 l; + while (c--) + { + l=(*lp++)>>(32-16-GUARD_BITS); + if (l > 32767) l=32767; + else if (l<-32768) l=-32768; + *sp++ = XCHG_SHORT(0x8000 ^ (uint16)(l)); + } +} diff --git a/project/jni/timidity/src/output.h b/project/jni/timidity/src/output.h new file mode 100644 index 000000000..9dcf339e5 --- /dev/null +++ b/project/jni/timidity/src/output.h @@ -0,0 +1,56 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + output.h + +*/ + +/* Data format encoding bits */ + +#define PE_MONO 0x01 /* versus stereo */ +#define PE_SIGNED 0x02 /* versus unsigned */ +#define PE_16BIT 0x04 /* versus 8-bit */ + +/* Conversion functions -- These overwrite the sint32 data in *lp with + data in another format */ + +/* 8-bit signed and unsigned*/ +extern void s32tos8(void *dp, sint32 *lp, sint32 c); +extern void s32tou8(void *dp, sint32 *lp, sint32 c); + +/* 16-bit */ +extern void s32tos16(void *dp, sint32 *lp, sint32 c); +extern void s32tou16(void *dp, sint32 *lp, sint32 c); + +/* byte-exchanged 16-bit */ +extern void s32tos16x(void *dp, sint32 *lp, sint32 c); +extern void s32tou16x(void *dp, sint32 *lp, sint32 c); + +/* little-endian and big-endian specific */ +#ifdef LITTLE_ENDIAN +#define s32tou16l s32tou16 +#define s32tou16b s32tou16x +#define s32tos16l s32tos16 +#define s32tos16b s32tos16x +#else +#define s32tou16l s32tou16x +#define s32tou16b s32tou16 +#define s32tos16l s32tos16x +#define s32tos16b s32tos16 +#endif diff --git a/project/jni/timidity/src/playmidi.c b/project/jni/timidity/src/playmidi.c new file mode 100644 index 000000000..3933c8376 --- /dev/null +++ b/project/jni/timidity/src/playmidi.c @@ -0,0 +1,804 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + playmidi.c -- random stuff in need of rearrangement + +*/ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include "timidity.h" +#include "timidity_internal.h" +#include "options.h" +#include "instrum.h" +#include "playmidi.h" +#include "output.h" +#include "mix.h" +#include "tables.h" + +static void adjust_amplification(MidSong *song) +{ + song->master_volume = (float)(song->amplification) / (float)100.0; +} + +static void reset_voices(MidSong *song) +{ + int i; + for (i=0; ivoice[i].status=VOICE_FREE; +} + +/* Process the Reset All Controllers event */ +static void reset_controllers(MidSong *song, int c) +{ + song->channel[c].volume=90; /* Some standard says, although the SCC docs say 0. */ + song->channel[c].expression=127; /* SCC-1 does this. */ + song->channel[c].sustain=0; + song->channel[c].pitchbend=0x2000; + song->channel[c].pitchfactor=0; /* to be computed */ +} + +static void reset_midi(MidSong *song) +{ + int i; + for (i=0; i<16; i++) + { + reset_controllers(song, i); + /* The rest of these are unaffected by the Reset All Controllers event */ + song->channel[i].program=song->default_program; + song->channel[i].panning=NO_PANNING; + song->channel[i].pitchsens=2; + song->channel[i].bank=0; /* tone bank or drum set */ + } + reset_voices(song); +} + +static void select_sample(MidSong *song, int v, MidInstrument *ip, int vel) +{ + sint32 f, cdiff, diff; + int s,i; + MidSample *sp, *closest; + + s=ip->samples; + sp=ip->sample; + + if (s==1) + { + song->voice[v].sample=sp; + return; + } + + f=song->voice[v].orig_frequency; + for (i=0; ilow_vel <= vel && sp->high_vel >= vel && + sp->low_freq <= f && sp->high_freq >= f) + { + song->voice[v].sample=sp; + return; + } + sp++; + } + + /* + No suitable sample found! We'll select the sample whose root + frequency is closest to the one we want. (Actually we should + probably convert the low, high, and root frequencies to MIDI note + values and compare those.) */ + + cdiff=0x7FFFFFFF; + closest=sp=ip->sample; + for(i=0; iroot_freq - f; + if (diff<0) diff=-diff; + if (diffvoice[v].sample=closest; + return; +} + +static void recompute_freq(MidSong *song, int v) +{ + int + sign=(song->voice[v].sample_increment < 0), /* for bidirectional loops */ + pb=song->channel[song->voice[v].channel].pitchbend; + double a; + + if (!song->voice[v].sample->sample_rate) + return; + + if (song->voice[v].vibrato_control_ratio) + { + /* This instrument has vibrato. Invalidate any precomputed + sample_increments. */ + + int i=MID_VIBRATO_SAMPLE_INCREMENTS; + while (i--) + song->voice[v].vibrato_sample_increment[i]=0; + } + + if (pb==0x2000 || pb<0 || pb>0x3FFF) + song->voice[v].frequency = song->voice[v].orig_frequency; + else + { + pb-=0x2000; + if (!(song->channel[song->voice[v].channel].pitchfactor)) + { + /* Damn. Somebody bent the pitch. */ + sint32 i=pb*song->channel[song->voice[v].channel].pitchsens; + if (pb<0) + i=-i; + song->channel[song->voice[v].channel].pitchfactor= + (float)(bend_fine[(i>>5) & 0xFF] * bend_coarse[i>>13]); + } + if (pb>0) + song->voice[v].frequency= + (sint32)(song->channel[song->voice[v].channel].pitchfactor * + (double)(song->voice[v].orig_frequency)); + else + song->voice[v].frequency= + (sint32)((double)(song->voice[v].orig_frequency) / + song->channel[song->voice[v].channel].pitchfactor); + } + + a = FSCALE(((double)(song->voice[v].sample->sample_rate) * + (double)(song->voice[v].frequency)) / + ((double)(song->voice[v].sample->root_freq) * + (double)(song->rate)), + FRACTION_BITS); + + if (sign) + a = -a; /* need to preserve the loop direction */ + + song->voice[v].sample_increment = (sint32)(a); +} + +static void recompute_amp(MidSong *song, int v) +{ + sint32 tempamp; + + /* TODO: use fscale */ + + tempamp= (song->voice[v].velocity * + song->channel[song->voice[v].channel].volume * + song->channel[song->voice[v].channel].expression); /* 21 bits */ + + if (!(song->encoding & PE_MONO)) + { + if (song->voice[v].panning > 60 && song->voice[v].panning < 68) + { + song->voice[v].panned=PANNED_CENTER; + + song->voice[v].left_amp= + FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume, + 21); + } + else if (song->voice[v].panning<5) + { + song->voice[v].panned = PANNED_LEFT; + + song->voice[v].left_amp= + FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume, + 20); + } + else if (song->voice[v].panning>123) + { + song->voice[v].panned = PANNED_RIGHT; + + song->voice[v].left_amp= /* left_amp will be used */ + FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume, + 20); + } + else + { + song->voice[v].panned = PANNED_MYSTERY; + + song->voice[v].left_amp= + FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume, + 27); + song->voice[v].right_amp = song->voice[v].left_amp * (song->voice[v].panning); + song->voice[v].left_amp *= (float)(127 - song->voice[v].panning); + } + } + else + { + song->voice[v].panned = PANNED_CENTER; + + song->voice[v].left_amp= + FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume, + 21); + } +} + +static void start_note(MidSong *song, MidEvent *e, int i) +{ + MidInstrument *ip; + int j; + + if (ISDRUMCHANNEL(song, e->channel)) + { + if (!(ip=song->drumset[song->channel[e->channel].bank]->instrument[e->a])) + { + if (!(ip=song->drumset[0]->instrument[e->a])) + return; /* No instrument? Then we can't play. */ + } + if (ip->samples != 1) + { + DEBUG_MSG("Strange: percussion instrument with %d samples!\n", + ip->samples); + } + + if (ip->sample->note_to_use) /* Do we have a fixed pitch? */ + song->voice[i].orig_frequency = freq_table[(int)(ip->sample->note_to_use)]; + else + song->voice[i].orig_frequency = freq_table[e->a & 0x7F]; + + /* drums are supposed to have only one sample */ + song->voice[i].sample = ip->sample; + } + else + { + if (song->channel[e->channel].program == SPECIAL_PROGRAM) + ip=song->default_instrument; + else if (!(ip=song->tonebank[song->channel[e->channel].bank]-> + instrument[song->channel[e->channel].program])) + { + if (!(ip=song->tonebank[0]->instrument[song->channel[e->channel].program])) + return; /* No instrument? Then we can't play. */ + } + + if (ip->sample->note_to_use) /* Fixed-pitch instrument? */ + song->voice[i].orig_frequency = freq_table[(int)(ip->sample->note_to_use)]; + else + song->voice[i].orig_frequency = freq_table[e->a & 0x7F]; + select_sample(song, i, ip, e->b); + } + + song->voice[i].status = VOICE_ON; + song->voice[i].channel = e->channel; + song->voice[i].note = e->a; + song->voice[i].velocity = e->b; + song->voice[i].sample_offset = 0; + song->voice[i].sample_increment = 0; /* make sure it isn't negative */ + + song->voice[i].tremolo_phase = 0; + song->voice[i].tremolo_phase_increment = song->voice[i].sample->tremolo_phase_increment; + song->voice[i].tremolo_sweep = song->voice[i].sample->tremolo_sweep_increment; + song->voice[i].tremolo_sweep_position = 0; + + song->voice[i].vibrato_sweep = song->voice[i].sample->vibrato_sweep_increment; + song->voice[i].vibrato_sweep_position = 0; + song->voice[i].vibrato_control_ratio = song->voice[i].sample->vibrato_control_ratio; + song->voice[i].vibrato_control_counter = song->voice[i].vibrato_phase = 0; + for (j=0; jvoice[i].vibrato_sample_increment[j] = 0; + + if (song->channel[e->channel].panning != NO_PANNING) + song->voice[i].panning = song->channel[e->channel].panning; + else + song->voice[i].panning = song->voice[i].sample->panning; + + recompute_freq(song, i); + recompute_amp(song, i); + if (song->voice[i].sample->modes & MODES_ENVELOPE) + { + /* Ramp up from 0 */ + song->voice[i].envelope_stage = 0; + song->voice[i].envelope_volume = 0; + song->voice[i].control_counter = 0; + recompute_envelope(song, i); + apply_envelope_to_amp(song, i); + } + else + { + song->voice[i].envelope_increment = 0; + apply_envelope_to_amp(song, i); + } +} + +static void kill_note(MidSong *song, int i) +{ + song->voice[i].status = VOICE_DIE; +} + +/* Only one instance of a note can be playing on a single channel. */ +static void note_on(MidSong *song) +{ + int i = song->voices, lowest=-1; + sint32 lv=0x7FFFFFFF, v; + MidEvent *e = song->current_event; + + while (i--) + { + if (song->voice[i].status == VOICE_FREE) + lowest=i; /* Can't get a lower volume than silence */ + else if (song->voice[i].channel==e->channel && + (song->voice[i].note==e->a || song->channel[song->voice[i].channel].mono)) + kill_note(song, i); + } + + if (lowest != -1) + { + /* Found a free voice. */ + start_note(song,e,lowest); + return; + } + + /* Look for the decaying note with the lowest volume */ + i = song->voices; + while (i--) + { + if ((song->voice[i].status != VOICE_ON) && + (song->voice[i].status != VOICE_DIE)) + { + v = song->voice[i].left_mix; + if ((song->voice[i].panned == PANNED_MYSTERY) + && (song->voice[i].right_mix > v)) + v = song->voice[i].right_mix; + if (vcut_notes++; + song->voice[lowest].status=VOICE_FREE; + start_note(song,e,lowest); + } + else + song->lost_notes++; +} + +static void finish_note(MidSong *song, int i) +{ + if (song->voice[i].sample->modes & MODES_ENVELOPE) + { + /* We need to get the envelope out of Sustain stage */ + song->voice[i].envelope_stage = 3; + song->voice[i].status = VOICE_OFF; + recompute_envelope(song, i); + apply_envelope_to_amp(song, i); + } + else + { + /* Set status to OFF so resample_voice() will let this voice out + of its loop, if any. In any case, this voice dies when it + hits the end of its data (ofs>=data_length). */ + song->voice[i].status = VOICE_OFF; + } +} + +static void note_off(MidSong *song) +{ + int i = song->voices; + MidEvent *e = song->current_event; + + while (i--) + if (song->voice[i].status == VOICE_ON && + song->voice[i].channel == e->channel && + song->voice[i].note == e->a) + { + if (song->channel[e->channel].sustain) + { + song->voice[i].status = VOICE_SUSTAINED; + } + else + finish_note(song, i); + return; + } +} + +/* Process the All Notes Off event */ +static void all_notes_off(MidSong *song) +{ + int i = song->voices; + int c = song->current_event->channel; + + DEBUG_MSG("All notes off on channel %d\n", c); + while (i--) + if (song->voice[i].status == VOICE_ON && + song->voice[i].channel == c) + { + if (song->channel[c].sustain) + song->voice[i].status = VOICE_SUSTAINED; + else + finish_note(song, i); + } +} + +/* Process the All Sounds Off event */ +static void all_sounds_off(MidSong *song) +{ + int i = song->voices; + int c = song->current_event->channel; + + while (i--) + if (song->voice[i].channel == c && + song->voice[i].status != VOICE_FREE && + song->voice[i].status != VOICE_DIE) + { + kill_note(song, i); + } +} + +static void adjust_pressure(MidSong *song) +{ + MidEvent *e = song->current_event; + int i = song->voices; + + while (i--) + if (song->voice[i].status == VOICE_ON && + song->voice[i].channel == e->channel && + song->voice[i].note == e->a) + { + song->voice[i].velocity = e->b; + recompute_amp(song, i); + apply_envelope_to_amp(song, i); + return; + } +} + +static void drop_sustain(MidSong *song) +{ + int i = song->voices; + int c = song->current_event->channel; + + while (i--) + if (song->voice[i].status == VOICE_SUSTAINED && song->voice[i].channel == c) + finish_note(song, i); +} + +static void adjust_pitchbend(MidSong *song) +{ + int c = song->current_event->channel; + int i = song->voices; + + while (i--) + if (song->voice[i].status != VOICE_FREE && song->voice[i].channel == c) + { + recompute_freq(song, i); + } +} + +static void adjust_volume(MidSong *song) +{ + int c = song->current_event->channel; + int i = song->voices; + + while (i--) + if (song->voice[i].channel == c && + (song->voice[i].status==VOICE_ON || song->voice[i].status==VOICE_SUSTAINED)) + { + recompute_amp(song, i); + apply_envelope_to_amp(song, i); + } +} + +static void seek_forward(MidSong *song, sint32 until_time) +{ + reset_voices(song); + while (song->current_event->time < until_time) + { + switch(song->current_event->type) + { + /* All notes stay off. Just handle the parameter changes. */ + + case ME_PITCH_SENS: + song->channel[song->current_event->channel].pitchsens = + song->current_event->a; + song->channel[song->current_event->channel].pitchfactor = 0; + break; + + case ME_PITCHWHEEL: + song->channel[song->current_event->channel].pitchbend = + song->current_event->a + song->current_event->b * 128; + song->channel[song->current_event->channel].pitchfactor = 0; + break; + + case ME_MAINVOLUME: + song->channel[song->current_event->channel].volume = + song->current_event->a; + break; + + case ME_PAN: + song->channel[song->current_event->channel].panning = + song->current_event->a; + break; + + case ME_EXPRESSION: + song->channel[song->current_event->channel].expression = + song->current_event->a; + break; + + case ME_PROGRAM: + if (ISDRUMCHANNEL(song, song->current_event->channel)) + /* Change drum set */ + song->channel[song->current_event->channel].bank = + song->current_event->a; + else + song->channel[song->current_event->channel].program = + song->current_event->a; + break; + + case ME_SUSTAIN: + song->channel[song->current_event->channel].sustain = + song->current_event->a; + break; + + case ME_RESET_CONTROLLERS: + reset_controllers(song, song->current_event->channel); + break; + + case ME_TONE_BANK: + song->channel[song->current_event->channel].bank = + song->current_event->a; + break; + + case ME_EOT: + song->current_sample = song->current_event->time; + return; + } + song->current_event++; + } + /*song->current_sample=song->current_event->time;*/ + if (song->current_event != song->events) + song->current_event--; + song->current_sample=until_time; +} + +static void skip_to(MidSong *song, sint32 until_time) +{ + if (song->current_sample > until_time) + song->current_sample = 0; + + reset_midi(song); + song->current_event = song->events; + + if (until_time) + seek_forward(song, until_time); +} + +static void do_compute_data(MidSong *song, sint32 count) +{ + int i; + memset(song->common_buffer, 0, + (song->encoding & PE_MONO) ? (count * 4) : (count * 8)); + for (i = 0; i < song->voices; i++) + { + if(song->voice[i].status != VOICE_FREE) + mix_voice(song, song->common_buffer, i, count); + } + song->current_sample += count; +} + +/* count=0 means flush remaining buffered data to output device, then + flush the device itself */ +static void compute_data(MidSong *song, sint8 **stream, sint32 count) +{ + int channels; + + if ( song->encoding & PE_MONO ) + channels = 1; + else + channels = 2; + + while (count) + { + sint32 block = count; + if (block > song->buffer_size) + block = song->buffer_size; + do_compute_data(song, block); + song->write(*stream, song->common_buffer, channels * block); + *stream += song->bytes_per_sample * block; + count -= block; + } +} + +void mid_song_start(MidSong *song) +{ + song->playing = 1; + adjust_amplification(song); + skip_to(song, 0); +} + +void mid_song_seek(MidSong *song, uint32 ms) +{ + skip_to(song, (ms * (song->rate / 100)) / 10); +} + +uint32 mid_song_get_total_time(MidSong *song) +{ + MidEvent *last_event = &song->events[song->groomed_event_count - 1]; + /* We want last_event->time * 1000 / song->rate */ + uint32 retvalue = (last_event->time / song->rate) * 1000; + retvalue += (last_event->time % song->rate) * 1000 / song->rate; + return retvalue; +} + +uint32 mid_song_get_time(MidSong *song) +{ + uint32 retvalue = (song->current_sample / song->rate) * 1000; + retvalue += (song->current_sample % song->rate) * 1000 / song->rate; + return retvalue; +} + +char *mid_song_get_meta(MidSong *song, MidSongMetaId what) +{ + return song->meta_data[what]; +} + +size_t mid_song_read_wave(MidSong *song, void *ptr, size_t size) +{ + sint32 start_sample, end_sample, samples; + + if (!song->playing) + return 0; + + samples = size / song->bytes_per_sample; + + start_sample = song->current_sample; + end_sample = song->current_sample+samples; + while ( song->current_sample < end_sample ) { + /* Handle all events that should happen at this time */ + while (song->current_event->time <= song->current_sample) { + switch(song->current_event->type) { + + /* Effects affecting a single note */ + + case ME_NOTEON: + if (!(song->current_event->b)) /* Velocity 0? */ + note_off(song); + else + note_on(song); + break; + + case ME_NOTEOFF: + note_off(song); + break; + + case ME_KEYPRESSURE: + adjust_pressure(song); + break; + + /* Effects affecting a single channel */ + + case ME_PITCH_SENS: + song->channel[song->current_event->channel].pitchsens = + song->current_event->a; + song->channel[song->current_event->channel].pitchfactor = 0; + break; + + case ME_PITCHWHEEL: + song->channel[song->current_event->channel].pitchbend = + song->current_event->a + song->current_event->b * 128; + song->channel[song->current_event->channel].pitchfactor = 0; + /* Adjust pitch for notes already playing */ + adjust_pitchbend(song); + break; + + case ME_MAINVOLUME: + song->channel[song->current_event->channel].volume = + song->current_event->a; + adjust_volume(song); + break; + + case ME_PAN: + song->channel[song->current_event->channel].panning = + song->current_event->a; + break; + + case ME_EXPRESSION: + song->channel[song->current_event->channel].expression = + song->current_event->a; + adjust_volume(song); + break; + + case ME_PROGRAM: + if (ISDRUMCHANNEL(song, song->current_event->channel)) { + /* Change drum set */ + song->channel[song->current_event->channel].bank = + song->current_event->a; + } + else + song->channel[song->current_event->channel].program = + song->current_event->a; + break; + + case ME_SUSTAIN: + song->channel[song->current_event->channel].sustain = + song->current_event->a; + if (!song->current_event->a) + drop_sustain(song); + break; + + case ME_RESET_CONTROLLERS: + reset_controllers(song, song->current_event->channel); + break; + + case ME_ALL_NOTES_OFF: + all_notes_off(song); + break; + + case ME_ALL_SOUNDS_OFF: + all_sounds_off(song); + break; + + case ME_TONE_BANK: + song->channel[song->current_event->channel].bank = + song->current_event->a; + break; + + case ME_EOT: + /* Give the last notes a couple of seconds to decay */ + DEBUG_MSG("Playing time: ~%d seconds\n", + song->current_sample/song->rate+2); + DEBUG_MSG("Notes cut: %d\n", song->cut_notes); + DEBUG_MSG("Notes lost totally: %d\n", song->lost_notes); + song->playing = 0; + return (song->current_sample - start_sample) * song->bytes_per_sample; + } + song->current_event++; + } + if (song->current_event->time > end_sample) + compute_data(song, (sint8 **)&ptr, end_sample-song->current_sample); + else + compute_data(song, (sint8 **)&ptr, song->current_event->time-song->current_sample); + } + return samples * song->bytes_per_sample; +} + +void mid_song_set_volume(MidSong *song, int volume) +{ + int i; + if (volume > MAX_AMPLIFICATION) + song->amplification = MAX_AMPLIFICATION; + else + if (volume < 0) + song->amplification = 0; + else + song->amplification = volume; + adjust_amplification(song); + for (i = 0; i < song->voices; i++) + if (song->voice[i].status != VOICE_FREE) + { + recompute_amp(song, i); + apply_envelope_to_amp(song, i); + } +} diff --git a/project/jni/timidity/src/playmidi.h b/project/jni/timidity/src/playmidi.h new file mode 100644 index 000000000..b4545ab27 --- /dev/null +++ b/project/jni/timidity/src/playmidi.h @@ -0,0 +1,64 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + playmidi.h + + */ + +/* Midi events */ +#define ME_NONE 0 +#define ME_NOTEON 1 +#define ME_NOTEOFF 2 +#define ME_KEYPRESSURE 3 +#define ME_MAINVOLUME 4 +#define ME_PAN 5 +#define ME_SUSTAIN 6 +#define ME_EXPRESSION 7 +#define ME_PITCHWHEEL 8 +#define ME_PROGRAM 9 +#define ME_TEMPO 10 +#define ME_PITCH_SENS 11 + +#define ME_ALL_SOUNDS_OFF 12 +#define ME_RESET_CONTROLLERS 13 +#define ME_ALL_NOTES_OFF 14 +#define ME_TONE_BANK 15 + +#define ME_LYRIC 16 + +#define ME_EOT 99 + +/* Causes the instrument's default panning to be used. */ +#define NO_PANNING -1 + +/* Voice status options: */ +#define VOICE_FREE 0 +#define VOICE_ON 1 +#define VOICE_SUSTAINED 2 +#define VOICE_OFF 3 +#define VOICE_DIE 4 + +/* Voice panned options: */ +#define PANNED_MYSTERY 0 +#define PANNED_LEFT 1 +#define PANNED_RIGHT 2 +#define PANNED_CENTER 3 +/* Anything but PANNED_MYSTERY only uses the left volume */ + +#define ISDRUMCHANNEL(s, c) (((s)->drumchannels & (1<<(c)))) diff --git a/project/jni/timidity/src/readmidi.c b/project/jni/timidity/src/readmidi.c new file mode 100644 index 000000000..95c7002b3 --- /dev/null +++ b/project/jni/timidity/src/readmidi.c @@ -0,0 +1,596 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include "timidity.h" +#include "timidity_internal.h" +#include "options.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" + +/* Computes how many (fractional) samples one MIDI delta-time unit contains */ +static void compute_sample_increment(MidSong *song, sint32 tempo, + sint32 divisions) +{ + double a; + a = (double) (tempo) * (double) (song->rate) * (65536.0/1000000.0) / + (double)(divisions); + + song->sample_correction = (sint32)(a) & 0xFFFF; + song->sample_increment = (sint32)(a) >> 16; + + DEBUG_MSG("Samples per delta-t: %d (correction %d)\n", + song->sample_increment, song->sample_correction); +} + +/* Read variable-length number (7 bits per byte, MSB first) */ +static sint32 getvl(MidIStream *stream) +{ + sint32 l=0; + uint8 c; + for (;;) + { + mid_istream_read(stream, &c, 1, 1); + l += (c & 0x7f); + if (!(c & 0x80)) return l; + l<<=7; + } +} + +/* Print a string from the file, followed by a newline. Any non-ASCII + or unprintable characters will be converted to periods. */ +static int read_meta_data(MidIStream *stream, sint32 len, uint8 type, MidSong *song) +{ + char *s=safe_malloc(len+1); + MidSongMetaId id; + static char *label[] = { + "Text event: ", "Text: ", "Copyright: ", "Track name: ", + "Instrument: ", "Lyric: ", "Marker: ", "Cue point: "}; + + if (len != (sint32) mid_istream_read(stream, s, 1, len)) + { + free(s); + return -1; + } + s[len]='\0'; + while (len--) + { + if (((unsigned char)s[len])<32) + s[len]='.'; + } + DEBUG_MSG("%s%s\n", label[(type > 7) ? 0 : type], s); + + switch (type) + { + case 1: id = MID_SONG_TEXT; break; + case 2: id = MID_SONG_COPYRIGHT; break; + default: free(s); s = NULL; + } + if (s) + { + if (song->meta_data[id]) + free(song->meta_data[id]); + song->meta_data[id] = s; + } + + return 0; +} + +#define MIDIEVENT(at,t,ch,pa,pb) \ + new=safe_malloc(sizeof(MidEventList)); \ + new->event.time=at; new->event.type=t; new->event.channel=ch; \ + new->event.a=pa; new->event.b=pb; new->next=0;\ + return new; + +#define MAGIC_EOT ((MidEventList *)(-1)) + +/* Read a MIDI event, returning a freshly allocated element that can + be linked to the event list */ +static MidEventList *read_midi_event(MidIStream *stream, MidSong *song) +{ + static uint8 laststatus, lastchan; + static uint8 nrpn=0, rpn_msb[16], rpn_lsb[16]; /* one per channel */ + uint8 me, type, a,b,c; + sint32 len; + MidEventList *new; + + for (;;) + { + song->at += getvl(stream); + if (mid_istream_read(stream, &me, 1, 1) != 1) + { + DEBUG_MSG("read_midi_event: mid_istream_read() failure\n"); + return 0; + } + + if(me==0xF0 || me == 0xF7) /* SysEx event */ + { + len=getvl(stream); + mid_istream_skip(stream, len); + } + else if(me==0xFF) /* Meta event */ + { + mid_istream_read(stream, &type, 1, 1); + len=getvl(stream); + if (type>0 && type<16) + { + read_meta_data(stream, len, type, song); + } + else + switch(type) + { + case 0x2F: /* End of Track */ + return MAGIC_EOT; + + case 0x51: /* Tempo */ + mid_istream_read(stream, &a, 1, 1); + mid_istream_read(stream, &b, 1, 1); + mid_istream_read(stream, &c, 1, 1); + MIDIEVENT(song->at, ME_TEMPO, c, a, b); + + default: + DEBUG_MSG("(Meta event type 0x%02x, length %d)\n", type, len); + mid_istream_skip(stream, len); + break; + } + } + else + { + a=me; + if (a & 0x80) /* status byte */ + { + lastchan=a & 0x0F; + laststatus=(a>>4) & 0x07; + mid_istream_read(stream, &a, 1, 1); + a &= 0x7F; + } + switch(laststatus) + { + case 0: /* Note off */ + mid_istream_read(stream, &b, 1, 1); + b &= 0x7F; + MIDIEVENT(song->at, ME_NOTEOFF, lastchan, a,b); + + case 1: /* Note on */ + mid_istream_read(stream, &b, 1, 1); + b &= 0x7F; + MIDIEVENT(song->at, ME_NOTEON, lastchan, a,b); + + case 2: /* Key Pressure */ + mid_istream_read(stream, &b, 1, 1); + b &= 0x7F; + MIDIEVENT(song->at, ME_KEYPRESSURE, lastchan, a, b); + + case 3: /* Control change */ + mid_istream_read(stream, &b, 1, 1); + b &= 0x7F; + { + int control=255; + switch(a) + { + case 7: control=ME_MAINVOLUME; break; + case 10: control=ME_PAN; break; + case 11: control=ME_EXPRESSION; break; + case 64: control=ME_SUSTAIN; break; + case 120: control=ME_ALL_SOUNDS_OFF; break; + case 121: control=ME_RESET_CONTROLLERS; break; + case 123: control=ME_ALL_NOTES_OFF; break; + + /* These should be the SCC-1 tone bank switch + commands. I don't know why there are two, or + why the latter only allows switching to bank 0. + Also, some MIDI files use 0 as some sort of + continuous controller. This will cause lots of + warnings about undefined tone banks. */ + case 0: control=ME_TONE_BANK; break; + case 32: + if (b!=0) + DEBUG_MSG("(Strange: tone bank change 0x20%02x)\n", b); + else + control=ME_TONE_BANK; + break; + + case 100: nrpn=0; rpn_msb[lastchan]=b; break; + case 101: nrpn=0; rpn_lsb[lastchan]=b; break; + case 99: nrpn=1; rpn_msb[lastchan]=b; break; + case 98: nrpn=1; rpn_lsb[lastchan]=b; break; + + case 6: + if (nrpn) + { + DEBUG_MSG("(Data entry (MSB) for NRPN %02x,%02x: %d)\n", + rpn_msb[lastchan], rpn_lsb[lastchan], b); + break; + } + + switch((rpn_msb[lastchan]<<8) | rpn_lsb[lastchan]) + { + case 0x0000: /* Pitch bend sensitivity */ + control=ME_PITCH_SENS; + break; + + case 0x7F7F: /* RPN reset */ + /* reset pitch bend sensitivity to 2 */ + MIDIEVENT(song->at, ME_PITCH_SENS, lastchan, 2, 0); + + default: + DEBUG_MSG("(Data entry (MSB) for RPN %02x,%02x: %d)\n", + rpn_msb[lastchan], rpn_lsb[lastchan], b); + break; + } + break; + + default: + DEBUG_MSG("(Control %d: %d)\n", a, b); + break; + } + if (control != 255) + { + MIDIEVENT(song->at, control, lastchan, b, 0); + } + } + break; + + case 4: /* Program change */ + a &= 0x7f; + MIDIEVENT(song->at, ME_PROGRAM, lastchan, a, 0); + + case 5: /* Channel pressure - NOT IMPLEMENTED */ + break; + + case 6: /* Pitch wheel */ + mid_istream_read(stream, &b, 1, 1); + b &= 0x7F; + MIDIEVENT(song->at, ME_PITCHWHEEL, lastchan, a, b); + + default: + DEBUG_MSG("*** Can't happen: status 0x%02X, channel 0x%02X\n", + laststatus, lastchan); + break; + } + } + } + + return new; +} + +#undef MIDIEVENT + +/* Read a midi track into the linked list, either merging with any previous + tracks or appending to them. */ +static int read_track(MidIStream *stream, MidSong *song, int append) +{ + MidEventList *meep; + MidEventList *next, *new; + sint32 len; + char tmp[4]; + + meep = song->evlist; + if (append && meep) + { + /* find the last event in the list */ + for (; meep->next; meep=meep->next) + ; + song->at = meep->event.time; + } + else + song->at=0; + + /* Check the formalities */ + + if (mid_istream_read(stream, tmp, 1, 4) != 4 || mid_istream_read(stream, &len, 4, 1) != 1) + { + DEBUG_MSG("Can't read track header.\n"); + return -1; + } + len=SWAPBE32(len); + if (memcmp(tmp, "MTrk", 4)) + { + DEBUG_MSG("Corrupt MIDI file.\n"); + return -2; + } + + for (;;) + { + if (!(new=read_midi_event(stream, song))) /* Some kind of error */ + return -2; + + if (new==MAGIC_EOT) /* End-of-track Hack. */ + { + return 0; + } + + next=meep->next; + while (next && (next->event.time < new->event.time)) + { + meep=next; + next=meep->next; + } + + new->next=next; + meep->next=new; + + song->event_count++; /* Count the event. (About one?) */ + meep=new; + } +} + +/* Free the linked event list from memory. */ +static void free_midi_list(MidSong *song) +{ + MidEventList *meep, *next; + if (!(meep = song->evlist)) return; + while (meep) + { + next=meep->next; + free(meep); + meep=next; + } + song->evlist=0; +} + +/* Allocate an array of MidiEvents and fill it from the linked list of + events, marking used instruments for loading. Convert event times to + samples: handle tempo changes. Strip unnecessary events from the list. + Free the linked list. */ +static MidEvent *groom_list(MidSong *song, sint32 divisions,sint32 *eventsp, + sint32 *samplesp) +{ + MidEvent *groomed_list, *lp; + MidEventList *meep; + sint32 i, our_event_count, tempo, skip_this_event, new_value; + sint32 sample_cum, samples_to_do, at, st, dt, counting_time; + + int current_bank[16], current_set[16], current_program[16]; + /* Or should each bank have its own current program? */ + + for (i=0; i<16; i++) + { + current_bank[i]=0; + current_set[i]=0; + current_program[i]=song->default_program; + } + + tempo=500000; + compute_sample_increment(song, tempo, divisions); + + /* This may allocate a bit more than we need */ + groomed_list=lp=safe_malloc(sizeof(MidEvent) * (song->event_count+1)); + meep=song->evlist; + + our_event_count=0; + st=at=sample_cum=0; + counting_time=2; /* We strip any silence before the first NOTE ON. */ + + for (i = 0; i < song->event_count; i++) + { + skip_this_event=0; + + if (meep->event.type==ME_TEMPO) + { + tempo= + meep->event.channel + meep->event.b * 256 + meep->event.a * 65536; + compute_sample_increment(song, tempo, divisions); + skip_this_event=1; + } + else switch (meep->event.type) + { + case ME_PROGRAM: + if (ISDRUMCHANNEL(song, meep->event.channel)) + { + if (song->drumset[meep->event.a]) /* Is this a defined drumset? */ + new_value=meep->event.a; + else + { + DEBUG_MSG("Drum set %d is undefined\n", meep->event.a); + new_value=meep->event.a=0; + } + if (current_set[meep->event.channel] != new_value) + current_set[meep->event.channel]=new_value; + else + skip_this_event=1; + } + else + { + new_value=meep->event.a; + if ((current_program[meep->event.channel] != SPECIAL_PROGRAM) + && (current_program[meep->event.channel] != new_value)) + current_program[meep->event.channel] = new_value; + else + skip_this_event=1; + } + break; + + case ME_NOTEON: + if (counting_time) + counting_time=1; + if (ISDRUMCHANNEL(song, meep->event.channel)) + { + /* Mark this instrument to be loaded */ + if (!(song->drumset[current_set[meep->event.channel]] + ->instrument[meep->event.a])) + song->drumset[current_set[meep->event.channel]] + ->instrument[meep->event.a] = MAGIC_LOAD_INSTRUMENT; + } + else + { + if (current_program[meep->event.channel]==SPECIAL_PROGRAM) + break; + /* Mark this instrument to be loaded */ + if (!(song->tonebank[current_bank[meep->event.channel]] + ->instrument[current_program[meep->event.channel]])) + song->tonebank[current_bank[meep->event.channel]] + ->instrument[current_program[meep->event.channel]] = + MAGIC_LOAD_INSTRUMENT; + } + break; + + case ME_TONE_BANK: + if (ISDRUMCHANNEL(song, meep->event.channel)) + { + skip_this_event=1; + break; + } + if (song->tonebank[meep->event.a]) /* Is this a defined tone bank? */ + new_value=meep->event.a; + else + { + DEBUG_MSG("Tone bank %d is undefined\n", meep->event.a); + new_value=meep->event.a=0; + } + if (current_bank[meep->event.channel]!=new_value) + current_bank[meep->event.channel]=new_value; + else + skip_this_event=1; + break; + } + + /* Recompute time in samples*/ + if ((dt=meep->event.time - at) && !counting_time) + { + samples_to_do = song->sample_increment * dt; + sample_cum += song->sample_correction * dt; + if (sample_cum & 0xFFFF0000) + { + samples_to_do += ((sample_cum >> 16) & 0xFFFF); + sample_cum &= 0x0000FFFF; + } + st += samples_to_do; + } + else if (counting_time==1) counting_time=0; + if (!skip_this_event) + { + /* Add the event to the list */ + *lp=meep->event; + lp->time=st; + lp++; + our_event_count++; + } + at=meep->event.time; + meep=meep->next; + } + /* Add an End-of-Track event */ + lp->time=st; + lp->type=ME_EOT; + our_event_count++; + free_midi_list(song); + + *eventsp=our_event_count; + *samplesp=st; + return groomed_list; +} + +MidEvent *read_midi_file(MidIStream *stream, MidSong *song, sint32 *count, sint32 *sp) +{ + sint32 len, divisions; + sint16 format, tracks, divisions_tmp; + int i; + char tmp[4]; + + song->event_count=0; + song->at=0; + song->evlist=0; + + if (mid_istream_read(stream, tmp, 1, 4) != 4 || mid_istream_read(stream, &len, 4, 1) != 1) + { + DEBUG_MSG("Not a MIDI file!\n"); + return 0; + } + len=SWAPBE32(len); + if (memcmp(tmp, "MThd", 4) || len < 6) + { + DEBUG_MSG("Not a MIDI file!\n"); + return 0; + } + + mid_istream_read(stream, &format, 2, 1); + mid_istream_read(stream, &tracks, 2, 1); + mid_istream_read(stream, &divisions_tmp, 2, 1); + format=SWAPBE16(format); + tracks=SWAPBE16(tracks); + divisions_tmp=SWAPBE16(divisions_tmp); + + if (divisions_tmp<0) + { + /* SMPTE time -- totally untested. Got a MIDI file that uses this? */ + divisions= + (sint32)(-(divisions_tmp/256)) * (sint32)(divisions_tmp & 0xFF); + } + else divisions=(sint32)(divisions_tmp); + + if (len > 6) + { + DEBUG_MSG("MIDI file header size %u bytes", len); + mid_istream_skip(stream, len-6); /* skip the excess */ + } + if (format<0 || format >2) + { + DEBUG_MSG("Unknown MIDI file format %d\n", format); + return 0; + } + DEBUG_MSG("Format: %d Tracks: %d Divisions: %d\n", + format, tracks, divisions); + + /* Put a do-nothing event first in the list for easier processing */ + song->evlist=safe_malloc(sizeof(MidEventList)); + song->evlist->event.time=0; + song->evlist->event.type=ME_NONE; + song->evlist->next=0; + song->event_count++; + + switch(format) + { + case 0: + if (read_track(stream, song, 0)) + { + free_midi_list(song); + return 0; + } + break; + + case 1: + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + readmidi.h + + */ + +extern MidEvent *read_midi_file(MidIStream *stream, MidSong *song, sint32 *count, sint32 *sp); diff --git a/project/jni/timidity/src/resample.c b/project/jni/timidity/src/resample.c new file mode 100644 index 000000000..20a5ada3e --- /dev/null +++ b/project/jni/timidity/src/resample.c @@ -0,0 +1,608 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + resample.c +*/ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include "timidity.h" +#include "timidity_internal.h" +#include "options.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" +#include "tables.h" +#include "resample.h" + +/*************** resampling with fixed increment *****************/ + +static sample_t *rs_plain(MidSong *song, int v, sint32 *countptr) +{ + + /* Play sample until end, then free the voice. */ + + sample_t v1, v2; + MidVoice + *vp=&(song->voice[v]); + sample_t + *dest=song->resample_buffer, + *src=vp->sample->data; + sint32 + ofs=vp->sample_offset, + incr=vp->sample_increment, + le=vp->sample->data_length, + count=*countptr; + sint32 i; + + if (incr<0) incr = -incr; /* In case we're coming out of a bidir loop */ + + /* Precalc how many times we should go through the loop. + NOTE: Assumes that incr > 0 and that ofs <= le */ + i = (le - ofs) / incr + 1; + + if (i > count) + { + i = count; + count = 0; + } + else count -= i; + + while (i--) + { + v1 = src[ofs >> FRACTION_BITS]; + v2 = src[(ofs >> FRACTION_BITS)+1]; + *dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS); + ofs += incr; + } + + if (ofs >= le) + { + if (ofs == le) + *dest++ = src[ofs >> FRACTION_BITS]; + vp->status=VOICE_FREE; + *countptr-=count+1; + } + + vp->sample_offset=ofs; /* Update offset */ + return song->resample_buffer; +} + +static sample_t *rs_loop(MidSong *song, MidVoice *vp, sint32 count) +{ + + /* Play sample until end-of-loop, skip back and continue. */ + + sample_t v1, v2; + sint32 + ofs=vp->sample_offset, + incr=vp->sample_increment, + le=vp->sample->loop_end, + ll=le - vp->sample->loop_start; + sample_t + *dest=song->resample_buffer, + *src=vp->sample->data; + sint32 i; + + while (count) + { + if (ofs >= le) + /* NOTE: Assumes that ll > incr and that incr > 0. */ + ofs -= ll; + /* Precalc how many times we should go through the loop */ + i = (le - ofs) / incr + 1; + if (i > count) + { + i = count; + count = 0; + } + else count -= i; + while (i--) + { + v1 = src[ofs >> FRACTION_BITS]; + v2 = src[(ofs >> FRACTION_BITS)+1]; + *dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS); + ofs += incr; + } + } + + vp->sample_offset=ofs; /* Update offset */ + return song->resample_buffer; +} + +static sample_t *rs_bidir(MidSong *song, MidVoice *vp, sint32 count) +{ + sample_t v1, v2; + sint32 + ofs=vp->sample_offset, + incr=vp->sample_increment, + le=vp->sample->loop_end, + ls=vp->sample->loop_start; + sample_t + *dest=song->resample_buffer, + *src=vp->sample->data; + sint32 + le2 = le<<1, + ls2 = ls<<1, + i; + /* Play normally until inside the loop region */ + + if (ofs <= ls) + { + /* NOTE: Assumes that incr > 0, which is NOT always the case + when doing bidirectional looping. I have yet to see a case + where both ofs <= ls AND incr < 0, however. */ + i = (ls - ofs) / incr + 1; + if (i > count) + { + i = count; + count = 0; + } + else count -= i; + while (i--) + { + v1 = src[ofs >> FRACTION_BITS]; + v2 = src[(ofs >> FRACTION_BITS)+1]; + *dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS); + ofs += incr; + } + } + + /* Then do the bidirectional looping */ + + while(count) + { + /* Precalc how many times we should go through the loop */ + i = ((incr > 0 ? le : ls) - ofs) / incr + 1; + if (i > count) + { + i = count; + count = 0; + } + else count -= i; + while (i--) + { + v1 = src[ofs >> FRACTION_BITS]; + v2 = src[(ofs >> FRACTION_BITS)+1]; + *dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS); + ofs += incr; + } + if (ofs>=le) + { + /* fold the overshoot back in */ + ofs = le2 - ofs; + incr *= -1; + } + else if (ofs <= ls) + { + ofs = ls2 - ofs; + incr *= -1; + } + } + + vp->sample_increment=incr; + vp->sample_offset=ofs; /* Update offset */ + return song->resample_buffer; +} + +/*********************** vibrato versions ***************************/ + +/* We only need to compute one half of the vibrato sine cycle */ +static int vib_phase_to_inc_ptr(int phase) +{ + if (phase < MID_VIBRATO_SAMPLE_INCREMENTS/2) + return MID_VIBRATO_SAMPLE_INCREMENTS/2-1-phase; + else if (phase >= 3*MID_VIBRATO_SAMPLE_INCREMENTS/2) + return 5*MID_VIBRATO_SAMPLE_INCREMENTS/2-1-phase; + else + return phase-MID_VIBRATO_SAMPLE_INCREMENTS/2; +} + +static sint32 update_vibrato(MidSong *song, MidVoice *vp, int sign) +{ + sint32 depth; + int phase, pb; + double a; + + if (vp->vibrato_phase++ >= 2*MID_VIBRATO_SAMPLE_INCREMENTS-1) + vp->vibrato_phase=0; + phase=vib_phase_to_inc_ptr(vp->vibrato_phase); + + if (vp->vibrato_sample_increment[phase]) + { + if (sign) + return -vp->vibrato_sample_increment[phase]; + else + return vp->vibrato_sample_increment[phase]; + } + + /* Need to compute this sample increment. */ + + depth=vp->sample->vibrato_depth<<7; + + if (vp->vibrato_sweep) + { + /* Need to update sweep */ + vp->vibrato_sweep_position += vp->vibrato_sweep; + if (vp->vibrato_sweep_position >= (1<vibrato_sweep=0; + else + { + /* Adjust depth */ + depth *= vp->vibrato_sweep_position; + depth >>= SWEEP_SHIFT; + } + } + + a = FSCALE(((double)(vp->sample->sample_rate) * + (double)(vp->frequency)) / + ((double)(vp->sample->root_freq) * + (double)(song->rate)), + FRACTION_BITS); + + pb=(int)((sine(vp->vibrato_phase * + (SINE_CYCLE_LENGTH/(2*MID_VIBRATO_SAMPLE_INCREMENTS))) + * (double)(depth) * VIBRATO_AMPLITUDE_TUNING)); + + if (pb<0) + { + pb=-pb; + a /= bend_fine[(pb>>5) & 0xFF] * bend_coarse[pb>>13]; + } + else + a *= bend_fine[(pb>>5) & 0xFF] * bend_coarse[pb>>13]; + + /* If the sweep's over, we can store the newly computed sample_increment */ + if (!vp->vibrato_sweep) + vp->vibrato_sample_increment[phase]=(sint32) a; + + if (sign) + a = -a; /* need to preserve the loop direction */ + + return (sint32) a; +} + +static sample_t *rs_vib_plain(MidSong *song, int v, sint32 *countptr) +{ + + /* Play sample until end, then free the voice. */ + + sample_t v1, v2; + MidVoice *vp=&(song->voice[v]); + sample_t + *dest=song->resample_buffer, + *src=vp->sample->data; + sint32 + le=vp->sample->data_length, + ofs=vp->sample_offset, + incr=vp->sample_increment, + count=*countptr; + int + cc=vp->vibrato_control_counter; + + /* This has never been tested */ + + if (incr<0) incr = -incr; /* In case we're coming out of a bidir loop */ + + while (count--) + { + if (!cc--) + { + cc=vp->vibrato_control_ratio; + incr=update_vibrato(song, vp, 0); + } + v1 = src[ofs >> FRACTION_BITS]; + v2 = src[(ofs >> FRACTION_BITS)+1]; + *dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS); + ofs += incr; + if (ofs >= le) + { + if (ofs == le) + *dest++ = src[ofs >> FRACTION_BITS]; + vp->status=VOICE_FREE; + *countptr-=count+1; + break; + } + } + + vp->vibrato_control_counter=cc; + vp->sample_increment=incr; + vp->sample_offset=ofs; /* Update offset */ + return song->resample_buffer; +} + +static sample_t *rs_vib_loop(MidSong *song, MidVoice *vp, sint32 count) +{ + + /* Play sample until end-of-loop, skip back and continue. */ + + sample_t v1, v2; + sint32 + ofs=vp->sample_offset, + incr=vp->sample_increment, + le=vp->sample->loop_end, + ll=le - vp->sample->loop_start; + sample_t + *dest=song->resample_buffer, + *src=vp->sample->data; + int + cc=vp->vibrato_control_counter; + sint32 i; + int + vibflag=0; + + while (count) + { + /* Hopefully the loop is longer than an increment */ + if(ofs >= le) + ofs -= ll; + /* Precalc how many times to go through the loop, taking + the vibrato control ratio into account this time. */ + i = (le - ofs) / incr + 1; + if(i > count) i = count; + if(i > cc) + { + i = cc; + vibflag = 1; + } + else cc -= i; + count -= i; + while(i--) + { + v1 = src[ofs >> FRACTION_BITS]; + v2 = src[(ofs >> FRACTION_BITS)+1]; + *dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS); + ofs += incr; + } + if(vibflag) + { + cc = vp->vibrato_control_ratio; + incr = update_vibrato(song, vp, 0); + vibflag = 0; + } + } + + vp->vibrato_control_counter=cc; + vp->sample_increment=incr; + vp->sample_offset=ofs; /* Update offset */ + return song->resample_buffer; +} + +static sample_t *rs_vib_bidir(MidSong *song, MidVoice *vp, sint32 count) +{ + sample_t v1, v2; + sint32 + ofs=vp->sample_offset, + incr=vp->sample_increment, + le=vp->sample->loop_end, + ls=vp->sample->loop_start; + sample_t + *dest=song->resample_buffer, + *src=vp->sample->data; + int + cc=vp->vibrato_control_counter; + sint32 + le2=le<<1, + ls2=ls<<1, + i; + int + vibflag = 0; + + /* Play normally until inside the loop region */ + while (count && (ofs <= ls)) + { + i = (ls - ofs) / incr + 1; + if (i > count) i = count; + if (i > cc) + { + i = cc; + vibflag = 1; + } + else cc -= i; + count -= i; + while (i--) + { + v1 = src[ofs >> FRACTION_BITS]; + v2 = src[(ofs >> FRACTION_BITS)+1]; + *dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS); + ofs += incr; + } + if (vibflag) + { + cc = vp->vibrato_control_ratio; + incr = update_vibrato(song, vp, 0); + vibflag = 0; + } + } + + /* Then do the bidirectional looping */ + + while (count) + { + /* Precalc how many times we should go through the loop */ + i = ((incr > 0 ? le : ls) - ofs) / incr + 1; + if(i > count) i = count; + if(i > cc) + { + i = cc; + vibflag = 1; + } + else cc -= i; + count -= i; + while (i--) + { + v1 = src[ofs >> FRACTION_BITS]; + v2 = src[(ofs >> FRACTION_BITS)+1]; + *dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS); + ofs += incr; + } + if (vibflag) + { + cc = vp->vibrato_control_ratio; + incr = update_vibrato(song, vp, (incr < 0)); + vibflag = 0; + } + if (ofs >= le) + { + /* fold the overshoot back in */ + ofs = le2 - ofs; + incr *= -1; + } + else if (ofs <= ls) + { + ofs = ls2 - ofs; + incr *= -1; + } + } + + vp->vibrato_control_counter=cc; + vp->sample_increment=incr; + vp->sample_offset=ofs; /* Update offset */ + return song->resample_buffer; +} + +sample_t *resample_voice(MidSong *song, int v, sint32 *countptr) +{ + sint32 ofs; + uint8 modes; + MidVoice *vp=&(song->voice[v]); + + if (!(vp->sample->sample_rate)) + { + /* Pre-resampled data -- just update the offset and check if + we're out of data. */ + ofs=vp->sample_offset >> FRACTION_BITS; /* Kind of silly to use + FRACTION_BITS here... */ + if (*countptr >= (vp->sample->data_length>>FRACTION_BITS) - ofs) + { + /* Note finished. Free the voice. */ + vp->status = VOICE_FREE; + + /* Let the caller know how much data we had left */ + *countptr = (vp->sample->data_length>>FRACTION_BITS) - ofs; + } + else + vp->sample_offset += *countptr << FRACTION_BITS; + + return vp->sample->data+ofs; + } + + /* Need to resample. Use the proper function. */ + modes=vp->sample->modes; + + if (vp->vibrato_control_ratio) + { + if ((modes & MODES_LOOPING) && + ((modes & MODES_ENVELOPE) || + (vp->status==VOICE_ON || vp->status==VOICE_SUSTAINED))) + { + if (modes & MODES_PINGPONG) + return rs_vib_bidir(song, vp, *countptr); + else + return rs_vib_loop(song, vp, *countptr); + } + else + return rs_vib_plain(song, v, countptr); + } + else + { + if ((modes & MODES_LOOPING) && + ((modes & MODES_ENVELOPE) || + (vp->status==VOICE_ON || vp->status==VOICE_SUSTAINED))) + { + if (modes & MODES_PINGPONG) + return rs_bidir(song, vp, *countptr); + else + return rs_loop(song, vp, *countptr); + } + else + return rs_plain(song, v, countptr); + } +} + +void pre_resample(MidSong *song, MidSample *sp) +{ + double a, xdiff; + sint32 incr, ofs, newlen, count; + sint16 *newdata, *dest, *src = (sint16 *) sp->data; + sint16 v1, v2, v3, v4, *vptr; +#ifdef DEBUG_CHATTER + static const char note_name[12][3] = + { + "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" + }; +#endif + + DEBUG_MSG(" * pre-resampling for note %d (%s%d)\n", + sp->note_to_use, + note_name[sp->note_to_use % 12], (sp->note_to_use & 0x7F) / 12); + + a = ((double) (sp->sample_rate) * freq_table[(int) (sp->note_to_use)]) / + ((double) (sp->root_freq) * song->rate); + newlen = (sint32)(sp->data_length / a); + dest = newdata = safe_malloc(newlen >> (FRACTION_BITS - 1)); + + count = (newlen >> FRACTION_BITS) - 1; + ofs = incr = (sp->data_length - (1 << FRACTION_BITS)) / count; + + if (--count) + *dest++ = src[0]; + + /* Since we're pre-processing and this doesn't have to be done in + real-time, we go ahead and do the full sliding cubic interpolation. */ + while (--count) + { + vptr = src + (ofs >> FRACTION_BITS); + /* + * Electric Fence to the rescue: Accessing *(vptr - 1) is not a + * good thing to do when vptr <= src. (TiMidity++ has a similar + * safe-guard here.) + */ + v1 = (vptr > src) ? *(vptr - 1) : 0; + v2 = *vptr; + v3 = *(vptr + 1); + v4 = *(vptr + 2); + xdiff = FSCALENEG(ofs & FRACTION_MASK, FRACTION_BITS); + *dest++ = (sint16)(v2 + (xdiff / 6.0) * (-2 * v1 - 3 * v2 + 6 * v3 - v4 + + xdiff * (3 * (v1 - 2 * v2 + v3) + xdiff * (-v1 + 3 * (v2 - v3) + v4)))); + ofs += incr; + } + + if (ofs & FRACTION_MASK) + { + v1 = src[ofs >> FRACTION_BITS]; + v2 = src[(ofs >> FRACTION_BITS) + 1]; + *dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS); + } + else + *dest++ = src[ofs >> FRACTION_BITS]; + + sp->data_length = newlen; + sp->loop_start = (sint32)(sp->loop_start / a); + sp->loop_end = (sint32)(sp->loop_end / a); + free(sp->data); + sp->data = (sample_t *) newdata; + sp->sample_rate = 0; +} diff --git a/project/jni/timidity/src/resample.h b/project/jni/timidity/src/resample.h new file mode 100644 index 000000000..73a23da54 --- /dev/null +++ b/project/jni/timidity/src/resample.h @@ -0,0 +1,24 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + resample.h +*/ + +extern sample_t *resample_voice(MidSong *song, int v, sint32 *countptr); +extern void pre_resample(MidSong *song, MidSample *sp); diff --git a/project/jni/timidity/src/stream.c b/project/jni/timidity/src/stream.c new file mode 100644 index 000000000..69d4eda76 --- /dev/null +++ b/project/jni/timidity/src/stream.c @@ -0,0 +1,188 @@ +#if HAVE_CONFIG_H +# include +#endif + +#include "string.h" + +#include "timidity.h" +#include "timidity_internal.h" +#include "common.h" + +struct _MidIStream +{ + MidIStreamReadFunc read; + MidIStreamCloseFunc close; + void *ctx; +}; + +typedef struct StdIOContext +{ + FILE *fp; + int autoclose; +} StdIOContext; + +size_t +stdio_istream_read (void *ctx, void *ptr, size_t size, size_t nmemb) +{ + return fread (ptr, size, nmemb, ((StdIOContext *) ctx)->fp); +} + +int +stdio_istream_close (void *ctx) +{ + int ret = 0; + if (((StdIOContext *) ctx)->autoclose) + ret = fclose (((StdIOContext *) ctx)->fp); + free (ctx); + return ret; +} + +typedef struct MemContext +{ + sint8 *base; + sint8 *current; + sint8 *end; + int autofree; +} MemContext; + +size_t +mem_istream_read (void *ctx, void *ptr, size_t size, size_t nmemb) +{ + MemContext *c; + size_t count; + + c = (MemContext *) ctx; + count = nmemb; + + if (c->current + count * size > c->end) + count = (c->end - c->current) / size; + + memcpy (ptr, c->current, count * size); + c->current += count * size; + + return count; +} + +int +mem_istream_close (void *ctx) +{ + if (((MemContext *) ctx)->autofree) + free (((MemContext *) ctx)->base); + free (ctx); + return 0; +} + +MidIStream * +mid_istream_open_fp (FILE * fp, int autoclose) +{ + StdIOContext *ctx; + MidIStream *stream; + + stream = safe_malloc (sizeof (MidIStream)); + if (stream == NULL) + return NULL; + + ctx = safe_malloc (sizeof (StdIOContext)); + if (ctx == NULL) + { + free (stream); + return NULL; + } + ctx->fp = fp; + ctx->autoclose = autoclose; + + stream->ctx = ctx; + stream->read = stdio_istream_read; + stream->close = stdio_istream_close; + + return stream; +} + +MidIStream * +mid_istream_open_file (const char *file) +{ + FILE *fp; + + fp = fopen (file, "rb"); + if (fp == NULL) + return NULL; + + return mid_istream_open_fp (fp, 1); +} + +MidIStream * +mid_istream_open_mem (void *mem, size_t size, int autofree) +{ + MemContext *ctx; + MidIStream *stream; + + stream = safe_malloc (sizeof (MidIStream)); + if (stream == NULL) + return NULL; + + ctx = safe_malloc (sizeof (MemContext)); + if (ctx == NULL) + { + free (stream); + return NULL; + } + ctx->base = mem; + ctx->current = mem; + ctx->end = ((sint8 *) mem) + size; + ctx->autofree = autofree; + + stream->ctx = ctx; + stream->read = mem_istream_read; + stream->close = mem_istream_close; + + return stream; +} + +MidIStream * +mid_istream_open_callbacks (MidIStreamReadFunc read, + MidIStreamCloseFunc close, void *context) +{ + MidIStream *stream; + + stream = safe_malloc (sizeof (MidIStream)); + if (stream == NULL) + return NULL; + + stream->ctx = context; + stream->read = read; + stream->close = close; + + return stream; +} + +size_t +mid_istream_read (MidIStream * stream, void *ptr, size_t size, size_t nmemb) +{ + return stream->read (stream->ctx, ptr, size, nmemb); +} + +void +mid_istream_skip (MidIStream * stream, size_t len) +{ + size_t c; + char tmp[1024]; + while (len > 0) + { + c = len; + if (c > 1024) + c = 1024; + len -= c; + if (c != mid_istream_read (stream, tmp, 1, c)) + { + DEBUG_MSG ("mid_istream_skip error\n"); + } + } +} + +int +mid_istream_close (MidIStream * stream) +{ + int ret = stream->close (stream->ctx); + free (stream); + return ret; +} diff --git a/project/jni/timidity/src/tables.c b/project/jni/timidity/src/tables.c new file mode 100644 index 000000000..b026de1c2 --- /dev/null +++ b/project/jni/timidity/src/tables.c @@ -0,0 +1,214 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#if HAVE_CONFIG_H +# include +#endif + +#include + +#include "timidity.h" +#include "tables.h" + +const sint32 freq_table[128]= +{ + 8176, 8662, 9177, 9723, + 10301, 10913, 11562, 12250, + 12978, 13750, 14568, 15434, + + 16352, 17324, 18354, 19445, + 20602, 21827, 23125, 24500, + 25957, 27500, 29135, 30868, + + 32703, 34648, 36708, 38891, + 41203, 43654, 46249, 48999, + 51913, 55000, 58270, 61735, + + 65406, 69296, 73416, 77782, + 82407, 87307, 92499, 97999, + 103826, 110000, 116541, 123471, + + 130813, 138591, 146832, 155563, + 164814, 174614, 184997, 195998, + 207652, 220000, 233082, 246942, + + 261626, 277183, 293665, 311127, + 329628, 349228, 369994, 391995, + 415305, 440000, 466164, 493883, + + 523251, 554365, 587330, 622254, + 659255, 698456, 739989, 783991, + 830609, 880000, 932328, 987767, + + 1046502, 1108731, 1174659, 1244508, + 1318510, 1396913, 1479978, 1567982, + 1661219, 1760000, 1864655, 1975533, + + 2093005, 2217461, 2349318, 2489016, + 2637020, 2793826, 2959955, 3135963, + 3322438, 3520000, 3729310, 3951066, + + 4186009, 4434922, 4698636, 4978032, + 5274041, 5587652, 5919911, 6271927, + 6644875, 7040000, 7458620, 7902133, + + 8372018, 8869844, 9397273, 9956063, + 10548082, 11175303, 11839822, 12543854 +}; + +/* v=2.^((x/127-1) * 6) */ +const double vol_table[128] = +{ + 0.015625, 0.016145143728351113, 0.016682602624583379, 0.017237953096759438, + 0.017811790741104401, 0.01840473098076444, 0.019017409725829021, 0.019650484055324921, + 0.020304632921913132, 0.020980557880044631, 0.021678983838355849, 0.02240065983711079, + 0.023146359851523596, 0.023916883621822989, 0.024713057510949051, 0.025535735390801884, + 0.026385799557992876, 0.027264161680080529, 0.028171763773305786, 0.029109579212875332, + 0.030078613776876421, 0.031079906724942836, 0.032114531912828696, 0.033183598944085631, + 0.034288254360078256, 0.035429682869614412, 0.036609108619508737, 0.037827796507442342, + 0.039087053538526394, 0.040388230227024875, 0.041732722044739302, 0.043121970917609151, + 0.044557466772132896, 0.046040749133268132, 0.047573408775524545, 0.049157089429020417, + 0.050793489542332405, 0.05248436410402918, 0.054231526524842463, 0.056036850582493913, + 0.057902272431264008, 0.059829792678457581, 0.061821478529993396, 0.063879466007418645, + 0.066005962238725971, 0.068203247825430205, 0.070473679288442961, 0.072819691595368496, + 0.075243800771931268, 0.077748606600335793, 0.080336795407452768, 0.083011142945821612, + 0.085774517370559328, 0.088629882315368294, 0.091580300070941839, 0.094628934869176312, + 0.097779056276712184, 0.10103404270144323, 0.1043973850157546, 0.1078726903003755, + 0.11146368571286204, 0.11517422248485852, 0.11900828005242428, 0.12296997032385605, + 0.12706354208958254, 0.13129338557886089, 0.13566403716816194, 0.14018018424629392, + 0.14484667024148207, 0.14966849981579558, 0.15465084423249356, 0.15979904690204472, + 0.16511862911277009, 0.17061529595225433, 0.17629494242587571, 0.18216365977901747, + 0.18822774202974024, 0.19449369271892172, 0.20096823188510385, 0.20765830327152621, + 0.21457108177307616, 0.22171398113114205, 0.2290946618846218, 0.23672103958561411, + 0.2446012932886038, 0.25274387432224471, 0.26115751535314891, 0.26985123975140174, + 0.27883437126784744, 0.28811654403352405, 0.29770771289197112, 0.30761816407549192, + 0.31785852623682015, 0.32843978184802081, 0.33937327897885317, 0.3506707434672246, + 0.36234429149478936, 0.37440644258117928, 0.38687013301080181, 0.39974872970660535, + 0.41305604456569134, 0.42680634927214656, 0.44101439060298442, 0.45569540624360722, + 0.47086514112975281, 0.48653986433345225, 0.50273638651110641, 0.51947207793239625, + 0.53676488710936021, 0.55463336004561792, 0.57309666012638816, 0.59217458867062556, + 0.61188760616732485, 0.63225685421876243, 0.65330417821421161, 0.67505215075844849, + 0.69752409588017272, 0.72074411404630734, 0.74473710800900605, 0.76952880951308478, + 0.79514580689252357, 0.82161557358563286, 0.84896649759946774, 0.87722791195508854, + 0.90643012614631979, 0.93660445864574493, 0.96778327049280244, 1 +}; + +const double bend_fine[256] = { + 1, 1.0002256593050698, 1.0004513695322617, 1.0006771306930664, + 1.0009029427989777, 1.0011288058614922, 1.0013547198921082, 1.0015806849023274, + 1.0018067009036538, 1.002032767907594, 1.0022588859256572, 1.0024850549693551, + 1.0027112750502025, 1.0029375461797159, 1.0031638683694153, 1.0033902416308227, + 1.0036166659754628, 1.0038431414148634, 1.0040696679605541, 1.0042962456240678, + 1.0045228744169397, 1.0047495543507072, 1.0049762854369111, 1.0052030676870944, + 1.0054299011128027, 1.0056567857255843, 1.00588372153699, 1.006110708558573, + 1.0063377468018897, 1.0065648362784985, 1.0067919769999607, 1.0070191689778405, + 1.0072464122237039, 1.0074737067491204, 1.0077010525656616, 1.0079284496849015, + 1.0081558981184175, 1.008383397877789, 1.008610948974598, 1.0088385514204294, + 1.0090662052268706, 1.0092939104055114, 1.0095216669679448, 1.0097494749257656, + 1.009977334290572, 1.0102052450739643, 1.0104332072875455, 1.0106612209429215, + 1.0108892860517005, 1.0111174026254934, 1.0113455706759138, 1.0115737902145781, + 1.0118020612531047, 1.0120303838031153, 1.0122587578762337, 1.012487183484087, + 1.0127156606383041, 1.0129441893505169, 1.0131727696323602, 1.0134014014954713, + 1.0136300849514894, 1.0138588200120575, 1.0140876066888203, 1.0143164449934257, + 1.0145453349375237, 1.0147742765327674, 1.0150032697908125, 1.0152323147233171, + 1.015461411341942, 1.0156905596583505, 1.0159197596842091, 1.0161490114311862, + 1.0163783149109531, 1.0166076701351838, 1.0168370771155553, 1.0170665358637463, + 1.0172960463914391, 1.0175256087103179, 1.0177552228320703, 1.0179848887683858, + 1.0182146065309567, 1.0184443761314785, 1.0186741975816487, 1.0189040708931674, + 1.0191339960777379, 1.0193639731470658, 1.0195940021128593, 1.0198240829868295, + 1.0200542157806898, 1.0202844005061564, 1.0205146371749483, 1.0207449257987866, + 1.0209752663893958, 1.0212056589585028, 1.0214361035178368, 1.0216666000791297, + 1.0218971486541166, 1.0221277492545349, 1.0223584018921241, 1.0225891065786274, + 1.0228198633257899, 1.0230506721453596, 1.023281533049087, 1.0235124460487257, + 1.0237434111560313, 1.0239744283827625, 1.0242054977406807, 1.0244366192415495, + 1.0246677928971357, 1.0248990187192082, 1.025130296719539, 1.0253616269099028, + 1.0255930093020766, 1.0258244439078401, 1.0260559307389761, 1.0262874698072693, + 1.0265190611245079, 1.0267507047024822, 1.0269824005529853, 1.027214148687813, + 1.0274459491187637, 1.0276778018576387, 1.0279097069162415, 1.0281416643063788, + 1.0283736740398595, 1.0286057361284953, 1.0288378505841009, 1.0290700174184932, + 1.0293022366434921, 1.0295345082709197, 1.0297668323126017, 1.0299992087803651, + 1.030231637686041, 1.0304641190414621, 1.0306966528584645, 1.0309292391488862, + 1.0311618779245688, 1.0313945691973556, 1.0316273129790936, 1.0318601092816313, + 1.0320929581168212, 1.0323258594965172, 1.0325588134325767, 1.0327918199368598, + 1.0330248790212284, 1.0332579906975481, 1.0334911549776868, 1.033724371873515, + 1.0339576413969056, 1.0341909635597348, 1.0344243383738811, 1.0346577658512259, + 1.034891246003653, 1.0351247788430489, 1.0353583643813031, 1.0355920026303078, + 1.0358256936019572, 1.0360594373081489, 1.0362932337607829, 1.0365270829717617, + 1.0367609849529913, 1.0369949397163791, 1.0372289472738365, 1.0374630076372766, + 1.0376971208186156, 1.0379312868297725, 1.0381655056826686, 1.0383997773892284, + 1.0386341019613787, 1.0388684794110492, 1.0391029097501721, 1.0393373929906822, + 1.0395719291445176, 1.0398065182236185, 1.0400411602399278, 1.0402758552053915, + 1.0405106031319582, 1.0407454040315787, 1.0409802579162071, 1.0412151647977996, + 1.0414501246883161, 1.0416851375997183, 1.0419202035439705, 1.0421553225330404, + 1.042390494578898, 1.042625719693516, 1.0428609978888699, 1.043096329176938, + 1.0433317135697009, 1.0435671510791424, 1.0438026417172486, 1.0440381854960086, + 1.0442737824274138, 1.044509432523459, 1.044745135796141, 1.0449808922574599, + 1.0452167019194181, 1.0454525647940205, 1.0456884808932754, 1.0459244502291931, + 1.0461604728137874, 1.0463965486590741, 1.046632677777072, 1.0468688601798024, + 1.0471050958792898, 1.047341384887561, 1.0475777272166455, 1.047814122878576, + 1.048050571885387, 1.0482870742491166, 1.0485236299818055, 1.0487602390954964, + 1.0489969016022356, 1.0492336175140715, 1.0494703868430555, 1.0497072096012419, + 1.0499440858006872, 1.0501810154534512, 1.050417998571596, 1.0506550351671864, + 1.0508921252522903, 1.0511292688389782, 1.0513664659393229, 1.0516037165654004, + 1.0518410207292894, 1.0520783784430709, 1.0523157897188296, 1.0525532545686513, + 1.0527907730046264, 1.0530283450388465, 1.0532659706834067, 1.0535036499504049, + 1.0537413828519411, 1.0539791694001188, 1.0542170096070436, 1.0544549034848243, + 1.0546928510455722, 1.0549308523014012, 1.0551689072644284, 1.0554070159467728, + 1.0556451783605572, 1.0558833945179062, 1.0561216644309479, 1.0563599881118126, + 1.0565983655726334, 1.0568367968255465, 1.0570752818826903, 1.0573138207562065, + 1.057552413458239, 1.0577910600009348, 1.0580297603964437, 1.058268514656918, + 1.0585073227945128, 1.0587461848213857, 1.058985100749698, 1.0592240705916123 +}; + +const double bend_coarse[128] = { + 1, 1.0594630943592953, 1.122462048309373, 1.189207115002721, + 1.2599210498948732, 1.3348398541700344, 1.4142135623730951, 1.4983070768766815, + 1.5874010519681994, 1.681792830507429, 1.7817974362806785, 1.8877486253633868, + 2, 2.1189261887185906, 2.244924096618746, 2.3784142300054421, + 2.5198420997897464, 2.6696797083400687, 2.8284271247461903, 2.996614153753363, + 3.1748021039363992, 3.363585661014858, 3.5635948725613571, 3.7754972507267741, + 4, 4.2378523774371812, 4.4898481932374912, 4.7568284600108841, + 5.0396841995794928, 5.3393594166801366, 5.6568542494923806, 5.993228307506727, + 6.3496042078727974, 6.727171322029716, 7.1271897451227151, 7.5509945014535473, + 8, 8.4757047548743625, 8.9796963864749824, 9.5136569200217682, + 10.079368399158986, 10.678718833360273, 11.313708498984761, 11.986456615013454, + 12.699208415745595, 13.454342644059432, 14.25437949024543, 15.101989002907095, + 16, 16.951409509748721, 17.959392772949972, 19.027313840043536, + 20.158736798317967, 21.357437666720553, 22.627416997969522, 23.972913230026901, + 25.398416831491197, 26.908685288118864, 28.508758980490853, 30.203978005814196, + 32, 33.902819019497443, 35.918785545899944, 38.054627680087073, + 40.317473596635935, 42.714875333441107, 45.254833995939045, 47.945826460053802, + 50.796833662982394, 53.817370576237728, 57.017517960981706, 60.407956011628393, + 64, 67.805638038994886, 71.837571091799887, 76.109255360174146, + 80.63494719327187, 85.429750666882214, 90.509667991878089, 95.891652920107603, + 101.59366732596479, 107.63474115247546, 114.03503592196341, 120.81591202325679, + 128, 135.61127607798977, 143.67514218359977, 152.21851072034829, + 161.26989438654374, 170.85950133376443, 181.01933598375618, 191.78330584021521, + 203.18733465192958, 215.26948230495091, 228.07007184392683, 241.63182404651357, + 256, 271.22255215597971, 287.35028436719938, 304.43702144069658, + 322.53978877308765, 341.71900266752868, 362.03867196751236, 383.56661168043064, + 406.37466930385892, 430.53896460990183, 456.14014368785394, 483.26364809302686, + 512, 542.44510431195943, 574.70056873439876, 608.87404288139317, + 645.0795775461753, 683.43800533505737, 724.07734393502471, 767.13322336086128, + 812.74933860771785, 861.07792921980365, 912.28028737570787, 966.52729618605372, + 1024, 1084.8902086239189, 1149.4011374687975, 1217.7480857627863, + 1290.1591550923506, 1366.8760106701147, 1448.1546878700494, 1534.2664467217226 +}; diff --git a/project/jni/timidity/src/tables.h b/project/jni/timidity/src/tables.h new file mode 100644 index 000000000..bed0558c1 --- /dev/null +++ b/project/jni/timidity/src/tables.h @@ -0,0 +1,30 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + tables.h +*/ + +#include +#define sine(x) (sin((2*PI/1024.0) * (x))) + +#define SINE_CYCLE_LENGTH 1024 +extern const sint32 freq_table[]; +extern const double vol_table[]; +extern const double bend_fine[]; +extern const double bend_coarse[]; diff --git a/project/jni/timidity/src/timidity.c b/project/jni/timidity/src/timidity.c new file mode 100644 index 000000000..0653e0be7 --- /dev/null +++ b/project/jni/timidity/src/timidity.c @@ -0,0 +1,606 @@ +/* + + TiMidity -- Experimental MIDI to WAVE converter + Copyright (C) 1995 Tuukka Toivonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include "timidity.h" +#include "timidity_internal.h" + +#include "options.h" +#include "common.h" +#include "instrum.h" +#include "playmidi.h" +#include "readmidi.h" +#include "output.h" + +#include "tables.h" + +MidToneBank *master_tonebank[128], *master_drumset[128]; + +static char def_instr_name[256] = ""; + +#define MAXWORDS 10 + +/* Quick-and-dirty fgets() replacement. */ + +static char *__fgets(char *s, int size, FILE *fp) +{ + int num_read = 0; + int newline = 0; + + while (num_read < size && !newline) + { + if (fread(&s[num_read], 1, 1, fp) != 1) + break; + + /* Unlike fgets(), don't store newline. Under Windows/DOS we'll + * probably get an extra blank line for every line that's being + * read, but that should be ok. + */ + if (s[num_read] == '\n' || s[num_read] == '\r') + { + s[num_read] = '\0'; + newline = 1; + } + + num_read++; + } + + s[num_read] = '\0'; + + return (num_read != 0) ? s : NULL; +} + +static int read_config_file(char *name) +{ + FILE *fp; + char tmp[1024], *w[MAXWORDS], *cp; + MidToneBank *bank=0; + int i, j, k, line=0, words; + static int rcf_count=0; + + if (rcf_count>50) + { + DEBUG_MSG("Probable source loop in configuration files\n"); + return (-1); + } + + if (!(fp=open_file(name))) + return -1; + + while (__fgets(tmp, sizeof(tmp), fp)) + { + line++; + w[words=0]=strtok(tmp, " \t\240"); + if (!w[0]) continue; + + /* Originally the TiMidity++ extensions were prefixed like this */ + if (strcmp(w[0], "#extension") == 0) + words = -1; + else if (*w[0] == '#') + continue; + + while (w[words] && *w[words] != '#' && (words < MAXWORDS)) + w[++words]=strtok(0," \t\240"); + + /* + * TiMidity++ adds a number of extensions to the config file format. + * Many of them are completely irrelevant to SDL_sound, but at least + * we shouldn't choke on them. + * + * Unfortunately the documentation for these extensions is often quite + * vague, gramatically strange or completely absent. + */ + if ( + !strcmp(w[0], "comm") /* "comm" program second */ + || !strcmp(w[0], "HTTPproxy") /* "HTTPproxy" hostname:port */ + || !strcmp(w[0], "FTPproxy") /* "FTPproxy" hostname:port */ + || !strcmp(w[0], "mailaddr") /* "mailaddr" your-mail-address */ + || !strcmp(w[0], "opt") /* "opt" timidity-options */ + ) + { + /* + * + "comm" sets some kind of comment -- the documentation is too + * vague for me to understand at this time. + * + "HTTPproxy", "FTPproxy" and "mailaddr" are for reading data + * over a network, rather than from the file system. + * + "opt" specifies default options for TiMidity++. + * + * These are all quite useless for our version of TiMidity, so + * they can safely remain no-ops. + */ + } else if (!strcmp(w[0], "timeout")) /* "timeout" program second */ + { + /* + * Specifies a timeout value of the program. A number of seconds + * before TiMidity kills the note. This may be useful to implement + * later, but I don't see any urgent need for it. + */ + DEBUG_MSG("FIXME: Implement \"timeout\" in TiMidity config.\n"); + } else if (!strcmp(w[0], "copydrumset") /* "copydrumset" drumset */ + || !strcmp(w[0], "copybank")) /* "copybank" bank */ + { + /* + * Copies all the settings of the specified drumset or bank to + * the current drumset or bank. May be useful later, but not a + * high priority. + */ + DEBUG_MSG("FIXME: Implement \"%s\" in TiMidity config.\n", w[0]); + } else if (!strcmp(w[0], "undef")) /* "undef" progno */ + { + /* + * Undefines the tone "progno" of the current tone bank (or + * drum set?). Not a high priority. + */ + DEBUG_MSG("FIXME: Implement \"undef\" in TiMidity config.\n"); + } else if (!strcmp(w[0], "altassign")) /* "altassign" prog1 prog2 ... */ + { + /* + * Sets the alternate assign for drum set. Whatever that's + * supposed to mean. + */ + DEBUG_MSG("FIXME: Implement \"altassign\" in TiMidity config.\n"); + } else if (!strcmp(w[0], "soundfont") + || !strcmp(w[0], "font")) + { + /* + * I can't find any documentation for these, but I guess they're + * an alternative way of loading/unloading instruments. + * + * "soundfont" sf_file "remove" + * "soundfont" sf_file ["order=" order] ["cutoff=" cutoff] + * ["reso=" reso] ["amp=" amp] + * "font" "exclude" bank preset keynote + * "font" "order" order bank preset keynote + */ + DEBUG_MSG("FIXME: Implmement \"%s\" in TiMidity config.\n", w[0]); + } else if (!strcmp(w[0], "progbase")) + { + /* + * The documentation for this makes absolutely no sense to me, but + * apparently it sets some sort of base offset for tone numbers. + * Why anyone would want to do this is beyond me. + */ + DEBUG_MSG("FIXME: Implement \"progbase\" in TiMidity config.\n"); + } else if (!strcmp(w[0], "map")) /* "map" name set1 elem1 set2 elem2 */ + { + /* + * This extension is the one we will need to implement, as it is + * used by the "eawpats". Unfortunately I cannot find any + * documentation whatsoever for it, but it looks like it's used + * for remapping one instrument to another somehow. + */ + DEBUG_MSG("FIXME: Implement \"map\" in TiMidity config.\n"); + } + + /* Standard TiMidity config */ + + else if (!strcmp(w[0], "dir")) + { + if (words < 2) + { + DEBUG_MSG("%s: line %d: No directory given\n", name, line); + return -2; + } + for (i=1; i127) + { + DEBUG_MSG("%s: line %d: Drum set must be between 0 and 127\n", + name, line); + return -2; + } + if (!master_drumset[i]) + { + master_drumset[i] = safe_malloc(sizeof(MidToneBank)); + memset(master_drumset[i], 0, sizeof(MidToneBank)); + master_drumset[i]->tone = safe_malloc(128 * sizeof(MidToneBankElement)); + memset(master_drumset[i]->tone, 0, 128 * sizeof(MidToneBankElement)); + } + bank=master_drumset[i]; + } + else if (!strcmp(w[0], "bank")) + { + if (words < 2) + { + DEBUG_MSG("%s: line %d: No bank number given\n", name, line); + return -2; + } + i=atoi(w[1]); + if (i<0 || i>127) + { + DEBUG_MSG("%s: line %d: Tone bank must be between 0 and 127\n", + name, line); + return -2; + } + if (!master_tonebank[i]) + { + master_tonebank[i] = safe_malloc(sizeof(MidToneBank)); + memset(master_tonebank[i], 0, sizeof(MidToneBank)); + master_tonebank[i]->tone = safe_malloc(128 * sizeof(MidToneBankElement)); + memset(master_tonebank[i]->tone, 0, 128 * sizeof(MidToneBankElement)); + } + bank=master_tonebank[i]; + } + else + { + if ((words < 2) || (*w[0] < '0' || *w[0] > '9')) + { + DEBUG_MSG("%s: line %d: syntax error\n", name, line); + return -2; + } + i=atoi(w[0]); + if (i<0 || i>127) + { + DEBUG_MSG("%s: line %d: Program must be between 0 and 127\n", + name, line); + return -2; + } + if (!bank) + { + DEBUG_MSG("%s: line %d: Must specify tone bank or drum set before assignment\n", + name, line); + return -2; + } + if (bank->tone[i].name) + free(bank->tone[i].name); + strcpy((bank->tone[i].name=safe_malloc(strlen(w[1])+1)),w[1]); + bank->tone[i].note=bank->tone[i].amp=bank->tone[i].pan= + bank->tone[i].strip_loop=bank->tone[i].strip_envelope= + bank->tone[i].strip_tail=-1; + + for (j=2; j '9')) + { + DEBUG_MSG("%s: line %d: amplification must be between 0 and %d\n", + name, line, MAX_AMPLIFICATION); + return -2; + } + bank->tone[i].amp=k; + } + else if (!strcmp(w[j], "note")) + { + k=atoi(cp); + if ((k<0 || k>127) || (*cp < '0' || *cp > '9')) + { + DEBUG_MSG("%s: line %d: note must be between 0 and 127\n", + name, line); + return -2; + } + bank->tone[i].note=k; + } + else if (!strcmp(w[j], "pan")) + { + if (!strcmp(cp, "center")) + k=64; + else if (!strcmp(cp, "left")) + k=0; + else if (!strcmp(cp, "right")) + k=127; + else + k=((atoi(cp)+100) * 100) / 157; + if ((k<0 || k>127) || (k==0 && *cp!='-' && (*cp < '0' || *cp > '9'))) + { + DEBUG_MSG("%s: line %d: panning must be left, right, center, or between -100 and 100\n", + name, line); + return -2; + } + bank->tone[i].pan=k; + } + else if (!strcmp(w[j], "keep")) + { + if (!strcmp(cp, "env")) + bank->tone[i].strip_envelope=0; + else if (!strcmp(cp, "loop")) + bank->tone[i].strip_loop=0; + else + { + DEBUG_MSG("%s: line %d: keep must be env or loop\n", name, line); + return -2; + } + } + else if (!strcmp(w[j], "strip")) + { + if (!strcmp(cp, "env")) + bank->tone[i].strip_envelope=1; + else if (!strcmp(cp, "loop")) + bank->tone[i].strip_loop=1; + else if (!strcmp(cp, "tail")) + bank->tone[i].strip_tail=1; + else + { + DEBUG_MSG("%s: line %d: strip must be env, loop, or tail\n", + name, line); + return -2; + } + } + else + { + DEBUG_MSG("%s: line %d: bad patch option %s\n", name, line, w[j]); + return -2; + } + } + } + } + fclose(fp); + return 0; +} + +int mid_init_no_config() +{ + /* Allocate memory for the standard tonebank and drumset */ + master_tonebank[0] = safe_malloc(sizeof(MidToneBank)); + memset(master_tonebank[0], 0, sizeof(MidToneBank)); + master_tonebank[0]->tone = safe_malloc(128 * sizeof(MidToneBankElement)); + memset(master_tonebank[0]->tone, 0, 128 * sizeof(MidToneBankElement)); + + master_drumset[0] = safe_malloc(sizeof(MidToneBank)); + memset(master_drumset[0], 0, sizeof(MidToneBank)); + master_drumset[0]->tone = safe_malloc(128 * sizeof(MidToneBankElement)); + memset(master_drumset[0]->tone, 0, 128 * sizeof(MidToneBankElement)); + + return 0; +} + +int mid_init(char *config_file) +{ + /* !!! FIXME: This may be ugly, but slightly less so than requiring the + * default search path to have only one element. I think. + * + * We only need to include the likely locations for the config + * file itself since that file should contain any other directory + * that needs to be added to the search path. + */ +#ifdef WIN32 + add_to_pathlist("\\TIMIDITY"); +#else + add_to_pathlist("/usr/local/lib/timidity"); + add_to_pathlist("/etc"); +#endif + + mid_init_no_config(); + + if (config_file == NULL || *config_file == '\0') + config_file = CONFIG_FILE; + + return read_config_file(config_file); +} + +MidSong *mid_song_load_dls(MidIStream *stream, MidDLSPatches *patches, MidSongOptions *options) +{ + MidSong *song; + int i; + + if (stream == NULL) + return NULL; + + /* Allocate memory for the song */ + song = (MidSong *)safe_malloc(sizeof(*song)); + memset(song, 0, sizeof(*song)); + song->patches = patches; + + for (i = 0; i < 128; i++) + { + if (master_tonebank[i]) + { + song->tonebank[i] = safe_malloc(sizeof(MidToneBank)); + memset(song->tonebank[i], 0, sizeof(MidToneBank)); + song->tonebank[i]->tone = master_tonebank[i]->tone; + } + if (master_drumset[i]) + { + song->drumset[i] = safe_malloc(sizeof(MidToneBank)); + memset(song->drumset[i], 0, sizeof(MidToneBank)); + song->drumset[i]->tone = master_drumset[i]->tone; + } + } + + song->amplification = DEFAULT_AMPLIFICATION; + song->voices = DEFAULT_VOICES; + song->drumchannels = DEFAULT_DRUMCHANNELS; + + song->rate = options->rate; + song->encoding = 0; + if ((options->format & 0xFF) == 16) + song->encoding |= PE_16BIT; + if (options->format & 0x8000) + song->encoding |= PE_SIGNED; + if (options->channels == 1) + song->encoding |= PE_MONO; + switch (options->format) { + case MID_AUDIO_S8: + song->write = s32tos8; + break; + case MID_AUDIO_U8: + song->write = s32tou8; + break; + case MID_AUDIO_S16LSB: + song->write = s32tos16l; + break; + case MID_AUDIO_S16MSB: + song->write = s32tos16b; + break; + case MID_AUDIO_U16LSB: + song->write = s32tou16l; + break; + default: + DEBUG_MSG("Unsupported audio format\n"); + song->write = s32tou16l; + break; + } + + song->buffer_size = options->buffer_size; + song->resample_buffer = safe_malloc(options->buffer_size * sizeof(sample_t)); + song->common_buffer = safe_malloc(options->buffer_size * 2 * sizeof(sint32)); + + song->bytes_per_sample = + ((song->encoding & PE_MONO) ? 1 : 2) + * ((song->encoding & PE_16BIT) ? 2 : 1); + + song->control_ratio = options->rate / CONTROLS_PER_SECOND; + if (song->control_ratio < 1) + song->control_ratio = 1; + else if (song->control_ratio > MAX_CONTROL_RATIO) + song->control_ratio = MAX_CONTROL_RATIO; + + song->lost_notes = 0; + song->cut_notes = 0; + + song->events = read_midi_file(stream, song, &(song->groomed_event_count), + &song->samples); + + /* Make sure everything is okay */ + if (!song->events) { + free(song); + return(NULL); + } + + song->default_instrument = 0; + song->default_program = DEFAULT_PROGRAM; + + if (*def_instr_name) + set_default_instrument(song, def_instr_name); + + load_missing_instruments(song); + + return(song); +} + +MidSong *mid_song_load(MidIStream *stream, MidSongOptions *options) +{ + return mid_song_load_dls(stream, NULL, options); +} + +void mid_song_free(MidSong *song) +{ + int i; + + free_instruments(song); + + for (i = 0; i < 128; i++) + { + if (song->tonebank[i]) + free(song->tonebank[i]); + if (song->drumset[i]) + free(song->drumset[i]); + } + + free(song->common_buffer); + free(song->resample_buffer); + free(song->events); + + for (i = 0; i < (sizeof(song->meta_data) / sizeof(song->meta_data[0])); i++) + { + if (song->meta_data[i]) + free(song->meta_data[i]); + } + + free(song); +} + +void mid_exit(void) +{ + int i, j; + + for (i = 0; i < 128; i++) + { + if (master_tonebank[i]) + { + MidToneBankElement *e = master_tonebank[i]->tone; + if (e != NULL) + { + for (j = 0; j < 128; j++) + { + if (e[j].name != NULL) + free(e[j].name); + } + free(e); + } + free(master_tonebank[i]); + } + if (master_drumset[i]) + { + MidToneBankElement *e = master_drumset[i]->tone; + if (e != NULL) + { + for (j = 0; j < 128; j++) + { + if (e[j].name != NULL) + free(e[j].name); + } + free(e); + } + free(master_drumset[i]); + } + } + + free_pathlist(); +} diff --git a/project/jni/timidity/src/timidity.h.in b/project/jni/timidity/src/timidity.h.in new file mode 100644 index 000000000..2f23c359a --- /dev/null +++ b/project/jni/timidity/src/timidity.h.in @@ -0,0 +1,206 @@ +/* + + libTiMidity -- MIDI to WAVE converter library + Copyright (C) 1995 Tuukka Toivonen + Copyright (C) 2004 Konstantin Korikov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef TIMIDITY_H +#define TIMIDITY_H + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define LIBTIMIDITY_VERSION_MAJOR @LIBTIMIDITY_MAJOR_VERSION@L +#define LIBTIMIDITY_VERSION_MINOR @LIBTIMIDITY_MINOR_VERSION@L +#define LIBTIMIDITY_PATCHLEVEL @LIBTIMIDITY_MICRO_VERSION@L + +#define LIBTIMIDITY_VERSION \ + ((LIBTIMIDITY_VERSION_MAJOR<<16)| \ + (LIBTIMIDITY_VERSION_MINOR<< 8)| \ + (LIBTIMIDITY_PATCHLEVEL)) + +/* Audio format flags (defaults to LSB byte order) + */ +#define MID_AUDIO_U8 0x0008 /* Unsigned 8-bit samples */ +#define MID_AUDIO_S8 0x8008 /* Signed 8-bit samples */ +#define MID_AUDIO_U16LSB 0x0010 /* Unsigned 16-bit samples */ +#define MID_AUDIO_S16LSB 0x8010 /* Signed 16-bit samples */ +#define MID_AUDIO_U16MSB 0x1010 /* As above, but big-endian byte order */ +#define MID_AUDIO_S16MSB 0x9010 /* As above, but big-endian byte order */ +#define MID_AUDIO_U16 MID_AUDIO_U16LSB +#define MID_AUDIO_S16 MID_AUDIO_S16LSB + +/* Core Library Types + */ + typedef unsigned char uint8; + typedef signed char sint8; + typedef unsigned short uint16; + typedef signed short sint16; + typedef unsigned int uint32; + typedef signed int sint32; + + typedef size_t (*MidIStreamReadFunc) (void *ctx, void *ptr, size_t size, + size_t nmemb); + typedef int (*MidIStreamCloseFunc) (void *ctx); + + typedef struct _MidIStream MidIStream; + typedef struct _MidDLSPatches MidDLSPatches; + typedef struct _MidSong MidSong; + + typedef struct _MidSongOptions MidSongOptions; + struct _MidSongOptions + { + sint32 rate; /* DSP frequency -- samples per second */ + uint16 format; /* Audio data format */ + uint8 channels; /* Number of channels: 1 mono, 2 stereo */ + uint16 buffer_size; /* Sample buffer size in samples */ + }; + + typedef enum + { + MID_SONG_TEXT = 0, + MID_SONG_COPYRIGHT = 1 + } MidSongMetaId; + + +/* Core Library Functions + * ====================== + */ + +/* Initialize the library. If config_file is NULL + * search for configuratin file in default directories + */ + extern int mid_init (char *config_file); + +/* Initialize the library without reading any + * configuratin file + */ + extern int mid_init_no_config (void); + +/* Shutdown the library + */ + extern void mid_exit (void); + + +/* Input Stream Functions + * ====================== + */ + +/* Create input stream from a file name + */ + extern MidIStream *mid_istream_open_file (const char *file); + +/* Create input stream from a file pointer + */ + extern MidIStream *mid_istream_open_fp (FILE * fp, int autoclose); + +/* Create input stream from memory + */ + extern MidIStream *mid_istream_open_mem (void *mem, size_t size, + int autofree); + +/* Create custom input stream + */ + extern MidIStream *mid_istream_open_callbacks (MidIStreamReadFunc read, + MidIStreamCloseFunc close, + void *context); + +/* Read data from input stream + */ + extern size_t mid_istream_read (MidIStream * stream, void *ptr, size_t size, + size_t nmemb); + +/* Skip data from input stream + */ + extern void mid_istream_skip (MidIStream * stream, size_t len); + +/* Close and destroy input stream + */ + extern int mid_istream_close (MidIStream * stream); + + +/* DLS Pathes Functions + * ==================== + */ + +/* Load DLS patches + */ + extern MidDLSPatches *mid_dlspatches_load (MidIStream * stream); + +/* Destroy DLS patches + */ + extern void mid_dlspatches_free (MidDLSPatches * patches); + + +/* MIDI Song Functions + * =================== + */ + +/* Load MIDI song + */ + extern MidSong *mid_song_load (MidIStream * stream, + MidSongOptions * options); + +/* Load MIDI song with specified DLS pathes + */ + extern MidSong *mid_song_load_dls (MidIStream * stream, + MidDLSPatches * patches, + MidSongOptions * options); + +/* Set song amplification value + */ + extern void mid_song_set_volume (MidSong * song, int volume); + +/* Seek song to the start position and initialize conversion + */ + extern void mid_song_start (MidSong * song); + +/* Read WAVE data + */ + extern size_t mid_song_read_wave (MidSong * song, void *ptr, size_t size); + +/* Seek song to specified offset in millseconds + */ + extern void mid_song_seek (MidSong * song, uint32 ms); + +/* Get total song time in millseconds + */ + extern uint32 mid_song_get_total_time (MidSong * song); + +/* Get current song time in millseconds + */ + extern uint32 mid_song_get_time (MidSong * song); + +/* Get song meta data. Return NULL if no meta data found + */ + extern char *mid_song_get_meta (MidSong * song, MidSongMetaId what); + +/* Destroy song + */ + extern void mid_song_free (MidSong * song); + +#ifdef __cplusplus +} +#endif +#endif /* TIMIDITY_H */ diff --git a/project/jni/timidity/src/timidity_internal.h b/project/jni/timidity/src/timidity_internal.h new file mode 100644 index 000000000..9237465eb --- /dev/null +++ b/project/jni/timidity/src/timidity_internal.h @@ -0,0 +1,192 @@ +#ifndef TIMIDITY_INTERNAL_H +#define TIMIDITY_INTERNAL_H + +#include "timidity.h" + +#if defined(__i386__) || defined(__ia64__) || defined(WIN32) || \ + (defined(__alpha__) || defined(__alpha)) || \ + defined(__arm__) || \ + (defined(__mips__) && defined(__MIPSEL__)) || \ + defined(__SYMBIAN32__) || \ + defined(__x86_64__) || \ + defined(__LITTLE_ENDIAN__) +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN +#endif +#undef BIG_ENDIAN +#else +#ifndef BIG_ENDIAN +#define BIG_ENDIAN +#endif +#undef LITTLE_ENDIAN +#endif + +/* Instrument files are little-endian, MIDI files big-endian, so we + need to do some conversions. */ + +#define XCHG_SHORT(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF)) +#ifdef __i486__ +# define XCHG_LONG(x) \ + ({ sint32 __value; \ + asm ("bswap %1; movl %1,%0" : "=g" (__value) : "r" (x)); \ + __value; }) +#else +# define XCHG_LONG(x) ((((x)&0xFF)<<24) | \ + (((x)&0xFF00)<<8) | \ + (((x)&0xFF0000)>>8) | \ + (((x)>>24)&0xFF)) +#endif + +#ifdef LITTLE_ENDIAN +#define SWAPLE16(x) x +#define SWAPLE32(x) x +#define SWAPBE16(x) XCHG_SHORT(x) +#define SWAPBE32(x) XCHG_LONG(x) +#else +#define SWAPBE16(x) x +#define SWAPBE32(x) x +#define SWAPLE16(x) XCHG_SHORT(x) +#define SWAPLE32(x) XCHG_LONG(x) +#endif + +#ifdef DEBUG +#define DEBUG_MSG(...) fprintf(stderr, __VA_ARGS__) +#else +#define DEBUG_MSG(...) +#endif + + +#define MID_VIBRATO_SAMPLE_INCREMENTS 32 + +/* Maximum polyphony. */ +#define MID_MAX_VOICES 48 + +typedef sint16 sample_t; +typedef sint32 final_volume_t; + +typedef struct _MidSample MidSample; +struct _MidSample +{ + sint32 + loop_start, loop_end, data_length, + sample_rate, low_vel, high_vel, low_freq, high_freq, root_freq; + sint32 envelope_rate[6], envelope_offset[6]; + float volume; + sample_t *data; + sint32 + tremolo_sweep_increment, tremolo_phase_increment, + vibrato_sweep_increment, vibrato_control_ratio; + uint8 tremolo_depth, vibrato_depth, modes; + sint8 panning, note_to_use; +}; + +typedef struct _MidChannel MidChannel; +struct _MidChannel +{ + int bank, program, volume, sustain, panning, pitchbend, expression; + int mono; /* one note only on this channel -- not implemented yet */ + int pitchsens; + /* chorus, reverb... Coming soon to a 300-MHz, eight-way superscalar + processor near you */ + float pitchfactor; /* precomputed pitch bend factor to save some fdiv's */ +}; + +typedef struct _MidVoice MidVoice; +struct _MidVoice +{ + uint8 status, channel, note, velocity; + MidSample *sample; + sint32 + orig_frequency, frequency, + sample_offset, sample_increment, + envelope_volume, envelope_target, envelope_increment, + tremolo_sweep, tremolo_sweep_position, + tremolo_phase, tremolo_phase_increment, + vibrato_sweep, vibrato_sweep_position; + + final_volume_t left_mix, right_mix; + + float left_amp, right_amp, tremolo_volume; + sint32 vibrato_sample_increment[MID_VIBRATO_SAMPLE_INCREMENTS]; + int + vibrato_phase, vibrato_control_ratio, vibrato_control_counter, + envelope_stage, control_counter, panning, panned; + +}; + +typedef struct _MidInstrument MidInstrument; +struct _MidInstrument +{ + int samples; + MidSample *sample; +}; + +typedef struct _MidToneBankElement MidToneBankElement; +struct _MidToneBankElement +{ + char *name; + int note, amp, pan, strip_loop, strip_envelope, strip_tail; +}; + +typedef struct _MidToneBank MidToneBank; +struct _MidToneBank +{ + MidToneBankElement *tone; + MidInstrument *instrument[128]; +}; + +typedef struct _MidEvent MidEvent; +struct _MidEvent +{ + sint32 time; + uint8 channel, type, a, b; +}; + +typedef struct _MidEventList MidEventList; +struct _MidEventList +{ + MidEvent event; + void *next; +}; + +struct _MidSong +{ + int playing; + sint32 rate; + sint32 encoding; + int bytes_per_sample; + float master_volume; + sint32 amplification; + MidDLSPatches *patches; + MidToneBank *tonebank[128]; + MidToneBank *drumset[128]; + MidInstrument *default_instrument; + int default_program; + void (*write) (void *dp, sint32 * lp, sint32 c); + int buffer_size; + sample_t *resample_buffer; + sint32 *common_buffer; + /* These would both fit into 32 bits, but they are often added in + large multiples, so it's simpler to have two roomy ints */ + /* samples per MIDI delta-t */ + sint32 sample_increment; + sint32 sample_correction; + MidChannel channel[16]; + MidVoice voice[MID_MAX_VOICES]; + int voices; + sint32 drumchannels; + sint32 control_ratio; + sint32 lost_notes; + sint32 cut_notes; + sint32 samples; + MidEvent *events; + MidEvent *current_event; + MidEventList *evlist; + sint32 current_sample; + sint32 event_count; + sint32 at; + sint32 groomed_event_count; + char *meta_data[8]; +}; + +#endif /* TIMIDITY_INTERNAL_H */