diff options
-rw-r--r-- | plugins/wildmidi/COPYING | 340 | ||||
-rw-r--r-- | plugins/wildmidi/Makefile.am | 13 | ||||
-rw-r--r-- | plugins/wildmidi/README | 82 | ||||
-rw-r--r-- | plugins/wildmidi/include/wildmidi_lib.h | 56 | ||||
-rw-r--r-- | plugins/wildmidi/src/wildmidi.c | 1128 | ||||
-rw-r--r-- | plugins/wildmidi/src/wildmidi_lib.c | 5072 | ||||
-rw-r--r-- | plugins/wildmidi/wildmidiplug.c | 170 |
7 files changed, 6861 insertions, 0 deletions
diff --git a/plugins/wildmidi/COPYING b/plugins/wildmidi/COPYING new file mode 100644 index 00000000..45645b4b --- /dev/null +++ b/plugins/wildmidi/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 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.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+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 give any other recipients of the Program a copy of this License
+along with the Program.
+
+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 Program or any portion
+of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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 Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) 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; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, 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 executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+ 5. 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 Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. 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 Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+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.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 Program
+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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), 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 Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/plugins/wildmidi/Makefile.am b/plugins/wildmidi/Makefile.am new file mode 100644 index 00000000..e12e95a7 --- /dev/null +++ b/plugins/wildmidi/Makefile.am @@ -0,0 +1,13 @@ +if HAVE_WILDMIDI +wildmidipath=@top_srcdir@/plugins/wildmidi + +EXTRA_DIST = $(wildmidipath)/README + +pkglib_LTLIBRARIES = wildmidi.la + +wildmidi_la_SOURCES = wildmidiplug.c src/wildmidi.c src/wildmidi_lib.c include/wildmidi_lib.h + +wildmidi_la_LDFLAGS = -module + +AM_CFLAGS = $(CFLAGS) -std=c99 -I$(wildmidipath)/include -DWILDMIDI_VERSION=\"0.2.2\" -DWILDMIDILIB_VERSION=\"0.2.2\" -DTIMIDITY_CFG=\"/etc/timidity.conf\" -fPIC +endif diff --git a/plugins/wildmidi/README b/plugins/wildmidi/README new file mode 100644 index 00000000..b9f60e5a --- /dev/null +++ b/plugins/wildmidi/README @@ -0,0 +1,82 @@ +WildMidi v0.2.0 Release + +Introduction +============== + +WildMidi is a wave table player that uses gus pat sound sets to play midi file. The WildMidi player +is the frontend, and is only designed to pass information to the core library and output any audio +data the library returns. + +The core of the project, libWildMidi, is the work horse behind the player. Designed to be used in future +and existing projects, like QuakeForge (http://www.quakeforge.net), its capable of multithreading and +multiprocessing of midi files, allowing for the mixing of multiple midi file at any one time if someone +had a desire to do so. This library is what turns the midi files into audio data using the gus pat sets. + +Not all midi events are supported by the core library at this stage, and more research is being done to +improve performance and quality so that more events can be added. + +At the moment Wildmidi will only work in linux and cygwin. Native windows support is not available +at this stage but is planned. + + +Installation +============== +to compile and install wildmidi use the following proceedure + +./configure +make +make install + +This will work for both linux and cygwin + +FreeBSD users, use ./configure --with-timidity-cfg=/usr/local/share/timidity/timidity.cfg + +If you want to change the default timidity.cfg path and/or file use --with-timidity-cfg=/full/path_and_filename/of/new/config.cfg + +Running +============= + +NOTE: it is assumed here that your system is setup correctly and that you have +installed some gus pats. I personally recommend eawpats, but you may have your +own favorates. + +If you have /etc/timidity.cfg, once installed you can simply do + +wildmidi <midi file name> + +if not, you can use the command line option -c to point wildmidi to the timidity.cfg + +example: wildmidi -c ~/guspats/timidity.cfg + + +Once the program is running, you can quit from it by pressing q + +Cygwin users: Yes, I know about the screen roll. I am looking at a different interface + to corrct this. + + +Further Information +=================== +For further information feel free to contact myself at one of the following + +Email: wildcode@users.sourceforge.net +IRC: #quakeforge on irc.oftc.net + + +Known Bugs +=========== + +1) Reverb engine fails badly at rates below 22050 +2) Output in cygwin scrolls +3) Surround options looses base on surround systems +4) Some midi file lengths (total time) are reported wrong + + +Thanks to +========== +Quakeforge .. http://www.quakeforge.net + irc: #quakeforge irc.oftc.net + +Eric A Welsh .. http://www.stardate.bc.ca/eawpatches/html/default.htm + +xmms .. http://www.xmms.org/ diff --git a/plugins/wildmidi/include/wildmidi_lib.h b/plugins/wildmidi/include/wildmidi_lib.h new file mode 100644 index 00000000..b95eb392 --- /dev/null +++ b/plugins/wildmidi/include/wildmidi_lib.h @@ -0,0 +1,56 @@ +/* + wildmidi_lib.h + + Midi Wavetable Processing library + + Copyright (C)2001-2004 Chris Ison + + 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 + + Email: cisos@bigpond.net.au + wildcode@users.sourceforge.net + + $Id: wildmidi_lib.h,v 1.6 2004/01/26 02:24:33 wildcode Exp $ +*/ + +#define WM_MO_LINEAR_VOLUME 0x0001 +#define WM_MO_EXPENSIVE_INTERPOLATION 0x0002 +#define WM_MO_REVERB 0x0004 +#define WM_MO_BIG_ENDIAN_OUTPUT 0x0020 + +#define WM_GS_VERSION 0x0001 + +struct _WM_Info { + unsigned long int current_sample; + unsigned long int approx_total_samples; + unsigned short int mixer_options; +}; + +typedef void midi; + +extern const char * WildMidi_GetString (unsigned short int info); +extern int WildMidi_Init (const char * config_file, unsigned short int rate, unsigned short int options); +extern int WildMidi_MasterVolume (unsigned char master_volume); +extern midi * WildMidi_Open (const char *midifile); +extern midi * WildMidi_OpenBuffer (unsigned char *midibuffer, unsigned long int size); +extern int WildMidi_LoadSamples ( midi * handle); +extern int WildMidi_GetOutput (midi * handle, char * buffer, unsigned long int size); +extern int WildMidi_SetOption (midi * handle, unsigned short int options, unsigned short int setting); +extern struct _WM_Info * WildMidi_GetInfo ( midi * handle ); +extern int WildMidi_FastSeek ( midi * handle, unsigned long int *sample_pos); +extern int WildMidi_SampledSeek ( midi * handle, unsigned long int *sample_pos); +extern int WildMidi_Close (midi * handle); +extern int WildMidi_Shutdown ( void ); +// extern void WildMidi_ReverbSet(midi * handle, float width, float wet, float dry, float damp, float roomsize); diff --git a/plugins/wildmidi/src/wildmidi.c b/plugins/wildmidi/src/wildmidi.c new file mode 100644 index 00000000..8baa53f5 --- /dev/null +++ b/plugins/wildmidi/src/wildmidi.c @@ -0,0 +1,1128 @@ + /* + wildmidi.c + + Midi Player using the WildMidi Midi Processing Library + + Copyright (C)2001-2004 Chris Ison + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Email: cisos@bigpond.net.au + wildcode@users.sourceforge.net + + $Id: wildmidi.c,v 1.21 2004/01/28 05:45:09 wildcode Exp $ +*/ + +#include "config.h" + +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifndef _WIN32 +#include <termios.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#endif +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#if (defined _WIN32) || (defined __CYGWIN__) +# include <conio.h> +# include <windows.h> +# include <mmsystem.h> +#else +# ifdef HAVE_ALSA +# include <alsa/asoundlib.h> +# else +# ifdef HAVE_SYS_SOUNDCARD_H +# include <sys/soundcard.h> +# elif defined HAVE_LINUX_SOUNDCARD_H +# include <linux/soundcard.h> +# elif HAVE_MACHINE_SOUNDCARD_H +# include <machine/soundcard.h> +# endif +# endif +#endif + +#include "wildmidi_lib.h" + +#ifndef FNONBLOCK +#define FNONBLOCK _FNONBLOCK +#endif + + +struct _midi_test { + unsigned char *data; + unsigned long int size; +}; + +// scale test from 0 to 127 +/* test a + * offset 18-21 (0x12-0x15) - track size + * offset 25 (0x1A) = bank number + * offset 28 (0x1D) = patch number + */ +unsigned char midi_test_c_scale[] = { + 0x4d, 0x54, 0x68, 0x64, 0x00, 0x00, 0x00, 0x06, // 0x00 + 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x4d, 0x54, // 0x08 + 0x72, 0x6b, 0x00, 0x00, 0x02, 0x63, 0x00, 0xb0, // 0x10 + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x90, 0x00, // 0x18 C + 0x64, 0x08, 0x80, 0x00, 0x00, 0x08, 0x90, 0x02, // 0x20 D + 0x64, 0x08, 0x80, 0x02, 0x00, 0x08, 0x90, 0x04, // 0x28 E + 0x64, 0x08, 0x80, 0x04, 0x00, 0x08, 0x90, 0x05, // 0x30 F + 0x64, 0x08, 0x80, 0x05, 0x00, 0x08, 0x90, 0x07, // 0x38 G + 0x64, 0x08, 0x80, 0x07, 0x00, 0x08, 0x90, 0x09, // 0x40 A + 0x64, 0x08, 0x80, 0x09, 0x00, 0x08, 0x90, 0x0b, // 0x48 B + 0x64, 0x08, 0x80, 0x0b, 0x00, 0x08, 0x90, 0x0c, // 0x50 C + 0x64, 0x08, 0x80, 0x0c, 0x00, 0x08, 0x90, 0x0e, // 0x58 D + 0x64, 0x08, 0x80, 0x0e, 0x00, 0x08, 0x90, 0x10, // 0x60 E + 0x64, 0x08, 0x80, 0x10, 0x00, 0x08, 0x90, 0x11, // 0x68 F + 0x64, 0x08, 0x80, 0x11, 0x00, 0x08, 0x90, 0x13, // 0x70 G + 0x64, 0x08, 0x80, 0x13, 0x00, 0x08, 0x90, 0x15, // 0x78 A + 0x64, 0x08, 0x80, 0x15, 0x00, 0x08, 0x90, 0x17, // 0x80 B + 0x64, 0x08, 0x80, 0x17, 0x00, 0x08, 0x90, 0x18, // 0x88 C + 0x64, 0x08, 0x80, 0x18, 0x00, 0x08, 0x90, 0x1a, // 0x90 D + 0x64, 0x08, 0x80, 0x1a, 0x00, 0x08, 0x90, 0x1c, // 0x98 E + 0x64, 0x08, 0x80, 0x1c, 0x00, 0x08, 0x90, 0x1d, // 0xA0 F + 0x64, 0x08, 0x80, 0x1d, 0x00, 0x08, 0x90, 0x1f, // 0xA8 G + 0x64, 0x08, 0x80, 0x1f, 0x00, 0x08, 0x90, 0x21, // 0xB0 A + 0x64, 0x08, 0x80, 0x21, 0x00, 0x08, 0x90, 0x23, // 0xB8 B + 0x64, 0x08, 0x80, 0x23, 0x00, 0x08, 0x90, 0x24, // 0xC0 C + 0x64, 0x08, 0x80, 0x24, 0x00, 0x08, 0x90, 0x26, // 0xC8 D + 0x64, 0x08, 0x80, 0x26, 0x00, 0x08, 0x90, 0x28, // 0xD0 E + 0x64, 0x08, 0x80, 0x28, 0x00, 0x08, 0x90, 0x29, // 0xD8 F + 0x64, 0x08, 0x80, 0x29, 0x00, 0x08, 0x90, 0x2b, // 0xE0 G + 0x64, 0x08, 0x80, 0x2b, 0x00, 0x08, 0x90, 0x2d, // 0xE8 A + 0x64, 0x08, 0x80, 0x2d, 0x00, 0x08, 0x90, 0x2f, // 0xF0 B + 0x64, 0x08, 0x80, 0x2f, 0x00, 0x08, 0x90, 0x30, // 0xF8 C + 0x64, 0x08, 0x80, 0x30, 0x00, 0x08, 0x90, 0x32, // 0x100 D + 0x64, 0x08, 0x80, 0x32, 0x00, 0x08, 0x90, 0x34, // 0x108 E + 0x64, 0x08, 0x80, 0x34, 0x00, 0x08, 0x90, 0x35, // 0x110 F + 0x64, 0x08, 0x80, 0x35, 0x00, 0x08, 0x90, 0x37, // 0x118 G + 0x64, 0x08, 0x80, 0x37, 0x00, 0x08, 0x90, 0x39, // 0x120 A + 0x64, 0x08, 0x80, 0x39, 0x00, 0x08, 0x90, 0x3b, // 0X128 B + 0x64, 0x08, 0x80, 0x3b, 0x00, 0x08, 0x90, 0x3c, // 0x130 C + 0x64, 0x08, 0x80, 0x3c, 0x00, 0x08, 0x90, 0x3e, // 0x138 D + 0x64, 0x08, 0x80, 0x3e, 0x00, 0x08, 0x90, 0x40, // 0X140 E + 0x64, 0x08, 0x80, 0x40, 0x00, 0x08, 0x90, 0x41, // 0x148 F + 0x64, 0x08, 0x80, 0x41, 0x00, 0x08, 0x90, 0x43, // 0x150 G + 0x64, 0x08, 0x80, 0x43, 0x00, 0x08, 0x90, 0x45, // 0x158 A + 0x64, 0x08, 0x80, 0x45, 0x00, 0x08, 0x90, 0x47, // 0x160 B + 0x64, 0x08, 0x80, 0x47, 0x00, 0x08, 0x90, 0x48, // 0x168 C + 0x64, 0x08, 0x80, 0x48, 0x00, 0x08, 0x90, 0x4a, // 0x170 D + 0x64, 0x08, 0x80, 0x4a, 0x00, 0x08, 0x90, 0x4c, // 0x178 E + 0x64, 0x08, 0x80, 0x4c, 0x00, 0x08, 0x90, 0x4d, // 0x180 F + 0x64, 0x08, 0x80, 0x4d, 0x00, 0x08, 0x90, 0x4f, // 0x188 G + 0x64, 0x08, 0x80, 0x4f, 0x00, 0x08, 0x90, 0x51, // 0x190 A + 0x64, 0x08, 0x80, 0x51, 0x00, 0x08, 0x90, 0x53, // 0x198 B + 0x64, 0x08, 0x80, 0x53, 0x00, 0x08, 0x90, 0x54, // 0x1A0 C + 0x64, 0x08, 0x80, 0x54, 0x00, 0x08, 0x90, 0x56, // 0x1A8 D + 0x64, 0x08, 0x80, 0x56, 0x00, 0x08, 0x90, 0x58, // 0x1B0 E + 0x64, 0x08, 0x80, 0x58, 0x00, 0x08, 0x90, 0x59, // 0x1B8 F + 0x64, 0x08, 0x80, 0x59, 0x00, 0x08, 0x90, 0x5b, // 0x1C0 G + 0x64, 0x08, 0x80, 0x5b, 0x00, 0x08, 0x90, 0x5d, // 0x1C8 A + 0x64, 0x08, 0x80, 0x5d, 0x00, 0x08, 0x90, 0x5f, // 0x1D0 B + 0x64, 0x08, 0x80, 0x5f, 0x00, 0x08, 0x90, 0x60, // 0x1D8 C + 0x64, 0x08, 0x80, 0x60, 0x00, 0x08, 0x90, 0x62, // 0x1E0 D + 0x64, 0x08, 0x80, 0x62, 0x00, 0x08, 0x90, 0x64, // 0x1E8 E + 0x64, 0x08, 0x80, 0x64, 0x00, 0x08, 0x90, 0x65, // 0x1F0 F + 0x64, 0x08, 0x80, 0x65, 0x00, 0x08, 0x90, 0x67, // 0x1F8 G + 0x64, 0x08, 0x80, 0x67, 0x00, 0x08, 0x90, 0x69, // 0x200 A + 0x64, 0x08, 0x80, 0x69, 0x00, 0x08, 0x90, 0x6b, // 0x208 B + 0x64, 0x08, 0x80, 0x6b, 0x00, 0x08, 0x90, 0x6c, // 0x210 C + 0x64, 0x08, 0x80, 0x6c, 0x00, 0x08, 0x90, 0x6e, // 0x218 D + 0x64, 0x08, 0x80, 0x6e, 0x00, 0x08, 0x90, 0x70, // 0x220 E + 0x64, 0x08, 0x80, 0x70, 0x00, 0x08, 0x90, 0x71, // 0x228 F + 0x64, 0x08, 0x80, 0x71, 0x00, 0x08, 0x90, 0x73, // 0x230 G + 0x64, 0x08, 0x80, 0x73, 0x00, 0x08, 0x90, 0x75, // 0x238 A + 0x64, 0x08, 0x80, 0x75, 0x00, 0x08, 0x90, 0x77, // 0x240 B + 0x64, 0x08, 0x80, 0x77, 0x00, 0x08, 0x90, 0x78, // 0x248 C + 0x64, 0x08, 0x80, 0x78, 0x00, 0x08, 0x90, 0x7a, // 0x250 D + 0x64, 0x08, 0x80, 0x7a, 0x00, 0x08, 0x90, 0x7c, // 0x258 E + 0x64, 0x08, 0x80, 0x7c, 0x00, 0x08, 0x90, 0x7d, // 0x260 F + 0x64, 0x08, 0x80, 0x7d, 0x00, 0x08, 0x90, 0x7f, // 0x268 G + 0x64, 0x08, 0x80, 0x7f, 0x00, 0x08, 0xff, 0x2f, // 0x270 + 0x00 // 0x278 +}; + +struct _midi_test midi_test[]= { + {midi_test_c_scale, 663}, + {NULL, 0} +}; + +int midi_test_max = 1; + +/* + ============================== + Audio Output Functions + ------------------------------ + ============================== +*/ + +unsigned int rate = 32072; +char *pcmname = NULL; + +int (*send_output) (char * output_data, int output_size); +void (*close_output) ( void ); +int audio_fd; + +inline void +shutdown_output ( void ) { + printf("Shutting Down Sound System\n"); + if (audio_fd != -1) + close(audio_fd); +} + +/* + Wav Output Functions +*/ + +static char wav_file[1024] = "\0"; +unsigned long int wav_size; + +int write_wav_output (char * output_data, int output_size); +void close_wav_output ( void ); + +int +open_wav_output ( void ) { + + char wav_hdr[] = { 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, + 0x57, 0x41, 0x56, 0x45, 0x66, 0x6D, 0x74, 0x20, + 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, + 0x44, 0xAC, 0x00, 0x00, 0x10, 0xB1, 0x02, 0x00, + 0x04, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, + 0x00, 0x00, 0x00, 0x00 }; + + if (wav_file[0] == '\0') + return -1; +#ifdef _WIN32 + if ((audio_fd = open(wav_file, (O_RDWR | O_CREAT | O_TRUNC | O_BINARY))) < 0) { +#else + if ((audio_fd = open(wav_file, (O_RDWR | O_CREAT | O_TRUNC), (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH))) < 0) { +#endif + return -1; + } else { + unsigned long int bytes_per_sec; + + wav_hdr[24] = (rate) & 0xFF; + wav_hdr[25] = (rate >> 8) & 0xFF; + + bytes_per_sec = rate * 4; + wav_hdr[28] = (bytes_per_sec) & 0xFF; + wav_hdr[29] = (bytes_per_sec >> 8) & 0xFF; + wav_hdr[30] = (bytes_per_sec >> 16) & 0xFF; + wav_hdr[31] = (bytes_per_sec >> 24) & 0xFF; + } + + if (write(audio_fd, &wav_hdr, 44) < 0) { + printf("ERROR: Writing Header %s\n", strerror(errno)); + shutdown_output(); + return -1; + } + + wav_size = 0; + send_output = write_wav_output; + close_output = close_wav_output; + return 0; +} + +int +write_wav_output (char * output_data, int output_size) { + if (write(audio_fd, output_data, output_size) < 0) { + printf("ERROR: Writing Wav %s\n", strerror(errno)); + shutdown_output(); + return -1; + } + + wav_size += output_size; + return 0; +} + +void +close_wav_output ( void ) { + char wav_count[4]; + if (audio_fd == -1) + return; + + wav_count[0] = (wav_size) & 0xFF; + wav_count[1] = (wav_size >> 8) & 0xFF; + wav_count[2] = (wav_size >> 16) & 0xFF; + wav_count[3] = (wav_size >> 24) & 0xFF; + lseek(audio_fd,40,SEEK_SET); + write(audio_fd,&wav_count,4); + + wav_size += 36; + wav_count[0] = (wav_size) & 0xFF; + wav_count[1] = (wav_size >> 8) & 0xFF; + wav_count[2] = (wav_size >> 16) & 0xFF; + wav_count[3] = (wav_size >> 24) & 0xFF; + lseek(audio_fd,4,SEEK_SET); + write(audio_fd,&wav_count,4); + + shutdown_output(); +} + + +#if (defined _WIN32) || (defined __CYGWIN__) + +HWAVEOUT hWaveOut; +WAVEHDR header; +unsigned long int mm_buffer_count; +static CRITICAL_SECTION waveCriticalSection; + +int write_mm_output (char * output_data, int output_size); +void close_mm_output ( void ); + +WAVEHDR *mm_blocks; +#define MM_BLOCK_SIZE 16384 +#define MM_BLOCK_COUNT 3 + +unsigned long int mm_free_blocks = MM_BLOCK_COUNT; +unsigned long int mm_current_block = 0; + +static void CALLBACK mmOutProc( HWAVEOUT hWaveOut, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 ) { + int* freeBlockCounter = (int*)dwInstance; + if(uMsg != WOM_DONE) + return; + EnterCriticalSection(&waveCriticalSection); + (*freeBlockCounter)++; + LeaveCriticalSection(&waveCriticalSection); +} + + +int +open_mm_output ( void ) { + WAVEFORMATEX wfx; + char *mm_buffer; + int i; + + InitializeCriticalSection(&waveCriticalSection); + + if((mm_buffer = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, ((MM_BLOCK_SIZE + sizeof(WAVEHDR)) * MM_BLOCK_COUNT))) == NULL) { + printf("Memory allocation error\n"); + return -1; + } + + mm_blocks = (WAVEHDR*)mm_buffer; + mm_buffer += sizeof(WAVEHDR) * MM_BLOCK_COUNT; + + for(i = 0; i < MM_BLOCK_COUNT; i++) { + mm_blocks[i].dwBufferLength = MM_BLOCK_SIZE; + mm_blocks[i].lpData = mm_buffer; + mm_buffer += MM_BLOCK_SIZE; + } + + wfx.nSamplesPerSec = rate; + wfx.wBitsPerSample = 16; + wfx.nChannels = 2; + wfx.cbSize = 0; + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels; + wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; + + if(waveOutOpen( &hWaveOut, WAVE_MAPPER, &wfx, (DWORD_PTR)mmOutProc, (DWORD_PTR)&mm_free_blocks, CALLBACK_FUNCTION ) != MMSYSERR_NOERROR) { + printf("unable to open WAVE_MAPPER device\n"); + return -1; + } + + + send_output = write_mm_output; + close_output = close_mm_output; + return 0; +} + +int +write_mm_output (char * output_data, int output_size) { + WAVEHDR* current; + int free_size = 0; + int data_read = 0; + current = &mm_blocks[mm_current_block]; + + while (output_size) { + if(current->dwFlags & WHDR_PREPARED) + waveOutUnprepareHeader(hWaveOut, current, sizeof(WAVEHDR)); + free_size = MM_BLOCK_SIZE - current->dwUser; + if (free_size > output_size) + free_size = output_size; + + memcpy(current->lpData + current->dwUser, &output_data[data_read], free_size); + current->dwUser += free_size; + output_size -= free_size; + data_read += free_size; + + if (current->dwUser < MM_BLOCK_SIZE) { + return 0; + } + + current->dwBufferLength = MM_BLOCK_SIZE; + waveOutPrepareHeader(hWaveOut, current, sizeof(WAVEHDR)); + waveOutWrite(hWaveOut, current, sizeof(WAVEHDR)); + EnterCriticalSection(&waveCriticalSection); + mm_free_blocks--; + LeaveCriticalSection(&waveCriticalSection); + while(!mm_free_blocks) + Sleep(10); + mm_current_block++; + mm_current_block %= MM_BLOCK_COUNT; + current = &mm_blocks[mm_current_block]; + current->dwUser = 0; + } + return 0; +} + +void +close_mm_output ( void ) { + WAVEHDR* current; + int i, j; + current = &mm_blocks[mm_current_block]; + i = MM_BLOCK_SIZE - current->dwUser; + + for (j = i; i; i--) write_mm_output (0, 0); + + waveOutClose (hWaveOut); + HeapFree(GetProcessHeap(), 0, mm_blocks); +} + +#else +#ifdef HAVE_ALSA + +void *buffer; +int bps; +int alsa_first_time = 1; +static snd_pcm_t *pcm; +static snd_pcm_uframes_t alsa_period_size; +static snd_pcm_channel_area_t *areas; + +int write_alsa_output (char * output_data, int output_size); +void close_alsa_output ( void ); + +int +open_alsa_output(void) { + snd_pcm_hw_params_t *hw; + snd_pcm_sw_params_t *sw; + int err; + int alsa_buffer_time, bits_per_sample; + unsigned int alsa_period_time; + snd_pcm_uframes_t alsa_buffer_size; + + if (!pcmname) + pcmname = "default"; + + if ((err = snd_pcm_open (&pcm, pcmname, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { + printf("Error: audio open error: %s\n", snd_strerror (-err)); + return -1; + } + + snd_pcm_hw_params_alloca (&hw); + + if ((err = snd_pcm_hw_params_any(pcm, hw)) < 0) { + printf("ERROR: No configuration available for playback: %s\n", snd_strerror(-err)); + + return -1; + } + + if ((err = snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0) { + printf("Cannot set mmap'ed mode: %s.\n", snd_strerror(-err)); + return -1; + } + + if (snd_pcm_hw_params_set_format (pcm, hw, SND_PCM_FORMAT_S16) < 0) { + printf("ALSA does not support 16bit signed audio for your soundcard\n"); + close_alsa_output(); + return -1; + } + + if (snd_pcm_hw_params_set_channels (pcm, hw, 2) < 0) { + printf("ALSA does not support stereo for your soundcard\n"); + close_alsa_output(); + return -1; + } +#ifdef ALSA_NEW + if (snd_pcm_hw_params_set_rate_near(pcm, hw, &rate, 0) < 0) { +#else + if (snd_pcm_hw_params_set_rate_near(pcm, hw, rate, 0) < 0) { +#endif + printf("ALSA does not support %iHz for your soundcard\n",rate); + close_alsa_output(); + return -1; + } + + alsa_buffer_time = 500000; + alsa_period_time = 50000; +#ifdef ALSA_NEW + if ((err = snd_pcm_hw_params_set_buffer_time_near(pcm, hw, &alsa_buffer_time, 0)) < 0) +#else + if ((err = snd_pcm_hw_params_set_buffer_time_near(pcm, hw, alsa_buffer_time, 0)) < 0) +#endif + { + printf("Set buffer time failed: %s.\n", snd_strerror(-err)); + return -1; + } + +#ifdef ALSA_NEW + if ((err = snd_pcm_hw_params_set_period_time_near(pcm, hw, &alsa_period_time, 0)) < 0) +#else + if ((err = snd_pcm_hw_params_set_period_time_near(pcm, hw, alsa_period_time, 0)) < 0) +#endif + { + printf("Set period time failed: %s.\n", snd_strerror(-err)); + return -1; + } + + if (snd_pcm_hw_params(pcm, hw) < 0) + { + printf("Unable to install hw params\n"); + return -1; + } + +#ifdef ALSA_NEW + if ((err = snd_pcm_hw_params_get_buffer_size(hw, &alsa_buffer_size)) < 0) +#else + if ((err = snd_pcm_hw_params_get_buffer_size(hw)) < 0) +#endif + { + printf ("snd_pcm_hw_params_get_buffer_size() failed: %s\n", snd_strerror(-err)); + return -1; + } +#ifdef ALSA_NEW + if ((err = snd_pcm_hw_params_get_period_size(hw, &alsa_period_size, 0)) < 0) +#else + alsa_buffer_size = err; + if ((err = snd_pcm_hw_params_get_period_size(hw, 0)) < 0) +#endif + { + printf ("snd_pcm_hw_params_get_period_size() failed: %s\n", snd_strerror(-err)); + return -1; + } +#ifndef ALSA_NEW + alsa_period_size = err; +#endif + snd_pcm_sw_params_alloca(&sw); + snd_pcm_sw_params_current(pcm, sw); + if (snd_pcm_sw_params(pcm, sw) < 0) + { + printf("Unable to install sw params\n"); + return -1; + } + + bits_per_sample = snd_pcm_format_physical_width(SND_PCM_FORMAT_S16); + bps = (rate * bits_per_sample * 2) / 8000; + + buffer = malloc(alsa_period_size * bits_per_sample / 8 * 2); + areas = malloc(2 * sizeof(snd_pcm_channel_area_t)); + + areas[0].addr = buffer; + areas[0].first = 0; + areas[0].step = 2 * bits_per_sample; + areas[1].addr = buffer; + areas[1].first = bits_per_sample; + areas[1].step = 2 * bits_per_sample; + + send_output = write_alsa_output; + close_output = close_alsa_output; + return 0; +} + +int +write_alsa_output (char * output_data, int output_size) { + int cnt = 0, err; + snd_pcm_uframes_t offset, frames; + snd_pcm_sframes_t avail; + const snd_pcm_channel_area_t *chan_areas = areas; + + while (output_size > 0) { + avail = snd_pcm_avail_update(pcm); + if (avail == -EPIPE) { + if (snd_pcm_state(pcm) == SND_PCM_STATE_XRUN) { + if ((err = snd_pcm_prepare(pcm)) < 0) + printf("snd_pcm_prepare() failed.\n"); + alsa_first_time = 1; + } + } else if (avail < 0) { + printf("snd_pcm_avail_update() failed: %s\n", snd_strerror(-avail)); + avail = 0; + } + if (avail < alsa_period_size) { + usleep(500); + continue; + } + frames = snd_pcm_bytes_to_frames(pcm, output_size); + if ((err = snd_pcm_mmap_begin(pcm, &chan_areas, &offset, &frames)) < 0) { + printf("snd_pcm_mmap_begin() failed: %s\n", snd_strerror(-err)); + } + cnt = snd_pcm_frames_to_bytes(pcm, frames); + memcpy((char*) chan_areas[0].addr + snd_pcm_frames_to_bytes(pcm, offset), output_data, cnt); + if ((err = snd_pcm_mmap_commit(pcm, offset, frames)) < 0) { + if (snd_pcm_state(pcm) == SND_PCM_STATE_XRUN) { + if ((err = snd_pcm_prepare(pcm)) < 0) + printf("snd_pcm_prepare() failed.\n"); + alsa_first_time = 1; + } + } + if (err != frames) + printf("snd_pcm_mmap_commit returned %d, expected %d\n", err, (int)frames); + + output_size -= cnt; + output_data += cnt; + if (alsa_first_time) { + alsa_first_time = 0; + snd_pcm_start(pcm); + } + } + return 0; +} + +void +close_alsa_output ( void ) { + snd_pcm_close (pcm); + free(areas); + free(buffer); +} + +#else +/* + OSS Output Functions + -------------------- + uses mmap'd audio +*/ + +char *buffer = NULL; +unsigned long int max_buffer; +unsigned long int buffer_delay; +unsigned long int counter; +struct audio_buf_info info; + +int write_oss_output (char * output_data, int output_size); +void close_oss_output ( void ); + +int +open_oss_output( void ) { + int caps, rc, tmp; + unsigned long int omode = O_RDWR; + unsigned long int mmmode = PROT_WRITE | PROT_READ; + unsigned long int mmflags = MAP_FILE | MAP_SHARED; + unsigned long int sz = sysconf (_SC_PAGESIZE); + + if (!pcmname) + pcmname = "/dev/dsp"; + if ((audio_fd = open(pcmname, omode)) < 0) { + printf("ERROR: Unable to open /dev/dsp (%s)\n",strerror(errno)); + return -1; + } + if (ioctl (audio_fd, SNDCTL_DSP_RESET, 0) < 0) { + printf("ERROR: Unable to reset /dev/dsp\n"); + shutdown_output(); + return -1; + } + if (ioctl (audio_fd, SNDCTL_DSP_GETCAPS, &caps) == -1) { + printf("ERROR: Driver Too Old\n"); + shutdown_output(); + return -1; + } + if (!(caps & DSP_CAP_TRIGGER) || !(caps & DSP_CAP_MMAP)) { + printf ("Sound device can't do memory-mapped I/O.\n"); + shutdown_output(); + return -1; + } + if (ioctl (audio_fd, SNDCTL_DSP_GETOSPACE, &info) == -1) { + printf ("Um, can't do GETOSPACE?\n"); + shutdown_output(); + return -1; + } + max_buffer = (info.fragstotal * info.fragsize + sz - 1) & ~(sz - 1); + + rc = AFMT_S16_LE; + if (ioctl (audio_fd, SNDCTL_DSP_SETFMT, &rc) < 0 ) { + printf("Can't set 16bit\n"); + shutdown_output();; + return -1; + } + + tmp = 2; + if (ioctl (audio_fd, SNDCTL_DSP_CHANNELS, &tmp) < 0) { + printf("Can't set stereo\n"); + shutdown_output(); + return -1; + } + + if (ioctl (audio_fd, SNDCTL_DSP_SPEED, &rate) < 0) { + printf("ERROR: /dev/dsp does not support %iHz output\n",rate); + shutdown_output(); + return -1; + } + + buffer = (unsigned char *) mmap(NULL, max_buffer, mmmode, mmflags, audio_fd, 0); + if (buffer == MAP_FAILED) { + printf("couldn't mmap %s\n",strerror(errno)); + shutdown_output(); + return -1; + } + + tmp = 0; + if (ioctl (audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp) < 0) { + printf("Couldn't toggle\n"); + munmap (buffer, info.fragstotal * info.fragsize); + shutdown_output(); + return -1; + } + + tmp = PCM_ENABLE_OUTPUT; + if (ioctl (audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp) < 0) { + printf("Couldn't toggle\n"); + munmap (buffer, info.fragstotal * info.fragsize); + shutdown_output(); + return -1; + } + buffer_delay = 1000000 / (rate / 4); + send_output = write_oss_output; + close_output = close_oss_output; + return 0; +} + +int +write_oss_output(char * output_data, int output_size) { + struct count_info count; + int data_read = 0; + int free_size = 0; + while (output_size != 0) { + while (1) { + if (ioctl (audio_fd, SNDCTL_DSP_GETOPTR, &count) == -1) { + printf("Dead Sound\n"); + munmap (buffer, info.fragstotal * info.fragsize); + shutdown_output(); + return -1; + } + if ((count.ptr < counter) || (count.ptr >= (counter+4))) { + break; + } + usleep(500); + } + if (count.ptr < counter) { + free_size = max_buffer - counter; + } else { + free_size = count.ptr - counter; + } + if (free_size > output_size) + free_size = output_size; + + memcpy(&buffer[counter], &output_data[data_read], free_size); + data_read += free_size; + counter += free_size; + if (counter >= max_buffer) + counter = 0; + output_size -= free_size; + } + return 0; +} + +void +close_oss_output(void) { + shutdown_output(); + if (buffer != NULL) + munmap (buffer, info.fragstotal * info.fragsize); + audio_fd = -1; +} + +#endif // HAVE_ALSA +#endif + +/* + ============================== + ============================== + ============================== +*/ + + +static struct option const long_options[] = { + {"version",0,0,'v'}, + {"help",0,0,'h'}, + {"rate",1,0,'r'}, + {"master_volume",1,0,'m'}, + {"config_file",1,0,'c'}, + {"wavout",1,0,'o'}, + {"linear_vol",0,0,'l'}, + {"reverb",0,0,'b'}, + {"midi_test",0,0,'t'}, + {"test_bank",1,0,'k'}, + {"test_patch",1,0,'p'}, + {"expensive",0,0,'e'}, + {"device",1,0,'d'}, + {NULL,0,NULL,0} +}; + +void +do_help (void) { + printf(" -v --version Display version\n"); + printf(" -h --help This help.\n"); +#ifndef _WIN32 + printf(" -d D --device=D Use device D for audio output instead\n"); + printf(" of the default\n"); +#endif + printf("Software Wavetable Options\n"); + printf(" -o W --wavout=W Saves the output to W in wav format\n"); + printf(" at 44100Hz 16 bit stereo\n"); + printf(" -l --linear_vol Use linear volume adjustments\n"); + printf(" -r N --rate=N output at N samples per second\n"); + printf(" -c P --config_file=P P is the path and filename to your timidity.cfg\n"); + printf(" Defaults to /etc/timidity.cfg\n\n"); + printf(" -m V --master_volume=V Sets the master volumes, default is 100\n"); + printf(" range is 0-127 with 127 being the loudest\n"); +} + +void +do_version (void) { + printf("WildMidi %s Open Source Midi Sequencer\n",WILDMIDI_VERSION); + printf("Copyright (C) 2001-2004 Chris Ison wildcode@users.sourceforge.net\n\n"); + printf("WildMidi comes with ABSOLUTELY NO WARRANTY\n"); + printf("This is free software, and you are welcome to redistribute it\n"); + printf("under the terms and conditions of the GNU General Public License version 2.\n"); + printf("For more information see COPYING\n\n"); +} + +void +do_syntax (void) { + printf("wildmidi [options] filename.mid\n\n"); +} + +int +main (int argc, char **argv) { + struct _WM_Info * wm_info = NULL; + int i; + int option_index = 0; + unsigned long int mixer_options = 0; + static char *config_file = NULL; + void *midi_ptr = NULL; + unsigned char master_volume = 100; + int output_result = 0; + char * output_buffer = NULL; + unsigned long int perc_play = 0; + unsigned long int pro_mins = 0; + unsigned long int pro_secs = 0; + unsigned long int apr_mins = 0; + unsigned long int apr_secs = 0; + unsigned char modes[4] = {0}; + unsigned long int count_diff; + unsigned char ch; + unsigned char test_midi = 0; + unsigned char test_count = 0; + unsigned char *test_data = NULL; + unsigned char test_bank = 0; + unsigned char test_patch = 0; + static char *spinner="|/-\\"; + static int spinpoint = 0; + +#ifndef _WIN32 + int my_tty; + struct termios _tty; + tcflag_t _res_oflg = _tty.c_oflag; + tcflag_t _res_lflg = _tty.c_lflag; + +#define raw() (_tty.c_lflag &= ~(ICANON | ICRNL | ISIG), \ + _tty.c_oflag &= ~ONLCR, tcsetattr(my_tty, TCSANOW, &_tty)) +#define savetty() ((void) tcgetattr(my_tty, &_tty), \ + _res_oflg = _tty.c_oflag, _res_lflg = _tty.c_lflag) +#define resetty() (_tty.c_oflag = _res_oflg, _tty.c_lflag = _res_lflg,\ + (void) tcsetattr(my_tty, TCSADRAIN, &_tty)) +#endif + + do_version(); + while (1) { + i = getopt_long (argc, argv, "vho:lr:c:m:btk:p:ed:", long_options, &option_index); + if (i == -1) + break; + switch (i) { + case 'v': // Version + return 0; + case 'h': // help + do_syntax(); + do_help(); + return 0; + case 'r': // SoundCard Rate + rate = atoi(optarg); + break; +#ifdef EPERIMENT_626 + case 'b': // Reverb + mixer_options ^= WM_MO_REVERB; + break; +#endif + case 'm': // Master Volume + master_volume = (unsigned char)atoi(optarg); + break; + case 'o': // Wav Output + strcpy(wav_file,optarg); + break; + case 'c': // Config File + config_file = malloc (strlen(optarg)+1); + strcpy(config_file,optarg); + break; + case 'd': // Output device + pcmname = malloc (strlen(optarg)+1); + strcpy(pcmname,optarg); + break; + case 'e': // Expensive Interpolation + mixer_options |= WM_MO_EXPENSIVE_INTERPOLATION; + break; + case 'l': // linear volume + mixer_options |= WM_MO_LINEAR_VOLUME; + break; + case 't': // play test midis + test_midi = 1; + break; + case 'k': // set test bank + test_bank = (unsigned char)atoi(optarg); + break; + case 'p': // set test patch + test_patch = (unsigned char)atoi(optarg); + break; + default: + printf ("Unknown Option -%o ??\n", i); + return 0; + } + } + + if (!config_file) { + config_file = malloc(sizeof(TIMIDITY_CFG)+1); + strncpy (config_file, TIMIDITY_CFG, sizeof(TIMIDITY_CFG)); + config_file[sizeof(TIMIDITY_CFG)] = '\0'; + } + if ((optind < argc) || (test_midi)) { + printf("Initializing Sound System\n"); + + if (wav_file[0] != '\0') { + if (open_wav_output() == -1) { + return 0; + } + } else { +#if (defined _WIN32) || (defined __CYGWIN__) + if (open_mm_output() == -1) { +#else +#ifdef HAVE_ALSA + if (open_alsa_output() == -1) { +#else + if (open_oss_output() == -1) { +#endif +#endif + return 0; + } + } + printf("Initializing %s\n\n", WildMidi_GetString(WM_GS_VERSION)); + printf(" + Volume up e Better resampling n Next Midi\n"); + printf(" - Volume down l Linear volume q Quit\n\n"); + + if (WildMidi_Init (config_file, rate, mixer_options) == -1) { + return 0; + } + WildMidi_MasterVolume(master_volume); + + output_buffer = malloc(16384); + if (output_buffer == NULL) { + printf("Not enough ram, exiting\n"); + WildMidi_Shutdown(); + return 0; + } +#ifndef _WIN32 + my_tty = fileno(stdin); + if (isatty(my_tty)) { + savetty(); + raw(); + fcntl(0, F_SETFL, FNONBLOCK); + } +#endif + + while ((optind < argc) || (test_midi)) { + if (!test_midi) { + char * real_file = strrchr(argv[optind], '/'); + if (real_file == NULL) { + real_file = strrchr(argv[optind], '\\'); + } + midi_ptr = WildMidi_Open (argv[optind]); + if (midi_ptr == NULL) { + optind++; + continue; + } + wm_info = WildMidi_GetInfo(midi_ptr); + + printf ("\rPlaying "); + if (real_file != NULL) { + printf("%s \n", (real_file+1)); + } else { + printf("%s \n", argv[optind]); + } + optind++; + } else { + if (test_count == midi_test_max) { + break; + } + test_data = malloc(midi_test[test_count].size); + memcpy(test_data, midi_test[test_count].data, midi_test[test_count].size); + test_data[25] = test_bank; + test_data[28] = test_patch; + midi_ptr = WildMidi_OpenBuffer(test_data, 633); + wm_info = WildMidi_GetInfo(midi_ptr); + test_count++; + printf ("\rPlaying test midi no. %i\n", test_count); + } + + apr_mins = wm_info->approx_total_samples / (rate * 60); + apr_secs = (wm_info->approx_total_samples % (rate * 60)) / rate; + + if (midi_ptr == NULL) { + fprintf(stderr,"\rSkipping %s\n",argv[optind]); + optind++; + continue; + } + mixer_options = wm_info->mixer_options; + WildMidi_LoadSamples(midi_ptr); + while (1) { + count_diff = wm_info->approx_total_samples - wm_info->current_sample; + if (count_diff == 0) + break; + + ch = 0; +#ifdef _WIN32 + if (_kbhit()) { + ch = _getch(); + putch(ch); + } +#else + if (read(my_tty, &ch, 1) != 1) + ch = 0; +#endif + if (ch) { + switch (ch) { + case 'l': + WildMidi_SetOption(midi_ptr, WM_MO_LINEAR_VOLUME, ((mixer_options & WM_MO_LINEAR_VOLUME) ^ WM_MO_LINEAR_VOLUME)); + mixer_options ^= WM_MO_LINEAR_VOLUME; + break; +#ifdef EXPERIMENT_626 + case 'r': + WildMidi_SetOption(midi_ptr, WM_MO_REVERB, ((mixer_options & WM_MO_REVERB) ^ WM_MO_REVERB)); + mixer_options ^= WM_MO_REVERB; + break; +#endif + case 'e': + WildMidi_SetOption(midi_ptr, WM_MO_EXPENSIVE_INTERPOLATION, ((mixer_options & WM_MO_EXPENSIVE_INTERPOLATION) ^ WM_MO_EXPENSIVE_INTERPOLATION)); + mixer_options ^= WM_MO_EXPENSIVE_INTERPOLATION; + break; + case 'n': + goto NEXTMIDI; + case 'q': + printf("\n"); + WildMidi_Close(midi_ptr); + WildMidi_Shutdown(); + printf("Shutting down Sound System\n"); + close_output(); +#ifndef _WIN32 + if (isatty(my_tty)) + resetty(); +#endif + printf("\r\n"); + exit (0); + case '-': + if (master_volume > 0) { + master_volume--; + WildMidi_MasterVolume(master_volume); + } + break; + case '+': + if (master_volume < 127) { + master_volume++; + WildMidi_MasterVolume(master_volume); + } + break; + } + } + + if (count_diff < 4096) { + output_result = WildMidi_GetOutput (midi_ptr, output_buffer, (count_diff * 4)); + } else { + output_result = WildMidi_GetOutput (midi_ptr, output_buffer, 16384); + } + + wm_info = WildMidi_GetInfo(midi_ptr); + perc_play = (wm_info->current_sample * 100) / wm_info->approx_total_samples; + pro_mins = wm_info->current_sample / (rate * 60); + pro_secs = (wm_info->current_sample % (rate * 60)) / rate; + { + int mode_count = 0; + if (mixer_options & WM_MO_LINEAR_VOLUME) { + modes[mode_count++] = 'l'; + } + if (mixer_options & WM_MO_REVERB) { + modes[mode_count++] = 'r'; + } + if (mixer_options & WM_MO_EXPENSIVE_INTERPOLATION) { + modes[mode_count++] = 'e'; + } + if (mode_count !=3) { + do { + modes[mode_count++] = ' '; + } while (mode_count != 3); + } + modes[3] = '\0'; + } + fprintf(stderr, "\r"); + fprintf(stderr, "\t [Approx %2lum %2lus Total] [%s] [%3i] [%2lum %2lus Processed] [%2lu%%] %c ", + apr_mins, apr_secs, modes, master_volume, + pro_mins, pro_secs, perc_play, spinner[spinpoint++%4]); + + if (output_result == 0) + break; + send_output (output_buffer, output_result); + } +NEXTMIDI: + fprintf(stderr, "\n\r"); + if (WildMidi_Close(midi_ptr) == -1) { + printf ("oops\n"); + } + memset(output_buffer, 0, 16384); + send_output (output_buffer, 16384); + } + memset(output_buffer, 0, 16384); + send_output (output_buffer, 16384); + send_output (output_buffer, 16384); +#ifdef _WIN32 + Sleep(5); +#else + usleep(5000); +#endif + if (WildMidi_Shutdown() == -1) + printf("oops\n"); + printf("Shutting down Sound System\n"); + close_output(); +#ifndef _WIN32 + if (isatty(my_tty)) + resetty(); +#endif + } else { + printf("ERROR: No midi file given\n"); + do_syntax(); + return 0; + } + + if (output_buffer != NULL) + free(output_buffer); + printf("\r"); + return 0; +} + +//============================ +//============================ +//============================ + diff --git a/plugins/wildmidi/src/wildmidi_lib.c b/plugins/wildmidi/src/wildmidi_lib.c new file mode 100644 index 00000000..315bf1a4 --- /dev/null +++ b/plugins/wildmidi/src/wildmidi_lib.c @@ -0,0 +1,5072 @@ +/* + wildmidi_lib.c + + Midi Wavetable Processing library + + Copyright (C)2001-2004 Chris Ison + + 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 + + Email: cisos@bigpond.net.au + wildcode@users.sourceforge.net + + $Id: wildmidi_lib.c,v 1.18 2004/01/28 05:45:09 wildcode Exp $ + + ======================================== + Changelog + ---------------------------------------- + Aug 3, 2003 + - changed kill envelope from 6th to 5th + - changed data_length, loop_start, loop_end to 22:10 fixed point + - added fractional position to loop_start and loop_end + - added fake reverb + + Aug 4, 2003 + - added MIDI_EVENT_DEBUG to midi event functions + - fixed hold release killing off notes that hadn't been turned off + by note off + - sped up fake reverb by doing it outside the channel mixing loop + + Aug 5, 2003 + - removed note_table init from ParseNewMidi, entries are now fully reset + in do_note_on + - moved fast kill envelope setting to sample loading + - removed envelopes from notes, use ones from sample instead + - optimized do_amp_setup functions + - do_control_volume, do_control_expression, do_channel_pressure, + changed from setting note_table[] to note[] for performance. + - optimizations of sample conversion, + + Aug 6, 2003 + - removed changing sample volumes, the amp setting is now apart of + the midi volume maths. + - re-write conversion functions to make them more clearer and less bug prone + + Aug 7, 2003 + - fixed volume, expression and preasure changes effecting all notes. + + Aug 8, 2003 + - spead up midi processing by using an event index + + Aug 9, 2003 + - spead up sampling and mixing by using a seperate function depending on fixed + modes + - fixed data lock where it would sleep reguardless of lock state + - fixed memory leak ... oops, forgot a free + - removed track data storge, isn't required by the core functions + + Aug 10, 2003 + - created error function and changed all error messages to use it + - malloc, calloc, realloc audit ensuring all are error checked. + - fixed potential reading beyond end of midi bug + + Aug 11, 2003 + - fixed expensive interpolation over-running the sample buffer + - changed stereo option so that changing it worked right away + + Aug 14, 2003 + - optimizations for and to the frequency calc code + - removal of wide stereo (it sucked anyway) + + Aug 15, 2003 + - fixed volume levels for panning when non-linear volumes are used + - removed fake reberb, it sucked on better sound systems. + + Aug 17, 2003 + - fixed autoamp + - added env_time#= and env_level#= to timiidty.cfg parser. + - fixed bug where last event in the midi before the last eot would + have a large delta. This is a bug in the midi file itself. + - fixed some midi's having no sound cause they don't supply patch information. + Now defaulting all channels to bank 0, patch 0. + + Aug 18, 2003 + - preload samples + - optimized envelope checking and controler code + - fixed bug where some samples have an envelope rate setting that doesn't + actually mean anything .. ie: 0x00 0x40 0x80 0xC0 + - fixed amp bug where initial left/right levels were set to 0 + - added timidity's keep=[loop|env] support for drum patches, + now ignores loops and env settings for drum patches unless keep=[loop|env] + is in the patch line in the config. + + Aug 21, 2003 + - float to fixed point math conversions. + - frequency range locked to 100 steps between notes + NOTE:need to test slow pitchbends with this, fast ones are fine + + Aug 24, 2003 + - optimized sample conversions + - optimized note handling and sample/envelope position checks + + Aug 28, 2003 + - compile level optimizations + NOTE: gcc builtins used + - numerous bug fixes + + Aug 30, 2003 + - fixed sample inc calculation bug + - fixed panning volumes, now percieved volume of the sample is the same no matter + panning position. + +` Sep 2, 2003 + - made noteoff/hold behaviour match midi standard + - added remove=sustain to patch line + - added all sound off controller + - fixed all notes off to only effect notes that aren't being held + - changed envelope behaviour so that only non-sustaned samples hit envelope 4 + while note is on. + - Added all controllers off event + + + Sep 4, 2003 + - added master sample data lock + - improved performance of the resampling algo + + ======================================== +*/ + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <math.h> +#ifndef _WIN32 +#include <pwd.h> +#endif +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#ifdef _WIN32 +# include <windows.h> +#endif +#include "config.h" +#include "wildmidi_lib.h" + +/* + * ========================= + * Global Data and Data Structs + * ========================= + */ + +int WM_Initialized = 0; +signed short int WM_MasterVolume = 948; +unsigned short int WM_SampleRate = 0; +unsigned short int WM_MixerOptions = 0; + +char WM_Version[] = "WildMidi Processing Library " WILDMIDILIB_VERSION; + +struct _lowpass { + signed long int in[2]; + signed long int out[2]; +}; + +struct _filter { + signed long int *delay[4][2]; + unsigned long int delay_pos[4][2]; + struct _lowpass lowpass[4][2]; + signed long int in[2][2]; + signed long int out[2][2]; +}; + +struct _env { + float time; + float level; + unsigned char set; +}; + +struct _sample { + unsigned long int data_length; + unsigned long int loop_start; + unsigned long int loop_end; + unsigned long int loop_size; + unsigned char loop_fraction; + unsigned short int rate; + unsigned long int freq_low; + unsigned long int freq_high; + unsigned long int freq_root; + unsigned char modes; + unsigned long int env_rate[7]; + unsigned long int env_target[7]; + unsigned long int inc_div; + signed short *data; + signed short max_peek; + signed short min_peek; + signed long int peek_adjust; + struct _sample *next; +}; + +struct _patch { + unsigned short patchid; + unsigned char loaded; + char *filename; + signed short int amp; + unsigned char keep; + unsigned char remove; + struct _env env[6]; + unsigned char note; + unsigned long int inuse_count; + struct _sample *first_sample; + struct _patch *next; +}; + +struct _patch *patch[128]; + +int patch_lock; + +struct _channel { + unsigned char bank; + struct _patch *patch; + unsigned char hold; + unsigned char volume; + unsigned char pressure; + unsigned char expression; + signed char balance; + signed char pan; + signed short int left_adjust; + signed short int right_adjust; + signed short int pitch; + signed short int pitch_range; + signed long int pitch_adjust; + unsigned short reg_data; +}; + +#define HOLD_OFF 0x02 + +struct _note { + unsigned short noteid; + unsigned char velocity; + struct _patch *patch; + struct _sample *sample; + unsigned long int sample_pos; + unsigned long int sample_inc; + signed long int env_inc; + unsigned char env; + unsigned long int env_level; + unsigned char modes; + unsigned char hold; + unsigned char active; + struct _note *next; + signed short int vol_lvl; +}; + +struct _miditrack { + unsigned long int length; + unsigned long int ptr; + unsigned long int delta; + unsigned char running_event; + unsigned char EOT; +}; + +struct _mdi_patches { + struct _patch *patch; + struct _mdi_patch *next; +}; + +struct _mdi_index { + unsigned long int offset; + unsigned char event; + unsigned long int delta; +}; + +struct _mdi { + int lock; + unsigned char *data; + unsigned long int size; + unsigned short int divisions ; + unsigned short midi_master_vol; + unsigned long int samples_per_delta; + unsigned long int samples_to_mix; + struct _mdi_index * index; + unsigned long int index_count; + unsigned long int index_size; + struct _WM_Info info; + struct _WM_Info *tmp_info; + unsigned char recalc_samples; + struct _channel channel[16]; + struct _note *note[128]; + struct _note **last_note; + struct _note note_table[2][16][128]; + + struct _patch **patches; + unsigned long int patch_count; + unsigned long int sample_count; + signed short int amp; + +// setup data for auto amp + signed long int log_cur_vol; + signed long int lin_cur_vol; + signed long int log_max_vol; + signed long int lin_max_vol; + + unsigned char ch_vol[16]; + unsigned char ch_exp[16]; + unsigned char note_vel[16][128]; + + struct _filter filter; +}; + +/* Gauss Interpolation code adapted from code supplied by Eric. A. Welsh */ + +double newt_coeffs[58][58]; /* for start/end of samples */ +float *gauss_table[(1<<10)] = {0}; /* don't need doubles */ +int gauss_window[35] = {0}; +int gauss_n = 34; /* do not set this value higher than 34 */ + /* 34 is as high as we can go before errors crop up */ + +void init_gauss (void) { + /* init gauss table */ + int n = 34; + int m, i, k, n_half = (n>>1); + int j; + int sign; + double ck; + double x, x_inc, xz; + double z[35]; + float *gptr; + + newt_coeffs[0][0] = 1; + + for (i = 0; i <= n; i++) { + newt_coeffs[i][0] = 1; + newt_coeffs[i][i] = 1; + + if (i > 1) { + newt_coeffs[i][0] = newt_coeffs[i-1][0] / i; + newt_coeffs[i][i] = newt_coeffs[i-1][0] / i; + } + + for (j = 1; j < i; j++) { + newt_coeffs[i][j] = newt_coeffs[i-1][j-1] + newt_coeffs[i-1][j]; + if (i > 1) + newt_coeffs[i][j] /= i; + } + z[i] = i / (4*M_PI); + } + + for (i = 0; i <= n; i++) + for (j = 0, sign = pow(-1, i); j <= i; j++, sign *= -1) + newt_coeffs[i][j] *= sign; + + + x_inc = 1.0 / (1<<10); + for (m = 0, x = 0.0; m < (1<<10); m++, x += x_inc) { + xz = (x + n_half) / (4*M_PI); + gptr = gauss_table[m] = realloc(gauss_table[m], (n+1)*sizeof(float)); + + for (k = 0; k <= n; k++) { + ck = 1.0; + + for (i = 0; i <= n; i++) { + if (i == k) + continue; + + ck *= (sin(xz - z[i])) / (sin(z[k] - z[i])); + } + *gptr++ = ck; + } + } +} + +unsigned long int delay_size[4][2]; +signed long int a[5][2]; +signed long int b[5][2]; +signed long int gain_in[4]; +signed long int gain_out[4]; + +void init_lowpass (void) { + float c = 0; + int i; + float f[] = { 512.0, 1024.0, 2048.0, 4096.0 , 8192.0}; + float aa, ab, ba, bb; + + for (i = 0; i < 5; i++) { + c = 1.0 / tan(3.141592654 * f[i] / WM_SampleRate); + aa = 1.0 / (1.0 + 1.4 * c + c * c); + ab = 2.0 * aa; + ba = 2.0 * (1.0 - c * c) * aa; + bb = (1.0 - 1.4 * c + c * c) * aa; + a[i][0] = (signed long int)(aa * 1024.0); + a[i][1] = (signed long int)(ab * 1024.0); + b[i][0] = (signed long int)(ba * 1024.0); + b[i][1] = (signed long int)(bb * 1024.0); + } + gain_in[0] = 772; + gain_out[0] = 772; + gain_in[1] = 570; + gain_out[1] = 570; + gain_in[2] = 520; + gain_out[2] = 520; + gain_in[3] = 512; + gain_out[3] = 512; + + delay_size[0][0] = 2191 * WM_SampleRate / 44100; + delay_size[0][1] = (2191 + 19) * WM_SampleRate / 44100; + delay_size[1][0] = (2971 + 19) * WM_SampleRate / 44100; + delay_size[1][1] = 2971 * WM_SampleRate / 44100; + delay_size[2][0] = 3253 * WM_SampleRate / 44100; + delay_size[2][1] = (3253 + 19) * WM_SampleRate / 44100; + delay_size[3][0] = (3307 + 19) * WM_SampleRate / 44100; + delay_size[3][1] = 3307 * WM_SampleRate / 44100; + +} + +struct _hndl { + void * handle; + struct _hndl *next; + struct _hndl *prev; +}; + +struct _hndl * first_handle = NULL; + +//f: ( VOLUME / 127 ) +//f: pow(( VOLUME / 127 ), 1.660964047 ) +//f: pow(( VOLUME / 127 ), 2.0 ) +//f: pow(( VOLUME / 127 ), 0.602059991 ) +//f: pow(( VOLUME / 127 ), 0.5 ) + +signed short int lin_volume[] = { 0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 129, 137, 145, 153, 161, 169, 177, 185, 193, 201, 209, 217, 225, 233, 241, 249, 258, 266, 274, 282, 290, 298, 306, 314, 322, 330, 338, 346, 354, 362, 370, 378, 387, 395, 403, 411, 419, 427, 435, 443, 451, 459, 467, 475, 483, 491, 499, 507, 516, 524, 532, 540, 548, 556, 564, 572, 580, 588, 596, 604, 612, 620, 628, 636, 645, 653, 661, 669, 677, 685, 693, 701, 709, 717, 725, 733, 741, 749, 757, 765, 774, 782, 790, 798, 806, 814, 822, 830, 838, 846, 854, 862, 870, 878, 886, 894, 903, 911, 919, 927, 935, 943, 951, 959, 967, 975, 983, 991, 999, 1007, 1015, 1024 }; +signed short int log_volume[] = { 0, 0, 1, 2, 3, 4, 6, 8, 10, 12, 15, 17, 20, 23, 26, 29, 32, 36, 39, 43, 47, 51, 55, 59, 64, 68, 73, 78, 83, 88, 93, 98, 103, 109, 114, 120, 126, 132, 138, 144, 150, 156, 162, 169, 176, 182, 189, 196, 203, 210, 217, 224, 232, 239, 247, 255, 262, 270, 278, 286, 294, 302, 311, 319, 328, 336, 345, 353, 362, 371, 380, 389, 398, 408, 417, 426, 436, 446, 455, 465, 475, 485, 495, 505, 515, 525, 535, 546, 556, 567, 577, 588, 599, 610, 621, 632, 643, 654, 665, 677, 688, 699, 711, 723, 734, 746, 758, 770, 782, 794, 806, 818, 831, 843, 855, 868, 880, 893, 906, 919, 931, 944, 957, 970, 984, 997, 1010, 1024 }; +signed short int sqr_volume[] = { 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 9, 10, 12, 14, 16, 18, 20, 22, 25, 27, 30, 33, 36, 39, 42, 46, 49, 53, 57, 61, 65, 69, 73, 77, 82, 86, 91, 96, 101, 106, 111, 117, 122, 128, 134, 140, 146, 152, 158, 165, 171, 178, 185, 192, 199, 206, 213, 221, 228, 236, 244, 251, 260, 268, 276, 284, 293, 302, 311, 320, 329, 338, 347, 357, 366, 376, 386, 396, 406, 416, 426, 437, 447, 458, 469, 480, 491, 502, 514, 525, 537, 549, 560, 572, 585, 597, 609, 622, 634, 647, 660, 673, 686, 699, 713, 726, 740, 754, 768, 782, 796, 810, 825, 839, 854, 869, 884, 899, 914, 929, 944, 960, 976, 992, 1007, 1024 }; +//signed short int pan_volume[] = { 0, 55, 84, 107, 127, 146, 162, 178, 193, 208, 221, 234, 247, 259, 271, 282, 294, 305, 315, 326, 336, 346, 356, 366, 375, 384, 394, 403, 412, 420, 429, 438, 446, 454, 463, 471, 479, 487, 495, 503, 510, 518, 525, 533, 540, 548, 555, 562, 570, 577, 584, 591, 598, 605, 611, 618, 625, 632, 638, 645, 651, 658, 664, 671, 677, 684, 690, 696, 703, 709, 715, 721, 727, 733, 739, 745, 751, 757, 763, 769, 775, 781, 786, 792, 798, 804, 809, 815, 821, 826, 832, 837, 843, 848, 854, 859, 865, 870, 876, 881, 886, 892, 897, 902, 907, 913, 918, 923, 928, 933, 939, 944, 949, 954, 959, 964, 969, 974, 979, 984, 989, 994, 999, 1004, 1009, 1014, 1019, 1024 }; +signed short int pan_volume[] = { 0, 90, 128, 157, 181, 203, 222, 240, 257, 272, 287, 301, 314, 327, 339, 351, 363, 374, 385, 396, 406, 416, 426, 435, 445, 454, 463, 472, 480, 489, 497, 505, 514, 521, 529, 537, 545, 552, 560, 567, 574, 581, 588, 595, 602, 609, 616, 622, 629, 636, 642, 648, 655, 661, 667, 673, 679, 686, 692, 697, 703, 709, 715, 721, 726, 732, 738, 743, 749, 754, 760, 765, 771, 776, 781, 786, 792, 797, 802, 807, 812, 817, 822, 827, 832, 837, 842, 847, 852, 857, 862, 866, 871, 876, 880, 885, 890, 894, 899, 904, 908, 913, 917, 922, 926, 931, 935, 939, 944, 948, 953, 957, 961, 965, 970, 974, 978, 982, 987, 991, 995, 999, 1003, 1007, 1011, 1015, 1019, 1024 }; + +unsigned long int reverb_val = 92; +unsigned long int comb_size[8][2]; +unsigned long int allpass_size[2][2]; + +float env_time_table[] = { + 0.0, 0.092857143, 0.046428571, 0.030952381, 0.023214286, 0.018571429, 0.015476190, 0.013265306, 0.011607143, 0.010317460, 0.009285714, 0.008441558, 0.007738095, 0.007142857, 0.006632653, 0.006190476, 0.005803571, 0.005462185, 0.005158730, 0.004887218, 0.004642857, 0.004421769, 0.004220779, 0.004037267, 0.003869048, 0.003714286, 0.003571429, 0.003439153, 0.003316327, 0.003201970, 0.003095238, 0.002995392, 0.002901786, 0.002813853, 0.002731092, 0.002653061, 0.002579365, 0.002509653, 0.002443609, 0.002380952, 0.002321429, 0.002264808, 0.002210884, 0.002159468, 0.002110390, 0.002063492, 0.002018634, 0.001975684, 0.001934524, 0.001895044, 0.001857143, 0.001820728, 0.001785714, 0.001752022, 0.001719577, 0.001688312, 0.001658163, 0.001629073, 0.001600985, 0.001573850, 0.001547619, 0.001522248, 0.001497696, 0.001473923, + 0.0, 0.742857143, 0.371428571, 0.247619048, 0.185714286, 0.148571429, 0.123809524, 0.106122449, 0.092857143, 0.082539683, 0.074285714, 0.067532468, 0.061904762, 0.057142857, 0.053061224, 0.049523810, 0.046428571, 0.043697479, 0.041269841, 0.039097744, 0.037142857, 0.035374150, 0.033766234, 0.032298137, 0.030952381, 0.029714286, 0.028571429, 0.027513228, 0.026530612, 0.025615764, 0.024761905, 0.023963134, 0.023214286, 0.022510823, 0.021848739, 0.021224490, 0.020634921, 0.020077220, 0.019548872, 0.019047619, 0.018571429, 0.018118467, 0.017687075, 0.017275748, 0.016883117, 0.016507937, 0.016149068, 0.015805471, 0.015476190, 0.015160350, 0.014857143, 0.014565826, 0.014285714, 0.014016173, 0.013756614, 0.013506494, 0.013265306, 0.013032581, 0.012807882, 0.012590799, 0.012380952, 0.012177986, 0.011981567, 0.011791383, + 0.0, 5.942857143, 2.971428571, 1.980952381, 1.485714286, 1.188571429, 0.990476190, 0.848979592, 0.742857143, 0.660317460, 0.594285714, 0.540259740, 0.495238095, 0.457142857, 0.424489796, 0.396190476, 0.371428571, 0.349579832, 0.330158730, 0.312781955, 0.297142857, 0.282993197, 0.270129870, 0.258385093, 0.247619048, 0.237714286, 0.228571429, 0.220105820, 0.212244898, 0.204926108, 0.198095238, 0.191705069, 0.185714286, 0.180086580, 0.174789916, 0.169795918, 0.165079365, 0.160617761, 0.156390977, 0.152380952, 0.148571429, 0.144947735, 0.141496599, 0.138205980, 0.135064935, 0.132063492, 0.129192547, 0.126443769, 0.123809524, 0.121282799, 0.118857143, 0.116526611, 0.114285714, 0.112129380, 0.110052910, 0.108051948, 0.106122449, 0.104260652, 0.102463054, 0.100726392, 0.099047619, 0.097423888, 0.095852535, 0.094331066, + 0.0, 47.542857143, 23.771428571, 15.847619048, 11.885714286, 9.508571429, 7.923809524, 6.791836735, 5.942857143, 5.282539683, 4.754285714, 4.322077922, 3.961904762, 3.657142857, 3.395918367, 3.169523810, 2.971428571, 2.796638655, 2.641269841, 2.502255639, 2.377142857, 2.263945578, 2.161038961, 2.067080745, 1.980952381, 1.901714286, 1.828571429, 1.760846561, 1.697959184, 1.639408867, 1.584761905, 1.533640553, 1.485714286, 1.440692641, 1.398319328, 1.358367347, 1.320634921, 1.284942085, 1.251127820, 1.219047619, 1.188571429, 1.159581882, 1.131972789, 1.105647841, 1.080519481, 1.056507937, 1.033540373, 1.011550152, 0.990476190, 0.970262391, 0.950857143, 0.932212885, 0.914285714, 0.897035040, 0.880423280, 0.864415584, 0.848979592, 0.834085213, 0.819704433, 0.805811138, 0.792380952, 0.779391101, 0.766820276, 0.754648526 +}; + +#if 0 +unsigned long int freq_table[] = { + 8372018, 8376855, 8381695, 8386538, 8391384, 8396232, 8401084, 8405938, 8410795, 8415654, 8420517, 8425382, 8430250, 8435121, 8439995, 8444871, 8449751, 8454633, 8459518, 8464406, 8469296, 8474190, 8479086, 8483985, 8488887, 8493792, 8498700, 8503610, 8508523, 8513439, 8518358, 8523280, 8528205, 8533132, 8538063, 8542996, 8547932, 8552871, 8557813, 8562757, 8567705, 8572655, 8577608, 8582564, 8587523, 8592485, 8597450, 8602417, 8607387, 8612361, 8617337, 8622316, 8627298, 8632282, 8637270, 8642261, 8647254, 8652250, 8657250, 8662252, 8667257, 8672264, 8677275, 8682289, 8687305, 8692325, 8697347, 8702372, 8707400, 8712431, 8717465, 8722502, 8727542, 8732585, 8737630, 8742679, 8747730, 8752785, 8757842, 8762902, 8767965, 8773031, 8778100, 8783172, 8788247, 8793325, 8798405, 8803489, 8808575, 8813665, 8818757, 8823853, 8828951, 8834052, 8839157, 8844264, 8849374, 8854487, 8859603, 8864722, 8869844, 8874969, 8880097, 8885227, 8890361, 8895498, 8900638, 8905780, 8910926, 8916075, 8921226, 8926381, 8931538, 8936699, 8941863, 8947029, 8952199, 8957371, 8962546, 8967725, 8972906, 8978091, 8983278, 8988469, 8993662, 8998859, 9004058, 9009260, 9014466, 9019674, 9024886, 9030100, 9035318, 9040538, 9045762, 9050988, 9056218, 9061451, 9066686, 9071925, 9077166, 9082411, 9087659, 9092910, 9098163, 9103420, 9108680, 9113943, 9119209, 9124478, 9129750, 9135025, 9140303, 9145584, 9150868, 9156156, 9161446, 9166739, 9172036, 9177335, 9182638, 9187944, 9193252, 9198564, 9203879, 9209197, 9214518, 9219842, 9225169, 9230499, 9235832, 9241169, 9246508, 9251851, 9257196, 9262545, 9267897, 9273252, 9278610, 9283971, 9289335, 9294702, 9300073, 9305446, 9310823, 9316202, 9321585, 9326971, 9332360, 9337752, 9343147, 9348546, 9353947, 9359352, 9364760, 9370171, 9375585, 9381002, 9386422, 9391845, 9397272, 9402701, 9408134, 9413570, 9419009, 9424451, 9429897, 9435345, 9440797, 9446252, 9451710, 9457171, 9462635, 9468102, 9473573, 9479047, 9484524, 9490004, 9495487, 9500973, 9506463, 9511955, 9517451, 9522950, 9528453, 9533958, 9539467, 9544979, 9550494, 9556012, 9561533, 9567058, 9572585, 9578116, 9583650, 9589188, 9594728, 9600272, 9605819, 9611369, 9616922, 9622479, 9628039, 9633602, 9639168, 9644737, 9650310, 9655886, 9661465, 9667047, 9672633, 9678221, 9683813, 9689409, 9695007, 9700609, 9706214, 9711822, 9717433, 9723048, 9728666, 9734287, 9739911, 9745539, 9751170, 9756804, 9762441, 9768082, 9773726, 9779373, 9785023, 9790677, 9796334, 9801994, 9807657, 9813324, 9818994, 9824667, 9830344, 9836024, 9841707, 9847394, 9853083, 9858776, 9864473, 9870172, 9875875, 9881581, 9887291, 9893003, 9898719, 9904439, 9910162, 9915887, 9921617, 9927349, 9933085, 9938825, 9944567, 9950313, 9956062, 9961815, 9967570, 9973330, 9979092, 9984858, 9990627, 9996399, 10002175, 10007954, 10013737, 10019523, 10025312, 10031104, 10036900, 10042700, 10048502, 10054308, 10060117, 10065930, 10071746, 10077565, 10083388, 10089214, 10095043, 10100876, 10106712, 10112552, 10118395, 10124241, 10130091, 10135944, 10141800, 10147660, 10153523, 10159390, 10165260, 10171133, 10177010, 10182890, 10188774, 10194661, 10200551, 10206445, 10212342, 10218243, 10224147, 10230054, 10235965, 10241879, 10247797, 10253718, 10259642, 10265570, 10271502, 10277436, 10283374, 10289316, 10295261, 10301210, 10307162, 10313117, 10319076, 10325038, 10331004, 10336973, 10342945, 10348921, 10354901, 10360884, 10366870, 10372860, 10378853, 10384850, 10390850, 10396854, 10402861, 10408872, 10414886, 10420904, 10426925, 10432949, 10438977, 10445009, 10451044, 10457083, 10463124, 10469170, 10475219, 10481271, 10487327, 10493387, 10499450, 10505516, 10511586, 10517660, 10523737, 10529817, 10535901, 10541989, 10548080, 10554174, 10560273, 10566374, 10572480, 10578589, 10584701, 10590817, 10596936, 10603059, 10609186, 10615316, 10621449, 10627586, 10633727, 10639871, 10646019, 10652170, 10658325, 10664483, 10670645, 10676811, 10682980, 10689153, 10695329, 10701509, 10707692, 10713879, 10720069, 10726264, 10732461, 10738662, 10744867, 10751076, 10757288, 10763503, 10769722, 10775945, 10782172, 10788402, 10794635, 10800872, 10807113, 10813357, 10819605, 10825857, 10832112, 10838371, 10844634, 10850900, 10857169, 10863443, 10869720, 10876000, 10882284, 10888572, 10894864, 10901159, 10907457, 10913760, 10920066, 10926375, 10932689, 10939006, 10945326, 10951650, 10957978, 10964310, 10970645, 10976984, 10983326, 10989673, 10996022, 11002376, 11008733, 11015094, 11021459, 11027827, 11034199, 11040574, 11046954, 11053337, 11059723, 11066114, 11072508, 11078905, 11085307, 11091712, 11098121, 11104533, 11110949, 11117369, 11123793, 11130220, 11136651, 11143086, 11149525, 11155967, 11162413, 11168863, 11175316, 11181773, 11188234, 11194699, 11201167, 11207639, 11214115, 11220594, 11227078, 11233565, 11240055, 11246550, 11253048, 11259550, 11266056, 11272566, 11279079, 11285596, 11292117, 11298642, 11305170, 11311702, 11318238, 11324778, 11331321, 11337868, 11344420, 11350974, 11357533, 11364095, 11370662, 11377232, 11383805, 11390383, 11396964, 11403550, 11410139, 11416731, 11423328, 11429928, 11436533, 11443141, 11449753, 11456368, 11462988, 11469611, 11476238, 11482869, 11489504, 11496143, 11502785, 11509432, 11516082, 11522736, 11529394, 11536056, 11542721, 11549390, 11556064, 11562741, 11569422, 11576107, 11582795, 11589488, 11596184, 11602885, 11609589, 11616297, 11623009, 11629725, 11636444, 11643168, 11649895, 11656627, 11663362, 11670101, 11676844, 11683591, 11690342, 11697097, 11703855, 11710618, 11717384, 11724154, 11730929, 11737707, 11744489, 11751275, 11758065, 11764859, 11771656, 11778458, 11785264, 11792073, 11798887, 11805704, 11812526, 11819351, 11826180, 11833013, 11839851, 11846692, 11853537, 11860386, 11867239, 11874096, 11880956, 11887821, 11894690, 11901563, 11908440, 11915320, 11922205, 11929094, 11935986, 11942883, 11949784, 11956688, 11963597, 11970510, 11977426, 11984347, 11991271, 11998200, 12005133, 12012069, 12019010, 12025954, 12032903, 12039856, 12046812, 12053773, 12060738, 12067706, 12074679, 12081656, 12088637, 12095622, 12102610, 12109603, 12116600, 12123601, 12130606, 12137615, 12144629, 12151646, 12158667, 12165692, 12172722, 12179755, 12186793, 12193834, 12200880, 12207930, 12214983, 12222041, 12229103, 12236169, 12243239, 12250313, 12257392, 12264474, 12271561, 12278651, 12285746, 12292844, 12299947, 12307054, 12314165, 12321280, 12328400, 12335523, 12342651, 12349782, 12356918, 12364058, 12371202, 12378350, 12385502, 12392658, 12399819, 12406984, 12414152, 12421325, 12428502, 12435684, 12442869, 12450059, 12457252, 12464450, 12471652, 12478858, 12486069, 12493283, 12500502, 12507725, 12514952, 12522183, 12529418, 12536658, 12543901, 12551149, 12558401, 12565658, 12572918, 12580183, 12587452, 12594725, 12602002, 12609283, 12616569, 12623859, 12631153, 12638451, 12645754, 12653061, 12660372, 12667687, 12675006, 12682330, 12689658, 12696990, 12704326, 12711667, 12719012, 12726361, 12733714, 12741072, 12748433, 12755800, 12763170, 12770544, 12777923, 12785306, 12792694, 12800085, 12807481, 12814882, 12822286, 12829695, 12837108, 12844525, 12851947, 12859373, 12866803, 12874237, 12881676, 12889119, 12896567, 12904018, 12911474, 12918934, 12926399, 12933868, 12941341, 12948819, 12956301, 12963787, 12971277, 12978772, 12986271, 12993775, 13001283, 13008795, 13016311, 13023832, 13031357, 13038887, 13046421, 13053959, 13061502, 13069049, 13076600, 13084156, 13091716, 13099280, 13106849, 13114422, 13122000, 13129582, 13137168, 13144759, 13152354, 13159953, 13167557, 13175165, 13182778, 13190395, 13198016, 13205642, 13213273, 13220907, 13228546, 13236190, 13243838, 13251490, 13259147, 13266808, 13274474, 13282144, 13289818, 13297497, 13305180, 13312868, 13320560, 13328257, 13335958, 13343663, 13351373, 13359088, 13366807, 13374530, 13382258, 13389990, 13397727, 13405468, 13413214, 13420964, 13428719, 13436478, 13444241, 13452010, 13459782, 13467559, 13475341, 13483127, 13490918, 13498713, 13506512, 13514316, 13522125, 13529938, 13537756, 13545578, 13553405, 13561236, 13569071, 13576912, 13584756, 13592606, 13600460, 13608318, 13616181, 13624048, 13631920, 13639797, 13647678, 13655564, 13663454, 13671349, 13679248, 13687152, 13695060, 13702974, 13710891, 13718813, 13726740, 13734671, 13742607, 13750548, 13758493, 13766443, 13774397, 13782356, 13790319, 13798287, 13806260, 13814237, 13822219, 13830206, 13838197, 13846193, 13854193, 13862198, 13870208, 13878222, 13886241, 13894264, 13902292, 13910325, 13918363, 13926405, 13934451, 13942503, 13950559, 13958619, 13966685, 13974755, 13982829, 13990909, 13998993, 14007081, 14015175, 14023273, 14031375, 14039483, 14047595, 14055712, 14063833, 14071959, 14080090, 14088225, 14096366, 14104511, 14112660, 14120815, 14128974, 14137137, 14145306, 14153479, 14161657, 14169840, 14178027, 14186219, 14194416, 14202618, 14210824, 14219035, 14227251, 14235471, 14243697, 14251927, 14260161, 14268401, 14276645, 14284894, 14293148, 14301407, 14309670, 14317938, 14326211, 14334489, 14342772, 14351059, 14359351, 14367648, 14375949, 14384256, 14392567, 14400883, 14409204, 14417530, 14425860, 14434196, 14442536, 14450881, 14459230, 14467585, 14475944, 14484309, 14492678, 14501052, 14509430, 14517814, 14526202, 14534596, 14542994, 14551397, 14559805, 14568217, 14576635, 14585057, 14593485, 14601917, 14610354, 14618796, 14627242, 14635694, 14644151, 14652612, 14661078, 14669550, 14678026, 14686507, 14694993, 14703483, 14711979, 14720480, 14728985, 14737496, 14746011, 14754531, 14763057, 14771587, 14780122, 14788662, 14797207, 14805757, 14814311, 14822871, 14831436, 14840005, 14848580, 14857160, 14865744, 14874334, 14882928, 14891527, 14900132, 14908741, 14917355, 14925975, 14934599, 14943228, 14951862, 14960502, 14969146, 14977795, 14986449, 14995109, 15003773, 15012442, 15021116, 15029795, 15038480, 15047169, 15055863, 15064563, 15073267, 15081976, 15090691, 15099410, 15108135, 15116864, 15125599, 15134338, 15143083, 15151833, 15160587, 15169347, 15178112, 15186882, 15195657, 15204437, 15213222, 15222013, 15230808, 15239608, 15248414, 15257224, 15266040, 15274861, 15283687, 15292518, 15301354, 15310195, 15319041, 15327893, 15336749, 15345611, 15354477, 15363349, 15372226, 15381108, 15389996, 15398888, 15407786, 15416688, 15425596, 15434509, 15443427, 15452350, 15461279, 15470212, 15479151, 15488095, 15497044, 15505998, 15514958, 15523922, 15532892, 15541867, 15550847, 15559832, 15568823, 15577819, 15586820, 15595826, 15604837, 15613853, 15622875, 15631902, 15640934, 15649972, 15659014, 15668062, 15677115, 15686173, 15695237, 15704306, 15713380, 15722459, 15731543, 15740633, 15749728, 15758828, 15767934, 15777045, 15786161, 15795282, 15804408, 15813540, 15822677, 15831820, 15840967, 15850120, 15859279, 15868442, 15877611, 15886785, 15895965, 15905149, 15914339, 15923535, 15932735, 15941941, 15951153, 15960369, 15969591, 15978818, 15988051, 15997289, 16006532, 16015781, 16025035, 16034294, 16043559, 16052829, 16062104, 16071385, 16080671, 16089962, 16099259, 16108561, 16117869, 16127182, 16136500, 16145824, 16155153, 16164488, 16173828, 16183173, 16192523, 16201880, 16211241, 16220608, 16229980, 16239358, 16248741, 16258130, 16267524, 16276923, 16286328, 16295738, 16305154, 16314575, 16324002, 16333434, 16342871, 16352314, 16361763, 16371217, 16380676, 16390141, 16399611, 16409087, 16418568, 16428055, 16437547, 16447044, 16456548, 16466056, 16475570, 16485090, 16494615, 16504146, 16513682, 16523224, 16532771, 16542323, 16551882, 16561445, 16571015, 16580589, 16590170, 16599755, 16609347, 16618944, 16628546, 16638154, 16647768, 16657387, 16667012, 16676642, 16686278, 16695919, 16705566, 16715219, 16724877, 16734540 +}; +#endif + +unsigned long int freq_table[] = { + 837201792, 837685632, 838169728, 838653568, 839138240, 839623232, 840108480, 840593984, 841079680, 841565184, 842051648, 842538240, 843025152, 843512320, 843999232, 844486976, 844975040, 845463360, 845951936, 846440320, 846929536, 847418944, 847908608, 848398656, 848888960, 849378944, 849869824, 850361024, 850852416, 851344192, 851835584, 852327872, 852820480, 853313280, 853806464, 854299328, 854793024, 855287040, 855781312, 856275904, 856770752, 857265344, 857760704, 858256448, 858752448, 859248704, 859744768, 860241600, 860738752, 861236160, 861733888, 862231360, 862729600, 863228160, 863727104, 864226176, 864725696, 865224896, 865724864, 866225152, 866725760, 867226688, 867727296, 868228736, 868730496, 869232576, 869734912, 870236928, 870739904, 871243072, 871746560, 872250368, 872754496, 873258240, 873762880, 874267840, 874773184, 875278720, 875783936, 876290112, 876796480, 877303232, 877810176, 878317504, 878824512, 879332416, 879840576, 880349056, 880857792, 881366272, 881875712, 882385280, 882895296, 883405440, 883915456, 884426304, 884937408, 885448832, 885960512, 886472512, + 886984192, 887496768, 888009728, 888522944, 889036352, 889549632, 890063680, 890578048, 891092736, 891607680, 892122368, 892637952, 893153792, 893670016, 894186496, 894703232, 895219648, 895737024, 896254720, 896772672, 897290880, 897808896, 898327744, 898846912, 899366336, 899886144, 900405568, 900925952, 901446592, 901967552, 902488768, 903010368, 903531584, 904053760, 904576256, 905099008, 905622016, 906144896, 906668480, 907192512, 907716800, 908241408, 908765632, 909290816, 909816256, 910342144, 910868160, 911394624, 911920768, 912447680, 912975104, 913502720, 914030592, 914558208, 915086784, 915615552, 916144768, 916674176, 917203968, 917733440, 918263744, 918794496, 919325440, 919856704, 920387712, 920919616, 921451840, 921984320, 922517184, 923049728, 923583168, 924116928, 924651008, 925185344, 925720000, 926254336, 926789696, 927325312, 927861120, 928397440, 928933376, 929470208, 930007296, 930544768, 931082560, 931619968, 932158464, 932697152, 933236160, 933775488, 934315072, 934854464, 935394688, 935935296, 936476224, 937017344, 937558208, 938100160, 938642304, 939184640, + 939727488, 940269888, 940813312, 941357056, 941900992, 942445440, 942990016, 943534400, 944079680, 944625280, 945171200, 945717440, 946263360, 946810176, 947357376, 947904832, 948452672, 949000192, 949548608, 950097280, 950646400, 951195776, 951745472, 952294912, 952845184, 953395904, 953946880, 954498176, 955049216, 955601088, 956153408, 956705920, 957258816, 957812032, 958364928, 958918848, 959472960, 960027456, 960582272, 961136768, 961692224, 962248000, 962804032, 963360448, 963916608, 964473600, 965031040, 965588736, 966146816, 966705152, 967263168, 967822144, 968381440, 968941120, 969501056, 970060736, 970621376, 971182272, 971743488, 972305088, 972866368, 973428608, 973991104, 974554048, 975117312, 975680768, 976243968, 976808192, 977372736, 977937536, 978502656, 979067584, 979633344, 980199488, 980765888, 981332736, 981899200, 982466688, 983034432, 983602624, 984171008, 984739776, 985308160, 985877632, 986447360, 987017472, 987587904, 988157952, 988729088, 989300416, 989872192, 990444224, 991016000, 991588672, 992161728, 992735168, 993308864, 993882880, 994456576, 995031296, + 995606336, 996181696, 996757440, 997332800, 997909184, 998485888, 999062912, 999640256, 1000217984, 1000795392, 1001373696, 1001952448, 1002531520, 1003110848, 1003689920, 1004270016, 1004850304, 1005431040, 1006012160, 1006592832, 1007174592, 1007756608, 1008339008, 1008921792, 1009504768, 1010087552, 1010671296, 1011255360, 1011839808, 1012424576, 1013009024, 1013594368, 1014180160, 1014766272, 1015352768, 1015938880, 1016526016, 1017113472, 1017701248, 1018289408, 1018877824, 1019465984, 1020055104, 1020644672, 1021234496, 1021824768, 1022414528, 1023005440, 1023596608, 1024188160, 1024780096, 1025371584, 1025964160, 1026557120, 1027150336, 1027744000, 1028337920, 1028931520, 1029526144, 1030121152, 1030716480, 1031312128, 1031907456, 1032503808, 1033100480, 1033697536, 1034294912, 1034892032, 1035490048, 1036088512, 1036687232, 1037286336, 1037885824, 1038484928, 1039085056, 1039685632, 1040286464, 1040887680, 1041488448, 1042090368, 1042692608, 1043295168, 1043898176, 1044501440, 1045104384, 1045708288, 1046312640, 1046917376, 1047522368, 1048127040, 1048732800, 1049338816, 1049945280, 1050552128, 1051158528, 1051765952, 1052373824, 1052982016, 1053590592, 1054199424, + 1054807936, 1055417600, 1056027456, 1056637760, 1057248448, 1057858752, 1058470016, 1059081728, 1059693824, 1060306304, 1060918336, 1061531392, 1062144896, 1062758656, 1063372928, 1063987392, 1064601664, 1065216896, 1065832448, 1066448448, 1067064704, 1067680704, 1068297728, 1068915136, 1069532864, 1070150976, 1070768640, 1071387520, 1072006720, 1072626240, 1073246080, 1073866368, 1074486272, 1075107200, 1075728512, 1076350208, 1076972160, 1077593856, 1078216704, 1078839680, 1079463296, 1080087040, 1080710528, 1081335168, 1081960064, 1082585344, 1083211008, 1083836928, 1084462592, 1085089280, 1085716352, 1086343936, 1086971648, 1087599104, 1088227712, 1088856576, 1089485824, 1090115456, 1090745472, 1091375104, 1092005760, 1092636928, 1093268352, 1093900160, 1094531584, 1095164160, 1095796992, 1096430336, 1097064064, 1097697280, 1098331648, 1098966400, 1099601536, 1100237056, 1100872832, 1101508224, 1102144768, 1102781824, 1103419136, 1104056832, 1104694144, 1105332608, 1105971328, 1106610432, 1107249920, 1107889152, 1108529408, 1109170048, 1109811072, 1110452352, 1111094144, 1111735552, 1112377984, 1113020928, 1113664128, 1114307712, 1114950912, 1115595264, 1116240000, 1116885120, + 1117530624, 1118175744, 1118821888, 1119468416, 1120115456, 1120762752, 1121410432, 1122057856, 1122706176, 1123355136, 1124004224, 1124653824, 1125303040, 1125953408, 1126604160, 1127255168, 1127906560, 1128557696, 1129209984, 1129862528, 1130515456, 1131168768, 1131822592, 1132475904, 1133130368, 1133785216, 1134440448, 1135096064, 1135751296, 1136407680, 1137064448, 1137721472, 1138379008, 1139036800, 1139694336, 1140353024, 1141012096, 1141671424, 1142331264, 1142990592, 1143651200, 1144312192, 1144973440, 1145635200, 1146296448, 1146958976, 1147621760, 1148285056, 1148948608, 1149612672, 1150276224, 1150940928, 1151606144, 1152271616, 1152937600, 1153603072, 1154269824, 1154936832, 1155604352, 1156272128, 1156939648, 1157608192, 1158277248, 1158946560, 1159616384, 1160286464, 1160956288, 1161627264, 1162298624, 1162970240, 1163642368, 1164314112, 1164987008, 1165660160, 1166333824, 1167007872, 1167681536, 1168356352, 1169031552, 1169707136, 1170383104, 1171059584, 1171735552, 1172412672, 1173090304, 1173768192, 1174446592, 1175124480, 1175803648, 1176483072, 1177163008, 1177843328, 1178523264, 1179204352, 1179885824, 1180567680, 1181249920, 1181932544, 1182614912, 1183298304, + 1183982208, 1184666368, 1185351040, 1186035328, 1186720640, 1187406464, 1188092672, 1188779264, 1189466368, 1190152960, 1190840832, 1191528960, 1192217600, 1192906624, 1193595136, 1194285056, 1194975232, 1195665792, 1196356736, 1197047296, 1197739136, 1198431360, 1199123968, 1199816960, 1200510336, 1201203328, 1201897600, 1202592128, 1203287040, 1203982464, 1204677504, 1205373696, 1206070272, 1206767232, 1207464704, 1208161664, 1208859904, 1209558528, 1210257536, 1210956928, 1211656832, 1212356224, 1213056768, 1213757952, 1214459392, 1215161216, 1215862656, 1216565376, 1217268352, 1217971840, 1218675712, 1219379200, 1220083840, 1220788992, 1221494528, 1222200448, 1222906752, 1223612672, 1224319872, 1225027456, 1225735424, 1226443648, 1227151616, 1227860864, 1228570496, 1229280512, 1229990912, 1230700928, 1231412096, 1232123776, 1232835840, 1233548288, 1234261248, 1234973696, 1235687424, 1236401536, 1237116032, 1237831040, 1238545536, 1239261312, 1239977472, 1240694144, 1241411072, 1242128512, 1242845568, 1243563776, 1244282496, 1245001600, 1245721088, 1246440192, 1247160448, 1247881216, 1248602368, 1249324032, 1250045184, 1250767616, 1251490432, 1252213632, 1252937344, 1253661440, + 1254385152, 1255110016, 1255835392, 1256561152, 1257287424, 1258013184, 1258740096, 1259467648, 1260195456, 1260923648, 1261651584, 1262380800, 1263110272, 1263840256, 1264570624, 1265301504, 1266031872, 1266763520, 1267495552, 1268227968, 1268961024, 1269693440, 1270427264, 1271161472, 1271896064, 1272631168, 1273365760, 1274101632, 1274838016, 1275574784, 1276311808, 1277049472, 1277786624, 1278525056, 1279264000, 1280003328, 1280743040, 1281482368, 1282222976, 1282963968, 1283705344, 1284447232, 1285188736, 1285931392, 1286674560, 1287418240, 1288162176, 1288906624, 1289650688, 1290395904, 1291141760, 1291887872, 1292634496, 1293380608, 1294128128, 1294875904, 1295624320, 1296373120, 1297122304, 1297870976, 1298621056, 1299371520, 1300122496, 1300873856, 1301624832, 1302376960, 1303129600, 1303882752, 1304636288, 1305389312, 1306143872, 1306898688, 1307654016, 1308409600, 1309165696, 1309921536, 1310678528, 1311435904, 1312193920, 1312952192, 1313710080, 1314469248, 1315228928, 1315988992, 1316749568, 1317509632, 1318271104, 1319032960, 1319795200, 1320557952, 1321321088, 1322083840, 1322847872, 1323612416, 1324377216, 1325142656, 1325907584, 1326673920, 1327440512, 1328207744, + 1328975360, 1329742464, 1330510976, 1331279872, 1332049152, 1332819072, 1333589248, 1334359168, 1335130240, 1335901824, 1336673920, 1337446400, 1338218368, 1338991744, 1339765632, 1340539904, 1341314560, 1342088832, 1342864512, 1343640576, 1344417024, 1345193984, 1345971456, 1346748416, 1347526656, 1348305408, 1349084672, 1349864320, 1350643456, 1351424000, 1352205056, 1352986496, 1353768448, 1354550784, 1355332608, 1356115968, 1356899712, 1357683840, 1358468480, 1359252608, 1360038144, 1360824192, 1361610624, 1362397440, 1363183872, 1363971712, 1364760064, 1365548672, 1366337792, 1367127424, 1367916672, 1368707200, 1369498240, 1370289664, 1371081472, 1371873024, 1372665856, 1373459072, 1374252800, 1375047040, 1375840768, 1376635904, 1377431552, 1378227584, 1379024000, 1379820928, 1380617472, 1381415296, 1382213760, 1383012480, 1383811840, 1384610560, 1385410816, 1386211456, 1387012480, 1387814144, 1388615168, 1389417728, 1390220672, 1391024128, 1391827968, 1392632320, 1393436288, 1394241536, 1395047296, 1395853568, 1396660224, 1397466368, 1398274048, 1399082112, 1399890688, 1400699648, 1401508224, 1402318080, 1403128576, 1403939456, 1404750848, 1405562624, 1406374016, 1407186816, + 1408000000, 1408813696, 1409627904, 1410441728, 1411256704, 1412072320, 1412888320, 1413704960, 1414521856, 1415338368, 1416156288, 1416974720, 1417793664, 1418612992, 1419431808, 1420252160, 1421072896, 1421894144, 1422715904, 1423537280, 1424359808, 1425183104, 1426006784, 1426830848, 1427655296, 1428479488, 1429305088, 1430131072, 1430957568, 1431784576, 1432611072, 1433438976, 1434267392, 1435096192, 1435925632, 1436754432, 1437584768, 1438415616, 1439246848, 1440078720, 1440910848, 1441742720, 1442575872, 1443409664, 1444243584, 1445078400, 1445912576, 1446748032, 1447584256, 1448420864, 1449257856, 1450094464, 1450932480, 1451771008, 1452609920, 1453449472, 1454289408, 1455128960, 1455969920, 1456811264, 1457653248, 1458495616, 1459337600, 1460180864, 1461024768, 1461869056, 1462713984, 1463558272, 1464404096, 1465250304, 1466097152, 1466944384, 1467792128, 1468639488, 1469488256, 1470337408, 1471187200, 1472037376, 1472887168, 1473738368, 1474589952, 1475442304, 1476294912, 1477148160, 1478000768, 1478854912, 1479709696, 1480564608, 1481420288, 1482275456, 1483132160, 1483989248, 1484846976, 1485704960, 1486562688, 1487421696, 1488281344, 1489141504, 1490002048, 1490863104, + 1491723776, 1492585856, 1493448448, 1494311424, 1495175040, 1496038144, 1496902656, 1497767808, 1498633344, 1499499392, 1500365056, 1501232128, 1502099712, 1502967808, 1503836416, 1504705536, 1505574016, 1506444032, 1507314688, 1508185856, 1509057408, 1509928576, 1510801280, 1511674240, 1512547840, 1513421952, 1514295680, 1515170816, 1516046464, 1516922624, 1517799296, 1518676224, 1519552896, 1520431104, 1521309824, 1522188928, 1523068800, 1523948032, 1524828672, 1525709824, 1526591616, 1527473792, 1528355456, 1529238784, 1530122496, 1531006720, 1531891712, 1532776832, 1533661824, 1534547968, 1535434880, 1536322304, 1537210112, 1538097408, 1538986368, 1539875840, 1540765696, 1541656192, 1542547072, 1543437440, 1544329472, 1545221888, 1546114944, 1547008384, 1547901440, 1548796032, 1549691136, 1550586624, 1551482752, 1552378368, 1553275520, 1554173184, 1555071232, 1555970048, 1556869248, 1557767936, 1558668288, 1559568896, 1560470272, 1561372032, 1562273408, 1563176320, 1564079616, 1564983424, 1565888000, 1566791808, 1567697408, 1568603392, 1569509760, 1570416896, 1571324416, 1572231424, 1573140096, 1574049152, 1574958976, 1575869184, 1576778752, 1577689984, 1578601728, 1579514112, + 1580426880, 1581339264, 1582253056, 1583167488, 1584082432, 1584997888, 1585913984, 1586829440, 1587746304, 1588663936, 1589582080, 1590500736, 1591418880, 1592338560, 1593258752, 1594179584, 1595100928, 1596021632, 1596944000, 1597866880, 1598790272, 1599714304, 1600638848, 1601562752, 1602488320, 1603414272, 1604340992, 1605268224, 1606194816, 1607123072, 1608051968, 1608981120, 1609911040, 1610841344, 1611771264, 1612702848, 1613634688, 1614567168, 1615500288, 1616432896, 1617367040, 1618301824, 1619237120, 1620172800, 1621108096, 1622044928, 1622982272, 1623920128, 1624858752, 1625797632, 1626736256, 1627676416, 1628616960, 1629558272, 1630499968, 1631441152, 1632384000, 1633327232, 1634271232, 1635215744, 1636159744, 1637105152, 1638051328, 1638998016, 1639945088, 1640892928, 1641840128, 1642788992, 1643738368, 1644688384, 1645638784, 1646588672, 1647540352, 1648492416, 1649445120, 1650398464, 1651351168, 1652305408, 1653260288, 1654215808, 1655171712, 1656128256, 1657084288, 1658041856, 1659000064, 1659958784, 1660918272, 1661876992, 1662837376, 1663798400, 1664759936, 1665721984, 1666683520, 1667646720, 1668610560, 1669574784, 1670539776, 1671505024, 1672470016, 1673436544, +}; + +#define SAMPLE_16BIT 0x01 +#define SAMPLE_UNSIGNED 0x02 +#define SAMPLE_LOOP 0x04 +#define SAMPLE_PINGPONG 0x08 +#define SAMPLE_REVERSE 0x10 +#define SAMPLE_SUSTAIN 0x20 +#define SAMPLE_ENVELOPE 0x40 + +#ifdef DEBUG_SAMPLES +#define SAMPLE_CONVERT_DEBUG(dx) printf("\r%s\n",dx) +#else +#define SAMPLE_CONVERT_DEBUG(dx) +#endif + +#ifdef DEBUG_MIDI +#define MIDI_EVENT_DEBUG(dx,dy) printf("\r%s, %x\n",dx,dy) +#else +#define MIDI_EVENT_DEBUG(dx,dy) +#endif + +#define WM_ERR_MEM 0 +#define WM_ERR_STAT 1 +#define WM_ERR_LOAD 2 +#define WM_ERR_OPEN 3 +#define WM_ERR_READ 4 +#define WM_ERR_INVALID 5 +#define WM_ERR_CORUPT 6 +#define WM_ERR_NOT_INIT 7 +#define WM_ERR_INVALID_ARG 8 + +#define MAX_AUTO_AMP 2.0 + +#define FPBITS 10 +#define FPMASK ((1L<<FPBITS)-1L) + + +void do_note_off (unsigned char ch, struct _mdi *mdi, unsigned long int ptr); +void do_note_on (unsigned char ch, struct _mdi *mdi, unsigned long int ptr); +void do_aftertouch (unsigned char ch, struct _mdi *mdi, unsigned long int ptr); +void do_control (unsigned char ch, struct _mdi *mdi, unsigned long int ptr); +void do_patch (unsigned char ch, struct _mdi *mdi, unsigned long int ptr); +void do_channel_pressure (unsigned char ch, struct _mdi *mdi, unsigned long int ptr); +void do_pitch (unsigned char ch, struct _mdi *mdi, unsigned long int ptr); +void do_message (unsigned char ch, struct _mdi *mdi, unsigned long int ptr); + +void (*do_event[])(unsigned char ch, struct _mdi *midifile, unsigned long int ptr) = { + *do_note_off, + *do_note_on, + *do_aftertouch, + *do_control, + *do_patch, + *do_channel_pressure, + *do_pitch, + *do_message +}; + +/* + * ========================= + * Internal Functions + * ========================= + */ + + +void +WM_ERROR( const char * func, unsigned long int lne, int wmerno, const char * wmfor, int error) { + const char * errors[] = { + "Unable to obtain memory\0", + "Unable to stat\0", + "Unable to load\0", + "Unable to open\0", + "Unable to read\0", + "Invalid or Unsuported file format\0", + "File corrupt\0", + "Library not Initialized\0", + "Invalid argument\0" + }; + if (wmfor != NULL) { + if (error != 0) { + fprintf(stderr,"\rlibWildMidi(%s:%lu): ERROR %s %s (%s)\n",func, lne, errors[wmerno], wmfor, strerror(error)); + } else { + fprintf(stderr,"\rlibWildMidi(%s:%lu): ERROR %s %s\n",func, lne, errors[wmerno], wmfor); + } + } else { + if (error != 0) { + fprintf(stderr,"\rlibWildMidi(%s:%lu): ERROR %s (%s)\n",func, lne, errors[wmerno], strerror(error)); + } else { + fprintf(stderr,"\rlibWildMidi(%s:%lu): ERROR %s\n",func, lne, errors[wmerno]); + } + } + +} + +unsigned char * +WM_BufferFile (const char *filename, unsigned long int *size) { + int buffer_fd; + char *data; + struct stat buffer_stat; +#ifndef _WIN32 + char *home = NULL; + struct passwd *pwd_ent; + char buffer_dir[1024]; +#endif + + char *buffer_file = malloc(strlen(filename) + 1); + + if (buffer_file == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, errno); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, errno); + return NULL; + } + + strcpy (buffer_file, filename); +#ifndef _WIN32 + if (strncmp(buffer_file,"~/",2) == 0) { + if ((pwd_ent = getpwuid (getuid ()))) { + home = pwd_ent->pw_dir; + } else { + home = getenv ("HOME"); + } + if (home) { + buffer_file = realloc(buffer_file,(strlen(buffer_file) + strlen(home) + 1)); + if (buffer_file == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, errno); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, errno); + free(buffer_file); + return NULL; + } + memmove((buffer_file + strlen(home)), (buffer_file + 1), (strlen(buffer_file))); + strncpy (buffer_file, home,strlen(home)); + } + } else if (buffer_file[0] != '/') { + getcwd(buffer_dir,1024); + if (buffer_dir[strlen(buffer_dir)-1] != '/') { + buffer_dir[strlen(buffer_dir)+1] = '\0'; + buffer_dir[strlen(buffer_dir)] = '/'; + } + buffer_file = realloc(buffer_file,(strlen(buffer_file) + strlen(buffer_dir) + 1)); + if (buffer_file == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, errno); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, errno); + free(buffer_file); + return NULL; + } + memmove((buffer_file + strlen(buffer_dir)), buffer_file, strlen(buffer_file)+1); + strncpy (buffer_file,buffer_dir,strlen(buffer_dir)); + } +#endif + if (stat(buffer_file,&buffer_stat)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_STAT, filename, errno); + free(buffer_file); + return NULL; + } + *size = buffer_stat.st_size; + data = malloc(*size); + if (data == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, errno); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, errno); + free(buffer_file); + return NULL; + } +#ifdef _WIN32 + if ((buffer_fd = open(buffer_file,(O_RDONLY | O_BINARY))) == -1) { +#else + if ((buffer_fd = open(buffer_file,O_RDONLY)) == -1) { +#endif + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_OPEN, filename, errno); + free(buffer_file); + free(data); + return NULL; + } + if (read(buffer_fd,data,*size) != buffer_stat.st_size) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_READ, filename, errno); + free(buffer_file); + free(data); + close(buffer_fd); + return NULL; + } + close(buffer_fd); + free(buffer_file); + return data; +} + +inline void +WM_Lock (int * wmlock) { + LOCK_START: + if (__builtin_expect(((*wmlock) == 0),1)) { + (*wmlock)++; + if (__builtin_expect(((*wmlock) == 1), 1)) { + return; + } + (*wmlock)--; + } +#ifdef _WIN32 + Sleep(10); +#else + usleep(500); +#endif + goto LOCK_START; +} + +inline void +WM_Unlock (int *wmlock) { + (*wmlock)--; +} + +void +WM_InitPatches ( void ) { + int i; + for (i = 0; i < 128; i++) { + patch[i] = NULL; + } +} + +void +WM_FreePatches ( void ) { + int i; + struct _patch * tmp_patch; + struct _sample * tmp_sample; + + WM_Lock(&patch_lock); + for (i = 0; i < 128; i++) { + if (patch[i] != NULL) { + while (patch[i] != NULL) { + if (patch[i]->filename != NULL) { + if (patch[i]->first_sample != NULL) { + while (patch[i]->first_sample != NULL) { + tmp_sample = patch[i]->first_sample->next; + if (patch[i]->first_sample->data != NULL) + free (patch[i]->first_sample->data); + free (patch[i]->first_sample); + patch[i]->first_sample = tmp_sample; + } + } + free (patch[i]->filename); + } + tmp_patch = patch[i]->next; + free(patch[i]); + patch[i] = tmp_patch; + } + } + } + WM_Unlock(&patch_lock); +} + +int +WM_LoadConfig (const char *config_file) { + unsigned long int config_size = 0; + unsigned char *config_buffer = NULL; + char * dir_end = NULL; + char * config_dir = NULL; + unsigned long int config_ptr = 0; + unsigned long int line_start_ptr = 0; + char * line_buffer = NULL; + unsigned long int line_ptr = 0; + char * chr_ptr = NULL; + unsigned short int patchid = 0; + char * new_config = NULL; + struct _patch * tmp_patch; + + if ((config_buffer = WM_BufferFile(config_file, &config_size)) == NULL) { + return -1; + } + if (config_buffer == NULL) { + WM_FreePatches(); + return -1; + } + + dir_end = strrchr(config_file,'/'); + if (dir_end != NULL) { + config_dir = malloc((dir_end - config_file + 2)); + if (config_dir == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse config", errno); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); + WM_FreePatches(); + free (config_buffer); + return -1; + } + strncpy(config_dir, config_file, (dir_end - config_file + 1)); + config_dir[dir_end - config_file + 1] = '\0'; + } + config_ptr = 0; + line_start_ptr = 0; + while (config_ptr < config_size) { + // find end of line + if (config_buffer[config_ptr] != '\n') { + // remove unwanted crud + if (config_buffer[config_ptr] == '\t') { + config_buffer[config_ptr] = ' '; + } else if (config_buffer[config_ptr] == '\r') { + config_buffer[config_ptr] = ' '; + } + if ((config_buffer[config_ptr] == ' ') && (config_ptr == line_start_ptr)) { + line_start_ptr++; + } + + config_ptr++; + continue; + } + config_buffer[config_ptr] = '\0'; + if (config_ptr == line_start_ptr) { + config_ptr++; + line_start_ptr++; + continue; + } + if (config_buffer[line_start_ptr] == '#') { + config_ptr++; + line_start_ptr = config_ptr; + continue; + } + + // copy line into a workable buffer + line_buffer = realloc(line_buffer, (config_ptr - line_start_ptr + 1)); + if (line_buffer == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse config", errno); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); + WM_FreePatches(); + if (config_dir != NULL) + free(config_dir); + free (config_buffer); + return -1; + } + strcpy(line_buffer, &config_buffer[line_start_ptr]); + config_ptr++; + line_start_ptr = config_ptr; + // remove unwnted crud from line for easier parsing + if ((chr_ptr = strstr(line_buffer," ")) != NULL) { + while ((chr_ptr = strstr(line_buffer," ")) != NULL) { + memmove(chr_ptr, &chr_ptr[1], strlen(chr_ptr)); + } + } + if ((chr_ptr = strchr(line_buffer, '#')) != NULL) { + *chr_ptr = '\0'; + } + if (line_buffer[strlen(line_buffer) -1] == ' ') { + while (line_buffer[strlen(line_buffer) -1] == ' ') { + line_buffer[strlen(line_buffer) -1] = '\0'; + } + } + + // now parse line + if (strncasecmp(line_buffer, "dir ", 4) == 0) { + if (line_buffer[strlen(line_buffer) - 1] == '/') { + config_dir = realloc(config_dir, strlen(&line_buffer[4]) + 1); + if (config_dir == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse config", errno); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); + WM_FreePatches(); + free (line_buffer); + free (config_buffer); + return -1; + } + strcpy(config_dir, &line_buffer[4]); + } else { + config_dir = realloc(config_dir, strlen(&line_buffer[4]) + 2); + if (config_dir == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse config", errno); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); + WM_FreePatches(); + free (line_buffer); + free (config_buffer); + return -1; + } + strcpy(config_dir, &line_buffer[4]); + strcat(config_dir,"/"); + } + printf ("wildmidi config_dir: %s\n", config_dir); + continue; + } else if (strncasecmp(line_buffer, "source ", 7) == 0) { + if (config_dir != NULL, 0) { + new_config = malloc(strlen(config_dir) + strlen(&line_buffer[7]) + 1); + if (new_config == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse config", errno); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); + WM_FreePatches(); + free (config_dir); + free (line_buffer); + free (config_buffer); + return -1; + } + strcpy(new_config,config_dir); + strcpy(&new_config[strlen(config_dir)], &line_buffer[7]); + } else { + new_config = malloc(strlen(&line_buffer[7]) + 1); + if (new_config == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse config", errno); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); + WM_FreePatches(); + free (line_buffer); + free (config_buffer); + return -1; + } + strcpy(new_config, &line_buffer[7]); + } + printf ("wildmidi load new_config: %s\n", new_config); + if (WM_LoadConfig(new_config) == -1) { + free (new_config); + free (line_buffer); + free (config_buffer); + //if (config_dir != NULL) + // free (config_dir); + return -1; + } + free (new_config); + continue; + } else if (strncasecmp(line_buffer, "bank ", 5) == 0) { + printf ("loading bank, config_dir: %s\n", config_dir); + if (!isdigit(line_buffer[5])) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in bank line)", 0); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); + WM_FreePatches(); + if (config_dir != NULL) + free (config_dir); + free (line_buffer); + free (config_buffer); + return -1; + } + patchid = (atoi(&line_buffer[5]) & 0xFF ) << 8; + continue; + } else if (strncasecmp(line_buffer, "drumset ", 8) == 0) { + if (!isdigit(line_buffer[8])) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in drumset line)", 0); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); + WM_FreePatches(); + if (config_dir != NULL) + free (config_dir); + free (line_buffer); + free (config_buffer); + return -1; + } + patchid = ((atoi(&line_buffer[8]) & 0xFF ) << 8) | 0x80; + continue; + } else if (isdigit(line_buffer[0])) { + patchid = (patchid & 0xFF80) | (atoi(line_buffer) & 0x7F); + if (patch[(patchid & 0x7F)] == NULL) { + patch[(patchid & 0x7F)] = malloc (sizeof(struct _patch)); + if (patch[(patchid & 0x7F)] == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, errno); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); + WM_FreePatches(); + if (config_dir != NULL) + free (config_dir); + free (line_buffer); + free (config_buffer); + return -1; + } + tmp_patch = patch[(patchid & 0x7F)]; + tmp_patch->patchid = patchid; + tmp_patch->filename = NULL; + tmp_patch->amp = 1024; + tmp_patch->note = 0; + tmp_patch->next = NULL; + tmp_patch->first_sample = NULL; + tmp_patch->loaded = 0; + tmp_patch->inuse_count = 0; + } else { + tmp_patch = patch[(patchid & 0x7F)]; + if (tmp_patch->patchid == patchid) { + free (tmp_patch->filename); + tmp_patch->filename = NULL; + tmp_patch->amp = 1024; + tmp_patch->note = 0; + } else { + if (tmp_patch->next != NULL) { + while (tmp_patch->next != NULL) { + if (tmp_patch->next->patchid == patchid) + break; + tmp_patch = tmp_patch->next; + } + if (tmp_patch->next == NULL) { + tmp_patch->next = malloc (sizeof(struct _patch)); + if (tmp_patch->next == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, 0); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); + WM_FreePatches(); + if (config_dir != NULL) + free (config_dir); + free (line_buffer); + free (config_buffer); + return -1; + } + tmp_patch = tmp_patch->next; + tmp_patch->patchid = patchid; + tmp_patch->filename = NULL; + tmp_patch->amp = 1024; + tmp_patch->note = 0; + tmp_patch->next = NULL; + tmp_patch->first_sample = NULL; + tmp_patch->loaded = 0; + tmp_patch->inuse_count = 0; + } else { + tmp_patch = tmp_patch->next; + free (tmp_patch->filename); + tmp_patch->filename = NULL; + tmp_patch->amp = 1024; + tmp_patch->note = 0; + } + } else { + tmp_patch->next = malloc (sizeof(struct _patch)); + if (tmp_patch->next == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, errno); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); + WM_FreePatches(); + if (config_dir != NULL) + free (config_dir); + free (line_buffer); + free (config_buffer); + return -1; + } + tmp_patch = tmp_patch->next; + tmp_patch->patchid = patchid; + tmp_patch->filename = NULL; + tmp_patch->amp = 1024; + tmp_patch->note = 0; + tmp_patch->next = NULL; + tmp_patch->first_sample = NULL; + tmp_patch->loaded = 0; + tmp_patch->inuse_count = 0; + } + } + } + + chr_ptr = strchr(line_buffer,' ') + 1; + if (chr_ptr == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); + WM_FreePatches(); + if (config_dir != NULL) + free (config_dir); + free (line_buffer); + free (config_buffer); + return -1; + } + line_ptr = chr_ptr - line_buffer; + chr_ptr = strchr(&line_buffer[line_ptr],' '); + if (chr_ptr != NULL) { + *chr_ptr = '\0'; + } + if (strncasecmp(&line_buffer[(line_ptr + strlen(&line_buffer[line_ptr]) - 5)], ".pat", 4) != 0, 0) { + if (config_dir != NULL) { + tmp_patch->filename = malloc(strlen(config_dir) + strlen(&line_buffer[line_ptr]) + 5); + if (tmp_patch->filename == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, 0); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); + WM_FreePatches(); + if (config_dir != NULL) + free (config_dir); + free (line_buffer); + free (config_buffer); + return -1; + } + strcpy(tmp_patch->filename, config_dir); + strcat(tmp_patch->filename, &line_buffer[line_ptr]); + } else { + tmp_patch->filename = malloc(strlen(&line_buffer[line_ptr]) + 5); + if (tmp_patch->filename == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, 0); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); + WM_FreePatches(); + free (line_buffer); + free (config_buffer); + return -1; + } + strcpy(tmp_patch->filename, &line_buffer[line_ptr]); + } + strcat(tmp_patch->filename, ".pat"); + } else { + if (config_dir != NULL) { + tmp_patch->filename = malloc(strlen(config_dir) + strlen(&line_buffer[line_ptr]) + 1); + if (tmp_patch->filename == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, 0); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); + WM_FreePatches(); + if (config_dir != NULL) + free (config_dir); + free (line_buffer); + free (config_buffer); + return -1; + } + strcpy(tmp_patch->filename, config_dir); + strcat(tmp_patch->filename, &line_buffer[line_ptr]); + } else { + tmp_patch->filename = malloc(strlen(&line_buffer[line_ptr]) + 1); + if (tmp_patch->filename == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, 0); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); + WM_FreePatches(); + free (line_buffer); + free (config_buffer); + return -1; + } + strcpy(tmp_patch->filename, &line_buffer[line_ptr]); + } + } + + tmp_patch->env[0].set = 0x00; + tmp_patch->env[1].set = 0x00; + tmp_patch->env[2].set = 0x00; + tmp_patch->env[3].set = 0x00; + tmp_patch->env[4].set = 0x00; + tmp_patch->env[5].set = 0x00; + tmp_patch->keep = 0; + tmp_patch->remove = 0; + + if (chr_ptr != NULL) { + line_ptr = chr_ptr - line_buffer + 1; + chr_ptr = strstr(&line_buffer[line_ptr], "amp="); + if (chr_ptr != NULL) { + if (!isdigit(chr_ptr[4])) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0); + } else { + tmp_patch->amp = ((atoi(&chr_ptr[4]) << 10) / 100); + } + } + chr_ptr = strstr(&line_buffer[line_ptr], "note="); + if (chr_ptr != NULL) { + if (!isdigit(chr_ptr[5])) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0); + } else { + tmp_patch->note = atoi(&chr_ptr[5]); + } + } + chr_ptr = strstr(&line_buffer[line_ptr], "keep=loop"); + if (chr_ptr != NULL) { + tmp_patch->keep |= SAMPLE_LOOP; + } + chr_ptr = strstr(&line_buffer[line_ptr], "keep=env"); + if (chr_ptr != NULL) { + tmp_patch->keep |= SAMPLE_ENVELOPE; + } + chr_ptr = strstr(&line_buffer[line_ptr], "remove=sustain"); + if (chr_ptr != NULL) { + tmp_patch->remove |= SAMPLE_SUSTAIN; + } + chr_ptr = strstr(&line_buffer[line_ptr], "env_time0="); + if (chr_ptr != NULL) { + if (!isdigit(chr_ptr[10])) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0); + } else { + tmp_patch->env[0].time = atof(&chr_ptr[10]); + if ((tmp_patch->env[0].time > 45000.0) || (tmp_patch->env[0].time < 1.47)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0); + tmp_patch->env[0].set &= 0xFE; + } else { + tmp_patch->env[0].set |= 0x01; + } + } + } + + chr_ptr = strstr(&line_buffer[line_ptr], "env_time1="); + if (chr_ptr != NULL) { + if (!isdigit(chr_ptr[10])) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0); + } else { + tmp_patch->env[1].time = atof(&chr_ptr[10]); + if ((tmp_patch->env[1].time > 45000.0) || (tmp_patch->env[1].time < 1.47)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0); + tmp_patch->env[1].set &= 0xFE; + } else { + tmp_patch->env[1].set |= 0x01; + } + } + } + chr_ptr = strstr(&line_buffer[line_ptr], "env_time2="); + if (chr_ptr != NULL) { + if (!isdigit(chr_ptr[10])) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0); + } else { + tmp_patch->env[2].time = atof(&chr_ptr[10]); + if ((tmp_patch->env[2].time > 45000.0) || (tmp_patch->env[2].time < 1.47)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0); + tmp_patch->env[2].set &= 0xFE; + } else { + tmp_patch->env[2].set |= 0x01; + } + } + } + chr_ptr = strstr(&line_buffer[line_ptr], "env_time3="); + if (chr_ptr != NULL) { + if (!isdigit(chr_ptr[10])) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0); + } else { + tmp_patch->env[3].time = atof(&chr_ptr[10]); + if ((tmp_patch->env[3].time > 45000.0) || (tmp_patch->env[3].time < 1.47)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0); + tmp_patch->env[3].set &= 0xFE; + } else { + tmp_patch->env[3].set |= 0x01; + } + } + } + chr_ptr = strstr(&line_buffer[line_ptr], "env_time4="); + if (chr_ptr != NULL) { + if (!isdigit(chr_ptr[10])) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0); + } else { + tmp_patch->env[4].time = atof(&chr_ptr[10]); + if ((tmp_patch->env[4].time > 45000.0) || (tmp_patch->env[4].time < 1.47)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0); + tmp_patch->env[4].set &= 0xFE; + } else { + tmp_patch->env[4].set |= 0x01; + } + } + } + chr_ptr = strstr(&line_buffer[line_ptr], "env_time5="); + if (chr_ptr != NULL) { + if (!isdigit(chr_ptr[10])) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0); + } else { + tmp_patch->env[5].time = atof(&chr_ptr[10]); + if ((tmp_patch->env[5].time > 45000.0) || (tmp_patch->env[5].time < 1.47)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0); + tmp_patch->env[5].set &= 0xFE; + } else { + tmp_patch->env[5].set |= 0x01; + } + } + } + chr_ptr = strstr(&line_buffer[line_ptr], "env_level0="); + if (chr_ptr != NULL) { + if (!isdigit(chr_ptr[11])) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0); + } else { + tmp_patch->env[0].level = atof(&chr_ptr[10]); + if ((tmp_patch->env[0].level > 1.0) || (tmp_patch->env[0].level < 0.0)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0); + tmp_patch->env[0].set &= 0xFD; + } else { + tmp_patch->env[0].set |= 0x02; + } + } + } + + chr_ptr = strstr(&line_buffer[line_ptr], "env_level1="); + if (chr_ptr != NULL) { + if (!isdigit(chr_ptr[11])) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0); + } else { + tmp_patch->env[1].level = atof(&chr_ptr[10]); + if ((tmp_patch->env[1].level > 1.0) || (tmp_patch->env[1].level < 0.0)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0); + tmp_patch->env[1].set &= 0xFD; + } else { + tmp_patch->env[1].set |= 0x02; + } + } + } + chr_ptr = strstr(&line_buffer[line_ptr], "env_level2="); + if (chr_ptr != NULL) { + if (!isdigit(chr_ptr[11])) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0); + } else { + tmp_patch->env[2].level = atof(&chr_ptr[10]); + if ((tmp_patch->env[2].level > 1.0) || (tmp_patch->env[2].level < 0.0)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0); + tmp_patch->env[2].set &= 0xFD; + } else { + tmp_patch->env[2].set |= 0x02; + } + } + } + chr_ptr = strstr(&line_buffer[line_ptr], "env_level3="); + if (chr_ptr != NULL) { + if (!isdigit(chr_ptr[11])) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0); + } else { + tmp_patch->env[3].level = atof(&chr_ptr[10]); + if ((tmp_patch->env[3].level > 1.0) || (tmp_patch->env[3].level < 0.0)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0); + tmp_patch->env[3].set &= 0xFD; + } else { + tmp_patch->env[3].set |= 0x02; + } + } + } + chr_ptr = strstr(&line_buffer[line_ptr], "env_level4="); + if (chr_ptr != NULL) { + if (!isdigit(chr_ptr[11])) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0); + } else { + tmp_patch->env[4].level = atof(&chr_ptr[10]); + if ((tmp_patch->env[4].level > 1.0) || (tmp_patch->env[4].level < 0.0)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0); + tmp_patch->env[4].set &= 0xFD; + } else { + tmp_patch->env[4].set |= 0x02; + } + } + } + chr_ptr = strstr(&line_buffer[line_ptr], "env_level5="); + if (chr_ptr != NULL) { + if (!isdigit(chr_ptr[11])) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0); + } else { + tmp_patch->env[5].level = atof(&chr_ptr[10]); + if ((tmp_patch->env[5].level > 1.0) || (tmp_patch->env[5].level < 0.0)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0); + tmp_patch->env[5].set &= 0xFD; + } else { + tmp_patch->env[5].set |= 0x02; + } + } + } + + } + } + } + + if (line_buffer != NULL) + free (line_buffer); + + free (config_buffer); + + if (config_dir != NULL) + free (config_dir); + + return 0; +} + +/* + * sample data conversion functions + * convert data to signed shorts + */ + +/* 8bit signed */ +int +convert_8s (unsigned char *data, struct _sample *gus_sample ) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = calloc((gus_sample->data_length + 1), sizeof(signed short int)); + if (__builtin_expect((gus_sample->data != NULL),1)) { + write_data = gus_sample->data; + do { + *write_data = (*read_data++) << 8; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data++; + } while (read_data != read_end); + return 0; + } + + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit signed ping pong */ +int +convert_8sp (unsigned char *data, struct _sample *gus_sample ) { + unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->loop_start; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = calloc((new_length + 1), sizeof(signed short int)); + if (__builtin_expect((gus_sample->data != NULL),1)) { + write_data = gus_sample->data; + do { + *write_data = (*read_data++) << 8; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data++; + } while (read_data != read_end); + + *write_data = (*read_data++ << 8); + write_data_a = write_data + dloop_length; + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + dloop_length; + read_end = data + gus_sample->loop_end; + do { + *write_data = (*read_data++) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data++; + } while (read_data != read_end); + + *write_data = (*read_data++ << 8); + *write_data_b++ = *write_data; + read_end = data + gus_sample->data_length; + if (__builtin_expect((read_data != read_end),1)) { + do { + *write_data_b = (*read_data++) << 8; + if (*write_data_b > gus_sample->max_peek) { + gus_sample->max_peek = *write_data_b; + } else if (*write_data_b < gus_sample->min_peek) { + gus_sample->min_peek = *write_data_b; + } + write_data_b++; + } while (read_data != read_end); + } + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG; + return 0; + } + + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit signed reverse */ +int +convert_8sr (unsigned char *data, struct _sample *gus_sample ) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + unsigned long int tmp_loop = 0; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = calloc((gus_sample->data_length + 1), sizeof(signed short int)); + if (__builtin_expect((gus_sample->data != NULL),1)) { + write_data = gus_sample->data + gus_sample->data_length - 1; + do { + *write_data = (*read_data++) << 8; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data--; + } while (read_data != read_end); + tmp_loop = gus_sample->loop_end; + gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start; + gus_sample->loop_start = gus_sample->data_length - tmp_loop; + gus_sample->loop_fraction = ((gus_sample->loop_fraction & 0x0f) << 4) | ((gus_sample->loop_fraction & 0xf0) >> 4); + gus_sample->modes ^= SAMPLE_REVERSE; + return 0; + } + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + + +/* 8bit signed reverse ping pong */ +int +convert_8srp (unsigned char *data, struct _sample *gus_sample ) { + unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data + gus_sample->data_length - 1; + unsigned char *read_end = data + gus_sample->loop_end; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = calloc((new_length + 1), sizeof(signed short int)); + if (__builtin_expect((gus_sample->data != NULL),1)) { + write_data = gus_sample->data; + do { + *write_data = (*read_data--) << 8; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data++; + } while (read_data != read_end); + + *write_data = (*read_data-- << 8); + write_data_a = write_data + dloop_length; + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + dloop_length; + read_end = data + gus_sample->loop_start; + do { + *write_data = (*read_data--) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data++; + } while (read_data != read_end); + + *write_data = (*read_data-- << 8); + *write_data_b++ = *write_data; + read_end = data - 1; + do { + *write_data_b = (*read_data--) << 8; + if (*write_data_b > gus_sample->max_peek) { + gus_sample->max_peek = *write_data_b; + } else if (*write_data_b < gus_sample->min_peek) { + gus_sample->min_peek = *write_data_b; + } + write_data_b++; + } while (read_data != read_end); + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE; + return 0; + } + + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit unsigned */ +int +convert_8u (unsigned char *data, struct _sample *gus_sample ) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = calloc((gus_sample->data_length + 1), sizeof(signed short int)); + if (__builtin_expect((gus_sample->data != NULL),1)) { + write_data = gus_sample->data; + do { + *write_data = ((*read_data++) ^ 0x80) << 8; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data++; + } while (read_data != read_end); + gus_sample->modes ^= SAMPLE_UNSIGNED; + return 0; + } + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit unsigned ping pong */ +int +convert_8up (unsigned char *data, struct _sample *gus_sample ) { + unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->loop_start; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = calloc((new_length + 1), sizeof(signed short int)); + if (__builtin_expect((gus_sample->data != NULL),1)) { + write_data = gus_sample->data; + do { + *write_data = ((*read_data++) ^ 0x80) << 8; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data++; + } while (read_data != read_end); + + *write_data = ((*read_data++) ^ 0x80) << 8; + write_data_a = write_data + dloop_length; + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + dloop_length; + read_end = data + gus_sample->loop_end; + do { + *write_data = ((*read_data++) ^ 0x80) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data++; + } while (read_data != read_end); + + *write_data = ((*read_data++) ^ 0x80) << 8; + *write_data_b++ = *write_data; + read_end = data + gus_sample->data_length; + if (__builtin_expect((read_data != read_end),1)) { + do { + *write_data_b = ((*read_data++) ^ 0x80) << 8; + if (*write_data_b > gus_sample->max_peek) { + gus_sample->max_peek = *write_data_b; + } else if (*write_data_b < gus_sample->min_peek) { + gus_sample->min_peek = *write_data_b; + } + write_data_b++; + } while (read_data != read_end); + } + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_UNSIGNED; + return 0; + } + + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit unsigned reverse */ +int +convert_8ur (unsigned char *data, struct _sample *gus_sample ) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + unsigned long int tmp_loop = 0; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = calloc((gus_sample->data_length + 1), sizeof(signed short int)); + if (__builtin_expect((gus_sample->data != NULL),1)) { + write_data = gus_sample->data + gus_sample->data_length - 1; + do { + *write_data = ((*read_data++) ^ 0x80) << 8; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data--; + } while (read_data != read_end); + tmp_loop = gus_sample->loop_end; + gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start; + gus_sample->loop_start = gus_sample->data_length - tmp_loop; + gus_sample->loop_fraction = ((gus_sample->loop_fraction & 0x0f) << 4) | ((gus_sample->loop_fraction & 0xf0) >> 4); + gus_sample->modes ^= SAMPLE_REVERSE | SAMPLE_UNSIGNED; + return 0; + } + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit unsigned reverse ping pong */ +int +convert_8urp (unsigned char *data, struct _sample *gus_sample ) { + unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data + gus_sample->data_length - 1; + unsigned char *read_end = data + gus_sample->loop_end; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = calloc((new_length + 1), sizeof(signed short int)); + if (__builtin_expect((gus_sample->data != NULL),1)) { + write_data = gus_sample->data; + do { + *write_data = ((*read_data--) ^ 0x80) << 8; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data++; + } while (read_data != read_end); + + *write_data = ((*read_data--) ^ 0x80) << 8; + write_data_a = write_data + dloop_length; + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + dloop_length; + read_end = data + gus_sample->loop_start; + do { + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data++; + } while (read_data != read_end); + + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data_b++ = *write_data; + read_end = data - 1; + do { + *write_data_b = ((*read_data--) ^ 0x80) << 8; + if (*write_data_b > gus_sample->max_peek) { + gus_sample->max_peek = *write_data_b; + } else if (*write_data_b < gus_sample->min_peek) { + gus_sample->min_peek = *write_data_b; + } + write_data_b++; + } while (read_data != read_end); + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE | SAMPLE_UNSIGNED; + return 0; + } + + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit signed */ +int +convert_16s (unsigned char *data, struct _sample *gus_sample ) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = calloc(((gus_sample->data_length >> 1) + 1),sizeof(signed short int)); + if (__builtin_expect((gus_sample->data != NULL),1)) { + write_data = gus_sample->data; + do { + *write_data = *read_data++; + *write_data |= (*read_data++) << 8; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data++; + } while (read_data < read_end); + + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + return 0; + } + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit signed ping pong */ +int +convert_16sp (unsigned char *data, struct _sample *gus_sample ) { + unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->loop_start; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = calloc(((new_length >> 1) + 1), sizeof(signed short int)); + if (__builtin_expect((gus_sample->data != NULL),1)) { + write_data = gus_sample->data; + do { + *write_data = (*read_data++); + *write_data |= (*read_data++) << 8; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data++; + } while (read_data < read_end); + + *write_data = (*read_data++); + *write_data |= (*read_data++) << 8; + write_data_a = write_data + (dloop_length >> 1); + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + (dloop_length >> 1); + read_end = data + gus_sample->loop_end; + do { + *write_data = (*read_data++); + *write_data |= (*read_data++) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data++; + } while (read_data < read_end); + + *write_data = *(read_data++); + *write_data |= (*read_data++) << 8; + *write_data_b++ = *write_data; + read_end = data + gus_sample->data_length; + if (__builtin_expect((read_data != read_end),1)) { + do { + *write_data_b = *(read_data++); + *write_data_b |= (*read_data++) << 8; + if (*write_data_b > gus_sample->max_peek) { + gus_sample->max_peek = *write_data_b; + } else if (*write_data_b < gus_sample->min_peek) { + gus_sample->min_peek = *write_data_b; + } + write_data_b++; + } while (read_data < read_end); + } + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG; + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + return 0; + } + + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit signed reverse */ +int +convert_16sr (unsigned char *data, struct _sample *gus_sample ) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + unsigned long int tmp_loop = 0; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = calloc(((gus_sample->data_length >> 1) + 1), sizeof(signed short int)); + if (__builtin_expect((gus_sample->data != NULL),1)) { + write_data = gus_sample->data + (gus_sample->data_length >> 1) - 1; + do { + *write_data = *read_data++; + *write_data |= (*read_data++) << 8; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data--; + } while (read_data < read_end); + tmp_loop = gus_sample->loop_end; + gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start; + gus_sample->loop_start = gus_sample->data_length - tmp_loop; + gus_sample->loop_fraction = ((gus_sample->loop_fraction & 0x0f) << 4) | ((gus_sample->loop_fraction & 0xf0) >> 4); + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + gus_sample->modes ^= SAMPLE_REVERSE; + return 0; + } + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + + +/* 16bit signed reverse ping pong */ +int +convert_16srp (unsigned char *data, struct _sample *gus_sample ) { + unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data + gus_sample->data_length - 1; + unsigned char *read_end = data + gus_sample->loop_end; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = calloc(((new_length >> 1) + 1), sizeof(signed short int)); + if (__builtin_expect((gus_sample->data != NULL),1)) { + write_data = gus_sample->data; + do { + + *write_data = (*read_data--) << 8; + *write_data |= *read_data--; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data++; + } while (read_data < read_end); + + *write_data = (*read_data-- << 8); + *write_data |= *read_data--; + write_data_a = write_data + (dloop_length >> 1); + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + (dloop_length >> 1); + read_end = data + gus_sample->loop_start; + do { + *write_data = (*read_data--) << 8; + *write_data |= *read_data--; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data++; + } while (read_data < read_end); + + *write_data = ((*read_data--) << 8); + *write_data |= *read_data--; + *write_data_b++ = *write_data; + read_end = data - 1; + do { + *write_data_b = (*read_data--) << 8; + *write_data_b |= *read_data--; + if (*write_data_b > gus_sample->max_peek) { + gus_sample->max_peek = *write_data_b; + } else if (*write_data_b < gus_sample->min_peek) { + gus_sample->min_peek = *write_data_b; + } + write_data_b++; + } while (read_data < read_end); + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE; + return 0; + } + + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit unsigned */ +int +convert_16u (unsigned char *data, struct _sample *gus_sample ) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = calloc(((gus_sample->data_length >> 1) + 1),sizeof(signed short int)); + if (__builtin_expect((gus_sample->data != NULL),1)) { + write_data = gus_sample->data; + do { + *write_data = *read_data++; + *write_data |= ((*read_data++) ^ 0x80) << 8; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data++; + } while (read_data < read_end); + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + gus_sample->modes ^= SAMPLE_UNSIGNED; + return 0; + } + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit unsigned ping pong */ +int +convert_16up (unsigned char *data, struct _sample *gus_sample ) { + unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->loop_start; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = calloc(((new_length >> 1) + 1), sizeof(signed short int)); + if (__builtin_expect((gus_sample->data != NULL),1)) { + write_data = gus_sample->data; + do { + *write_data = (*read_data++); + *write_data |= ((*read_data++) ^ 0x80) << 8; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data++; + } while (read_data < read_end); + + *write_data = (*read_data++); + *write_data |= ((*read_data++) ^ 0x80) << 8; + write_data_a = write_data + (dloop_length >> 1); + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + (dloop_length >> 1); + read_end = data + gus_sample->loop_end; + do { + *write_data = (*read_data++); + *write_data |= ((*read_data++) ^ 0x80) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data++; + } while (read_data < read_end); + + *write_data = (*read_data++); + *write_data |= ((*read_data++) ^ 0x80) << 8; + *write_data_b++ = *write_data; + read_end = data + gus_sample->data_length; + if (__builtin_expect((read_data != read_end),1)) { + do { + *write_data_b = (*read_data++); + *write_data_b |= ((*read_data++) ^ 0x80) << 8; + if (*write_data_b > gus_sample->max_peek) { + gus_sample->max_peek = *write_data_b; + } else if (*write_data_b < gus_sample->min_peek) { + gus_sample->min_peek = *write_data_b; + } + write_data_b++; + } while (read_data < read_end); + } + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG; + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + return 0; + } + + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit unsigned reverse */ +int +convert_16ur (unsigned char *data, struct _sample *gus_sample ) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + unsigned long int tmp_loop = 0; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = calloc(((gus_sample->data_length >> 1) + 1), sizeof(signed short int)); + if (__builtin_expect((gus_sample->data != NULL),1)) { + write_data = gus_sample->data + (gus_sample->data_length >> 1) - 1; + do { + *write_data = *read_data++; + *write_data |= ((*read_data++) ^ 0x80) << 8; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data--; + } while (read_data < read_end); + tmp_loop = gus_sample->loop_end; + gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start; + gus_sample->loop_start = gus_sample->data_length - tmp_loop; + gus_sample->loop_fraction = ((gus_sample->loop_fraction & 0x0f) << 4) | ((gus_sample->loop_fraction & 0xf0) >> 4); + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + gus_sample->modes ^= SAMPLE_REVERSE | SAMPLE_UNSIGNED; + return 0; + } + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit unsigned reverse ping pong */ +int +convert_16urp (unsigned char *data, struct _sample *gus_sample ) { + unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data + gus_sample->data_length - 1; + unsigned char *read_end = data + gus_sample->loop_end; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = calloc(((new_length >> 1) + 1), sizeof(signed short int)); + if (__builtin_expect((gus_sample->data != NULL),1)) { + write_data = gus_sample->data; + do { + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data |= *read_data--; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data++; + } while (read_data < read_end); + + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data |= *read_data--; + write_data_a = write_data + (dloop_length >> 1); + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + (dloop_length >> 1); + read_end = data + gus_sample->loop_start; + do { + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data |= *read_data--; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + if (*write_data > gus_sample->max_peek) { + gus_sample->max_peek = *write_data; + } else if (*write_data < gus_sample->min_peek) { + gus_sample->min_peek = *write_data; + } + write_data++; + } while (read_data < read_end); + + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data |= *read_data--; + *write_data_b++ = *write_data; + read_end = data - 1; + do { + *write_data_b = ((*read_data--) ^ 0x80) << 8; + *write_data_b |= *read_data--; + if (*write_data_b > gus_sample->max_peek) { + gus_sample->max_peek = *write_data_b; + } else if (*write_data_b < gus_sample->min_peek) { + gus_sample->min_peek = *write_data_b; + } + write_data_b++; + } while (read_data < read_end); + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE | SAMPLE_UNSIGNED; + return 0; + } + + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + + +/* sample loading */ + +int +load_sample (struct _patch *sample_patch) { + unsigned char *gus_patch; + unsigned long int gus_size; + unsigned long int gus_ptr; + unsigned char no_of_samples; + struct _sample *gus_sample = NULL; + unsigned long int i = 0; + + int (*do_convert[])(unsigned char *data, struct _sample *gus_sample ) = { + convert_8s, + convert_16s, + convert_8u, + convert_16u, + convert_8sp, + convert_16sp, + convert_8up, + convert_16up, + convert_8sr, + convert_16sr, + convert_8ur, + convert_16ur, + convert_8srp, + convert_16srp, + convert_8urp, + convert_16urp + }; + unsigned long int tmp_loop; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + SAMPLE_CONVERT_DEBUG(sample_patch->filename); + sample_patch->loaded = 1; + if ((gus_patch = WM_BufferFile(sample_patch->filename,&gus_size)) == NULL) { + return -1; + } + if (gus_size < 239) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, sample_patch->filename, 0); + free(gus_patch); + return -1; + } + if (memcmp(gus_patch, "GF1PATCH110\0ID#000002", 22) && memcmp(gus_patch, "GF1PATCH100\0ID#000002", 22)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID,"(unsupported format)", 0); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, sample_patch->filename, 0); + free(gus_patch); + return -1; + } + if (gus_patch[82] > 1) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID,"(unsupported format)", 0); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, sample_patch->filename, 0); + free(gus_patch); + return -1; + } + if (gus_patch[151] > 1) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID,"(unsupported format)", 0); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, sample_patch->filename, 0); + free(gus_patch); + return -1; + } + + no_of_samples = gus_patch[198]; + sample_patch->first_sample = NULL; + gus_ptr = 239; + while (no_of_samples) { + unsigned long int tmp_cnt; + if (sample_patch->first_sample == NULL) { + sample_patch->first_sample = malloc(sizeof(struct _sample)); + gus_sample = sample_patch->first_sample; + } else { + gus_sample->next = malloc(sizeof(struct _sample)); + gus_sample = gus_sample->next; + } + if (gus_sample == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, 0); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, sample_patch->filename, 0); + return -1; + } + + gus_sample->next = NULL; + gus_sample->loop_fraction = gus_patch[gus_ptr+7]; + gus_sample->data_length = (gus_patch[gus_ptr+11] << 24) | (gus_patch[gus_ptr+10] << 16) | (gus_patch[gus_ptr+9] << 8) | gus_patch[gus_ptr+8]; + gus_sample->loop_start = (gus_patch[gus_ptr+15] << 24) | (gus_patch[gus_ptr+14] << 16) | (gus_patch[gus_ptr+13] << 8) | gus_patch[gus_ptr+12]; + gus_sample->loop_end = (gus_patch[gus_ptr+19] << 24) | (gus_patch[gus_ptr+18] << 16) | (gus_patch[gus_ptr+17] << 8) | gus_patch[gus_ptr+16]; + gus_sample->rate = (gus_patch[gus_ptr+21] << 8) | gus_patch[gus_ptr+20]; + gus_sample->freq_low = ((gus_patch[gus_ptr+25] << 24) | (gus_patch[gus_ptr+24] << 16) | (gus_patch[gus_ptr+23] << 8) | gus_patch[gus_ptr+22]); + gus_sample->freq_high = ((gus_patch[gus_ptr+29] << 24) | (gus_patch[gus_ptr+28] << 16) | (gus_patch[gus_ptr+27] << 8) | gus_patch[gus_ptr+26]); + gus_sample->freq_root = ((gus_patch[gus_ptr+33] << 24) | (gus_patch[gus_ptr+32] << 16) | (gus_patch[gus_ptr+31] << 8) | gus_patch[gus_ptr+30]); + + /* This is done this way instead of ((freq * 1024) / rate) to avoid 32bit overflow. */ + /* Result is 0.001% inacurate */ + gus_sample->inc_div = ((gus_sample->freq_root * 512) / gus_sample->rate) * 2; + +#if 0 + printf("\rTremolo Sweep: %i, Rate: %i, Depth %i\n", + gus_patch[gus_ptr+49], gus_patch[gus_ptr+50], gus_patch[gus_ptr+51]); + printf("\rVibrato Sweep: %i, Rate: %i, Depth %i\n", + gus_patch[gus_ptr+52], gus_patch[gus_ptr+53], gus_patch[gus_ptr+54]); +#endif + gus_sample->modes = gus_patch[gus_ptr+55] & 0x7F; + if ((sample_patch->remove & SAMPLE_SUSTAIN) && (gus_sample->modes & SAMPLE_SUSTAIN)) { + gus_sample->modes ^= SAMPLE_SUSTAIN; + } + if (sample_patch->patchid & 0x0080) { + if (!(sample_patch->keep & SAMPLE_LOOP)) { + gus_sample->modes &= 0xFB; + } + if (!(sample_patch->keep & SAMPLE_ENVELOPE)) { + gus_sample->modes &= 0xBF; + } + } + + + if (gus_sample->loop_start > gus_sample->loop_end) { + tmp_loop = gus_sample->loop_end; + gus_sample->loop_end = gus_sample->loop_start; + gus_sample->loop_start = tmp_loop; + gus_sample->loop_fraction = ((gus_sample->loop_fraction & 0x0f) << 4) | ((gus_sample->loop_fraction & 0xf0) >> 4); + } + for (i = 0; i < 6; i++) { + if (gus_sample->modes & SAMPLE_ENVELOPE) { + unsigned char env_rate = gus_patch[gus_ptr+37+i]; + if (sample_patch->env[i].set & 0x02) { + gus_sample->env_target[i] = 16448 * (unsigned long int)(255.0 * sample_patch->env[i].level); + } else { + gus_sample->env_target[i] = 16448 * gus_patch[gus_ptr+43+i]; + } + + if (sample_patch->env[i].set & 0x01) { + gus_sample->env_rate[i] = (unsigned long int)(4194303.0 / ((float)WM_SampleRate * (sample_patch->env[i].time / 1000.0))); + } else { + gus_sample->env_rate[i] = (unsigned long int)(4194303.0 / ((float)WM_SampleRate * env_time_table[env_rate])); + if (gus_sample->env_rate[i] == 0) { + fprintf(stderr,"\rWarning: libWildMidi %s found invalid envelope(%lu) rate setting in %s. Using %f instead.\n",__FUNCTION__, i, sample_patch->filename, env_time_table[63]); + gus_sample->env_rate[i] = (unsigned long int)(4194303.0 / ((float)WM_SampleRate * env_time_table[63])); + } + } + } else { + gus_sample->env_target[i] = 4194303; + gus_sample->env_rate[i] = (unsigned long int)(4194303.0 / ((float)WM_SampleRate * env_time_table[63])); + } + } + + gus_sample->env_target[6] = 0; + gus_sample->env_rate[6] = (unsigned long int)(4194303.0 / ((float)WM_SampleRate * env_time_table[63])); + + if ((sample_patch->patchid == 47) && (!(gus_sample->modes & SAMPLE_LOOP))) { + for (i = 3; i < 6; i++) { + gus_sample->env_target[i] = gus_sample->env_target[2]; + gus_sample->env_rate[i] = gus_sample->env_rate[2]; + } + } + + gus_ptr += 96; + tmp_cnt = gus_sample->data_length; + +/* convert to float */ + gus_sample->min_peek = 0; + gus_sample->max_peek = 0; + + if (do_convert[(((gus_sample->modes & 0x18) >> 1)| (gus_sample->modes & 0x03))](&gus_patch[gus_ptr], gus_sample) == -1) { + return -1; + + }; + + if (gus_sample->max_peek > (-gus_sample->min_peek)) { + gus_sample->peek_adjust = 33553408 / gus_sample->max_peek; + } else { + gus_sample->peek_adjust = 33554432 / (-gus_sample->min_peek); + } + gus_sample->peek_adjust = (gus_sample->peek_adjust * sample_patch->amp) >> 10; + + gus_ptr += tmp_cnt; + gus_sample->loop_start = (gus_sample->loop_start << 10) | (((gus_sample->loop_fraction & 0x0f) << 10) / 16); + gus_sample->loop_end = (gus_sample->loop_end << 10) | (((gus_sample->loop_fraction & 0xf0) << 6) / 16); + gus_sample->loop_size = gus_sample->loop_end - gus_sample->loop_start; + gus_sample->data_length = gus_sample->data_length << 10; + no_of_samples--; + } + free(gus_patch); + return 0; +} + + +struct _patch * +get_patch_data(struct _mdi *mdi, unsigned short patchid) { + struct _patch *search_patch; + + WM_Lock(&patch_lock); + + search_patch = patch[patchid & 0x007F]; + + if (search_patch == NULL) { + WM_Unlock(&patch_lock); + return NULL; + } + + while(search_patch != NULL) { + if (search_patch->patchid == patchid) { + WM_Unlock(&patch_lock); + return search_patch; + } + search_patch = search_patch->next; + } + if ((patchid >> 8) != 0) { + WM_Unlock(&patch_lock); + return (get_patch_data(mdi, patchid & 0x00FF)); + } + WM_Unlock(&patch_lock); + return NULL; +} + +void +load_patch (struct _mdi *mdi, unsigned short patchid) { + int i; + struct _patch *tmp_patch = NULL; + + for (i = 0; i < mdi->patch_count; i++) { + if (mdi->patches[i]->patchid == patchid) { + return; + } + } + + tmp_patch = get_patch_data(mdi, patchid); + if (tmp_patch == NULL) { + return; + } + + WM_Lock(&patch_lock); + if (!tmp_patch->loaded) { + if (load_sample(tmp_patch) == -1) { + WM_Unlock(&patch_lock); + return; + } + } + + if (tmp_patch->first_sample == NULL) { + WM_Unlock(&patch_lock); + return; + } + + mdi->patch_count++; + mdi->patches = realloc(mdi->patches, (sizeof(struct _patch) * mdi->patch_count)); + mdi->patches[mdi->patch_count -1] = tmp_patch; + tmp_patch->inuse_count++; + WM_Unlock(&patch_lock); + return; +} + + +struct _sample * +get_sample_data (struct _patch *sample_patch, unsigned long int freq) { + struct _sample *last_sample = NULL; + struct _sample *return_sample = NULL; + + WM_Lock(&patch_lock); + if (sample_patch == NULL) { + WM_Unlock(&patch_lock); + return NULL; + } + if (sample_patch->first_sample == NULL) { + WM_Unlock(&patch_lock); + return NULL; + } + if (freq == 0) { + WM_Unlock(&patch_lock); + return sample_patch->first_sample; + } + + return_sample = sample_patch->first_sample; + last_sample = sample_patch->first_sample; + while (last_sample != NULL) { + if (freq > last_sample->freq_low) { + if (freq < last_sample->freq_high) { + WM_Unlock(&patch_lock); + return last_sample; + } else { + return_sample = last_sample; + } + } + last_sample = last_sample->next; + } + WM_Unlock(&patch_lock); + return return_sample; +} + +unsigned long int +read_var_length (struct _mdi *mdi, struct _miditrack *track) { + unsigned long int var_data = 0; + if (mdi->data[track->ptr] > 0x7f) { + while(mdi->data[track->ptr] > 0x7f) { + var_data |= mdi->data[track->ptr] & 0x7f; + track->ptr++; + if (track->ptr > mdi->size) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0); + return 0xFFFFFFFF; + } + var_data = (var_data << 7); + } + } + var_data |= mdi->data[track->ptr] & 0x7f; + track->ptr++; + + if (track->ptr > mdi->size) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0); + return 0xFFFFFFFF; + } + + return var_data; +} + +void +do_note_off (unsigned char ch, struct _mdi *mdi, unsigned long int ptr) { + struct _note *nte; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + + nte = &mdi->note_table[0][ch][mdi->data[ptr]]; + if (!nte->active) + nte = &mdi->note_table[1][ch][mdi->data[ptr]]; + if (!nte->active) { + return; + } + + if ((ch == 9) && (!(nte->modes & SAMPLE_LOOP))) { + return; + } + + if (nte->hold) { + nte->hold |= HOLD_OFF; + } else { +#if 0 + if (nte->modes & SAMPLE_SUSTAIN) { + nte->env = 3; + if (nte->env_level > nte->sample->env_target[3]) { + nte->env_inc = -nte->sample->env_rate[3]; + } else { + nte->env_inc = nte->sample->env_rate[3]; + } + } else +#endif + { + if (nte->env < 4) { + nte->env = 4; + if (nte->env_level > nte->sample->env_target[4]) { + nte->env_inc = -nte->sample->env_rate[4]; + } else { + nte->env_inc = nte->sample->env_rate[4]; + } + } + } + } + return; +} + +inline unsigned long int +get_inc (struct _mdi *mdi, struct _note *nte) { + int ch = nte->noteid >> 8; + signed long int note_f; + unsigned long int freq; + + if (__builtin_expect((nte->patch->note != 0),0)) { + note_f = nte->patch->note * 100; + } else { + note_f = (nte->noteid & 0x7f) * 100; + } + note_f += mdi->channel[ch].pitch_adjust; + if (__builtin_expect((note_f < 0), 0)) { + note_f = 0; + } else if (__builtin_expect((note_f > 12700), 0)) { + note_f = 12700; + } + freq = freq_table[(note_f % 1200)] >> (10 - (note_f / 1200)); + return (((freq / ((WM_SampleRate * 100) / 1024)) * 1024 / nte->sample->inc_div)); +} + +inline signed short int +get_volume(struct _mdi *mdi, unsigned char ch, struct _note *nte) { + signed long int volume; + + if (mdi->info.mixer_options & WM_MO_LINEAR_VOLUME) { + volume = (lin_volume[mdi->channel[ch].volume] * + lin_volume[mdi->channel[ch].expression] * + lin_volume[nte->velocity]) / 1048576; + } else { + volume = (sqr_volume[mdi->channel[ch].volume] * + sqr_volume[mdi->channel[ch].expression] * + sqr_volume[nte->velocity]) / 1048576; + } + return ((volume * nte->sample->peek_adjust) >> 10); +} + +void +do_note_on (unsigned char ch, struct _mdi *mdi, unsigned long int ptr) { + struct _note *nte; + unsigned long int freq = 0; + struct _patch *patch; + struct _sample *sample; + + if (mdi->data[ptr+1] == 0x00) { + do_note_off(ch, mdi, ptr); + return; + } + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + + if (ch != 9) { + patch = mdi->channel[ch].patch; + if (patch == NULL) { + return; + } + freq = freq_table[(mdi->data[ptr] % 12) * 100] >> (10 -(mdi->data[ptr] / 12)); + } else { + patch = get_patch_data(mdi, ((mdi->channel[ch].bank << 8) | mdi->data[ptr] | 0x80)); + if (patch == NULL) { + return; + } + if (patch->note) { + freq = freq_table[(patch->note % 12) * 100] >> (10 -(patch->note / 12)); + } else { + freq = freq_table[(mdi->data[ptr] % 12) * 100] >> (10 -(mdi->data[ptr] / 12)); + } + } + + sample = get_sample_data(patch, (freq / 100)); + + if (sample == NULL) { + return; + } + + nte = &mdi->note_table[0][ch][mdi->data[ptr]]; + + if (nte->active) { + if ((nte->modes & SAMPLE_ENVELOPE) && (nte->env < 3) && (!(nte->hold & HOLD_OFF))) + return; + nte->next = &mdi->note_table[1][ch][mdi->data[ptr]]; + nte->env = 6; + nte->env_inc = -nte->sample->env_rate[6]; + nte = &mdi->note_table[1][ch][mdi->data[ptr]]; + } else { + if (mdi->note_table[1][ch][mdi->data[ptr]].active) { + if ((nte->modes & SAMPLE_ENVELOPE) && (nte->env < 3) && (!(nte->hold & HOLD_OFF))) + return; + mdi->note_table[1][ch][mdi->data[ptr]].next = nte; + mdi->note_table[1][ch][mdi->data[ptr]].env = 6; + mdi->note_table[1][ch][mdi->data[ptr]].env_inc = -mdi->note_table[1][ch][mdi->data[ptr]].sample->env_rate[6]; + } else { + *mdi->last_note = nte; + mdi->last_note++; + nte->active = 1; + } + } + nte->noteid = (ch << 8) | mdi->data[ptr]; + nte->patch = patch; + nte->sample = sample; + nte->sample_pos = 0; + nte->sample_inc = get_inc (mdi, nte); + nte->velocity = mdi->data[ptr+1]; + nte->env = 0; + nte->env_inc = nte->sample->env_rate[0]; + nte->env_level = 0; + nte->modes = sample->modes; + nte->hold = mdi->channel[ch].hold; + nte->vol_lvl = get_volume(mdi, ch, nte); + nte->next = NULL; +} + +void +do_aftertouch (unsigned char ch, struct _mdi *mdi, unsigned long int ptr) { + struct _note *nte; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + + nte = &mdi->note_table[0][ch][mdi->data[ptr]]; + if (!nte->active) { + nte = &mdi->note_table[1][ch][mdi->data[ptr]]; + if (!nte->active) { + return; + } + } + + nte->velocity = mdi->data[ptr+1]; + nte->vol_lvl = get_volume(mdi, ch, nte); + + if (nte->next) { + nte->next->velocity = mdi->data[ptr+1]; + nte->next->vol_lvl = get_volume(mdi, ch, nte->next); + } +} + + +void +do_pan_adjust (struct _mdi *mdi, unsigned char ch) { + signed short int pan_adjust = mdi->channel[ch].balance + mdi->channel[ch].pan; + signed long int left, right; + + if (pan_adjust > 63) { + pan_adjust = 63; + } else if (pan_adjust < -64) { + pan_adjust = -64; + } + + pan_adjust += 64; + + if (mdi->info.mixer_options & WM_MO_LINEAR_VOLUME) { + left = (lin_volume[127 - pan_adjust] * WM_MasterVolume * mdi->amp) / 1048576; + right= (lin_volume[pan_adjust] * WM_MasterVolume * mdi->amp) / 1048576; + } else { + left = (pan_volume[127 - pan_adjust] * WM_MasterVolume * mdi->amp) / 1048576; + right = (pan_volume[pan_adjust] * WM_MasterVolume * mdi->amp) / 1048576; + } + + mdi->channel[ch].left_adjust = left; + mdi->channel[ch].right_adjust = right; +} + +void +do_control (unsigned char ch, struct _mdi *mdi, unsigned long int ptr) { + struct _note **note_data = mdi->note; + + switch (mdi->data[ptr]) { + case 0: // Bank Select + mdi->channel[ch].bank = mdi->data[ptr+1]; + break; + case 1: + case 2: + case 3: + case 4: + case 5: + break; + case 6: // Data Entry Course + if (mdi->channel[ch].reg_data == 0x0000) { // Pitch Bend Range + int data_tmp; + data_tmp = mdi->channel[ch].pitch_range % 100; + mdi->channel[ch].pitch_range = mdi->data[ptr+1] * 100 + data_tmp; + } + break; + case 7: // Channel Volume + mdi->channel[ch].volume = mdi->data[ptr+1]; + + if (note_data != mdi->last_note) { + do { + if (((*note_data)->noteid >> 8) == ch) { + (*note_data)->vol_lvl = get_volume(mdi, ch, *note_data); + if ((*note_data)->next) + (*note_data)->next->vol_lvl = get_volume(mdi, ch, (*note_data)->next); + } + note_data++; + } while (note_data != mdi->last_note); + } + break; + case 8: // Channel Balance + mdi->channel[ch].balance = mdi->data[ptr+1] - 64; + do_pan_adjust(mdi, ch); + break; + case 9: + break; + case 10: // Channel Pan + mdi->channel[ch].pan = mdi->data[ptr+1] - 64; + do_pan_adjust(mdi, ch); + break; + case 11: // Channel Expression + mdi->channel[ch].expression = mdi->data[ptr+1]; + + if (note_data != mdi->last_note) { + do { + if (((*note_data)->noteid >> 8) == ch) { + (*note_data)->vol_lvl = get_volume(mdi, ch, *note_data); + if ((*note_data)->next) + (*note_data)->next->vol_lvl = get_volume(mdi, ch, (*note_data)->next); + } + note_data++; + } while (note_data != mdi->last_note); + } + break; + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + case 28: + case 29: + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + break; + case 38: // Data Entry Fine + if (mdi->channel[ch].reg_data == 0x0000) { // Pitch Bend Range + int data_tmp; + data_tmp = mdi->channel[ch].pitch_range / 100; + mdi->channel[ch].pitch_range = (data_tmp * 100) + mdi->data[ptr+1]; + } + break; + case 39: + case 40: + case 41: + case 42: + case 43: + printf("\rController %i used\n",mdi->data[ptr]); + break; + case 44: + case 45: + case 46: + case 47: + case 48: + case 49: + case 50: + case 51: + case 52: + case 53: + case 54: + case 55: + case 56: + case 57: + case 58: + case 59: + case 60: + case 61: + case 62: + case 63: + break; + case 64: // Channel Hold + if (mdi->data[ptr+1] > 63) { + mdi->channel[ch].hold = 1; + } else { + mdi->channel[ch].hold = 0; + if (note_data != mdi->last_note) { + do { + if (((*note_data)->noteid >> 8) == ch) { + if ((*note_data)->hold & HOLD_OFF) { + if ((*note_data)->modes & SAMPLE_ENVELOPE) { +#if 0 + if ((*note_data)->modes & SAMPLE_SUSTAIN) { + (*note_data)->env = 3; + if ((*note_data)->env_level > (*note_data)->sample->env_target[3]) { + (*note_data)->env_inc = -(*note_data)->sample->env_rate[3]; + } else { + (*note_data)->env_inc = (*note_data)->sample->env_rate[3]; + } + } else +#endif + { + if ((*note_data)->env < 4) { + (*note_data)->env = 4; + if ((*note_data)->env_level > (*note_data)->sample->env_target[4]) { + (*note_data)->env_inc = -(*note_data)->sample->env_rate[4]; + } else { + (*note_data)->env_inc = (*note_data)->sample->env_rate[4]; + } + } + } + } + } + (*note_data)->hold = 0x00; + } + note_data++; + } while (note_data != mdi->last_note); + } + } + break; + case 65: + case 66: + case 67: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 78: + case 79: + case 80: + case 81: + case 82: + case 83: + case 84: + case 85: + case 86: + case 87: + case 88: + case 89: + case 90: + case 91: + case 92: + case 93: + case 94: + case 95: + case 96: + case 97: + case 98: + case 99: + break; + case 100: // Registered Param Fine + mdi->channel[ch].reg_data = (mdi->channel[ch].reg_data & 0xFF00) | mdi->data[ptr+1]; + break; + case 101: // Registered Param Course + mdi->channel[ch].reg_data = (mdi->channel[ch].reg_data & 0xFF) | (mdi->data[ptr+1] << 8); + break; + case 102: + case 103: + case 104: + case 105: + case 106: + case 107: + case 108: + case 109: + case 110: + case 111: + case 112: + case 113: + case 114: + case 115: + case 116: + case 117: + case 118: + case 119: + break; + case 120: // All Channel Sound Off + if (note_data != mdi->last_note) { + do { + if (((*note_data)->noteid >> 8) == ch) { + (*note_data)->active = 0; + if ((*note_data)->next) { + (*note_data)->next = NULL; + } + } + note_data++; + } while (note_data != mdi->last_note); + mdi->last_note = mdi->note; + } + break; + case 121: // All Controlers Off + mdi->channel[ch].expression = 127; + mdi->channel[ch].pressure = 0; + mdi->channel[ch].volume = 100; + mdi->channel[ch].pan = 0; + mdi->channel[ch].balance = 0; + mdi->channel[ch].reg_data = 0xffff; + mdi->channel[ch].pitch_range = 200; + mdi->channel[ch].pitch = 0; + mdi->channel[ch].pitch_adjust = 0; + mdi->channel[ch].hold = 0; + do_pan_adjust(mdi, ch); + + if (note_data != mdi->last_note) { + do { + if (((*note_data)->noteid >> 8 ) == ch) { + (*note_data)->sample_inc = get_inc (mdi, *note_data); + (*note_data)->velocity = 0; + (*note_data)->vol_lvl = get_volume(mdi, ch, *note_data); + (*note_data)->hold = 0; + + if ((*note_data)->next) { + (*note_data)->next->velocity = mdi->data[ptr]; + (*note_data)->next->vol_lvl = get_volume(mdi, ch, (*note_data)->next); + } + + } + note_data++; + } while (note_data != mdi->last_note); + } + break; + case 122: + break; + case 123: // All Channel Notes Off + if (ch == 9) + return; + if (note_data != mdi->last_note) { + do { + if (((*note_data)->noteid >> 8) == ch) { + if (!(*note_data)->hold){ + if ((*note_data)->modes & SAMPLE_ENVELOPE) { + if ((*note_data)->env < 5) { + if ((*note_data)->env_level > (*note_data)->sample->env_target[5]) { + (*note_data)->env_inc = -(*note_data)->sample->env_rate[5]; + } else { + (*note_data)->env_inc = (*note_data)->sample->env_rate[5]; + } + (*note_data)->env = 5; + } + } + } else { + (*note_data)->hold |= HOLD_OFF; + } + } + note_data++; + } while (note_data != mdi->last_note); + } + break; + case 124: + case 125: + case 126: + case 127: + break; + } +} + +void +do_patch (unsigned char ch, struct _mdi *mdi, unsigned long int ptr) { + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + if (ch != 9) { + mdi->channel[ch].patch = get_patch_data(mdi, ((mdi->channel[ch].bank << 8) | mdi->data[ptr])); + } else { + mdi->channel[ch].bank = mdi->data[ptr]; + } +} + +void +do_channel_pressure (unsigned char ch, struct _mdi *mdi, unsigned long int ptr) { + struct _note **note_data = mdi->note; + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + + if (note_data != mdi->last_note) { + do { + if (((*note_data)->noteid >> 8 ) == ch) { + (*note_data)->velocity = mdi->data[ptr]; + (*note_data)->vol_lvl = get_volume(mdi, ch, *note_data); + + if ((*note_data)->next) { + (*note_data)->next->velocity = mdi->data[ptr]; + (*note_data)->next->vol_lvl = get_volume(mdi, ch, (*note_data)->next); + } + } + note_data++; + } while (note_data != mdi->last_note); + } +} + +void +do_pitch (unsigned char ch, struct _mdi *mdi, unsigned long int ptr) { + struct _note **note_data = mdi->note; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + mdi->channel[ch].pitch = ((mdi->data[ptr+1] << 7) | mdi->data[ptr]) - 0x2000; + + if (mdi->channel[ch].pitch < 0) { + mdi->channel[ch].pitch_adjust = mdi->channel[ch].pitch_range * mdi->channel[ch].pitch / 8192; + } else { + mdi->channel[ch].pitch_adjust = mdi->channel[ch].pitch_range * mdi->channel[ch].pitch / 8191; + } + + if (note_data != mdi->last_note) { + do { + if (((*note_data)->noteid >> 8 ) == ch) { + (*note_data)->sample_inc = get_inc (mdi, *note_data); + } + note_data++; + } while (note_data != mdi->last_note); + } +} + +void +do_message (unsigned char ch, struct _mdi *mdi, unsigned long int ptr) { + unsigned char event_type = 0xF0 | ch; + static unsigned long int tempo = 500000; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + if (event_type == 0xFF) { + if ((mdi->data[ptr] == 0x51) && (mdi->data[ptr+1] == 3)) { + tempo = (mdi->data[ptr+2] << 16) | (mdi->data[ptr+3] << 8) | mdi->data[ptr+4]; + if (tempo == 0) + mdi->samples_per_delta = (WM_SampleRate << 10) / (2 * mdi->divisions); + else + mdi->samples_per_delta = (WM_SampleRate << 10) / ((1000000 * mdi->divisions) / tempo); + } + } +} + + +void +do_null (unsigned char ch, struct _mdi *mdi, unsigned long int ptr) { + MIDI_EVENT_DEBUG(__FUNCTION__,ch); +}; + + +void +WM_ResetToStart(midi * handle) { + struct _mdi *mdi = (struct _mdi *)handle; + int i; + + mdi->index_count = 0; + mdi->samples_per_delta = (WM_SampleRate << 10) / (2 * mdi->divisions); + mdi->samples_to_mix = 0; + mdi->info.current_sample= 0; + + for (i=0; i<16; i++) { + mdi->channel[i].bank = 0; + mdi->channel[i].patch = NULL; + mdi->channel[i].hold = 0; + mdi->channel[i].volume = 100; + mdi->channel[i].pressure = 127; + mdi->channel[i].expression = 127; + mdi->channel[i].balance = 0; + mdi->channel[i].pan = 0; + mdi->channel[i].left_adjust = 1.0; + mdi->channel[i].right_adjust = 1.0; + mdi->channel[i].pitch = 0; + mdi->channel[i].pitch_range = 200; + mdi->channel[i].reg_data = 0xFFFF; + } +} + +void +WM_RecalcSamples (midi * handle) { + struct _mdi *mdi = (struct _mdi *)handle; + struct _note **note_data = mdi->note; + unsigned long int total_samples = 0; + unsigned long int count_a; + unsigned long int count_b; + unsigned long int env_level; + + if (note_data != mdi->last_note) { + do { + env_level = (*note_data)->env_level; + count_a = 0; + count_b = 0; + if ((*note_data)->env < 4) { + if (env_level > (*note_data)->sample->env_target[3]) { + count_a += (env_level - (*note_data)->sample->env_target[3] + (*note_data)->sample->env_rate[3] - 1) / (*note_data)->sample->env_rate[3]; + } else { + count_a += ((*note_data)->sample->env_target[3] - env_level + (*note_data)->sample->env_rate[3] - 1) / (*note_data)->sample->env_rate[3]; + } + env_level = (*note_data)->sample->env_target[3]; + } + if ((*note_data)->env < 5) { + if (env_level > (*note_data)->sample->env_target[4]) { + count_a += (env_level - (*note_data)->sample->env_target[4] + (*note_data)->sample->env_rate[4] - 1) / (*note_data)->sample->env_rate[4]; + } else { + count_a += ((*note_data)->sample->env_target[4] - env_level + (*note_data)->sample->env_rate[4] - 1) / (*note_data)->sample->env_rate[4]; + } + env_level = (*note_data)->sample->env_target[4]; + } + if ((*note_data)->env < 6) { + if (env_level > (*note_data)->sample->env_target[5]) { + count_a += (env_level - (*note_data)->sample->env_target[5] + (*note_data)->sample->env_rate[5] - 1) / (*note_data)->sample->env_rate[5]; + } else { + count_a += ((*note_data)->sample->env_target[5] - env_level + (*note_data)->sample->env_rate[5] - 1) / (*note_data)->sample->env_rate[5]; + } + env_level = (*note_data)->sample->env_target[5]; + } + if ((*note_data)->env == 6) { + count_a = (env_level + (*note_data)->sample->env_rate[6] - 1) / (*note_data)->sample->env_rate[6]; + env_level = (*note_data)->sample->env_target[6]; + } + if (env_level != 0) { + if ((*note_data)->modes & SAMPLE_LOOP) { + unsigned long int smpl_pos = (*note_data)->sample_pos + (count_a * (*note_data)->sample_inc); + if (smpl_pos > ((*note_data)->sample->loop_end << 10)) { + while (smpl_pos > ((*note_data)->sample->loop_end << 10)) { + smpl_pos -= ((*note_data)->sample->loop_end - (*note_data)->sample->loop_start) << 10; + } + count_a += (((*note_data)->sample->data_length << 10) - smpl_pos + (*note_data)->sample_inc - 1) / (*note_data)->sample_inc; + } + } else { + count_b = (((*note_data)->sample->data_length << 10) - (*note_data)->sample_pos + (*note_data)->sample_inc - 1) / (*note_data)->sample_inc; + } + if (count_b != 0) { + if (count_b < count_a) { + if (total_samples < count_b) + total_samples = count_b; + } else { + if (total_samples < count_a) + total_samples = count_a; + } + } else { + if (total_samples < count_a) + total_samples = count_a; + } + } else { + if (!((*note_data)->modes & SAMPLE_LOOP)) { + count_b = (((*note_data)->sample->data_length << 10) - (*note_data)->sample_pos) / (*note_data)->sample_inc; + if (count_b < count_a) { + if (total_samples < count_b) + total_samples = count_b; + } else { + if (total_samples < count_a) + total_samples = count_a; + } + } else { + if (total_samples < count_a) + total_samples = count_a; + } + } + note_data++; + } while (note_data != mdi->last_note); + } + mdi->info.approx_total_samples += total_samples; + mdi->recalc_samples = 0; +} + +void +do_amp_setup_note_off (unsigned char ch, struct _mdi *mdi, struct _miditrack *track) { + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + + mdi->lin_cur_vol -= (lin_volume[mdi->note_vel[ch][mdi->data[track->ptr]]] * + lin_volume[mdi->ch_exp[ch]] * lin_volume[mdi->ch_vol[ch]]) / 1048576; + mdi->log_cur_vol -= (sqr_volume[mdi->note_vel[ch][mdi->data[track->ptr]]] * + log_volume[mdi->ch_exp[ch]] * log_volume[mdi->ch_vol[ch]]) / 1048576; + + mdi->note_vel[ch][mdi->data[track->ptr]] = 0; + + track->running_event = 0x80 | ch; + track->ptr += 2; + return; +} + +void +do_amp_setup_note_on (unsigned char ch, struct _mdi *mdi, struct _miditrack *track) { + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + if (mdi->data[track->ptr+1] == 0x00) { + do_amp_setup_note_off(ch, mdi, track); + track->running_event = 0x90 | ch; + return; + } + + if (mdi->note_vel[ch][mdi->data[track->ptr]]) { + mdi->lin_cur_vol -= (lin_volume[mdi->note_vel[ch][mdi->data[track->ptr]]] * + lin_volume[mdi->ch_exp[ch]] * lin_volume[mdi->ch_vol[ch]]) / 1048576; + mdi->log_cur_vol -= (sqr_volume[mdi->note_vel[ch][mdi->data[track->ptr]]] * + log_volume[mdi->ch_exp[ch]] * log_volume[mdi->ch_vol[ch]]) / 1048576; + } + + mdi->note_vel[ch][mdi->data[track->ptr]] = mdi->data[track->ptr+1]; + + mdi->lin_cur_vol += (lin_volume[mdi->note_vel[ch][mdi->data[track->ptr]]] * + lin_volume[mdi->ch_exp[ch]] * lin_volume[mdi->ch_vol[ch]]) / 1048576; + mdi->log_cur_vol += (sqr_volume[mdi->note_vel[ch][mdi->data[track->ptr]]] * + log_volume[mdi->ch_exp[ch]] * log_volume[mdi->ch_vol[ch]]) / 1048576; + + if (mdi->lin_cur_vol > mdi->lin_max_vol) { + mdi->lin_max_vol = mdi->lin_cur_vol; + } + if (mdi->log_cur_vol > mdi->log_max_vol) { + mdi->log_max_vol = mdi->log_cur_vol; + } + if (ch == 9) { + load_patch(mdi, ((mdi->channel[ch].bank << 8) | (mdi->data[track->ptr] | 0x80))); + } + + track->running_event = 0x90 | ch; + track->ptr += 2; + return; +} + +void +do_amp_setup_aftertouch (unsigned char ch, struct _mdi *mdi, struct _miditrack *track) { + unsigned char pres = mdi->data[track->ptr+1]; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + + if (pres == 0) + pres = 1; + + if (mdi->note_vel[ch][mdi->data[track->ptr]] != 0) { + mdi->lin_cur_vol -= (lin_volume[mdi->note_vel[ch][mdi->data[track->ptr]]] * + lin_volume[mdi->ch_exp[ch]] * lin_volume[mdi->ch_vol[ch]]) / 1048576; + mdi->log_cur_vol -= (sqr_volume[mdi->note_vel[ch][mdi->data[track->ptr]]] * + log_volume[mdi->ch_exp[ch]] * log_volume[mdi->ch_vol[ch]]) / 1048576; + + mdi->note_vel[ch][mdi->data[track->ptr]] = pres; + + mdi->lin_cur_vol += (lin_volume[mdi->note_vel[ch][mdi->data[track->ptr]]] * + lin_volume[mdi->ch_exp[ch]] * lin_volume[mdi->ch_vol[ch]]) / 1048576; + mdi->log_cur_vol += (sqr_volume[mdi->note_vel[ch][mdi->data[track->ptr]]] * + log_volume[mdi->ch_exp[ch]] * log_volume[mdi->ch_vol[ch]]) / 1048576; + + if (mdi->lin_cur_vol > mdi->lin_max_vol) { + mdi->lin_max_vol = mdi->lin_cur_vol; + } + if (mdi->log_cur_vol > mdi->log_max_vol) { + mdi->log_max_vol = mdi->log_cur_vol; + } + } + track->running_event = 0xA0 | ch; + track->ptr += 2; + return; +} + + +void +do_amp_setup_control (unsigned char ch, struct _mdi *mdi, struct _miditrack *track) { + int i; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + if (mdi->data[track->ptr] == 0x00) { + mdi->channel[ch].bank = mdi->data[track->ptr + 1]; + } else if (mdi->data[track->ptr] == 0x07) { + for (i=0; i < 128; i++) { + if (mdi->note_vel[ch][i] == 0) + continue; + mdi->lin_cur_vol -= (lin_volume[mdi->note_vel[ch][i]] * + lin_volume[mdi->ch_exp[ch]] * lin_volume[mdi->ch_vol[ch]]) / 1048576; + mdi->log_cur_vol -= (sqr_volume[mdi->note_vel[ch][i]] * + log_volume[mdi->ch_exp[ch]] * log_volume[mdi->ch_vol[ch]]) / 1048576; + + mdi->lin_cur_vol += (lin_volume[mdi->note_vel[ch][i]] * + lin_volume[mdi->ch_exp[ch]] * lin_volume[mdi->data[track->ptr + 1]]) / 1048576; + mdi->log_cur_vol += (sqr_volume[mdi->note_vel[ch][i]] * + log_volume[mdi->ch_exp[ch]] * log_volume[mdi->data[track->ptr + 1]]) / 1048576; + + } + mdi->ch_vol[ch] = mdi->data[track->ptr + 1]; + if (mdi->lin_cur_vol > mdi->lin_max_vol) { + mdi->lin_max_vol = mdi->lin_cur_vol; + } + if (mdi->log_cur_vol > mdi->log_max_vol) { + mdi->log_max_vol = mdi->log_cur_vol; + } + } else if (mdi->data[track->ptr] == 0x0B) { + for (i=0; i < 128; i++) { + if (mdi->note_vel[ch][i] == 0) + continue; + mdi->lin_cur_vol -= (lin_volume[mdi->note_vel[ch][i]] * + lin_volume[mdi->ch_vol[ch]] * lin_volume[mdi->ch_exp[ch]]) / 1048576; + mdi->log_cur_vol -= (sqr_volume[mdi->note_vel[ch][i]] * + log_volume[mdi->ch_vol[ch]] * log_volume[mdi->ch_exp[ch]]) / 1048576; + + mdi->lin_cur_vol += (lin_volume[mdi->note_vel[ch][i]] * + lin_volume[mdi->ch_vol[ch]] * lin_volume[mdi->data[track->ptr + 1]]) / 1048576; + mdi->log_cur_vol += (sqr_volume[mdi->note_vel[ch][i]] * + log_volume[mdi->ch_vol[ch]] * log_volume[mdi->data[track->ptr + 1]]) / 1048576; + + } + mdi->ch_exp[ch] = mdi->data[track->ptr + 1]; + if (mdi->lin_cur_vol > mdi->lin_max_vol) { + mdi->lin_max_vol = mdi->lin_cur_vol; + } + if (mdi->log_cur_vol > mdi->log_max_vol) { + mdi->log_max_vol = mdi->log_cur_vol; + } + } + + track->running_event = 0xB0 | ch; + track->ptr += 2; + return; +} + +void +do_amp_setup_patch (unsigned char ch, struct _mdi *mdi, struct _miditrack *track) { + if (ch == 9) { + mdi->channel[ch].bank = mdi->data[track->ptr]; + } else { + load_patch(mdi, ((mdi->channel[ch].bank << 8) | mdi->data[track->ptr])); + } + track->running_event = 0xC0 | ch; + track->ptr++; + return; +} + +void +do_amp_setup_channel_pressure (unsigned char ch, struct _mdi *mdi, struct _miditrack *track) { + int i; + unsigned char pres = mdi->data[track->ptr]; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + + if (pres == 0) + pres = 1; + + for (i=0; i < 128; i++) { + if (mdi->note_vel[ch][i] == 0) + continue; + mdi->lin_cur_vol -= (lin_volume[mdi->note_vel[ch][i]] * + lin_volume[mdi->ch_exp[ch]] * lin_volume[mdi->ch_vol[ch]]) / 1048576; + mdi->log_cur_vol -= (sqr_volume[mdi->note_vel[ch][i]] * + log_volume[mdi->ch_exp[ch]] * log_volume[mdi->ch_vol[ch]]) / 1048576; + + mdi->note_vel[ch][i] = pres; + + mdi->lin_cur_vol += (lin_volume[mdi->note_vel[ch][i]] * + lin_volume[mdi->ch_exp[ch]] * lin_volume[mdi->ch_vol[ch]]) / 1048576; + mdi->log_cur_vol += (sqr_volume[mdi->note_vel[ch][i]] * + log_volume[mdi->ch_exp[ch]] * log_volume[mdi->ch_vol[ch]]) / 1048576; + + } + if (mdi->lin_cur_vol > mdi->lin_max_vol) { + mdi->lin_max_vol = mdi->lin_cur_vol; + } + if (mdi->log_cur_vol > mdi->log_max_vol) { + mdi->log_max_vol = mdi->log_cur_vol; + } + track->running_event = 0xD0 | ch; + track->ptr++; + return; +} + +void +do_amp_setup_pitch (unsigned char ch, struct _mdi *mdi, struct _miditrack *track) { + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + track->running_event = 0xE0 | ch; + track->ptr += 2; + return; +} + +void +do_amp_setup_message (unsigned char ch, struct _mdi *mdi, struct _miditrack *track) { + unsigned long int event_length; + unsigned char event_type = 0xF0 | ch; + unsigned char event_data = mdi->data[track->ptr]; + static unsigned long int tempo = 500000; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + if (event_type == 0xF0) { + track->running_event = 0x00; + do { + track->ptr++; + } while ( mdi->data[track->ptr] != 0xF7); + track->ptr++; + } else { + track->ptr += 1; + event_length = read_var_length(mdi, track); + if (event_length == 0xFFFFFFFF) { + track->delta = 0xFFFFFFFF; + return; + } + if (event_type == 0xFF) { + if ((event_data == 0x2F) && (event_length == 0)) { // Track End + track->EOT = 1; + return; + } else if ((event_data == 0x51) && (event_length == 3)) { // Tempo Change + tempo = (mdi->data[track->ptr] << 16) | (mdi->data[track->ptr+1] << 8) | mdi->data[track->ptr+2]; + if (tempo == 0) + mdi->samples_per_delta = (WM_SampleRate << 10) / (2 * mdi->divisions); + else + mdi->samples_per_delta = (WM_SampleRate << 10) / ((1000000 * mdi->divisions) / tempo); + } + } + track->ptr += event_length; + } +} + + +struct _mdi * +WM_ParseNewMidi(unsigned char *mididata, unsigned long int midisize ) { + int i; + unsigned char eot[] = { 0xff, 0x2f, 0x00}; + unsigned long int index_count = 0; + unsigned long int temp_delta = 0xffffff00; + struct _mdi *mdi = NULL; + void (*do_event[])(unsigned char ch, struct _mdi *midifile, struct _miditrack *track) = { + *do_amp_setup_note_off, + *do_amp_setup_note_on, + *do_amp_setup_aftertouch, + *do_amp_setup_control, + *do_amp_setup_patch, + *do_amp_setup_channel_pressure, + *do_amp_setup_pitch, + *do_amp_setup_message + }; + unsigned long int midiofs = 0; + unsigned long int midi_track_counter = 0; + unsigned long int last_delta; + unsigned long int minus_delta = 0; + unsigned char current_event = 0; + unsigned short int no_tracks; + unsigned long int EOT_count = 0; + struct _miditrack *tmp_trackdata; + struct _hndl *tmp_handle = NULL; + + mdi = malloc(sizeof(struct _mdi)); + if (mdi == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM," to parse midi data", errno); + free (mididata); + return NULL; + } + + // Initialize data + memset(mdi, 0, sizeof(struct _mdi)); + mdi->lock = 0; + mdi->data = mididata; + mdi->size = midisize; + mdi->info.mixer_options = WM_MixerOptions; + + mdi->index = malloc(((midisize / 2) + 1)* sizeof(struct _mdi_index)); + if (mdi->index == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM," to parse midi data", errno); + free(mdi); + return NULL; + } + + load_patch(mdi, 0x0000); + + for (i=0; i<16; i++) { + mdi->channel[i].volume = 100; + mdi->channel[i].pressure = 127; + mdi->channel[i].expression = 127; + mdi->channel[i].pitch_range = 200; + mdi->channel[i].reg_data = 0xFFFF; + mdi->ch_vol[i] = 100; + mdi->ch_exp[i] = 127; + mdi->channel[i].patch = get_patch_data(mdi, 0x0000); + } + + midiofs = 0; + if (strncmp(mididata,"RIFF",4) == 0) + midiofs = 20; + + if (strncmp(&mididata[midiofs],"MThd",4) != 0) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID,"(not a midi file)", 0); + free(mdi->index); + free(mdi); + return NULL; + } + + if ((midiofs + 25) > midisize) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT,"(too short)", 0); + free(mdi->index); + free(mdi); + return NULL; + } + + midiofs += 9; + if (mididata[midiofs] > 1) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, NULL, 0); + free(mdi->index); + free(mdi); + return NULL; + } + midiofs++; + + no_tracks = mididata[midiofs] << 8 | mididata[midiofs+1]; + midiofs += 2; + + mdi->divisions = mididata[midiofs] << 8 | mididata[midiofs+1]; + mdi->samples_per_delta = (WM_SampleRate << 10) / (2 * mdi->divisions); + midiofs += 2; + + tmp_trackdata = calloc(no_tracks, sizeof(struct _miditrack)); + + if (first_handle == NULL) { + first_handle = malloc(sizeof(struct _hndl)); + if (first_handle == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM," to parse midi data", errno); + free(mdi->data); + free(mdi); + return NULL; + } + first_handle->handle = (void *)mdi; + first_handle->prev = NULL; + first_handle->next = NULL; + } else { + tmp_handle = first_handle; + if (tmp_handle->next != NULL) { + while (tmp_handle->next != NULL) + tmp_handle = tmp_handle->next; + } + tmp_handle->next = malloc(sizeof(struct _hndl)); + if (tmp_handle->next == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM," to parse midi data", errno); + free(tmp_trackdata); + free(mdi->data); + free(mdi); + return NULL; + } + tmp_handle->next->prev = tmp_handle; + tmp_handle = tmp_handle->next; + tmp_handle->next = NULL; + tmp_handle->handle = (void *)mdi; + } + + + // grab track offsets; + + midi_track_counter = 0; + while (midi_track_counter != no_tracks) { + if ((midiofs + 12) > midisize) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0); + WildMidi_Close(mdi); + free(tmp_trackdata); + return NULL; + } + if (strncmp(&mididata[midiofs],"MTrk",4) != 0) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(Expected track header)", 0); + WildMidi_Close(mdi); + free(tmp_trackdata); + return NULL; + } + midiofs += 4; + tmp_trackdata[midi_track_counter].length = mididata[midiofs] << 24 | mididata[midiofs+1] << 16 | mididata[midiofs+2] << 8 | mididata[midiofs+3]; + midiofs += 4; + + if (midisize < (midiofs + tmp_trackdata[midi_track_counter].length)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0); + WildMidi_Close(mdi); + free(tmp_trackdata); + return NULL; + } + tmp_trackdata[midi_track_counter].ptr = midiofs; + tmp_trackdata[midi_track_counter].EOT = 0; + tmp_trackdata[midi_track_counter].running_event = 0; + tmp_trackdata[midi_track_counter].delta = read_var_length(mdi, &tmp_trackdata[midi_track_counter]); + if (tmp_trackdata[midi_track_counter].delta == 0xFFFFFFFF) { + WildMidi_Close(mdi); + free(tmp_trackdata); + return NULL; + } + midiofs += tmp_trackdata[midi_track_counter].length; + + if (memcmp(&mididata[midiofs-3], eot,3) != 0) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(Expected EOT)", 0); + WildMidi_Close(mdi); + free(tmp_trackdata); + return NULL; + } + if (tmp_trackdata[midi_track_counter].delta < temp_delta) { + temp_delta = tmp_trackdata[midi_track_counter].delta; + } + midi_track_counter++; + } + +// set midi info + mdi->index[0].offset = 0; + mdi->index[0].delta = temp_delta; + + while (EOT_count != no_tracks) { + last_delta = 0; + for (i = 0; i < no_tracks; i++) { + if (tmp_trackdata[i].EOT) { + continue; + } + if (tmp_trackdata[i].delta) { + tmp_trackdata[i].delta -= minus_delta; + if (tmp_trackdata[i].delta) { + if ((last_delta == 0) || (last_delta > tmp_trackdata[i].delta)) { + last_delta = tmp_trackdata[i].delta; + } + continue; + } + } + do { + if (mdi->data[tmp_trackdata[i].ptr] < 0x80) { + current_event = tmp_trackdata[i].running_event; + if (current_event < 0x80) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(expected event)", 0); + WildMidi_Close(mdi); + free(tmp_trackdata); + return NULL; + } + } else { + current_event = mdi->data[tmp_trackdata[i].ptr]; + tmp_trackdata[i].ptr++; + } + + + index_count++; + mdi->index[index_count].offset = tmp_trackdata[i].ptr; + mdi->index[index_count].delta = 0; + mdi->index[index_count].event = current_event; + + do_event[((current_event & 0xF0) >> 4) - 8]((current_event & 0x0F), mdi, &tmp_trackdata[i]); + if (tmp_trackdata[i].delta == 0xFFFFFFFF) { + WildMidi_Close(mdi); + free(tmp_trackdata); + return NULL; + } + if (tmp_trackdata[i].EOT) { + EOT_count++; + break; + } + tmp_trackdata[i].delta = read_var_length(mdi, &tmp_trackdata[i]); + if (tmp_trackdata[i].delta == 0xFFFFFFFF) { + WildMidi_Close(mdi); + free(tmp_trackdata); + return NULL; + } + } while (!(tmp_trackdata[i].delta)); + if ((last_delta == 0) || (last_delta > tmp_trackdata[i].delta)) { + if (tmp_trackdata[i].delta != 0) { + last_delta = tmp_trackdata[i].delta; + } + } + } +// printf("\rLast Delta %lu\n",last_delta); + mdi->index[index_count].delta = last_delta; + mdi->samples_to_mix += last_delta * mdi->samples_per_delta; + mdi->sample_count += mdi->samples_to_mix >> 10; + mdi->samples_to_mix %= 1024; + minus_delta = last_delta; + } + mdi->sample_count -= (mdi->index[index_count - 1].delta * mdi->samples_per_delta) >> 10; + mdi->index[index_count - 1].delta = 0; + mdi->index_size = index_count; + mdi->index = realloc(mdi->index, (sizeof(struct _mdi_index) * mdi->index_size)); + if (mdi->index == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, errno); + WildMidi_Close(mdi); + free(tmp_trackdata); + return NULL; + } + mdi->info.approx_total_samples = mdi->sample_count + 1; + mdi->samples_to_mix = 0; + mdi->sample_count = 0; + mdi->info.current_sample = 0; + mdi->samples_per_delta = (WM_SampleRate << 10) / (2 * mdi->divisions); + mdi->recalc_samples = 1; + mdi->last_note = mdi->note; + if (mdi->info.mixer_options & WM_MO_LINEAR_VOLUME) { + mdi->amp = 281; + } else { + mdi->amp = 281 * mdi->lin_max_vol / mdi->log_max_vol; + } + + for (i = 0; i < 16; i++) { + mdi->channel[i].bank = 0; + do_pan_adjust(mdi, i); + } + + for (i = 0; i < 4; i++) { + mdi->filter.lowpass[i][0].in[0] = 0; + mdi->filter.lowpass[i][0].in[1] = 0; + mdi->filter.lowpass[i][1].in[0] = 0; + mdi->filter.lowpass[i][1].in[1] = 0; + + mdi->filter.lowpass[i][0].out[0] = 0; + mdi->filter.lowpass[i][0].out[1] = 0; + mdi->filter.lowpass[i][1].out[0] = 0; + mdi->filter.lowpass[i][1].out[1] = 0; + + mdi->filter.delay_pos[i][0] = 0; + mdi->filter.delay_pos[i][1] = 0; + + mdi->filter.delay[i][0] = malloc(delay_size[i][0] * sizeof(signed long int)); + mdi->filter.delay[i][1] = malloc(delay_size[i][1] * sizeof(signed long int)); + memset (mdi->filter.delay[i][0], 0, (delay_size[i][0] * sizeof(signed long int))); + memset (mdi->filter.delay[i][1], 0, (delay_size[i][1] * sizeof(signed long int))); + + } + mdi->filter.in[0][0] = 0; + mdi->filter.in[0][1] = 0; + mdi->filter.in[1][0] = 0; + mdi->filter.in[1][1] = 0; + mdi->filter.out[0][0] = 0; + mdi->filter.out[0][1] = 0; + mdi->filter.out[1][0] = 0; + mdi->filter.out[1][1] = 0; + + free(tmp_trackdata); + return (mdi); +} + +/* + * ========================= + * External Functions + * ========================= + */ + +const char * +WildMidi_GetString (unsigned short int info) { + switch (info) { + case WM_GS_VERSION: + return WM_Version; + } + return NULL; +} + +int +WildMidi_Init (const char * config_file, unsigned short int rate, unsigned short int options) { + if (WM_Initialized) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return -1; + } + + if (config_file == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL config file pointer)", 0); + return -1; + } + WM_InitPatches(); + if (WM_LoadConfig(config_file) == -1) { + return -1; + } + + if (options & 0xFFD8) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(invalid option)", 0); + WM_FreePatches(); + return -1; + } + WM_MixerOptions = options; + + if ((rate < 11000) || (rate > 65000)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(rate out of bounds, range is 11000 - 65000)", 0); + WM_FreePatches(); + return -1; + } + WM_SampleRate = rate; + WM_Initialized = 1; + patch_lock = 0; + + init_gauss(); + init_lowpass(); + return 0; +} + +int +WildMidi_MasterVolume (unsigned char master_volume) { + struct _mdi *mdi = NULL; + struct _hndl * tmp_handle = first_handle; + int i = 0; + + if (!WM_Initialized) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return -1; + } + if (master_volume > 127) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(master volume out of range, range is 0-127)", 0); + return -1; + } + + WM_MasterVolume = lin_volume[master_volume]; + + if (tmp_handle != NULL) { + while(tmp_handle != NULL) { + mdi = (struct _mdi *)tmp_handle->handle; + for (i = 0; i < 16; i++) { + do_pan_adjust(mdi, i); + } + tmp_handle = tmp_handle->next; + } + } + + return 0; +} + +int +WildMidi_Close (midi * handle) { + struct _mdi *mdi = (struct _mdi *)handle; + struct _hndl * tmp_handle; + struct _sample *tmp_sample; + int i; + + if (!WM_Initialized) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return -1; + } + if (handle == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", 0); + return -1; + } + if (first_handle == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(no midi's open)", 0); + return -1; + } + WM_Lock(&mdi->lock); + if (first_handle->handle == handle) { + tmp_handle = first_handle->next; + free (first_handle); + first_handle = tmp_handle; + if (first_handle != NULL) + first_handle->prev = NULL; + } else { + tmp_handle = first_handle; + while (tmp_handle->handle != handle) { + tmp_handle = tmp_handle->next; + if (tmp_handle == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(handle does not exist)", 0); + return -1; + } + } + tmp_handle->prev->next = tmp_handle->next; + if (tmp_handle->next != NULL) { + tmp_handle->next->prev = tmp_handle->prev; + } + free (tmp_handle); + } + + if (mdi->patch_count != 0) { + WM_Lock(&patch_lock); + for (i = 0; i < mdi->patch_count; i++) { + mdi->patches[i]->inuse_count--; + if (mdi->patches[i]->inuse_count == 0) { + //free samples here + if (mdi->patches[i]->first_sample != NULL) { + while (mdi->patches[i]->first_sample != NULL) { + tmp_sample = mdi->patches[i]->first_sample->next; + if (mdi->patches[i]->first_sample->data) + free(mdi->patches[i]->first_sample->data); + free(mdi->patches[i]->first_sample); + mdi->patches[i]->first_sample = tmp_sample; + } + mdi->patches[i]->loaded = 0; + } + } + } + WM_Unlock(&patch_lock); + free (mdi->patches); + } + if (mdi->data != NULL) { + free (mdi->data); + } + if (mdi->tmp_info != NULL) { + free (mdi->tmp_info); + } + if (mdi->index != NULL) + free (mdi->index); + free (mdi); + // no need to unlock cause the struct containing the lock no-longer exists; + return 0; +} + +midi * +WildMidi_Open (const char *midifile) { + unsigned char *mididata = NULL; + unsigned long int midisize = 0; + + if (!WM_Initialized) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return NULL; + } + if (midifile == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL filename)", 0); + return NULL; + } + + if ((mididata = WM_BufferFile(midifile, &midisize)) == NULL) { + return NULL; + } + + return (void *)WM_ParseNewMidi(mididata,midisize); +} + +midi * +WildMidi_OpenBuffer (unsigned char *midibuffer, unsigned long int size) { + if (!WM_Initialized) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return NULL; + } + if (midibuffer == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL midi data buffer)", 0); + return NULL; + } + + return (void *)WM_ParseNewMidi(midibuffer,size); +} + +int +WildMidi_LoadSamples( midi * handle) { + return 0; +} + +int +WildMidi_FastSeek ( midi * handle, unsigned long int *sample_pos) { + struct _mdi *mdi = (struct _mdi *)handle; + struct _note **note_data = mdi->note; + void (*do_event[])(unsigned char ch, struct _mdi *midifile, unsigned long int ptr) = { + *do_null, + *do_null, + *do_aftertouch, + *do_control, + *do_patch, + *do_channel_pressure, + *do_pitch, + *do_message + }; + unsigned long int real_samples_to_mix = 0; + + if (!WM_Initialized) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return -1; + } + if (handle == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", 0); + return -1; + } + WM_Lock(&mdi->lock); + if (sample_pos == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL seek position pointer)", 0); + WM_Unlock(&mdi->lock); + return -1; + } + + if (*sample_pos == mdi->info.current_sample) { + WM_Unlock(&mdi->lock); + return 0; + } + + if (*sample_pos > mdi->info.current_sample) { + if ((mdi->sample_count == 0) && (mdi->index_count == mdi->index_size) && (mdi->last_note == 0)) { + *sample_pos = mdi->info.current_sample; + WM_Unlock(&mdi->lock); + return 0; + } + } else { + WM_ResetToStart(handle); + } + + //reset all notes + if (note_data != mdi->last_note) { + do { + (*note_data)->active = 0; + *note_data = NULL; + note_data++; + } while (note_data != mdi->last_note); + mdi->last_note = mdi->note; + } + + while (*sample_pos != mdi->info.current_sample) { + if (!mdi->sample_count) { + if (mdi->index_count != mdi->index_size) { + + do { + if (mdi->index_count == mdi->index_size) { + break; + } + + if (mdi->index_count != 0) { + do_event[((mdi->index[mdi->index_count].event & 0xF0) >> 4) - 8]((mdi->index[mdi->index_count].event & 0x0F), mdi, mdi->index[mdi->index_count].offset); + } + } while (mdi->index[mdi->index_count++].delta == 0); + + mdi->samples_to_mix += mdi->index[mdi->index_count-1].delta * mdi->samples_per_delta; + mdi->sample_count = mdi->samples_to_mix >> 10; + mdi->samples_to_mix %= 1024; + } else { + mdi->sample_count = WM_SampleRate; + } + } + + if (mdi->sample_count <= (*sample_pos - mdi->info.current_sample)) { + real_samples_to_mix = mdi->sample_count; + if (real_samples_to_mix == 0) { + continue; + } + } else { + real_samples_to_mix = (*sample_pos - mdi->info.current_sample); + } + + mdi->info.current_sample += real_samples_to_mix; + mdi->sample_count -= real_samples_to_mix; + if ((mdi->index_count == mdi->index_size) && (mdi->last_note == 0)) { + mdi->sample_count = 0; + *sample_pos = mdi->info.current_sample; + WM_Unlock(&mdi->lock); + return 0; + } + } + WM_Unlock(&mdi->lock); + return 0; +} + +int +WildMidi_SampledSeek ( midi * handle, unsigned long int *sample_pos) { + struct _mdi *mdi = (struct _mdi *)handle; + struct _note **note_data = mdi->note; + unsigned long int real_samples_to_mix = 0; + unsigned long int tmp_samples_to_mix = 0; + + if (!WM_Initialized) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return -1; + } + if (handle == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", 0); + return -1; + } + WM_Lock(&mdi->lock); + if (sample_pos == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL seek position pointer)", 0); + WM_Unlock(&mdi->lock); + return -1; + } + + if (*sample_pos == mdi->info.current_sample) { + WM_Unlock(&mdi->lock); + return 0; + } + + if (*sample_pos > mdi->info.current_sample) { + if ((mdi->sample_count == 0) && (mdi->index_count == mdi->index_size) && (mdi->last_note == 0)) { + *sample_pos = mdi->info.current_sample; + WM_Unlock(&mdi->lock); + return 0; + } + } else { + WM_ResetToStart(handle); + if (note_data != mdi->last_note) { + do { + (*note_data)->active = 0; + *note_data = NULL; + note_data++; + } while (note_data != mdi->last_note); + mdi->last_note = mdi->note; + } + } + + while (*sample_pos != mdi->info.current_sample) { + if (!mdi->sample_count) { + if (mdi->index_count != mdi->index_size) { + + do { + if (mdi->index_count == mdi->index_size) { + break; + } + + if (mdi->index_count != 0) { + do_event[((mdi->index[mdi->index_count].event & 0xF0) >> 4) - 8]((mdi->index[mdi->index_count].event & 0x0F), mdi, mdi->index[mdi->index_count].offset); + } + } while (mdi->index[mdi->index_count++].delta == 0); + + mdi->samples_to_mix += mdi->index[mdi->index_count-1].delta * mdi->samples_per_delta; + mdi->sample_count = mdi->samples_to_mix >> 10; + mdi->samples_to_mix %= 1024; + } else { + if (mdi->recalc_samples) { + WM_RecalcSamples(mdi); + } + mdi->sample_count = mdi->info.approx_total_samples - mdi->info.current_sample; + if (mdi->sample_count == 0) { + WM_Unlock(&mdi->lock); + return 0; + } + } + } + + if (mdi->sample_count <= (*sample_pos - mdi->info.current_sample)) { + real_samples_to_mix = mdi->sample_count; + if (real_samples_to_mix == 0) { + continue; + } + } else { + real_samples_to_mix = (*sample_pos - mdi->info.current_sample); + } + + // do mixing here + tmp_samples_to_mix = real_samples_to_mix; + do { + + if (mdi->last_note != mdi->note) { + note_data = mdi->note; + while (note_data != mdi->last_note) { + + +/* + * ======================== + * sample position checking + * ======================== + */ + (*note_data)->sample_pos += (*note_data)->sample_inc; + if (__builtin_expect(((*note_data)->sample_pos > (*note_data)->sample->loop_end), 0)) { + if ((*note_data)->modes & SAMPLE_LOOP) { + (*note_data)->sample_pos = (*note_data)->sample->loop_start + (((*note_data)->sample_pos - (*note_data)->sample->loop_start) % (*note_data)->sample->loop_size); + } else if (__builtin_expect(((*note_data)->sample_pos >= (*note_data)->sample->data_length), 0)) { + if (__builtin_expect(((*note_data)->next == NULL), 1)) { + goto KILL_NOTE; + } + goto RESTART_NOTE; + } + } + if (__builtin_expect(((*note_data)->env_inc == 0), 0)) { + note_data++; + continue; + } + (*note_data)->env_level += (*note_data)->env_inc; + if (__builtin_expect(((*note_data)->env_level > 4194304), 0)) { + (*note_data)->env_level = (*note_data)->sample->env_target[(*note_data)->env]; + } + if (__builtin_expect((((*note_data)->env_inc < 0) && + ((*note_data)->env_level > (*note_data)->sample->env_target[(*note_data)->env])) || + (((*note_data)->env_inc > 0) && + ((*note_data)->env_level < (*note_data)->sample->env_target[(*note_data)->env])), 1)) { + note_data++; + continue; + } + (*note_data)->env_level = (*note_data)->sample->env_target[(*note_data)->env]; + switch ((*note_data)->env) { + case 0: + if (!((*note_data)->modes & SAMPLE_ENVELOPE)) { + (*note_data)->env_inc = 0; + note_data++; + continue; + } + break; + case 2: + if ((*note_data)->modes & SAMPLE_SUSTAIN) { + (*note_data)->env_inc = 0; + note_data++; + continue; + } + break; + case 5: + if (__builtin_expect(((*note_data)->env_level == 0), 1)) { + goto KILL_NOTE; + } + // sample release + if ((*note_data)->modes & SAMPLE_LOOP) + (*note_data)->modes ^= SAMPLE_LOOP; + (*note_data)->env_inc = 0; + note_data++; + continue; + case 6: + if (__builtin_expect(((*note_data)->next != NULL), 1)) { + RESTART_NOTE: + (*note_data)->active = 0; + *note_data = (*note_data)->next; + (*note_data)->active = 1; + note_data++; + + } else { + KILL_NOTE: + (*note_data)->active = 0; + mdi->last_note--; + if (note_data != mdi->last_note) { + *note_data = *mdi->last_note; + } + } + continue; + } + (*note_data)->env++; + if ((*note_data)->env_level > (*note_data)->sample->env_target[(*note_data)->env]) { + (*note_data)->env_inc = -(*note_data)->sample->env_rate[(*note_data)->env]; + } else { + (*note_data)->env_inc = (*note_data)->sample->env_rate[(*note_data)->env]; + } + note_data++; + continue; + } + } else { + break; + } + } while (--tmp_samples_to_mix); + mdi->info.current_sample += real_samples_to_mix; + mdi->sample_count -= real_samples_to_mix; + if (mdi->index_count == mdi->index_size) { + if (mdi->last_note == 0) { + mdi->sample_count = 0; + *sample_pos = mdi->info.current_sample; + WM_Unlock(&mdi->lock); + return 0; + } + } + } + WM_Unlock(&mdi->lock); + return 0; +} + +int +WildMidi_GetOutput_Linear (midi * handle, char * buffer, unsigned long int size) { + unsigned long int buffer_used = 0; + struct _mdi *mdi = (struct _mdi *)handle; + unsigned long int real_samples_to_mix = 0; + unsigned long int data_pos; + signed long int premix, left_mix, right_mix; + signed long int vol_mul; + struct _note **note_data = NULL; + unsigned long int count; + + if (__builtin_expect((!WM_Initialized),0)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return -1; + } + if (__builtin_expect((handle == NULL),0)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", 0); + return -1; + } + if (__builtin_expect((buffer == NULL),0)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL buffer pointer)", 0); + return -1; + } + + if (__builtin_expect((size == 0),0)) { + return 0; + } + + if (__builtin_expect((size % 4),0)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(size not a multiple of 4)", 0); + return -1; + } + + WM_Lock(&mdi->lock); + if (__builtin_expect(((mdi->index_count == mdi->index_size) && (mdi->last_note == 0)), 0)) { + WM_Unlock(&mdi->lock); + return 0; + } + + buffer_used = 0; + memset(buffer, 0, size); + + do { + if (__builtin_expect((!mdi->sample_count),0)) { + if (__builtin_expect((mdi->index_count != mdi->index_size),1)) { + do { + if (__builtin_expect((mdi->index_count == mdi->index_size), 0)) { + break; + } + + if (__builtin_expect((mdi->index_count != 0), 1)) { + do_event[((mdi->index[mdi->index_count].event & 0xF0) >> 4) - 8]((mdi->index[mdi->index_count].event & 0x0F), mdi, mdi->index[mdi->index_count].offset); + } + } while (mdi->index[mdi->index_count++].delta == 0); + + mdi->samples_to_mix += mdi->index[mdi->index_count-1].delta * mdi->samples_per_delta; + mdi->sample_count = mdi->samples_to_mix >> 10; + mdi->samples_to_mix %= 1024; + } else { + if (mdi->recalc_samples) { + WM_RecalcSamples(mdi); + } + mdi->sample_count = mdi->info.approx_total_samples - mdi->info.current_sample; + if (mdi->sample_count == 0) { + WM_Unlock(&mdi->lock); + return buffer_used; + } + } + } + if (__builtin_expect((mdi->sample_count > (size >> 2)),1)) { + real_samples_to_mix = size >> 2; + } else { + real_samples_to_mix = mdi->sample_count; + if (real_samples_to_mix == 0) { + continue; + } + } + + // do mixing here + count = real_samples_to_mix; + do { + note_data = mdi->note; + left_mix = right_mix = 0; + if (__builtin_expect((mdi->last_note != mdi->note),1)) { + while (note_data != mdi->last_note) { +/* + * =================== + * resample the sample + * =================== + */ + data_pos = (*note_data)->sample_pos >> FPBITS; + vol_mul = (((*note_data)->vol_lvl * ((*note_data)->env_level >> 12)) >> FPBITS); + + premix = ((*note_data)->sample->data[data_pos] + + (((*note_data)->sample->data[data_pos + 1] - (*note_data)->sample->data[data_pos]) * + (signed long int)((*note_data)->sample_pos & FPMASK) >> FPBITS)) * vol_mul / 1024; + + left_mix += premix * mdi->channel[(*note_data)->noteid >> 8].left_adjust; + right_mix += premix * mdi->channel[(*note_data)->noteid >> 8].right_adjust; + +/* + * ======================== + * sample position checking + * ======================== + */ + (*note_data)->sample_pos += (*note_data)->sample_inc; + if (__builtin_expect(((*note_data)->sample_pos > (*note_data)->sample->loop_end), 0)) { + if ((*note_data)->modes & SAMPLE_LOOP) { + (*note_data)->sample_pos = (*note_data)->sample->loop_start + (((*note_data)->sample_pos - (*note_data)->sample->loop_start) % (*note_data)->sample->loop_size); + } else if (__builtin_expect(((*note_data)->sample_pos >= (*note_data)->sample->data_length), 0)) { + if (__builtin_expect(((*note_data)->next == NULL), 1)) { + goto KILL_NOTE; + + } + goto RESTART_NOTE; + } + } + + if (__builtin_expect(((*note_data)->env_inc == 0), 0)) { + note_data++; + continue; + } + + (*note_data)->env_level += (*note_data)->env_inc; + if (__builtin_expect(((*note_data)->env_level > 4194304), 0)) { + (*note_data)->env_level = (*note_data)->sample->env_target[(*note_data)->env]; + } + if (__builtin_expect((((*note_data)->env_inc < 0) && + ((*note_data)->env_level > (*note_data)->sample->env_target[(*note_data)->env])) || + (((*note_data)->env_inc > 0) && + ((*note_data)->env_level < (*note_data)->sample->env_target[(*note_data)->env])), 1)) { + note_data++; + continue; + } + + (*note_data)->env_level = (*note_data)->sample->env_target[(*note_data)->env]; + switch ((*note_data)->env) { + case 0: + if (!((*note_data)->modes & SAMPLE_ENVELOPE)) { + (*note_data)->env_inc = 0; + note_data++; + continue; + } + break; + case 2: + if ((*note_data)->modes & SAMPLE_SUSTAIN) { + (*note_data)->env_inc = 0; + note_data++; + continue; + } + break; + case 5: + if (__builtin_expect(((*note_data)->env_level == 0), 1)) { + goto KILL_NOTE; + } + // sample release + if ((*note_data)->modes & SAMPLE_LOOP) + (*note_data)->modes ^= SAMPLE_LOOP; + (*note_data)->env_inc = 0; + note_data++; + continue; + case 6: + if (__builtin_expect(((*note_data)->next != NULL), 1)) { + RESTART_NOTE: + (*note_data)->active = 0; + *note_data = (*note_data)->next; + (*note_data)->active = 1; + note_data++; + + } else { + KILL_NOTE: + (*note_data)->active = 0; + mdi->last_note--; + if (note_data != mdi->last_note) { + *note_data = *mdi->last_note; + } + } + continue; + } + (*note_data)->env++; + if ((*note_data)->env_level > (*note_data)->sample->env_target[(*note_data)->env]) { + (*note_data)->env_inc = -(*note_data)->sample->env_rate[(*note_data)->env]; + } else { + (*note_data)->env_inc = (*note_data)->sample->env_rate[(*note_data)->env]; + } + note_data++; + continue; + } +/* + * ========================= + * mix the channels together + * ========================= + */ + + left_mix /= 1024; + right_mix /= 1024; + } + +#ifdef EXPERIMENT_626 +/* + * ========================== + * Experimental Reverb Engine + * ========================== + */ + + if (mdi->info.mixer_options & WM_MO_REVERB) { + signed long int filteral = mdi->filter.delay[0][0][mdi->filter.delay_pos[0][0]]; + signed long int filterar = mdi->filter.delay[0][1][mdi->filter.delay_pos[0][1]]; + signed long int filterbl = mdi->filter.delay[1][0][mdi->filter.delay_pos[1][0]]; + signed long int filterbr = mdi->filter.delay[1][1][mdi->filter.delay_pos[1][1]]; + signed long int filtercl = mdi->filter.delay[2][0][mdi->filter.delay_pos[2][0]]; + signed long int filtercr = mdi->filter.delay[2][1][mdi->filter.delay_pos[2][1]]; + signed long int filterdl = mdi->filter.delay[3][0][mdi->filter.delay_pos[3][0]]; + signed long int filterdr = mdi->filter.delay[3][1][mdi->filter.delay_pos[3][1]]; + signed long int tfal = (a[0][0] * filteral + a[0][1] * mdi->filter.lowpass[0][0].in[0] + a[0][0] * mdi->filter.lowpass[0][0].in[1] - b[0][0] * mdi->filter.lowpass[0][0].out[0] - b[0][1] * mdi->filter.lowpass[0][0].out[1]) / 1024; + signed long int tfar = (a[0][0] * filterar + a[0][1] * mdi->filter.lowpass[0][1].in[0] + a[0][0] * mdi->filter.lowpass[0][1].in[1] - b[0][0] * mdi->filter.lowpass[0][1].out[0] - b[0][1] * mdi->filter.lowpass[0][1].out[1]) / 1024; + signed long int tfbl = (a[1][0] * filterbl + a[1][1] * mdi->filter.lowpass[1][0].in[0] + a[1][0] * mdi->filter.lowpass[1][0].in[1] - b[1][0] * mdi->filter.lowpass[1][0].out[0] - b[1][1] * mdi->filter.lowpass[1][0].out[1]) / 1024; + signed long int tfbr = (a[1][0] * filterbr + a[1][1] * mdi->filter.lowpass[1][1].in[0] + a[1][0] * mdi->filter.lowpass[1][1].in[1] - b[1][0] * mdi->filter.lowpass[1][1].out[0] - b[1][1] * mdi->filter.lowpass[1][1].out[1]) / 1024; + signed long int tfcl = (a[2][0] * filtercl + a[2][1] * mdi->filter.lowpass[2][0].in[0] + a[2][0] * mdi->filter.lowpass[2][0].in[1] - b[2][0] * mdi->filter.lowpass[2][0].out[0] - b[2][1] * mdi->filter.lowpass[2][0].out[1]) / 1024; + signed long int tfcr = (a[2][0] * filtercr + a[2][1] * mdi->filter.lowpass[2][1].in[0] + a[2][0] * mdi->filter.lowpass[2][1].in[1] - b[2][0] * mdi->filter.lowpass[2][1].out[0] - b[2][1] * mdi->filter.lowpass[2][1].out[1]) / 1024; + signed long int tfdl = (a[3][0] * filterdl + a[3][1] * mdi->filter.lowpass[3][0].in[0] + a[3][0] * mdi->filter.lowpass[3][0].in[1] - b[3][0] * mdi->filter.lowpass[3][0].out[0] - b[3][1] * mdi->filter.lowpass[3][0].out[1]) / 1024; + signed long int tfdr = (a[3][0] * filterdr + a[3][1] * mdi->filter.lowpass[3][1].in[0] + a[3][0] * mdi->filter.lowpass[3][1].in[1] - b[3][0] * mdi->filter.lowpass[3][1].out[0] - b[3][1] * mdi->filter.lowpass[3][1].out[1]) / 1024; + signed long int tfl, tflo; + signed long int tfr, tfro; + + mdi->filter.lowpass[0][0].in[1] = mdi->filter.lowpass[0][0].in[0]; + mdi->filter.lowpass[0][0].in[0] = filteral; + mdi->filter.lowpass[0][1].in[1] = mdi->filter.lowpass[0][1].in[0]; + mdi->filter.lowpass[0][1].in[0] = filterar; + mdi->filter.lowpass[1][0].in[1] = mdi->filter.lowpass[1][0].in[0]; + mdi->filter.lowpass[1][0].in[0] = filterbl; + mdi->filter.lowpass[1][1].in[1] = mdi->filter.lowpass[1][1].in[0]; + mdi->filter.lowpass[1][1].in[0] = filterbr; + mdi->filter.lowpass[2][0].in[1] = mdi->filter.lowpass[2][0].in[0]; + mdi->filter.lowpass[2][0].in[0] = filtercl; + mdi->filter.lowpass[2][1].in[1] = mdi->filter.lowpass[2][1].in[0]; + mdi->filter.lowpass[2][1].in[0] = filtercr; + mdi->filter.lowpass[3][0].in[1] = mdi->filter.lowpass[3][0].in[0]; + mdi->filter.lowpass[3][0].in[0] = filterdl; + mdi->filter.lowpass[3][1].in[1] = mdi->filter.lowpass[3][1].in[0]; + mdi->filter.lowpass[3][1].in[0] = filterdr; + + mdi->filter.lowpass[0][0].out[1] = mdi->filter.lowpass[0][0].out[0]; + mdi->filter.lowpass[0][0].out[0] = tfal; + mdi->filter.lowpass[0][1].out[1] = mdi->filter.lowpass[0][1].out[0]; + mdi->filter.lowpass[0][1].out[0] = tfar; + mdi->filter.lowpass[1][0].out[1] = mdi->filter.lowpass[1][0].out[0]; + mdi->filter.lowpass[1][0].out[0] = tfbl; + mdi->filter.lowpass[1][1].out[1] = mdi->filter.lowpass[1][1].out[0]; + mdi->filter.lowpass[1][1].out[0] = tfbr; + mdi->filter.lowpass[2][0].out[1] = mdi->filter.lowpass[2][0].out[0]; + mdi->filter.lowpass[2][0].out[0] = tfcl; + mdi->filter.lowpass[2][1].out[1] = mdi->filter.lowpass[2][1].out[0]; + mdi->filter.lowpass[2][1].out[0] = tfcr; + mdi->filter.lowpass[3][0].out[1] = mdi->filter.lowpass[3][0].out[0]; + mdi->filter.lowpass[3][0].out[0] = tfdl; + mdi->filter.lowpass[3][1].out[1] = mdi->filter.lowpass[3][1].out[0]; + mdi->filter.lowpass[3][1].out[0] = tfdr; + + mdi->filter.delay[0][0][mdi->filter.delay_pos[0][0]] = (tfbr * 405 + tfcr * 368) / 1024 + (left_mix * gain_in[0] / 1024); + mdi->filter.delay[0][1][mdi->filter.delay_pos[0][1]] = (tfbl * 402 + tfcl * 370) / 1024 + (right_mix * gain_in[0] / 1024); + mdi->filter.delay[1][0][mdi->filter.delay_pos[1][0]] = (tfar * -545 + tfdr * -364) / 1024 + (left_mix * gain_in[1] / 1024); + mdi->filter.delay[1][1][mdi->filter.delay_pos[1][1]] = (tfal * -550 + tfdl * -362) / 1024 + (right_mix * gain_in[1] / 1024); + mdi->filter.delay[2][0][mdi->filter.delay_pos[2][0]] = (tfar * 545 + tfdr * -364) / 1024 + (left_mix * gain_in[2] / 1024); + mdi->filter.delay[2][1][mdi->filter.delay_pos[2][1]] = (tfal * 550 + tfdl * 362) / 1024 + (right_mix * gain_in[2] / 1024); + mdi->filter.delay[3][0][mdi->filter.delay_pos[3][0]] = (tfbr * 405 + tfcr * -368) / 1024 + (left_mix * gain_in[3] / 1024); + mdi->filter.delay[3][1][mdi->filter.delay_pos[3][1]] = (tfbl * 402 + tfcl * -370) / 1024 + (right_mix * gain_in[3] / 1024); + + tfl = ((tfal * gain_out[0] / 1024) + (tfbl * gain_out[1] / 1024) + (tfcl * gain_out[2] / 1024) + (tfdl * gain_out[3] / 1024)); + tfr = ((tfar * gain_out[0] / 1024) + (tfbr * gain_out[1] / 1024) + (tfcr * gain_out[2] / 1024) + (tfdr * gain_out[3] / 1024)); + + tflo = (a[4][0] * tfl + a[4][1] * mdi->filter.in[0][0] + a[4][0] * mdi->filter.in[1][0] - b[4][0] * mdi->filter.out[0][0] - b[4][1] * mdi->filter.out[1][0]) / 1024; + tfro = (a[4][0] * tfr + a[4][1] * mdi->filter.in[0][1] + a[4][0] * mdi->filter.in[1][1] - b[4][0] * mdi->filter.out[0][1] - b[4][1] * mdi->filter.out[1][1]) / 1024; + + mdi->filter.in[1][0] = mdi->filter.in[0][0]; + mdi->filter.in[0][0] = tfl; + mdi->filter.in[1][1] = mdi->filter.in[0][1]; + mdi->filter.in[0][1] = tfr; + mdi->filter.out[1][0] = mdi->filter.out[0][0]; + mdi->filter.out[0][0] = tflo; + mdi->filter.out[1][1] = mdi->filter.out[0][1]; + mdi->filter.out[0][1] = tfro; + + left_mix += tflo; + right_mix += tfro; + + mdi->filter.delay_pos[0][0]++; + mdi->filter.delay_pos[0][1]++; + mdi->filter.delay_pos[1][0]++; + mdi->filter.delay_pos[1][1]++; + mdi->filter.delay_pos[2][0]++; + mdi->filter.delay_pos[2][1]++; + mdi->filter.delay_pos[3][0]++; + mdi->filter.delay_pos[3][1]++; + + if (mdi->filter.delay_pos[0][0] == delay_size[0][0]) mdi->filter.delay_pos[0][0] = 0; + if (mdi->filter.delay_pos[0][1] == delay_size[0][1]) mdi->filter.delay_pos[0][1] = 0; + if (mdi->filter.delay_pos[1][0] == delay_size[1][0]) mdi->filter.delay_pos[1][0] = 0; + if (mdi->filter.delay_pos[1][1] == delay_size[1][1]) mdi->filter.delay_pos[1][1] = 0; + if (mdi->filter.delay_pos[2][0] == delay_size[2][0]) mdi->filter.delay_pos[2][0] = 0; + if (mdi->filter.delay_pos[2][1] == delay_size[2][1]) mdi->filter.delay_pos[2][1] = 0; + if (mdi->filter.delay_pos[3][0] == delay_size[3][0]) mdi->filter.delay_pos[3][0] = 0; + if (mdi->filter.delay_pos[3][1] == delay_size[3][1]) mdi->filter.delay_pos[3][1] = 0; + + } +#endif + if (left_mix > 32767) { + left_mix = 32767; + } else if (left_mix < -32768) { + left_mix = -32768; + } + + if (right_mix > 32767) { + right_mix = 32767; + } else if (right_mix < -32768) { + right_mix = -32768; + } + + +/* + * =================== + * Write to the buffer + * =================== + */ + (*buffer++) = left_mix & 0xff; + (*buffer++) = (left_mix >> 8) & 0xff; + (*buffer++) = right_mix & 0xff; + (*buffer++) = (right_mix >> 8) & 0xff; + } while (--count); + + buffer_used += real_samples_to_mix * 4; + size -= (real_samples_to_mix << 2); + mdi->info.current_sample += real_samples_to_mix; + mdi->sample_count -= real_samples_to_mix; + if (mdi->index_count == mdi->index_size) { + if (mdi->last_note == 0) { + mdi->sample_count = 0; + WM_Unlock(&mdi->lock); + return buffer_used; + } + } + } while (size); + + if ((mdi->index_count == mdi->index_size) && (mdi->recalc_samples)) { + WM_RecalcSamples(mdi); + mdi->sample_count = mdi->info.approx_total_samples - mdi->info.current_sample; + } + WM_Unlock(&mdi->lock); + return buffer_used; +} + +int +WildMidi_GetOutput_Gauss (midi * handle, char * buffer, unsigned long int size) { + unsigned long int buffer_used = 0; + struct _mdi *mdi = (struct _mdi *)handle; + unsigned long int real_samples_to_mix = 0; + unsigned long int data_pos; + signed long int premix, left_mix, right_mix; + signed long int vol_mul; + struct _note **note_data = NULL; + unsigned long int count; + signed short int *sptr; + double y, xd; + float *gptr, *gend; + int left, right, temp_n; + int ii, jj; + + + if (__builtin_expect((!WM_Initialized),0)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return -1; + } + if (__builtin_expect((handle == NULL),0)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", 0); + return -1; + } + if (__builtin_expect((buffer == NULL),0)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL buffer pointer)", 0); + return -1; + } + + if (__builtin_expect((size == 0),0)) { + return 0; + } + + if (__builtin_expect((size % 4),0)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(size not a multiple of 4)", 0); + return -1; + } + + WM_Lock(&mdi->lock); + if (__builtin_expect(((mdi->index_count == mdi->index_size) && (mdi->last_note == 0)), 0)) { + WM_Unlock(&mdi->lock); + return 0; + } + + buffer_used = 0; + memset(buffer, 0, size); + + do { + if (__builtin_expect((!mdi->sample_count),0)) { + if (__builtin_expect((mdi->index_count != mdi->index_size),1)) { + do { + if (__builtin_expect((mdi->index_count == mdi->index_size), 0)) { + break; + } + + if (__builtin_expect((mdi->index_count != 0), 1)) { + do_event[((mdi->index[mdi->index_count].event & 0xF0) >> 4) - 8]((mdi->index[mdi->index_count].event & 0x0F), mdi, mdi->index[mdi->index_count].offset); + } + } while (mdi->index[mdi->index_count++].delta == 0); + + mdi->samples_to_mix += mdi->index[mdi->index_count-1].delta * mdi->samples_per_delta; + mdi->sample_count = mdi->samples_to_mix >> 10; + mdi->samples_to_mix %= 1024; + } else { + if (mdi->recalc_samples) { + WM_RecalcSamples(mdi); + } + mdi->sample_count = mdi->info.approx_total_samples - mdi->info.current_sample; + if (mdi->sample_count == 0) { + WM_Unlock(&mdi->lock); + return buffer_used; + } + } + } + if (__builtin_expect((mdi->sample_count > (size >> 2)),1)) { + real_samples_to_mix = size >> 2; + } else { + real_samples_to_mix = mdi->sample_count; + if (real_samples_to_mix == 0) { + continue; + } + } + + // do mixing here + count = real_samples_to_mix; + do { + note_data = mdi->note; + left_mix = right_mix = 0; + if (__builtin_expect((mdi->last_note != mdi->note),1)) { + while (note_data != mdi->last_note) { +/* + * =================== + * resample the sample + * =================== + */ + data_pos = (*note_data)->sample_pos >> FPBITS; + vol_mul = (((*note_data)->vol_lvl * ((*note_data)->env_level >> 12)) >> FPBITS); + + /* check to see if we're near one of the ends */ + left = data_pos; + right = ((*note_data)->sample->data_length>>FPBITS)- left -1; + temp_n = (right<<1)-1; + if (temp_n <= 0) + temp_n = 1; + if (temp_n > (left<<1)+1) + temp_n = (left<<1)+1; + + /* use Newton if we can't fill the window */ + if (temp_n < gauss_n) { + xd = (*note_data)->sample_pos & FPMASK; + xd /= (1L<<FPBITS); + xd += temp_n>>1; + y = 0; + sptr = (*note_data)->sample->data + ((*note_data)->sample_pos>>FPBITS) - (temp_n>>1); + for (ii = temp_n; ii;) { + for (jj = 0; jj <= ii; jj++) + y += sptr[jj] * newt_coeffs[ii][jj]; + y *= xd - --ii; + } + y += *sptr; + } else { /* otherwise, use Gauss as usual */ + y = 0; + gptr = gauss_table[(*note_data)->sample_pos & FPMASK]; + gend = gptr + gauss_n; + sptr = (*note_data)->sample->data + ((*note_data)->sample_pos >> FPBITS) - (gauss_n>>1); + do { + y += *(sptr++) * *(gptr++); + } while (gptr <= gend); + } + + premix = y * vol_mul / 1024; + + left_mix += premix * mdi->channel[(*note_data)->noteid >> 8].left_adjust; + right_mix += premix * mdi->channel[(*note_data)->noteid >> 8].right_adjust; + +/* + * ======================== + * sample position checking + * ======================== + */ + (*note_data)->sample_pos += (*note_data)->sample_inc; + if (__builtin_expect(((*note_data)->sample_pos > (*note_data)->sample->loop_end), 0)) { + if ((*note_data)->modes & SAMPLE_LOOP) { + (*note_data)->sample_pos = (*note_data)->sample->loop_start + (((*note_data)->sample_pos - (*note_data)->sample->loop_start) % (*note_data)->sample->loop_size); + } else if (__builtin_expect(((*note_data)->sample_pos >= (*note_data)->sample->data_length), 0)) { + if (__builtin_expect(((*note_data)->next == NULL), 1)) { + goto KILL_NOTE; + + } + goto RESTART_NOTE; + } + } + + if (__builtin_expect(((*note_data)->env_inc == 0), 0)) { + note_data++; + continue; + } + + (*note_data)->env_level += (*note_data)->env_inc; + if (__builtin_expect(((*note_data)->env_level > 4194304), 0)) { + (*note_data)->env_level = (*note_data)->sample->env_target[(*note_data)->env]; + } + if (__builtin_expect((((*note_data)->env_inc < 0) && + ((*note_data)->env_level > (*note_data)->sample->env_target[(*note_data)->env])) || + (((*note_data)->env_inc > 0) && + ((*note_data)->env_level < (*note_data)->sample->env_target[(*note_data)->env])), 1)) { + note_data++; + continue; + } + + (*note_data)->env_level = (*note_data)->sample->env_target[(*note_data)->env]; + switch ((*note_data)->env) { + case 0: + if (!((*note_data)->modes & SAMPLE_ENVELOPE)) { + (*note_data)->env_inc = 0; + note_data++; + continue; + } + break; + case 2: + if ((*note_data)->modes & SAMPLE_SUSTAIN) { + (*note_data)->env_inc = 0; + note_data++; + continue; + } + break; + case 5: + if (__builtin_expect(((*note_data)->env_level == 0), 1)) { + goto KILL_NOTE; + } + // sample release + if ((*note_data)->modes & SAMPLE_LOOP) + (*note_data)->modes ^= SAMPLE_LOOP; + (*note_data)->env_inc = 0; + note_data++; + continue; + case 6: + if (__builtin_expect(((*note_data)->next != NULL), 1)) { + RESTART_NOTE: + (*note_data)->active = 0; + *note_data = (*note_data)->next; + (*note_data)->active = 1; + note_data++; + + } else { + KILL_NOTE: + (*note_data)->active = 0; + mdi->last_note--; + if (note_data != mdi->last_note) { + *note_data = *mdi->last_note; + } + } + continue; + } + (*note_data)->env++; + if ((*note_data)->env_level > (*note_data)->sample->env_target[(*note_data)->env]) { + (*note_data)->env_inc = -(*note_data)->sample->env_rate[(*note_data)->env]; + } else { + (*note_data)->env_inc = (*note_data)->sample->env_rate[(*note_data)->env]; + } + note_data++; + continue; + } +/* + * ========================= + * mix the channels together + * ========================= + */ + + left_mix /= 1024; + right_mix /= 1024; + } + +#ifdef EXPERIMENT_626 +/* + * ========================== + * Experimental Reverb Engine + * ========================== + */ + + if (mdi->info.mixer_options & WM_MO_REVERB) { + signed long int filteral = mdi->filter.delay[0][0][mdi->filter.delay_pos[0][0]]; + signed long int filterar = mdi->filter.delay[0][1][mdi->filter.delay_pos[0][1]]; + signed long int filterbl = mdi->filter.delay[1][0][mdi->filter.delay_pos[1][0]]; + signed long int filterbr = mdi->filter.delay[1][1][mdi->filter.delay_pos[1][1]]; + signed long int filtercl = mdi->filter.delay[2][0][mdi->filter.delay_pos[2][0]]; + signed long int filtercr = mdi->filter.delay[2][1][mdi->filter.delay_pos[2][1]]; + signed long int filterdl = mdi->filter.delay[3][0][mdi->filter.delay_pos[3][0]]; + signed long int filterdr = mdi->filter.delay[3][1][mdi->filter.delay_pos[3][1]]; + signed long int tfal = (a[0][0] * filteral + a[0][1] * mdi->filter.lowpass[0][0].in[0] + a[0][0] * mdi->filter.lowpass[0][0].in[1] - b[0][0] * mdi->filter.lowpass[0][0].out[0] - b[0][1] * mdi->filter.lowpass[0][0].out[1]) / 1024; + signed long int tfar = (a[0][0] * filterar + a[0][1] * mdi->filter.lowpass[0][1].in[0] + a[0][0] * mdi->filter.lowpass[0][1].in[1] - b[0][0] * mdi->filter.lowpass[0][1].out[0] - b[0][1] * mdi->filter.lowpass[0][1].out[1]) / 1024; + signed long int tfbl = (a[1][0] * filterbl + a[1][1] * mdi->filter.lowpass[1][0].in[0] + a[1][0] * mdi->filter.lowpass[1][0].in[1] - b[1][0] * mdi->filter.lowpass[1][0].out[0] - b[1][1] * mdi->filter.lowpass[1][0].out[1]) / 1024; + signed long int tfbr = (a[1][0] * filterbr + a[1][1] * mdi->filter.lowpass[1][1].in[0] + a[1][0] * mdi->filter.lowpass[1][1].in[1] - b[1][0] * mdi->filter.lowpass[1][1].out[0] - b[1][1] * mdi->filter.lowpass[1][1].out[1]) / 1024; + signed long int tfcl = (a[2][0] * filtercl + a[2][1] * mdi->filter.lowpass[2][0].in[0] + a[2][0] * mdi->filter.lowpass[2][0].in[1] - b[2][0] * mdi->filter.lowpass[2][0].out[0] - b[2][1] * mdi->filter.lowpass[2][0].out[1]) / 1024; + signed long int tfcr = (a[2][0] * filtercr + a[2][1] * mdi->filter.lowpass[2][1].in[0] + a[2][0] * mdi->filter.lowpass[2][1].in[1] - b[2][0] * mdi->filter.lowpass[2][1].out[0] - b[2][1] * mdi->filter.lowpass[2][1].out[1]) / 1024; + signed long int tfdl = (a[3][0] * filterdl + a[3][1] * mdi->filter.lowpass[3][0].in[0] + a[3][0] * mdi->filter.lowpass[3][0].in[1] - b[3][0] * mdi->filter.lowpass[3][0].out[0] - b[3][1] * mdi->filter.lowpass[3][0].out[1]) / 1024; + signed long int tfdr = (a[3][0] * filterdr + a[3][1] * mdi->filter.lowpass[3][1].in[0] + a[3][0] * mdi->filter.lowpass[3][1].in[1] - b[3][0] * mdi->filter.lowpass[3][1].out[0] - b[3][1] * mdi->filter.lowpass[3][1].out[1]) / 1024; + signed long int tfl, tflo; + signed long int tfr, tfro; + + mdi->filter.lowpass[0][0].in[1] = mdi->filter.lowpass[0][0].in[0]; + mdi->filter.lowpass[0][0].in[0] = filteral; + mdi->filter.lowpass[0][1].in[1] = mdi->filter.lowpass[0][1].in[0]; + mdi->filter.lowpass[0][1].in[0] = filterar; + mdi->filter.lowpass[1][0].in[1] = mdi->filter.lowpass[1][0].in[0]; + mdi->filter.lowpass[1][0].in[0] = filterbl; + mdi->filter.lowpass[1][1].in[1] = mdi->filter.lowpass[1][1].in[0]; + mdi->filter.lowpass[1][1].in[0] = filterbr; + mdi->filter.lowpass[2][0].in[1] = mdi->filter.lowpass[2][0].in[0]; + mdi->filter.lowpass[2][0].in[0] = filtercl; + mdi->filter.lowpass[2][1].in[1] = mdi->filter.lowpass[2][1].in[0]; + mdi->filter.lowpass[2][1].in[0] = filtercr; + mdi->filter.lowpass[3][0].in[1] = mdi->filter.lowpass[3][0].in[0]; + mdi->filter.lowpass[3][0].in[0] = filterdl; + mdi->filter.lowpass[3][1].in[1] = mdi->filter.lowpass[3][1].in[0]; + mdi->filter.lowpass[3][1].in[0] = filterdr; + + mdi->filter.lowpass[0][0].out[1] = mdi->filter.lowpass[0][0].out[0]; + mdi->filter.lowpass[0][0].out[0] = tfal; + mdi->filter.lowpass[0][1].out[1] = mdi->filter.lowpass[0][1].out[0]; + mdi->filter.lowpass[0][1].out[0] = tfar; + mdi->filter.lowpass[1][0].out[1] = mdi->filter.lowpass[1][0].out[0]; + mdi->filter.lowpass[1][0].out[0] = tfbl; + mdi->filter.lowpass[1][1].out[1] = mdi->filter.lowpass[1][1].out[0]; + mdi->filter.lowpass[1][1].out[0] = tfbr; + mdi->filter.lowpass[2][0].out[1] = mdi->filter.lowpass[2][0].out[0]; + mdi->filter.lowpass[2][0].out[0] = tfcl; + mdi->filter.lowpass[2][1].out[1] = mdi->filter.lowpass[2][1].out[0]; + mdi->filter.lowpass[2][1].out[0] = tfcr; + mdi->filter.lowpass[3][0].out[1] = mdi->filter.lowpass[3][0].out[0]; + mdi->filter.lowpass[3][0].out[0] = tfdl; + mdi->filter.lowpass[3][1].out[1] = mdi->filter.lowpass[3][1].out[0]; + mdi->filter.lowpass[3][1].out[0] = tfdr; + + mdi->filter.delay[0][0][mdi->filter.delay_pos[0][0]] = (tfbr * 405 + tfcr * 368) / 1024 + (left_mix * gain_in[0] / 1024); + mdi->filter.delay[0][1][mdi->filter.delay_pos[0][1]] = (tfbl * 402 + tfcl * 370) / 1024 + (right_mix * gain_in[0] / 1024); + mdi->filter.delay[1][0][mdi->filter.delay_pos[1][0]] = (tfar * -545 + tfdr * -364) / 1024 + (left_mix * gain_in[1] / 1024); + mdi->filter.delay[1][1][mdi->filter.delay_pos[1][1]] = (tfal * -550 + tfdl * -362) / 1024 + (right_mix * gain_in[1] / 1024); + mdi->filter.delay[2][0][mdi->filter.delay_pos[2][0]] = (tfar * 545 + tfdr * -364) / 1024 + (left_mix * gain_in[2] / 1024); + mdi->filter.delay[2][1][mdi->filter.delay_pos[2][1]] = (tfal * 550 + tfdl * 362) / 1024 + (right_mix * gain_in[2] / 1024); + mdi->filter.delay[3][0][mdi->filter.delay_pos[3][0]] = (tfbr * 405 + tfcr * -368) / 1024 + (left_mix * gain_in[3] / 1024); + mdi->filter.delay[3][1][mdi->filter.delay_pos[3][1]] = (tfbl * 402 + tfcl * -370) / 1024 + (right_mix * gain_in[3] / 1024); + + tfl = ((tfal * gain_out[0] / 1024) + (tfbl * gain_out[1] / 1024) + (tfcl * gain_out[2] / 1024) + (tfdl * gain_out[3] / 1024)); + tfr = ((tfar * gain_out[0] / 1024) + (tfbr * gain_out[1] / 1024) + (tfcr * gain_out[2] / 1024) + (tfdr * gain_out[3] / 1024)); + + tflo = (a[4][0] * tfl + a[4][1] * mdi->filter.in[0][0] + a[4][0] * mdi->filter.in[1][0] - b[4][0] * mdi->filter.out[0][0] - b[4][1] * mdi->filter.out[1][0]) / 1024; + tfro = (a[4][0] * tfr + a[4][1] * mdi->filter.in[0][1] + a[4][0] * mdi->filter.in[1][1] - b[4][0] * mdi->filter.out[0][1] - b[4][1] * mdi->filter.out[1][1]) / 1024; + + mdi->filter.in[1][0] = mdi->filter.in[0][0]; + mdi->filter.in[0][0] = tfl; + mdi->filter.in[1][1] = mdi->filter.in[0][1]; + mdi->filter.in[0][1] = tfr; + mdi->filter.out[1][0] = mdi->filter.out[0][0]; + mdi->filter.out[0][0] = tflo; + mdi->filter.out[1][1] = mdi->filter.out[0][1]; + mdi->filter.out[0][1] = tfro; + + left_mix += tflo; + right_mix += tfro; + + mdi->filter.delay_pos[0][0]++; + mdi->filter.delay_pos[0][1]++; + mdi->filter.delay_pos[1][0]++; + mdi->filter.delay_pos[1][1]++; + mdi->filter.delay_pos[2][0]++; + mdi->filter.delay_pos[2][1]++; + mdi->filter.delay_pos[3][0]++; + mdi->filter.delay_pos[3][1]++; + + if (mdi->filter.delay_pos[0][0] == delay_size[0][0]) mdi->filter.delay_pos[0][0] = 0; + if (mdi->filter.delay_pos[0][1] == delay_size[0][1]) mdi->filter.delay_pos[0][1] = 0; + if (mdi->filter.delay_pos[1][0] == delay_size[1][0]) mdi->filter.delay_pos[1][0] = 0; + if (mdi->filter.delay_pos[1][1] == delay_size[1][1]) mdi->filter.delay_pos[1][1] = 0; + if (mdi->filter.delay_pos[2][0] == delay_size[2][0]) mdi->filter.delay_pos[2][0] = 0; + if (mdi->filter.delay_pos[2][1] == delay_size[2][1]) mdi->filter.delay_pos[2][1] = 0; + if (mdi->filter.delay_pos[3][0] == delay_size[3][0]) mdi->filter.delay_pos[3][0] = 0; + if (mdi->filter.delay_pos[3][1] == delay_size[3][1]) mdi->filter.delay_pos[3][1] = 0; + + } +#endif + if (left_mix > 32767) { + left_mix = 32767; + } else if (left_mix < -32768) { + left_mix = -32768; + } + + if (right_mix > 32767) { + right_mix = 32767; + } else if (right_mix < -32768) { + right_mix = -32768; + } + + +/* + * =================== + * Write to the buffer + * =================== + */ + (*buffer++) = left_mix & 0xff; + (*buffer++) = (left_mix >> 8) & 0xff; + (*buffer++) = right_mix & 0xff; + (*buffer++) = (right_mix >> 8) & 0xff; + } while (--count); + + buffer_used += real_samples_to_mix * 4; + size -= (real_samples_to_mix << 2); + mdi->info.current_sample += real_samples_to_mix; + mdi->sample_count -= real_samples_to_mix; + if (mdi->index_count == mdi->index_size) { + if (mdi->last_note == 0) { + mdi->sample_count = 0; + WM_Unlock(&mdi->lock); + return buffer_used; + } + } + } while (size); + + if ((mdi->index_count == mdi->index_size) && (mdi->recalc_samples)) { + WM_RecalcSamples(mdi); + mdi->sample_count = mdi->info.approx_total_samples - mdi->info.current_sample; + } + WM_Unlock(&mdi->lock); + return buffer_used; +} + +int +WildMidi_GetOutput (midi * handle, char * buffer, unsigned long int size) { + struct _mdi *mdi = (struct _mdi *)handle; + + if (__builtin_expect((!WM_Initialized),0)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return -1; + } + if (__builtin_expect((handle == NULL),0)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", 0); + return -1; + } + if (__builtin_expect((buffer == NULL),0)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL buffer pointer)", 0); + return -1; + } + + if (__builtin_expect((size == 0),0)) { + return 0; + } + + if (__builtin_expect((size % 4),0)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(size not a multiple of 4)", 0); + return -1; + } + if (mdi->info.mixer_options & WM_MO_EXPENSIVE_INTERPOLATION) { + return WildMidi_GetOutput_Gauss (handle, buffer,size); + } else { + return WildMidi_GetOutput_Linear (handle, buffer, size); + } +} + +int +WildMidi_SetOption (midi * handle, unsigned short int options, unsigned short int setting) { + struct _mdi *mdi = (struct _mdi *)handle; + struct _note **note_data = mdi->note; + int i; + + if (!WM_Initialized) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return -1; + } + if (handle == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", 0); + return -1; + } + WM_Lock(&mdi->lock); + if ((!(options & 0x0007)) || (options & 0xFFF8)){ + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(invalid option)", 0); + WM_Unlock(&mdi->lock); + return -1; + } + if (setting & 0xFFF8) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(invalid setting)", 0); + WM_Unlock(&mdi->lock); + return -1; + } + + mdi->info.mixer_options = ((mdi->info.mixer_options & (0x00FF ^ options)) | (options & setting)); + + if (options & WM_MO_LINEAR_VOLUME) { + if (mdi->info.mixer_options & WM_MO_LINEAR_VOLUME) { + mdi->amp = 281; + } else { + mdi->amp = 281 * mdi->lin_max_vol / mdi->log_max_vol; + } + for (i = 0; i < 16; i++) { + do_pan_adjust(mdi, i); + } + if (note_data != mdi->last_note) { + do { + (*note_data)->vol_lvl = get_volume(mdi, ((*note_data)->noteid >> 8), *note_data); + if ((*note_data)->next) + (*note_data)->next->vol_lvl = get_volume(mdi, ((*note_data)->noteid >> 8), (*note_data)->next); + note_data++; + } while (note_data != mdi->last_note); + } + } + + if (options & WM_MO_REVERB) { + for (i = 0; i < 4; i++) { + mdi->filter.lowpass[i][0].in[0] = 0; + mdi->filter.lowpass[i][0].in[1] = 0; + mdi->filter.lowpass[i][1].in[0] = 0; + mdi->filter.lowpass[i][1].in[1] = 0; + + mdi->filter.lowpass[i][0].out[0] = 0; + mdi->filter.lowpass[i][0].out[1] = 0; + mdi->filter.lowpass[i][1].out[0] = 0; + mdi->filter.lowpass[i][1].out[1] = 0; + + mdi->filter.delay_pos[i][0] = 0; + mdi->filter.delay_pos[i][1] = 0; + + memset (mdi->filter.delay[i][0], 0, (delay_size[i][0] * sizeof(signed long int))); + memset (mdi->filter.delay[i][1], 0, (delay_size[i][1] * sizeof(signed long int))); + } + } + WM_Unlock(&mdi->lock); + return 0; +} + +struct _WM_Info * +WildMidi_GetInfo (midi * handle) { + struct _mdi *mdi = (struct _mdi *)handle; + if (!WM_Initialized) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return NULL; + } + if (handle == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", 0); + return NULL; + } + WM_Lock(&mdi->lock); + if (mdi->tmp_info == NULL) { + mdi->tmp_info = malloc(sizeof(struct _WM_Info)); + if (mdi->tmp_info == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to set info", 0); + WM_Unlock(&mdi->lock); + return NULL; + } + } + mdi->tmp_info->current_sample = mdi->info.current_sample; + mdi->tmp_info->approx_total_samples = mdi->info.approx_total_samples; + mdi->tmp_info->mixer_options = mdi->info.mixer_options; + WM_Unlock(&mdi->lock); + return mdi->tmp_info; +} + +int +WildMidi_Shutdown ( void ) { + struct _hndl * tmp_hdle; + + if (!WM_Initialized) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return -1; + } + if (first_handle != NULL) { + while (first_handle != NULL) { + tmp_hdle = first_handle->next; + WildMidi_Close((struct _mdi *)first_handle->handle); + free (first_handle); + first_handle = tmp_hdle; + } + } + WM_FreePatches(); + WM_Initialized = 0; + return 0; +} + diff --git a/plugins/wildmidi/wildmidiplug.c b/plugins/wildmidi/wildmidiplug.c new file mode 100644 index 00000000..3c5e4d81 --- /dev/null +++ b/plugins/wildmidi/wildmidiplug.c @@ -0,0 +1,170 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include <stdlib.h> +#include <string.h> +#include "../../deadbeef.h" +#include "wildmidi_lib.h" + +extern DB_decoder_t wmidi_plugin; + +#define trace(...) { fprintf(stderr, __VA_ARGS__); } +//#define trace(fmt,...) + +static DB_functions_t *deadbeef; + +#define min(x,y) ((x)<(y)?(x):(y)) +#define max(x,y) ((x)>(y)?(x):(y)) + +typedef struct { + DB_fileinfo_t info; + midi *m; +} wmidi_info_t; + +DB_fileinfo_t * +wmidi_open (void) { + DB_fileinfo_t *_info = (DB_fileinfo_t *)malloc (sizeof (wmidi_info_t)); + memset (_info, 0, sizeof (wmidi_info_t)); + return _info; +} + +int +wmidi_init (DB_fileinfo_t *_info, DB_playItem_t *it) { + wmidi_info_t *info = (wmidi_info_t *)_info; + + info->m = WildMidi_Open (it->fname); + if (!info->m) { + fprintf (stderr, "wmidi: failed to open %s\n", it->fname); + return -1; + } + + _info->plugin = &wmidi_plugin; + _info->channels = 2; + _info->bps = 16; + _info->samplerate = 44100; + _info->readpos = 0; + + return 0; +} + +void +wmidi_free (DB_fileinfo_t *_info) { + wmidi_info_t *info = (wmidi_info_t *)_info; + if (info) { + if (info->m) { + WildMidi_Close (info->m); + info->m = NULL; + } + free (info); + } +} + +int +wmidi_read (DB_fileinfo_t *_info, char *bytes, int size) { + wmidi_info_t *info = (wmidi_info_t *)_info; + int bufferused = WildMidi_GetOutput (info->m, (char *)bytes, size); + if (bufferused < 0) { + fprintf (stderr, "WildMidi_GetOutput returned %d\n", bufferused); + return 0; + } + + return bufferused; +} + +int +wmidi_seek_sample (DB_fileinfo_t *_info, int sample) { + wmidi_info_t *info = (wmidi_info_t *)_info; + unsigned long int s = sample; + WildMidi_SampledSeek (info->m, &s); + return 0; +} + +int +wmidi_seek (DB_fileinfo_t *_info, float time) { + return wmidi_seek_sample (_info, time * 44100); +} + +DB_playItem_t * +wmidi_insert (DB_playItem_t *after, const char *fname) { + DB_playItem_t *it = NULL; + + midi *m = WildMidi_Open (fname); + if (!m) { + fprintf (stderr, "wmidi: failed to open %s\n", fname); + return NULL; + } + + struct _WM_Info *inf = WildMidi_GetInfo (m); + it = deadbeef->pl_item_alloc (); + it->decoder_id = deadbeef->plug_get_decoder_id (wmidi_plugin.plugin.id); + it->fname = strdup (fname); + deadbeef->pl_add_meta (it, "title", NULL); + deadbeef->pl_set_item_duration (it, inf->approx_total_samples / 44100.f); + it->filetype = "MID"; + after = deadbeef->pl_insert_item (after, it); + deadbeef->pl_item_unref (it); + WildMidi_Close (m); + return after; +} + +int +wmidi_start (void) { + WildMidi_Init ("/etc/timidity++/timidity-freepats.cfg", 44100, 0); + return 0; +} + +int +wmidi_stop (void) { + WildMidi_Shutdown (); + return 0; +} + +DB_plugin_t * +wildmidi_load (DB_functions_t *api) { + deadbeef = api; + return DB_PLUGIN (&wmidi_plugin); +} + +static const char *exts[] = { "mid",NULL }; +const char *filetypes[] = { "MID", NULL }; + +// define plugin interface +DB_decoder_t wmidi_plugin = { + DB_PLUGIN_SET_API_VERSION + .plugin.type = DB_PLUGIN_DECODER, + .plugin.version_major = 0, + .plugin.version_minor = 1, + .plugin.name = "WildMidi player", + .plugin.descr = "MIDI player based on WildMidi library", + .plugin.author = "Alexey Yakovenko", + .plugin.email = "waker@users.sourceforge.net", + .plugin.website = "http://deadbeef.sf.net", + .plugin.start = wmidi_start, + .plugin.stop = wmidi_stop, + .plugin.id = "wmidi", + .open = wmidi_open, + .init = wmidi_init, + .free = wmidi_free, + .read_int16 = wmidi_read, + .seek = wmidi_seek, + .seek_sample = wmidi_seek_sample, + .insert = wmidi_insert, + .exts = exts, + .filetypes = filetypes, +}; |