Added timidity for use in openttd

This commit is contained in:
pelya
2010-11-16 18:19:46 +02:00
parent 1ca0105303
commit 633ee57739
37 changed files with 8146 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
* Tuukka Toivonen <toivonen@clinet.fi>
wrote TiMidity and maintained it until version v0.2i.
* Torbjörn Andersson <d91tan@Update.UU.SE>
code cleanup and import it to the SDL_sound library as MIDI decoder.
* Konstantin Korikov <lostclus@ua.fm>
make a separate library from SDL_sound MIDI decoder.

View File

@@ -0,0 +1,24 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := timidity
APP_SUBDIRS := $(patsubst $(LOCAL_PATH)/%, %, $(shell find $(LOCAL_PATH)/src -type d))
LOCAL_CFLAGS := -O3 $(foreach D, $(APP_SUBDIRS), -I$(LOCAL_PATH)/$(D)) \
-I$(LOCAL_PATH)/include -DHAVE_CONFIG_H
LOCAL_CPP_EXTENSION := .cpp
LOCAL_SRC_FILES := $(foreach F, $(APP_SUBDIRS), $(addprefix $(F)/,$(notdir $(wildcard $(LOCAL_PATH)/$(F)/*.cpp))))
LOCAL_SRC_FILES += $(foreach F, $(APP_SUBDIRS), $(addprefix $(F)/,$(notdir $(wildcard $(LOCAL_PATH)/$(F)/*.c))))
LOCAL_SHARED_LIBRARIES :=
LOCAL_STATIC_LIBRARIES :=
LOCAL_LDLIBS :=
include $(BUILD_SHARED_LIBRARY)

View File

@@ -0,0 +1,39 @@
This version of TiMidity should contain all the fixes from the
November 15 2004 SDL_sound Subversion snapshot. In addition, I've made some
changes of my own, e.g.:
* Replacing SDL types and endian-handling with owns.
* File access to the config file and instruments is done through
stdio functions. File access to MIDI files is done through abstract
input stream. Implemented functions to create input stream from
file name, from stdio file pointer, from memory, from callback functions.
* Renamed interface functions
Timidity_Init -> mid_init
Timidity_Init_NoConfig -> mid_init_no_config
Timidity_SetVolume -> mid_song_set_volume
Timidity_PlaySome -> mid_song_read_wave
Timidity_LoadDLS -> mid_dlspatches_load
Timidity_FreeDLS -> mid_dlspatches_free
Timidity_LoadDLSSong -> mid_song_load_dls
Timidity_LoadSong -> mid_song_load
Timidity_Start -> mid_song_start
Timidity_Seek -> mid_song_seek
Timidity_GetSongLength -> mid_song_get_total_time
Timidity_FreeSong -> mid_song_free
* Most structures and macro definition made hidden and placed in
timidity_internal.h.
* Results of mid_song_read_wave (Timidity_PlaySome) not
depends with internal sample buffer size.
* mid_init can accept timidity config file name.
* Added functions: mid_song_get_time, mid_song_get_meta.
* Added examples/tests midi2raw.c and playmidi.c.
--
Konstantin Korikov <lostclus@ua.fm>

View File

@@ -0,0 +1,513 @@
Please note that the included source from TiMidity, is also licensed
under the following terms (GNU LGPL), but can also be used separately
under the GNU GPL, or the Perl Artistic License. Those licensing
terms are not reprinted here, but can be found on the web easily.
-------------------
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@@ -0,0 +1,53 @@
2004-11-23 Konstantin Korikov <lostclus@ua.fm>
* configure.ac: Changed version to 0.1.0.
* NEWS: NEWS updated.
* libtimidity.spec.in: Spec updated.
2004-11-22 Konstantin Korikov <lostclus@ua.fm>
* src/timidity.h.in, tests/midi2raw.c, tests/playmidi.c,
src/readmidi.c: Replaced MID_SONG_TITLE to MID_SONG_TEXT.
* README: README updated.
* tests/playmidi.c: Added ao_close() call.
* src/playmidi.c: Fixed mid_song_seek, seeking to high positions.
* configure.ac, libtimidity.pc.in, libtimidity.spec.in,
Makefile.am: Added pkg-config metadata file.
2004-11-21 Konstantin Korikov <lostclus@ua.fm>
* libtimidity.spec.in: Call ldconfig in post and postun scriplets.
* CHANGES, COPYING, ChangeLog, src/Makefile.am, src/common.c,
src/common.h, src/dls1.h, src/dls2.h, src/instrum.c,
src/instrum.h, src/instrum_dls.c, src/instrum_dls.h,
src/mix.c, src/mix.h, src/options.h, src/output.c,
src/output.h, src/playmidi.c, src/playmidi.h, src/readmidi.c,
src/readmidi.h, src/resample.c, src/resample.h, src/stream.c,
src/tables.c, src/tables.h, src/timidity.c, src/timidity.h.in,
src/timidity_internal.h, tests/Makefile.am, tests/ame002.mid,
tests/midi2raw.c, tests/playmidi.c, tests/runtest.sh: Imported
sources.
* CHANGES, COPYING, ChangeLog, src/Makefile.am, src/common.c,
src/common.h, src/dls1.h, src/dls2.h, src/instrum.c,
src/instrum.h, src/instrum_dls.c, src/instrum_dls.h,
src/mix.c, src/mix.h, src/options.h, src/output.c,
src/output.h, src/playmidi.c, src/playmidi.h, src/readmidi.c,
src/readmidi.h, src/resample.c, src/resample.h, src/stream.c,
src/tables.c, src/tables.h, src/timidity.c, src/timidity.h.in,
src/timidity_internal.h, tests/Makefile.am, tests/ame002.mid,
tests/midi2raw.c, tests/playmidi.c, tests/runtest.sh: New file.
* AUTHORS, INSTALL, Makefile.am, NEWS, README, README.timidity,
TODO, configure.ac, libtimidity.spec.in: Imported sources.
* AUTHORS, INSTALL, Makefile.am, NEWS, README, README.timidity,
TODO, configure.ac, libtimidity.spec.in: New file.

View File

@@ -0,0 +1,229 @@
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software
Foundation, Inc.
This file is free documentation; the Free Software Foundation gives
unlimited permission to copy, distribute and modify it.
Basic Installation
==================
These are generic installation instructions.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. (Caching is
disabled by default to prevent problems with accidental use of stale
cache files.)
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You only need
`configure.ac' if you want to change it or regenerate `configure' using
a newer version of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system. If you're
using `csh' on an old version of System V, you might need to type
`sh ./configure' instead to prevent `csh' from trying to execute
`configure' itself.
Running `configure' takes awhile. While running, it prints some
messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package.
4. Type `make install' to install the programs and any data files and
documentation.
5. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that
the `configure' script does not know about. Run `./configure --help'
for details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you must use a version of `make' that
supports the `VPATH' variable, such as GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'.
If you have to use a `make' that does not support the `VPATH'
variable, you have to compile the package for one architecture at a
time in the source code directory. After you have installed the
package for one architecture, use `make distclean' before reconfiguring
for another architecture.
Installation Names
==================
By default, `make install' will install the package's files in
`/usr/local/bin', `/usr/local/man', etc. You can specify an
installation prefix other than `/usr/local' by giving `configure' the
option `--prefix=PATH'.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
give `configure' the option `--exec-prefix=PATH', the package will use
PATH as the prefix for installing programs and libraries.
Documentation and other data files will still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=PATH' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Optional Features
=================
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Specifying the System Type
==========================
There may be some features `configure' cannot figure out
automatically, but needs to determine by the type of machine the package
will run on. Usually, assuming the package is built to be run on the
_same_ architectures, `configure' can figure that out, but if it prints
a message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the `--target=TYPE' option to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share,
you can create a site shell script called `config.site' that gives
default values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
will cause the specified gcc to be used as the C compiler (unless it is
overridden in the site shell script).
`configure' Invocation
======================
`configure' recognizes the following options to control how it
operates.
`--help'
`-h'
Print a summary of the options to `configure', and exit.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.

View File

@@ -0,0 +1,3 @@
2004-11-23 - 0.1.0 released
First release.

View File

@@ -0,0 +1,12 @@
libTiMidity -- MIDI to WAVE converter library.
This library is based on the TiMidity decoder from SDL_sound library.
Purpose to create this library is to avoid unnecessary dependences.
SDL_sound requires SDL and some other libraries, that not needed to
process MIDI files. In addition libTiMidity provides more suitable
API to work with MIDI songs, it enables to specify full path to the
timidity configuration file, and have function to retrieve meta data
from MIDI song.
If you have propositions regarding development of this library please
mail to me <lostclus@ua.fm>

View File

@@ -0,0 +1,85 @@
---------------------------------*-text-*---------------------------------
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
--------------------------------------------------------------------------
This is the README file for TiMidity v0.2i
TiMidity is a MIDI to WAVE converter that uses Gravis
Ultrasound(*)-compatible patch files to generate digital audio data
from General MIDI files. The audio data can be played through any
sound device or stored on disk. On a fast machine, music can be
played in real time. TiMidity runs under Linux, FreeBSD, HP-UX, SunOS, and
Win32, and porting to other systems with gcc should be easy.
TiMidity Features:
* 32 or more dynamically allocated fully independent voices
* Compatibility with GUS patch files
* Output to 16- or 8-bit PCM or uLaw audio device, file, or
stdout at any sampling rate
* Optional interactive mode with real-time status display
under ncurses and SLang terminal control libraries. Also
a user friendly motif interface since version 0.2h
* Support for transparent loading of compressed MIDI files and
patch files
* Support for the following MIDI events:
- Program change
- Key pressure
- Channel main volume
- Tempo
- Panning
- Damper pedal (Sustain)
- Pitch wheel
- Pitch wheel sensitivity
- Change drum set
* The file "CHANGES" contains a brief list of what has changed since
the previous version. Please check it if you've been using a TiMidity
version older than 0.2d. You may have to modify your configuration
files slightly -- look at the included "gravis.cfg" for examples.
* Installation instructions are in the file "INSTALL".
* Instructions for use can be found in the manual page "timidity.man",
preformatted in "timidity.txt", and in comments in the configuration
files.
* The GNU General Public License can, as always, be found in the file
"COPYING".
* TiMidity requires sampled instruments (patches) to play MIDI files. You
should get the file "timidity-lib-0.1.tar.gz" and unpack it in the same
directory where you unpacked the source code archive. You'll want more
patches later -- read the file "FAQ" for pointers.
* The latest version of TiMidity can be found on the TiMidity Home Page,
URL http://www.clinet.fi/~toivonen/timidity/
An alternative URL for Motif version is http://www.loria.fr/~pagel
* If you have any comments or suggestions, please email me!
Tuukka Toivonen <toivonen@clinet.fi>
[(*) Any Registered Trademarks used anywhere in the documentation or
source code for TiMidity are acknowledged as belonging to their
respective owners.]

View File

@@ -0,0 +1,2 @@
* Need documentation
* Error handling API (mid_errno, mid_strerror)

View File

@@ -0,0 +1,93 @@
/* config.h. Generated by configure. */
/* config.h.in. Generated from configure.ac by autoheader. */
/* Debug mode */
/* #undef DEBUG */
/* Debug chatter */
/* #undef DEBUG_CHATTER */
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1
/* Define to 1 if you have the `dlopen' function. */
/* #undef HAVE_DLOPEN */
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
to 0 otherwise. */
#define HAVE_MALLOC 1
/* Define to 1 if you have the <math.h> header file. */
#define HAVE_MATH_H 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 if you have the `memset' function. */
#define HAVE_MEMSET 1
/* Define to 1 if you have the `pow' function. */
#define HAVE_POW 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdio.h> header file. */
#define HAVE_STDIO_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the `strchr' function. */
#define HAVE_STRCHR 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Name of package */
#define PACKAGE "libtimidity"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT ""
/* Define to the full name of this package. */
#define PACKAGE_NAME ""
/* Define to the full name and version of this package. */
#define PACKAGE_STRING ""
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME ""
/* Define to the version of this package. */
#define PACKAGE_VERSION ""
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Version number of package */
#define VERSION "0.1.0"
/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */
/* Define to rpl_malloc if the replacement function should be used. */
//#define malloc rpl_malloc
/* Define to `unsigned' if <sys/types.h> does not define. */
/* #undef size_t */

View File

@@ -0,0 +1,206 @@
/*
libTiMidity -- MIDI to WAVE converter library
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
Copyright (C) 2004 Konstantin Korikov <lostclus@ua.fm>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef TIMIDITY_H
#define TIMIDITY_H
#include <stdio.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C"
{
#endif
#define LIBTIMIDITY_VERSION_MAJOR 0L
#define LIBTIMIDITY_VERSION_MINOR 1L
#define LIBTIMIDITY_PATCHLEVEL 0L
#define LIBTIMIDITY_VERSION \
((LIBTIMIDITY_VERSION_MAJOR<<16)| \
(LIBTIMIDITY_VERSION_MINOR<< 8)| \
(LIBTIMIDITY_PATCHLEVEL))
/* Audio format flags (defaults to LSB byte order)
*/
#define MID_AUDIO_U8 0x0008 /* Unsigned 8-bit samples */
#define MID_AUDIO_S8 0x8008 /* Signed 8-bit samples */
#define MID_AUDIO_U16LSB 0x0010 /* Unsigned 16-bit samples */
#define MID_AUDIO_S16LSB 0x8010 /* Signed 16-bit samples */
#define MID_AUDIO_U16MSB 0x1010 /* As above, but big-endian byte order */
#define MID_AUDIO_S16MSB 0x9010 /* As above, but big-endian byte order */
#define MID_AUDIO_U16 MID_AUDIO_U16LSB
#define MID_AUDIO_S16 MID_AUDIO_S16LSB
/* Core Library Types
*/
typedef unsigned char uint8;
typedef signed char sint8;
typedef unsigned short uint16;
typedef signed short sint16;
typedef unsigned int uint32;
typedef signed int sint32;
typedef size_t (*MidIStreamReadFunc) (void *ctx, void *ptr, size_t size,
size_t nmemb);
typedef int (*MidIStreamCloseFunc) (void *ctx);
typedef struct _MidIStream MidIStream;
typedef struct _MidDLSPatches MidDLSPatches;
typedef struct _MidSong MidSong;
typedef struct _MidSongOptions MidSongOptions;
struct _MidSongOptions
{
sint32 rate; /* DSP frequency -- samples per second */
uint16 format; /* Audio data format */
uint8 channels; /* Number of channels: 1 mono, 2 stereo */
uint16 buffer_size; /* Sample buffer size in samples */
};
typedef enum
{
MID_SONG_TEXT = 0,
MID_SONG_COPYRIGHT = 1
} MidSongMetaId;
/* Core Library Functions
* ======================
*/
/* Initialize the library. If config_file is NULL
* search for configuratin file in default directories
*/
extern int mid_init (char *config_file);
/* Initialize the library without reading any
* configuratin file
*/
extern int mid_init_no_config (void);
/* Shutdown the library
*/
extern void mid_exit (void);
/* Input Stream Functions
* ======================
*/
/* Create input stream from a file name
*/
extern MidIStream *mid_istream_open_file (const char *file);
/* Create input stream from a file pointer
*/
extern MidIStream *mid_istream_open_fp (FILE * fp, int autoclose);
/* Create input stream from memory
*/
extern MidIStream *mid_istream_open_mem (void *mem, size_t size,
int autofree);
/* Create custom input stream
*/
extern MidIStream *mid_istream_open_callbacks (MidIStreamReadFunc read,
MidIStreamCloseFunc close,
void *context);
/* Read data from input stream
*/
extern size_t mid_istream_read (MidIStream * stream, void *ptr, size_t size,
size_t nmemb);
/* Skip data from input stream
*/
extern void mid_istream_skip (MidIStream * stream, size_t len);
/* Close and destroy input stream
*/
extern int mid_istream_close (MidIStream * stream);
/* DLS Pathes Functions
* ====================
*/
/* Load DLS patches
*/
extern MidDLSPatches *mid_dlspatches_load (MidIStream * stream);
/* Destroy DLS patches
*/
extern void mid_dlspatches_free (MidDLSPatches * patches);
/* MIDI Song Functions
* ===================
*/
/* Load MIDI song
*/
extern MidSong *mid_song_load (MidIStream * stream,
MidSongOptions * options);
/* Load MIDI song with specified DLS pathes
*/
extern MidSong *mid_song_load_dls (MidIStream * stream,
MidDLSPatches * patches,
MidSongOptions * options);
/* Set song amplification value
*/
extern void mid_song_set_volume (MidSong * song, int volume);
/* Seek song to the start position and initialize conversion
*/
extern void mid_song_start (MidSong * song);
/* Read WAVE data
*/
extern size_t mid_song_read_wave (MidSong * song, void *ptr, size_t size);
/* Seek song to specified offset in millseconds
*/
extern void mid_song_seek (MidSong * song, uint32 ms);
/* Get total song time in millseconds
*/
extern uint32 mid_song_get_total_time (MidSong * song);
/* Get current song time in millseconds
*/
extern uint32 mid_song_get_time (MidSong * song);
/* Get song meta data. Return NULL if no meta data found
*/
extern char *mid_song_get_meta (MidSong * song, MidSongMetaId what);
/* Destroy song
*/
extern void mid_song_free (MidSong * song);
#ifdef __cplusplus
}
#endif
#endif /* TIMIDITY_H */

View File

@@ -0,0 +1,137 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
common.c
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* I guess "rb" should be right for any libc */
#define OPEN_MODE "rb"
#include "timidity.h"
#include "timidity_internal.h"
#include "options.h"
#include "common.h"
/* The paths in this list will be tried whenever we're reading a file */
static PathList *pathlist = NULL; /* This is a linked list */
/* This is meant to find and open files for reading */
FILE *open_file(char *name)
{
FILE *fp;
if (!name || !(*name))
{
DEBUG_MSG("Attempted to open nameless file.\n");
return 0;
}
/* First try the given name */
DEBUG_MSG("Trying to open %s\n", name);
if ((fp = fopen(name, OPEN_MODE)))
return fp;
if (name[0] != PATH_SEP)
{
char current_filename[1024];
PathList *plp = pathlist;
int l;
while (plp) /* Try along the path then */
{
*current_filename = 0;
l = strlen(plp->path);
if(l)
{
strcpy(current_filename, plp->path);
if(current_filename[l - 1] != PATH_SEP)
{
current_filename[l] = PATH_SEP;
current_filename[l + 1] = '\0';
}
}
strcat(current_filename, name);
DEBUG_MSG("Trying to open %s\n", current_filename);
if ((fp = fopen(current_filename, OPEN_MODE)))
return fp;
plp = plp->next;
}
}
/* Nothing could be opened. */
DEBUG_MSG("Could not open %s\n", name);
return 0;
}
/* This'll allocate memory or die. */
void *safe_malloc(size_t count)
{
void *p;
p = malloc(count);
if (p == NULL)
DEBUG_MSG("Sorry. Couldn't malloc %d bytes.\n", count);
return p;
}
/* This adds a directory to the path list */
void add_to_pathlist(char *s)
{
PathList *plp = safe_malloc(sizeof(PathList));
if (plp == NULL)
return;
plp->path = safe_malloc(strlen(s) + 1);
if (plp->path == NULL)
{
free(plp);
return;
}
strcpy(plp->path, s);
plp->next = pathlist;
pathlist = plp;
}
void free_pathlist(void)
{
PathList *plp = pathlist;
PathList *next;
while (plp)
{
next = plp->next;
free(plp->path);
free(plp);
plp = next;
}
pathlist = NULL;
}

View File

@@ -0,0 +1,32 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
common.h
*/
typedef struct {
char *path;
void *next;
} PathList;
extern FILE *open_file(char *name);
extern void add_to_pathlist(char *s);
extern void *safe_malloc(size_t count);
extern void free_pathlist(void);

View File

@@ -0,0 +1,266 @@
/*==========================================================================;
//
// dls1.h
//
//
// Description:
//
// Interface defines and structures for the Instrument Collection Form
// RIFF DLS.
//
//
// Written by Sonic Foundry 1996. Released for public use.
//
//=========================================================================*/
#ifndef _INC_DLS1
#define _INC_DLS1
/*//////////////////////////////////////////////////////////////////////////
//
//
// Layout of an instrument collection:
//
//
// RIFF [] 'DLS ' [dlid,colh,INSTLIST,WAVEPOOL,INFOLIST]
//
// INSTLIST
// LIST [] 'lins'
// LIST [] 'ins ' [dlid,insh,RGNLIST,ARTLIST,INFOLIST]
// LIST [] 'ins ' [dlid,insh,RGNLIST,ARTLIST,INFOLIST]
// LIST [] 'ins ' [dlid,insh,RGNLIST,ARTLIST,INFOLIST]
//
// RGNLIST
// LIST [] 'lrgn'
// LIST [] 'rgn ' [rgnh,wsmp,wlnk,ARTLIST]
// LIST [] 'rgn ' [rgnh,wsmp,wlnk,ARTLIST]
// LIST [] 'rgn ' [rgnh,wsmp,wlnk,ARTLIST]
//
// ARTLIST
// LIST [] 'lart'
// 'art1' level 1 Articulation connection graph
// 'art2' level 2 Articulation connection graph
// '3rd1' Possible 3rd party articulation structure 1
// '3rd2' Possible 3rd party articulation structure 2 .... and so on
//
// WAVEPOOL
// ptbl [] [pool table]
// LIST [] 'wvpl'
// [path],
// [path],
// LIST [] 'wave' [dlid,RIFFWAVE]
// LIST [] 'wave' [dlid,RIFFWAVE]
// LIST [] 'wave' [dlid,RIFFWAVE]
// LIST [] 'wave' [dlid,RIFFWAVE]
// LIST [] 'wave' [dlid,RIFFWAVE]
//
// INFOLIST
// LIST [] 'INFO'
// 'icmt' 'One of those crazy comments.'
// 'icop' 'Copyright (C) 1996 Sonic Foundry'
//
/////////////////////////////////////////////////////////////////////////*/
/*/////////////////////////////////////////////////////////////////////////
// FOURCC's used in the DLS file
/////////////////////////////////////////////////////////////////////////*/
#define FOURCC_DLS mmioFOURCC('D','L','S',' ')
#define FOURCC_DLID mmioFOURCC('d','l','i','d')
#define FOURCC_COLH mmioFOURCC('c','o','l','h')
#define FOURCC_WVPL mmioFOURCC('w','v','p','l')
#define FOURCC_PTBL mmioFOURCC('p','t','b','l')
#define FOURCC_PATH mmioFOURCC('p','a','t','h')
#define FOURCC_wave mmioFOURCC('w','a','v','e')
#define FOURCC_LINS mmioFOURCC('l','i','n','s')
#define FOURCC_INS mmioFOURCC('i','n','s',' ')
#define FOURCC_INSH mmioFOURCC('i','n','s','h')
#define FOURCC_LRGN mmioFOURCC('l','r','g','n')
#define FOURCC_RGN mmioFOURCC('r','g','n',' ')
#define FOURCC_RGNH mmioFOURCC('r','g','n','h')
#define FOURCC_LART mmioFOURCC('l','a','r','t')
#define FOURCC_ART1 mmioFOURCC('a','r','t','1')
#define FOURCC_WLNK mmioFOURCC('w','l','n','k')
#define FOURCC_WSMP mmioFOURCC('w','s','m','p')
#define FOURCC_VERS mmioFOURCC('v','e','r','s')
/*/////////////////////////////////////////////////////////////////////////
// Articulation connection graph definitions
/////////////////////////////////////////////////////////////////////////*/
/* Generic Sources */
#define CONN_SRC_NONE 0x0000
#define CONN_SRC_LFO 0x0001
#define CONN_SRC_KEYONVELOCITY 0x0002
#define CONN_SRC_KEYNUMBER 0x0003
#define CONN_SRC_EG1 0x0004
#define CONN_SRC_EG2 0x0005
#define CONN_SRC_PITCHWHEEL 0x0006
/* Midi Controllers 0-127 */
#define CONN_SRC_CC1 0x0081
#define CONN_SRC_CC7 0x0087
#define CONN_SRC_CC10 0x008a
#define CONN_SRC_CC11 0x008b
/* Generic Destinations */
#define CONN_DST_NONE 0x0000
#define CONN_DST_ATTENUATION 0x0001
#define CONN_DST_PITCH 0x0003
#define CONN_DST_PAN 0x0004
/* LFO Destinations */
#define CONN_DST_LFO_FREQUENCY 0x0104
#define CONN_DST_LFO_STARTDELAY 0x0105
/* EG1 Destinations */
#define CONN_DST_EG1_ATTACKTIME 0x0206
#define CONN_DST_EG1_DECAYTIME 0x0207
#define CONN_DST_EG1_RELEASETIME 0x0209
#define CONN_DST_EG1_SUSTAINLEVEL 0x020a
/* EG2 Destinations */
#define CONN_DST_EG2_ATTACKTIME 0x030a
#define CONN_DST_EG2_DECAYTIME 0x030b
#define CONN_DST_EG2_RELEASETIME 0x030d
#define CONN_DST_EG2_SUSTAINLEVEL 0x030e
#define CONN_TRN_NONE 0x0000
#define CONN_TRN_CONCAVE 0x0001
typedef struct _DLSID {
ULONG ulData1;
USHORT usData2;
USHORT usData3;
BYTE abData4[8];
} DLSID, FAR *LPDLSID;
typedef struct _DLSVERSION {
DWORD dwVersionMS;
DWORD dwVersionLS;
} DLSVERSION, FAR *LPDLSVERSION;
typedef struct _CONNECTION {
USHORT usSource;
USHORT usControl;
USHORT usDestination;
USHORT usTransform;
LONG lScale;
} CONNECTION, FAR *LPCONNECTION;
/* Level 1 Articulation Data */
typedef struct _CONNECTIONLIST {
ULONG cbSize; /* size of the connection list structure */
ULONG cConnections; /* count of connections in the list */
} CONNECTIONLIST, FAR *LPCONNECTIONLIST;
/*/////////////////////////////////////////////////////////////////////////
// Generic type defines for regions and instruments
/////////////////////////////////////////////////////////////////////////*/
typedef struct _RGNRANGE {
USHORT usLow;
USHORT usHigh;
} RGNRANGE, FAR * LPRGNRANGE;
#define F_INSTRUMENT_DRUMS 0x80000000
typedef struct _MIDILOCALE {
ULONG ulBank;
ULONG ulInstrument;
} MIDILOCALE, FAR *LPMIDILOCALE;
/*/////////////////////////////////////////////////////////////////////////
// Header structures found in an DLS file for collection, instruments, and
// regions.
/////////////////////////////////////////////////////////////////////////*/
#define F_RGN_OPTION_SELFNONEXCLUSIVE 0x0001
typedef struct _RGNHEADER {
RGNRANGE RangeKey; /* Key range */
RGNRANGE RangeVelocity; /* Velocity Range */
USHORT fusOptions; /* Synthesis options for this range */
USHORT usKeyGroup; /* Key grouping for non simultaneous play */
/* 0 = no group, 1 up is group */
/* for Level 1 only groups 1-15 are allowed */
} RGNHEADER, FAR *LPRGNHEADER;
typedef struct _INSTHEADER {
ULONG cRegions; /* Count of regions in this instrument */
MIDILOCALE Locale; /* Intended MIDI locale of this instrument */
} INSTHEADER, FAR *LPINSTHEADER;
typedef struct _DLSHEADER {
ULONG cInstruments; /* Count of instruments in the collection */
} DLSHEADER, FAR *LPDLSHEADER;
/*////////////////////////////////////////////////////////////////////////////
// definitions for the Wave link structure
////////////////////////////////////////////////////////////////////////////*/
/* **** For level 1 only WAVELINK_CHANNEL_MONO is valid **** */
/* ulChannel allows for up to 32 channels of audio with each bit position */
/* specifiying a channel of playback */
#define WAVELINK_CHANNEL_LEFT 0x0001l
#define WAVELINK_CHANNEL_RIGHT 0x0002l
#define F_WAVELINK_PHASE_MASTER 0x0001
typedef struct _WAVELINK { /* any paths or links are stored right after struct */
USHORT fusOptions; /* options flags for this wave */
USHORT usPhaseGroup; /* Phase grouping for locking channels */
ULONG ulChannel; /* channel placement */
ULONG ulTableIndex; /* index into the wave pool table, 0 based */
} WAVELINK, FAR *LPWAVELINK;
#define POOL_CUE_NULL 0xffffffffl
typedef struct _POOLCUE {
ULONG ulOffset; /* Offset to the entry in the list */
} POOLCUE, FAR *LPPOOLCUE;
typedef struct _POOLTABLE {
ULONG cbSize; /* size of the pool table structure */
ULONG cCues; /* count of cues in the list */
} POOLTABLE, FAR *LPPOOLTABLE;
/*////////////////////////////////////////////////////////////////////////////
// Structures for the "wsmp" chunk
////////////////////////////////////////////////////////////////////////////*/
#define F_WSMP_NO_TRUNCATION 0x0001l
#define F_WSMP_NO_COMPRESSION 0x0002l
typedef struct _rwsmp {
ULONG cbSize;
USHORT usUnityNote; /* MIDI Unity Playback Note */
SHORT sFineTune; /* Fine Tune in log tuning */
LONG lAttenuation; /* Overall Attenuation to be applied to data */
ULONG fulOptions; /* Flag options */
ULONG cSampleLoops; /* Count of Sample loops, 0 loops is one shot */
} WSMPL, FAR *LPWSMPL;
/* This loop type is a normal forward playing loop which is continually */
/* played until the envelope reaches an off threshold in the release */
/* portion of the volume envelope */
#define WLOOP_TYPE_FORWARD 0
typedef struct _rloop {
ULONG cbSize;
ULONG ulType; /* Loop Type */
ULONG ulStart; /* Start of loop in samples */
ULONG ulLength; /* Length of loop in samples */
} WLOOP, FAR *LPWLOOP;
#endif /*_INC_DLS1 */

View File

@@ -0,0 +1,130 @@
/*
dls2.h
Description:
Interface defines and structures for the DLS2 extensions of DLS.
Written by Microsoft 1998. Released for public use.
*/
#ifndef _INC_DLS2
#define _INC_DLS2
/*
FOURCC's used in the DLS2 file, in addition to DLS1 chunks
*/
#define FOURCC_RGN2 mmioFOURCC('r','g','n','2')
#define FOURCC_LAR2 mmioFOURCC('l','a','r','2')
#define FOURCC_ART2 mmioFOURCC('a','r','t','2')
#define FOURCC_CDL mmioFOURCC('c','d','l',' ')
#define FOURCC_DLID mmioFOURCC('d','l','i','d')
/*
Articulation connection graph definitions. These are in addition to
the definitions in the DLS1 header.
*/
/* Generic Sources (in addition to DLS1 sources. */
#define CONN_SRC_POLYPRESSURE 0x0007 /* Polyphonic Pressure */
#define CONN_SRC_CHANNELPRESSURE 0x0008 /* Channel Pressure */
#define CONN_SRC_VIBRATO 0x0009 /* Vibrato LFO */
#define CONN_SRC_MONOPRESSURE 0x000a /* MIDI Mono pressure */
/* Midi Controllers */
#define CONN_SRC_CC91 0x00db /* Reverb Send */
#define CONN_SRC_CC93 0x00dd /* Chorus Send */
/* Generic Destinations */
#define CONN_DST_GAIN 0x0001 /* Same as CONN_DST_ ATTENUATION, but more appropriate terminology. */
#define CONN_DST_KEYNUMBER 0x0005 /* Key Number Generator */
/* Audio Channel Output Destinations */
#define CONN_DST_LEFT 0x0010 /* Left Channel Send */
#define CONN_DST_RIGHT 0x0011 /* Right Channel Send */
#define CONN_DST_CENTER 0x0012 /* Center Channel Send */
#define CONN_DST_LEFTREAR 0x0013 /* Left Rear Channel Send */
#define CONN_DST_RIGHTREAR 0x0014 /* Right Rear Channel Send */
#define CONN_DST_LFE_CHANNEL 0x0015 /* LFE Channel Send */
#define CONN_DST_CHORUS 0x0080 /* Chorus Send */
#define CONN_DST_REVERB 0x0081 /* Reverb Send */
/* Vibrato LFO Destinations */
#define CONN_DST_VIB_FREQUENCY 0x0114 /* Vibrato Frequency */
#define CONN_DST_VIB_STARTDELAY 0x0115 /* Vibrato Start Delay */
/* EG1 Destinations */
#define CONN_DST_EG1_DELAYTIME 0x020B /* EG1 Delay Time */
#define CONN_DST_EG1_HOLDTIME 0x020C /* EG1 Hold Time */
#define CONN_DST_EG1_SHUTDOWNTIME 0x020D /* EG1 Shutdown Time */
/* EG2 Destinations */
#define CONN_DST_EG2_DELAYTIME 0x030F /* EG2 Delay Time */
#define CONN_DST_EG2_HOLDTIME 0x0310 /* EG2 Hold Time */
/* Filter Destinations */
#define CONN_DST_FILTER_CUTOFF 0x0500 /* Filter Cutoff Frequency */
#define CONN_DST_FILTER_Q 0x0501 /* Filter Resonance */
/* Transforms */
#define CONN_TRN_CONVEX 0x0002 /* Convex Transform */
#define CONN_TRN_SWITCH 0x0003 /* Switch Transform */
/* Conditional chunk operators */
#define DLS_CDL_AND 0x0001 /* X = X & Y */
#define DLS_CDL_OR 0x0002 /* X = X | Y */
#define DLS_CDL_XOR 0x0003 /* X = X ^ Y */
#define DLS_CDL_ADD 0x0004 /* X = X + Y */
#define DLS_CDL_SUBTRACT 0x0005 /* X = X - Y */
#define DLS_CDL_MULTIPLY 0x0006 /* X = X * Y */
#define DLS_CDL_DIVIDE 0x0007 /* X = X / Y */
#define DLS_CDL_LOGICAL_AND 0x0008 /* X = X && Y */
#define DLS_CDL_LOGICAL_OR 0x0009 /* X = X || Y */
#define DLS_CDL_LT 0x000A /* X = (X < Y) */
#define DLS_CDL_LE 0x000B /* X = (X <= Y) */
#define DLS_CDL_GT 0x000C /* X = (X > Y) */
#define DLS_CDL_GE 0x000D /* X = (X >= Y) */
#define DLS_CDL_EQ 0x000E /* X = (X == Y) */
#define DLS_CDL_NOT 0x000F /* X = !X */
#define DLS_CDL_CONST 0x0010 /* 32-bit constant */
#define DLS_CDL_QUERY 0x0011 /* 32-bit value returned from query */
#define DLS_CDL_QUERYSUPPORTED 0x0012 /* Test to see if query is supported by synth */
/*
Loop and release
*/
#define WLOOP_TYPE_RELEASE 1
/*
WaveLink chunk <wlnk-ck>
*/
#define F_WAVELINK_MULTICHANNEL 0x0002
/*
DLSID queries for <cdl-ck>
*/
DEFINE_GUID(DLSID_GMInHardware, 0x178f2f24, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
DEFINE_GUID(DLSID_GSInHardware, 0x178f2f25, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
DEFINE_GUID(DLSID_XGInHardware, 0x178f2f26, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
DEFINE_GUID(DLSID_SupportsDLS1, 0x178f2f27, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
DEFINE_GUID(DLSID_SupportsDLS2, 0xf14599e5, 0x4689, 0x11d2, 0xaf, 0xa6, 0x0, 0xaa, 0x0, 0x24, 0xd8, 0xb6);
DEFINE_GUID(DLSID_SampleMemorySize, 0x178f2f28, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
DEFINE_GUID(DLSID_ManufacturersID, 0xb03e1181, 0x8095, 0x11d2, 0xa1, 0xef, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8);
DEFINE_GUID(DLSID_ProductID, 0xb03e1182, 0x8095, 0x11d2, 0xa1, 0xef, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8);
DEFINE_GUID(DLSID_SamplePlaybackRate, 0x2a91f713, 0xa4bf, 0x11d2, 0xbb, 0xdf, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8);
#endif /* _INC_DLS2 */

View File

@@ -0,0 +1,619 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
instrum.c
Code to load and unload GUS-compatible instrument patches.
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "timidity.h"
#include "timidity_internal.h"
#include "options.h"
#include "common.h"
#include "instrum.h"
#include "instrum_dls.h"
#include "resample.h"
#include "tables.h"
static void free_instrument(MidInstrument *ip)
{
MidSample *sp;
int i;
if (!ip) return;
for (i=0; i<ip->samples; i++)
{
sp=&(ip->sample[i]);
free(sp->data);
}
free(ip->sample);
free(ip);
}
static void free_bank(MidSong *song, int dr, int b)
{
int i;
MidToneBank *bank=((dr) ? song->drumset[b] : song->tonebank[b]);
for (i=0; i<128; i++)
if (bank->instrument[i])
{
/* Not that this could ever happen, of course */
if (bank->instrument[i] != MAGIC_LOAD_INSTRUMENT)
free_instrument(bank->instrument[i]);
bank->instrument[i]=0;
}
}
static sint32 convert_envelope_rate(MidSong *song, uint8 rate)
{
sint32 r;
r = 3 - ((rate >> 6) & 0x3);
r *= 3;
r = (sint32) (rate & 0x3f) << r; /* 6.9 fixed point */
/* 15.15 fixed point. */
r = ((r * 44100) / song->rate) * song->control_ratio;
#ifdef FAST_DECAY
return r << 10;
#else
return r << 9;
#endif
}
static sint32 convert_envelope_offset(uint8 offset)
{
/* This is not too good... Can anyone tell me what these values mean?
Are they GUS-style "exponential" volumes? And what does that mean? */
/* 15.15 fixed point */
return offset << (7+15);
}
static sint32 convert_tremolo_sweep(MidSong *song, uint8 sweep)
{
if (!sweep)
return 0;
return
((song->control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) /
(song->rate * sweep);
}
static sint32 convert_vibrato_sweep(MidSong *song, uint8 sweep,
sint32 vib_control_ratio)
{
if (!sweep)
return 0;
return
(sint32) (FSCALE((double) (vib_control_ratio) * SWEEP_TUNING, SWEEP_SHIFT)
/ (double)(song->rate * sweep));
/* this was overflowing with seashore.pat
((vib_control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) /
(song->rate * sweep); */
}
static sint32 convert_tremolo_rate(MidSong *song, uint8 rate)
{
return
((SINE_CYCLE_LENGTH * song->control_ratio * rate) << RATE_SHIFT) /
(TREMOLO_RATE_TUNING * song->rate);
}
static sint32 convert_vibrato_rate(MidSong *song, uint8 rate)
{
/* Return a suitable vibrato_control_ratio value */
return
(VIBRATO_RATE_TUNING * song->rate) /
(rate * 2 * MID_VIBRATO_SAMPLE_INCREMENTS);
}
static void reverse_data(sint16 *sp, sint32 ls, sint32 le)
{
sint16 s, *ep=sp+le;
sp+=ls;
le-=ls;
le/=2;
while (le--)
{
s=*sp;
*sp++=*ep;
*ep--=s;
}
}
/*
If panning or note_to_use != -1, it will be used for all samples,
instead of the sample-specific values in the instrument file.
For note_to_use, any value <0 or >127 will be forced to 0.
For other parameters, 1 means yes, 0 means no, other values are
undefined.
TODO: do reverse loops right */
static MidInstrument *load_instrument(MidSong *song, char *name, int percussion,
int panning, int amp, int note_to_use,
int strip_loop, int strip_envelope,
int strip_tail)
{
MidInstrument *ip;
MidSample *sp;
FILE *fp;
char tmp[1024];
int i,j,noluck=0;
static char *patch_ext[] = PATCH_EXT_LIST;
if (!name) return 0;
/* Open patch file */
if ((fp=open_file(name)) == NULL)
{
noluck=1;
/* Try with various extensions */
for (i=0; patch_ext[i]; i++)
{
if (strlen(name)+strlen(patch_ext[i])<1024)
{
strcpy(tmp, name);
strcat(tmp, patch_ext[i]);
if ((fp=open_file(tmp)) != NULL)
{
noluck=0;
break;
}
}
}
}
if (noluck)
{
DEBUG_MSG("Instrument `%s' can't be found.\n", name);
return 0;
}
DEBUG_MSG("Loading instrument %s\n", tmp);
/* Read some headers and do cursory sanity checks. There are loads
of magic offsets. This could be rewritten... */
if ((239 != fread(tmp, 1, 239, fp)) ||
(memcmp(tmp, "GF1PATCH110\0ID#000002", 22) &&
memcmp(tmp, "GF1PATCH100\0ID#000002", 22))) /* don't know what the
differences are */
{
DEBUG_MSG("%s: not an instrument\n", name);
return 0;
}
if (tmp[82] != 1 && tmp[82] != 0) /* instruments. To some patch makers,
0 means 1 */
{
DEBUG_MSG("Can't handle patches with %d instruments\n", tmp[82]);
return 0;
}
if (tmp[151] != 1 && tmp[151] != 0) /* layers. What's a layer? */
{
DEBUG_MSG("Can't handle instruments with %d layers\n", tmp[151]);
return 0;
}
ip=safe_malloc(sizeof(MidInstrument));
ip->samples = tmp[198];
ip->sample = safe_malloc(sizeof(MidSample) * ip->samples);
for (i=0; i<ip->samples; i++)
{
uint8 fractions;
sint32 tmplong;
uint16 tmpshort;
uint8 tmpchar;
#define READ_CHAR(thing) \
if (1 != fread(&tmpchar, 1, 1, fp)) goto fail; \
thing = tmpchar;
#define READ_SHORT(thing) \
if (1 != fread(&tmpshort, 2, 1, fp)) goto fail; \
thing = SWAPLE16(tmpshort);
#define READ_LONG(thing) \
if (1 != fread(&tmplong, 4, 1, fp)) goto fail; \
thing = SWAPLE32(tmplong);
fseek(fp, 7, SEEK_CUR); /* Skip the wave name */
if (1 != fread(&fractions, 1, 1, fp))
{
fail:
DEBUG_MSG("Error reading sample %d\n", i);
for (j=0; j<i; j++)
free(ip->sample[j].data);
free(ip->sample);
free(ip);
return 0;
}
sp=&(ip->sample[i]);
READ_LONG(sp->data_length);
READ_LONG(sp->loop_start);
READ_LONG(sp->loop_end);
READ_SHORT(sp->sample_rate);
READ_LONG(sp->low_freq);
READ_LONG(sp->high_freq);
READ_LONG(sp->root_freq);
sp->low_vel = 0;
sp->high_vel = 127;
fseek(fp, 2, SEEK_CUR); /* Why have a "root frequency" and then
* "tuning"?? */
READ_CHAR(tmp[0]);
if (panning==-1)
sp->panning = (tmp[0] * 8 + 4) & 0x7f;
else
sp->panning=(uint8)(panning & 0x7F);
/* envelope, tremolo, and vibrato */
if (18 != fread(tmp, 1, 18, fp)) goto fail;
if (!tmp[13] || !tmp[14])
{
sp->tremolo_sweep_increment=
sp->tremolo_phase_increment=sp->tremolo_depth=0;
DEBUG_MSG(" * no tremolo\n");
}
else
{
sp->tremolo_sweep_increment=convert_tremolo_sweep(song, tmp[12]);
sp->tremolo_phase_increment=convert_tremolo_rate(song, tmp[13]);
sp->tremolo_depth=tmp[14];
DEBUG_MSG(" * tremolo: sweep %d, phase %d, depth %d\n",
sp->tremolo_sweep_increment, sp->tremolo_phase_increment,
sp->tremolo_depth);
}
if (!tmp[16] || !tmp[17])
{
sp->vibrato_sweep_increment=
sp->vibrato_control_ratio=sp->vibrato_depth=0;
DEBUG_MSG(" * no vibrato\n");
}
else
{
sp->vibrato_control_ratio=convert_vibrato_rate(song, tmp[16]);
sp->vibrato_sweep_increment=
convert_vibrato_sweep(song, tmp[15], sp->vibrato_control_ratio);
sp->vibrato_depth=tmp[17];
DEBUG_MSG(" * vibrato: sweep %d, ctl %d, depth %d\n",
sp->vibrato_sweep_increment, sp->vibrato_control_ratio,
sp->vibrato_depth);
}
READ_CHAR(sp->modes);
fseek(fp, 40, SEEK_CUR); /* skip the useless scale frequency, scale
factor (what's it mean?), and reserved
space */
/* Mark this as a fixed-pitch instrument if such a deed is desired. */
if (note_to_use!=-1)
sp->note_to_use=(uint8)(note_to_use);
else
sp->note_to_use=0;
/* seashore.pat in the Midia patch set has no Sustain. I don't
understand why, and fixing it by adding the Sustain flag to
all looped patches probably breaks something else. We do it
anyway. */
if (sp->modes & MODES_LOOPING)
sp->modes |= MODES_SUSTAIN;
/* Strip any loops and envelopes we're permitted to */
if ((strip_loop==1) &&
(sp->modes & (MODES_SUSTAIN | MODES_LOOPING |
MODES_PINGPONG | MODES_REVERSE)))
{
DEBUG_MSG(" - Removing loop and/or sustain\n");
sp->modes &=~(MODES_SUSTAIN | MODES_LOOPING |
MODES_PINGPONG | MODES_REVERSE);
}
if (strip_envelope==1)
{
if (sp->modes & MODES_ENVELOPE)
DEBUG_MSG(" - Removing envelope\n");
sp->modes &= ~MODES_ENVELOPE;
}
else if (strip_envelope != 0)
{
/* Have to make a guess. */
if (!(sp->modes & (MODES_LOOPING | MODES_PINGPONG | MODES_REVERSE)))
{
/* No loop? Then what's there to sustain? No envelope needed
either... */
sp->modes &= ~(MODES_SUSTAIN|MODES_ENVELOPE);
DEBUG_MSG(" - No loop, removing sustain and envelope\n");
}
else if (!memcmp(tmp, "??????", 6) || tmp[11] >= 100)
{
/* Envelope rates all maxed out? Envelope end at a high "offset"?
That's a weird envelope. Take it out. */
sp->modes &= ~MODES_ENVELOPE;
DEBUG_MSG(" - Weirdness, removing envelope\n");
}
else if (!(sp->modes & MODES_SUSTAIN))
{
/* No sustain? Then no envelope. I don't know if this is
justified, but patches without sustain usually don't need the
envelope either... at least the Gravis ones. They're mostly
drums. I think. */
sp->modes &= ~MODES_ENVELOPE;
DEBUG_MSG(" - No sustain, removing envelope\n");
}
}
for (j=0; j<6; j++)
{
sp->envelope_rate[j]=
convert_envelope_rate(song, tmp[j]);
sp->envelope_offset[j]=
convert_envelope_offset(tmp[6+j]);
}
/* Then read the sample data */
sp->data = safe_malloc(sp->data_length);
if (1 != fread(sp->data, sp->data_length, 1, fp))
goto fail;
if (!(sp->modes & MODES_16BIT)) /* convert to 16-bit data */
{
sint32 i=sp->data_length;
uint8 *cp=(uint8 *)(sp->data);
uint16 *tmp,*new;
tmp=new=safe_malloc(sp->data_length*2);
while (i--)
*tmp++ = (uint16)(*cp++) << 8;
cp=(uint8 *)(sp->data);
sp->data = (sample_t *)new;
free(cp);
sp->data_length *= 2;
sp->loop_start *= 2;
sp->loop_end *= 2;
}
#ifndef LITTLE_ENDIAN
else
/* convert to machine byte order */
{
sint32 i=sp->data_length/2;
sint16 *tmp=(sint16 *)sp->data,s;
while (i--)
{
s=SWAPLE16(*tmp);
*tmp++=s;
}
}
#endif
if (sp->modes & MODES_UNSIGNED) /* convert to signed data */
{
sint32 i=sp->data_length/2;
sint16 *tmp=(sint16 *)sp->data;
while (i--)
*tmp++ ^= 0x8000;
}
/* Reverse reverse loops and pass them off as normal loops */
if (sp->modes & MODES_REVERSE)
{
sint32 t;
/* The GUS apparently plays reverse loops by reversing the
whole sample. We do the same because the GUS does not SUCK. */
DEBUG_MSG("Reverse loop in %s\n", name);
reverse_data((sint16 *)sp->data, 0, sp->data_length/2);
t=sp->loop_start;
sp->loop_start=sp->data_length - sp->loop_end;
sp->loop_end=sp->data_length - t;
sp->modes &= ~MODES_REVERSE;
sp->modes |= MODES_LOOPING; /* just in case */
}
#ifdef ADJUST_SAMPLE_VOLUMES
if (amp!=-1)
sp->volume=(float)((amp) / 100.0);
else
{
/* Try to determine a volume scaling factor for the sample.
This is a very crude adjustment, but things sound more
balanced with it. Still, this should be a runtime option. */
sint32 i=sp->data_length/2;
sint16 maxamp=0,a;
sint16 *tmp=(sint16 *)sp->data;
while (i--)
{
a=*tmp++;
if (a<0) a=-a;
if (a>maxamp)
maxamp=a;
}
sp->volume=(float)(32768.0 / maxamp);
DEBUG_MSG(" * volume comp: %f\n", sp->volume);
}
#else
if (amp!=-1)
sp->volume=(double)(amp) / 100.0;
else
sp->volume=1.0;
#endif
sp->data_length /= 2; /* These are in bytes. Convert into samples. */
sp->loop_start /= 2;
sp->loop_end /= 2;
/* Then fractional samples */
sp->data_length <<= FRACTION_BITS;
sp->loop_start <<= FRACTION_BITS;
sp->loop_end <<= FRACTION_BITS;
/* Adjust for fractional loop points. This is a guess. Does anyone
know what "fractions" really stands for? */
sp->loop_start |=
(fractions & 0x0F) << (FRACTION_BITS-4);
sp->loop_end |=
((fractions>>4) & 0x0F) << (FRACTION_BITS-4);
/* If this instrument will always be played on the same note,
and it's not looped, we can resample it now. */
if (sp->note_to_use && !(sp->modes & MODES_LOOPING))
pre_resample(song, sp);
if (strip_tail==1)
{
/* Let's not really, just say we did. */
DEBUG_MSG(" - Stripping tail\n");
sp->data_length = sp->loop_end;
}
}
fclose(fp);
return ip;
}
static int fill_bank(MidSong *song, int dr, int b)
{
int i, errors=0;
MidToneBank *bank=((dr) ? song->drumset[b] : song->tonebank[b]);
if (!bank)
{
DEBUG_MSG("Huh. Tried to load instruments in non-existent %s %d\n",
(dr) ? "drumset" : "tone bank", b);
return 0;
}
for (i=0; i<128; i++)
{
if (bank->instrument[i]==MAGIC_LOAD_INSTRUMENT)
{
bank->instrument[i]=load_instrument_dls(song, dr, b, i);
if (bank->instrument[i])
{
continue;
}
if (!(bank->tone[i].name))
{
DEBUG_MSG("No instrument mapped to %s %d, program %d%s\n",
(dr)? "drum set" : "tone bank", b, i,
(b!=0) ? "" : " - this instrument will not be heard");
if (b!=0)
{
/* Mark the corresponding instrument in the default
bank / drumset for loading (if it isn't already) */
if (!dr)
{
if (!(song->tonebank[0]->instrument[i]))
song->tonebank[0]->instrument[i] =
MAGIC_LOAD_INSTRUMENT;
}
else
{
if (!(song->drumset[0]->instrument[i]))
song->drumset[0]->instrument[i] =
MAGIC_LOAD_INSTRUMENT;
}
}
bank->instrument[i] = 0;
errors++;
}
else if (!(bank->instrument[i] =
load_instrument(song,
bank->tone[i].name,
(dr) ? 1 : 0,
bank->tone[i].pan,
bank->tone[i].amp,
(bank->tone[i].note!=-1) ?
bank->tone[i].note :
((dr) ? i : -1),
(bank->tone[i].strip_loop!=-1) ?
bank->tone[i].strip_loop :
((dr) ? 1 : -1),
(bank->tone[i].strip_envelope != -1) ?
bank->tone[i].strip_envelope :
((dr) ? 1 : -1),
bank->tone[i].strip_tail )))
{
DEBUG_MSG("Couldn't load instrument %s (%s %d, program %d)\n",
bank->tone[i].name,
(dr)? "drum set" : "tone bank", b, i);
errors++;
}
}
}
return errors;
}
int load_missing_instruments(MidSong *song)
{
int i=128,errors=0;
while (i--)
{
if (song->tonebank[i])
errors+=fill_bank(song,0,i);
if (song->drumset[i])
errors+=fill_bank(song,1,i);
}
return errors;
}
void free_instruments(MidSong *song)
{
int i=128;
while(i--)
{
if (song->tonebank[i])
free_bank(song, 0, i);
if (song->drumset[i])
free_bank(song, 1, i);
}
}
int set_default_instrument(MidSong *song, char *name)
{
MidInstrument *ip;
if (!(ip=load_instrument(song, name, 0, -1, -1, -1, 0, 0, 0)))
return -1;
song->default_instrument = ip;
song->default_program = SPECIAL_PROGRAM;
return 0;
}

View File

@@ -0,0 +1,41 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
instrum.h
*/
/* Bits in modes: */
#define MODES_16BIT (1<<0)
#define MODES_UNSIGNED (1<<1)
#define MODES_LOOPING (1<<2)
#define MODES_PINGPONG (1<<3)
#define MODES_REVERSE (1<<4)
#define MODES_SUSTAIN (1<<5)
#define MODES_ENVELOPE (1<<6)
/* A hack to delay instrument loading until after reading the
entire MIDI file. */
#define MAGIC_LOAD_INSTRUMENT ((MidInstrument *) (-1))
#define SPECIAL_PROGRAM -1
extern int load_missing_instruments(MidSong *song);
extern void free_instruments(MidSong *song);
extern int set_default_instrument(MidSong *song, char *name);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,24 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
instrum.h
*/
extern MidInstrument *load_instrument_dls(MidSong *song, int drum, int bank, int instrument);

View File

@@ -0,0 +1,569 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
Suddenly, you realize that this program is free software; you get
an overwhelming urge to redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received another copy of the GNU General Public
License along with this program; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
I bet they'll be amazed.
mix.c */
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "timidity.h"
#include "timidity_internal.h"
#include "options.h"
#include "instrum.h"
#include "playmidi.h"
#include "output.h"
#include "tables.h"
#include "resample.h"
#include "mix.h"
/* Returns 1 if envelope runs out */
int recompute_envelope(MidSong *song, int v)
{
int stage;
stage = song->voice[v].envelope_stage;
if (stage>5)
{
/* Envelope ran out. */
song->voice[v].status = VOICE_FREE;
return 1;
}
if (song->voice[v].sample->modes & MODES_ENVELOPE)
{
if (song->voice[v].status==VOICE_ON || song->voice[v].status==VOICE_SUSTAINED)
{
if (stage>2)
{
/* Freeze envelope until note turns off. Trumpets want this. */
song->voice[v].envelope_increment=0;
return 0;
}
}
}
song->voice[v].envelope_stage=stage+1;
if (song->voice[v].envelope_volume==song->voice[v].sample->envelope_offset[stage])
return recompute_envelope(song, v);
song->voice[v].envelope_target = song->voice[v].sample->envelope_offset[stage];
song->voice[v].envelope_increment = song->voice[v].sample->envelope_rate[stage];
if (song->voice[v].envelope_target < song->voice[v].envelope_volume)
song->voice[v].envelope_increment = -song->voice[v].envelope_increment;
return 0;
}
void apply_envelope_to_amp(MidSong *song, int v)
{
float lamp = song->voice[v].left_amp, ramp;
sint32 la,ra;
if (song->voice[v].panned == PANNED_MYSTERY)
{
ramp = song->voice[v].right_amp;
if (song->voice[v].tremolo_phase_increment)
{
lamp *= song->voice[v].tremolo_volume;
ramp *= song->voice[v].tremolo_volume;
}
if (song->voice[v].sample->modes & MODES_ENVELOPE)
{
lamp *= (float)vol_table[song->voice[v].envelope_volume>>23];
ramp *= (float)vol_table[song->voice[v].envelope_volume>>23];
}
la = (sint32)FSCALE(lamp,AMP_BITS);
if (la>MAX_AMP_VALUE)
la=MAX_AMP_VALUE;
ra = (sint32)FSCALE(ramp,AMP_BITS);
if (ra>MAX_AMP_VALUE)
ra=MAX_AMP_VALUE;
song->voice[v].left_mix = la;
song->voice[v].right_mix = ra;
}
else
{
if (song->voice[v].tremolo_phase_increment)
lamp *= song->voice[v].tremolo_volume;
if (song->voice[v].sample->modes & MODES_ENVELOPE)
lamp *= (float)vol_table[song->voice[v].envelope_volume>>23];
la = (sint32)FSCALE(lamp,AMP_BITS);
if (la>MAX_AMP_VALUE)
la=MAX_AMP_VALUE;
song->voice[v].left_mix = la;
}
}
static int update_envelope(MidSong *song, int v)
{
song->voice[v].envelope_volume += song->voice[v].envelope_increment;
/* Why is there no ^^ operator?? */
if (((song->voice[v].envelope_increment < 0) &&
(song->voice[v].envelope_volume <= song->voice[v].envelope_target)) ||
((song->voice[v].envelope_increment > 0) &&
(song->voice[v].envelope_volume >= song->voice[v].envelope_target)))
{
song->voice[v].envelope_volume = song->voice[v].envelope_target;
if (recompute_envelope(song, v))
return 1;
}
return 0;
}
static void update_tremolo(MidSong *song, int v)
{
sint32 depth = song->voice[v].sample->tremolo_depth << 7;
if (song->voice[v].tremolo_sweep)
{
/* Update sweep position */
song->voice[v].tremolo_sweep_position += song->voice[v].tremolo_sweep;
if (song->voice[v].tremolo_sweep_position >= (1 << SWEEP_SHIFT))
song->voice[v].tremolo_sweep=0; /* Swept to max amplitude */
else
{
/* Need to adjust depth */
depth *= song->voice[v].tremolo_sweep_position;
depth >>= SWEEP_SHIFT;
}
}
song->voice[v].tremolo_phase += song->voice[v].tremolo_phase_increment;
/* if (song->voice[v].tremolo_phase >= (SINE_CYCLE_LENGTH<<RATE_SHIFT))
song->voice[v].tremolo_phase -= SINE_CYCLE_LENGTH<<RATE_SHIFT; */
song->voice[v].tremolo_volume = (float)
(1.0 - FSCALENEG((sine(song->voice[v].tremolo_phase >> RATE_SHIFT) + 1.0)
* depth * TREMOLO_AMPLITUDE_TUNING,
17));
/* I'm not sure about the +1.0 there -- it makes tremoloed voices'
volumes on average the lower the higher the tremolo amplitude. */
}
/* Returns 1 if the note died */
static int update_signal(MidSong *song, int v)
{
if (song->voice[v].envelope_increment && update_envelope(song, v))
return 1;
if (song->voice[v].tremolo_phase_increment)
update_tremolo(song, v);
apply_envelope_to_amp(song, v);
return 0;
}
#define MIXATION(a) *lp++ += (a)*s;
static void mix_mystery_signal(MidSong *song, sample_t *sp, sint32 *lp, int v,
int count)
{
MidVoice *vp = song->voice + v;
final_volume_t
left=vp->left_mix,
right=vp->right_mix;
int cc;
sample_t s;
if (!(cc = vp->control_counter))
{
cc = song->control_ratio;
if (update_signal(song, v))
return; /* Envelope ran out */
left = vp->left_mix;
right = vp->right_mix;
}
while (count)
if (cc < count)
{
count -= cc;
while (cc--)
{
s = *sp++;
MIXATION(left);
MIXATION(right);
}
cc = song->control_ratio;
if (update_signal(song, v))
return; /* Envelope ran out */
left = vp->left_mix;
right = vp->right_mix;
}
else
{
vp->control_counter = cc - count;
while (count--)
{
s = *sp++;
MIXATION(left);
MIXATION(right);
}
return;
}
}
static void mix_center_signal(MidSong *song, sample_t *sp, sint32 *lp, int v,
int count)
{
MidVoice *vp = song->voice + v;
final_volume_t
left=vp->left_mix;
int cc;
sample_t s;
if (!(cc = vp->control_counter))
{
cc = song->control_ratio;
if (update_signal(song, v))
return; /* Envelope ran out */
left = vp->left_mix;
}
while (count)
if (cc < count)
{
count -= cc;
while (cc--)
{
s = *sp++;
MIXATION(left);
MIXATION(left);
}
cc = song->control_ratio;
if (update_signal(song, v))
return; /* Envelope ran out */
left = vp->left_mix;
}
else
{
vp->control_counter = cc - count;
while (count--)
{
s = *sp++;
MIXATION(left);
MIXATION(left);
}
return;
}
}
static void mix_single_signal(MidSong *song, sample_t *sp, sint32 *lp, int v,
int count)
{
MidVoice *vp = song->voice + v;
final_volume_t
left=vp->left_mix;
int cc;
sample_t s;
if (!(cc = vp->control_counter))
{
cc = song->control_ratio;
if (update_signal(song, v))
return; /* Envelope ran out */
left = vp->left_mix;
}
while (count)
if (cc < count)
{
count -= cc;
while (cc--)
{
s = *sp++;
MIXATION(left);
lp++;
}
cc = song->control_ratio;
if (update_signal(song, v))
return; /* Envelope ran out */
left = vp->left_mix;
}
else
{
vp->control_counter = cc - count;
while (count--)
{
s = *sp++;
MIXATION(left);
lp++;
}
return;
}
}
static void mix_mono_signal(MidSong *song, sample_t *sp, sint32 *lp, int v,
int count)
{
MidVoice *vp = song->voice + v;
final_volume_t
left=vp->left_mix;
int cc;
sample_t s;
if (!(cc = vp->control_counter))
{
cc = song->control_ratio;
if (update_signal(song, v))
return; /* Envelope ran out */
left = vp->left_mix;
}
while (count)
if (cc < count)
{
count -= cc;
while (cc--)
{
s = *sp++;
MIXATION(left);
}
cc = song->control_ratio;
if (update_signal(song, v))
return; /* Envelope ran out */
left = vp->left_mix;
}
else
{
vp->control_counter = cc - count;
while (count--)
{
s = *sp++;
MIXATION(left);
}
return;
}
}
static void mix_mystery(MidSong *song, sample_t *sp, sint32 *lp, int v, int count)
{
final_volume_t
left = song->voice[v].left_mix,
right = song->voice[v].right_mix;
sample_t s;
while (count--)
{
s = *sp++;
MIXATION(left);
MIXATION(right);
}
}
static void mix_center(MidSong *song, sample_t *sp, sint32 *lp, int v, int count)
{
final_volume_t
left = song->voice[v].left_mix;
sample_t s;
while (count--)
{
s = *sp++;
MIXATION(left);
MIXATION(left);
}
}
static void mix_single(MidSong *song, sample_t *sp, sint32 *lp, int v, int count)
{
final_volume_t
left = song->voice[v].left_mix;
sample_t s;
while (count--)
{
s = *sp++;
MIXATION(left);
lp++;
}
}
static void mix_mono(MidSong *song, sample_t *sp, sint32 *lp, int v, int count)
{
final_volume_t
left = song->voice[v].left_mix;
sample_t s;
while (count--)
{
s = *sp++;
MIXATION(left);
}
}
/* Ramp a note out in c samples */
static void ramp_out(MidSong *song, sample_t *sp, sint32 *lp, int v, sint32 c)
{
/* should be final_volume_t, but uint8 gives trouble. */
sint32 left, right, li, ri;
sample_t s=0; /* silly warning about uninitialized s */
/* Fix by James Caldwell */
if ( c == 0 ) c = 1;
left=song->voice[v].left_mix;
li=-(left/c);
if (!li) li=-1;
/* printf("Ramping out: left=%d, c=%d, li=%d\n", left, c, li); */
if (!(song->encoding & PE_MONO))
{
if (song->voice[v].panned==PANNED_MYSTERY)
{
right=song->voice[v].right_mix;
ri=-(right/c);
while (c--)
{
left += li;
if (left<0)
left=0;
right += ri;
if (right<0)
right=0;
s=*sp++;
MIXATION(left);
MIXATION(right);
}
}
else if (song->voice[v].panned==PANNED_CENTER)
{
while (c--)
{
left += li;
if (left<0)
return;
s=*sp++;
MIXATION(left);
MIXATION(left);
}
}
else if (song->voice[v].panned==PANNED_LEFT)
{
while (c--)
{
left += li;
if (left<0)
return;
s=*sp++;
MIXATION(left);
lp++;
}
}
else if (song->voice[v].panned==PANNED_RIGHT)
{
while (c--)
{
left += li;
if (left<0)
return;
s=*sp++;
lp++;
MIXATION(left);
}
}
}
else
{
/* Mono output. */
while (c--)
{
left += li;
if (left<0)
return;
s=*sp++;
MIXATION(left);
}
}
}
/**************** interface function ******************/
void mix_voice(MidSong *song, sint32 *buf, int v, sint32 c)
{
MidVoice *vp = song->voice + v;
sample_t *sp;
if (vp->status==VOICE_DIE)
{
if (c>=MAX_DIE_TIME)
c=MAX_DIE_TIME;
sp=resample_voice(song, v, &c);
ramp_out(song, sp, buf, v, c);
vp->status=VOICE_FREE;
}
else
{
sp=resample_voice(song, v, &c);
if (song->encoding & PE_MONO)
{
/* Mono output. */
if (vp->envelope_increment || vp->tremolo_phase_increment)
mix_mono_signal(song, sp, buf, v, c);
else
mix_mono(song, sp, buf, v, c);
}
else
{
if (vp->panned == PANNED_MYSTERY)
{
if (vp->envelope_increment || vp->tremolo_phase_increment)
mix_mystery_signal(song, sp, buf, v, c);
else
mix_mystery(song, sp, buf, v, c);
}
else if (vp->panned == PANNED_CENTER)
{
if (vp->envelope_increment || vp->tremolo_phase_increment)
mix_center_signal(song, sp, buf, v, c);
else
mix_center(song, sp, buf, v, c);
}
else
{
/* It's either full left or full right. In either case,
every other sample is 0. Just get the offset right: */
if (vp->panned == PANNED_RIGHT) buf++;
if (vp->envelope_increment || vp->tremolo_phase_increment)
mix_single_signal(song, sp, buf, v, c);
else
mix_single(song, sp, buf, v, c);
}
}
}
}

View File

@@ -0,0 +1,27 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
In case you haven't heard, this program is free software;
you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
mix.h
*/
extern void mix_voice(MidSong *song, sint32 *buf, int v, sint32 c);
extern int recompute_envelope(MidSong *song, int v);
extern void apply_envelope_to_amp(MidSong *song, int v);

View File

@@ -0,0 +1,113 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* When a patch file can't be opened, one of these extensions is
appended to the filename and the open is tried again.
*/
#define PATCH_EXT_LIST { ".pat", 0 }
/* Acoustic Grand Piano seems to be the usual default instrument. */
#define DEFAULT_PROGRAM 0
/* 9 here is MIDI channel 10, which is the standard percussion channel.
Some files (notably C:\WINDOWS\CANYON.MID) think that 16 is one too.
On the other hand, some files know that 16 is not a drum channel and
try to play music on it. This is now a runtime option, so this isn't
a critical choice anymore. */
#define DEFAULT_DRUMCHANNELS ((1<<9) | (1<<15))
/* In percent. */
#define DEFAULT_AMPLIFICATION 70
/* Default polyphony */
#define DEFAULT_VOICES 32
/* 1000 here will give a control ratio of 22:1 with 22 kHz output.
Higher CONTROLS_PER_SECOND values allow more accurate rendering
of envelopes and tremolo. The cost is CPU time. */
#define CONTROLS_PER_SECOND 1000
/* Make envelopes twice as fast. Saves ~20% CPU time (notes decay
faster) and sounds more like a GUS. There is now a command line
option to toggle this as well. */
#define FAST_DECAY
/* How many bits to use for the fractional part of sample positions.
This affects tonal accuracy. The entire position counter must fit
in 32 bits, so with FRACTION_BITS equal to 12, the maximum size of
a sample is 1048576 samples (2 megabytes in memory). The GUS gets
by with just 9 bits and a little help from its friends...
"The GUS does not SUCK!!!" -- a happy user :) */
#define FRACTION_BITS 12
/* For some reason the sample volume is always set to maximum in all
patch files. Define this for a crude adjustment that may help
equalize instrument volumes. */
#define ADJUST_SAMPLE_VOLUMES
/* The number of samples to use for ramping out a dying note. Affects
click removal. */
#define MAX_DIE_TIME 20
/**************************************************************************/
/* Anything below this shouldn't need to be changed unless you're porting
to a new machine with other than 32-bit, big-endian words. */
/**************************************************************************/
/* change FRACTION_BITS above, not these */
#define INTEGER_MASK (0xFFFFFFFF << FRACTION_BITS)
#define FRACTION_MASK (~ INTEGER_MASK)
/* This is enforced by some computations that must fit in an int */
#define MAX_CONTROL_RATIO 255
#define MAX_AMPLIFICATION 800
/* The TiMidity configuration file */
#define CONFIG_FILE "timidity.cfg"
/* These affect general volume */
#define GUARD_BITS 3
#define AMP_BITS (15-GUARD_BITS)
#define MAX_AMP_VALUE ((1<<(AMP_BITS+1))-1)
#define FSCALE(a,b) (float)((a) * (double)(1<<(b)))
#define FSCALENEG(a,b) (float)((a) * (1.0L / (double)(1<<(b))))
/* Vibrato and tremolo Choices of the Day */
#define SWEEP_TUNING 38
#define VIBRATO_AMPLITUDE_TUNING 1.0L
#define VIBRATO_RATE_TUNING 38
#define TREMOLO_AMPLITUDE_TUNING 1.0L
#define TREMOLO_RATE_TUNING 38
#define SWEEP_SHIFT 16
#define RATE_SHIFT 5
#ifndef PI
#define PI 3.14159265358979323846
#endif
/* The path separator (D.M.) */
#ifdef WIN32
# define PATH_SEP '\\'
#else
# define PATH_SEP '/'
#endif

View File

@@ -0,0 +1,113 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
output.c
Audio output (to file / device) functions.
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include "timidity.h"
#include "timidity_internal.h"
#include "options.h"
#include "output.h"
/*****************************************************************/
/* Some functions to convert signed 32-bit data to other formats */
void s32tos8(void *dp, sint32 *lp, sint32 c)
{
sint8 *cp=(sint8 *)(dp);
sint32 l;
while (c--)
{
l=(*lp++)>>(32-8-GUARD_BITS);
if (l>127) l=127;
else if (l<-128) l=-128;
*cp++ = (sint8) (l);
}
}
void s32tou8(void *dp, sint32 *lp, sint32 c)
{
uint8 *cp=(uint8 *)(dp);
sint32 l;
while (c--)
{
l=(*lp++)>>(32-8-GUARD_BITS);
if (l>127) l=127;
else if (l<-128) l=-128;
*cp++ = 0x80 ^ ((uint8) l);
}
}
void s32tos16(void *dp, sint32 *lp, sint32 c)
{
sint16 *sp=(sint16 *)(dp);
sint32 l;
while (c--)
{
l=(*lp++)>>(32-16-GUARD_BITS);
if (l > 32767) l=32767;
else if (l<-32768) l=-32768;
*sp++ = (sint16)(l);
}
}
void s32tou16(void *dp, sint32 *lp, sint32 c)
{
uint16 *sp=(uint16 *)(dp);
sint32 l;
while (c--)
{
l=(*lp++)>>(32-16-GUARD_BITS);
if (l > 32767) l=32767;
else if (l<-32768) l=-32768;
*sp++ = 0x8000 ^ (uint16)(l);
}
}
void s32tos16x(void *dp, sint32 *lp, sint32 c)
{
sint16 *sp=(sint16 *)(dp);
sint32 l;
while (c--)
{
l=(*lp++)>>(32-16-GUARD_BITS);
if (l > 32767) l=32767;
else if (l<-32768) l=-32768;
*sp++ = XCHG_SHORT((sint16)(l));
}
}
void s32tou16x(void *dp, sint32 *lp, sint32 c)
{
uint16 *sp=(uint16 *)(dp);
sint32 l;
while (c--)
{
l=(*lp++)>>(32-16-GUARD_BITS);
if (l > 32767) l=32767;
else if (l<-32768) l=-32768;
*sp++ = XCHG_SHORT(0x8000 ^ (uint16)(l));
}
}

View File

@@ -0,0 +1,56 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
output.h
*/
/* Data format encoding bits */
#define PE_MONO 0x01 /* versus stereo */
#define PE_SIGNED 0x02 /* versus unsigned */
#define PE_16BIT 0x04 /* versus 8-bit */
/* Conversion functions -- These overwrite the sint32 data in *lp with
data in another format */
/* 8-bit signed and unsigned*/
extern void s32tos8(void *dp, sint32 *lp, sint32 c);
extern void s32tou8(void *dp, sint32 *lp, sint32 c);
/* 16-bit */
extern void s32tos16(void *dp, sint32 *lp, sint32 c);
extern void s32tou16(void *dp, sint32 *lp, sint32 c);
/* byte-exchanged 16-bit */
extern void s32tos16x(void *dp, sint32 *lp, sint32 c);
extern void s32tou16x(void *dp, sint32 *lp, sint32 c);
/* little-endian and big-endian specific */
#ifdef LITTLE_ENDIAN
#define s32tou16l s32tou16
#define s32tou16b s32tou16x
#define s32tos16l s32tos16
#define s32tos16b s32tos16x
#else
#define s32tou16l s32tou16x
#define s32tou16b s32tou16
#define s32tos16l s32tos16x
#define s32tos16b s32tos16
#endif

View File

@@ -0,0 +1,804 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
playmidi.c -- random stuff in need of rearrangement
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "timidity.h"
#include "timidity_internal.h"
#include "options.h"
#include "instrum.h"
#include "playmidi.h"
#include "output.h"
#include "mix.h"
#include "tables.h"
static void adjust_amplification(MidSong *song)
{
song->master_volume = (float)(song->amplification) / (float)100.0;
}
static void reset_voices(MidSong *song)
{
int i;
for (i=0; i<MID_MAX_VOICES; i++)
song->voice[i].status=VOICE_FREE;
}
/* Process the Reset All Controllers event */
static void reset_controllers(MidSong *song, int c)
{
song->channel[c].volume=90; /* Some standard says, although the SCC docs say 0. */
song->channel[c].expression=127; /* SCC-1 does this. */
song->channel[c].sustain=0;
song->channel[c].pitchbend=0x2000;
song->channel[c].pitchfactor=0; /* to be computed */
}
static void reset_midi(MidSong *song)
{
int i;
for (i=0; i<16; i++)
{
reset_controllers(song, i);
/* The rest of these are unaffected by the Reset All Controllers event */
song->channel[i].program=song->default_program;
song->channel[i].panning=NO_PANNING;
song->channel[i].pitchsens=2;
song->channel[i].bank=0; /* tone bank or drum set */
}
reset_voices(song);
}
static void select_sample(MidSong *song, int v, MidInstrument *ip, int vel)
{
sint32 f, cdiff, diff;
int s,i;
MidSample *sp, *closest;
s=ip->samples;
sp=ip->sample;
if (s==1)
{
song->voice[v].sample=sp;
return;
}
f=song->voice[v].orig_frequency;
for (i=0; i<s; i++)
{
if (sp->low_vel <= vel && sp->high_vel >= vel &&
sp->low_freq <= f && sp->high_freq >= f)
{
song->voice[v].sample=sp;
return;
}
sp++;
}
/*
No suitable sample found! We'll select the sample whose root
frequency is closest to the one we want. (Actually we should
probably convert the low, high, and root frequencies to MIDI note
values and compare those.) */
cdiff=0x7FFFFFFF;
closest=sp=ip->sample;
for(i=0; i<s; i++)
{
diff=sp->root_freq - f;
if (diff<0) diff=-diff;
if (diff<cdiff)
{
cdiff=diff;
closest=sp;
}
sp++;
}
song->voice[v].sample=closest;
return;
}
static void recompute_freq(MidSong *song, int v)
{
int
sign=(song->voice[v].sample_increment < 0), /* for bidirectional loops */
pb=song->channel[song->voice[v].channel].pitchbend;
double a;
if (!song->voice[v].sample->sample_rate)
return;
if (song->voice[v].vibrato_control_ratio)
{
/* This instrument has vibrato. Invalidate any precomputed
sample_increments. */
int i=MID_VIBRATO_SAMPLE_INCREMENTS;
while (i--)
song->voice[v].vibrato_sample_increment[i]=0;
}
if (pb==0x2000 || pb<0 || pb>0x3FFF)
song->voice[v].frequency = song->voice[v].orig_frequency;
else
{
pb-=0x2000;
if (!(song->channel[song->voice[v].channel].pitchfactor))
{
/* Damn. Somebody bent the pitch. */
sint32 i=pb*song->channel[song->voice[v].channel].pitchsens;
if (pb<0)
i=-i;
song->channel[song->voice[v].channel].pitchfactor=
(float)(bend_fine[(i>>5) & 0xFF] * bend_coarse[i>>13]);
}
if (pb>0)
song->voice[v].frequency=
(sint32)(song->channel[song->voice[v].channel].pitchfactor *
(double)(song->voice[v].orig_frequency));
else
song->voice[v].frequency=
(sint32)((double)(song->voice[v].orig_frequency) /
song->channel[song->voice[v].channel].pitchfactor);
}
a = FSCALE(((double)(song->voice[v].sample->sample_rate) *
(double)(song->voice[v].frequency)) /
((double)(song->voice[v].sample->root_freq) *
(double)(song->rate)),
FRACTION_BITS);
if (sign)
a = -a; /* need to preserve the loop direction */
song->voice[v].sample_increment = (sint32)(a);
}
static void recompute_amp(MidSong *song, int v)
{
sint32 tempamp;
/* TODO: use fscale */
tempamp= (song->voice[v].velocity *
song->channel[song->voice[v].channel].volume *
song->channel[song->voice[v].channel].expression); /* 21 bits */
if (!(song->encoding & PE_MONO))
{
if (song->voice[v].panning > 60 && song->voice[v].panning < 68)
{
song->voice[v].panned=PANNED_CENTER;
song->voice[v].left_amp=
FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume,
21);
}
else if (song->voice[v].panning<5)
{
song->voice[v].panned = PANNED_LEFT;
song->voice[v].left_amp=
FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume,
20);
}
else if (song->voice[v].panning>123)
{
song->voice[v].panned = PANNED_RIGHT;
song->voice[v].left_amp= /* left_amp will be used */
FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume,
20);
}
else
{
song->voice[v].panned = PANNED_MYSTERY;
song->voice[v].left_amp=
FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume,
27);
song->voice[v].right_amp = song->voice[v].left_amp * (song->voice[v].panning);
song->voice[v].left_amp *= (float)(127 - song->voice[v].panning);
}
}
else
{
song->voice[v].panned = PANNED_CENTER;
song->voice[v].left_amp=
FSCALENEG((double)(tempamp) * song->voice[v].sample->volume * song->master_volume,
21);
}
}
static void start_note(MidSong *song, MidEvent *e, int i)
{
MidInstrument *ip;
int j;
if (ISDRUMCHANNEL(song, e->channel))
{
if (!(ip=song->drumset[song->channel[e->channel].bank]->instrument[e->a]))
{
if (!(ip=song->drumset[0]->instrument[e->a]))
return; /* No instrument? Then we can't play. */
}
if (ip->samples != 1)
{
DEBUG_MSG("Strange: percussion instrument with %d samples!\n",
ip->samples);
}
if (ip->sample->note_to_use) /* Do we have a fixed pitch? */
song->voice[i].orig_frequency = freq_table[(int)(ip->sample->note_to_use)];
else
song->voice[i].orig_frequency = freq_table[e->a & 0x7F];
/* drums are supposed to have only one sample */
song->voice[i].sample = ip->sample;
}
else
{
if (song->channel[e->channel].program == SPECIAL_PROGRAM)
ip=song->default_instrument;
else if (!(ip=song->tonebank[song->channel[e->channel].bank]->
instrument[song->channel[e->channel].program]))
{
if (!(ip=song->tonebank[0]->instrument[song->channel[e->channel].program]))
return; /* No instrument? Then we can't play. */
}
if (ip->sample->note_to_use) /* Fixed-pitch instrument? */
song->voice[i].orig_frequency = freq_table[(int)(ip->sample->note_to_use)];
else
song->voice[i].orig_frequency = freq_table[e->a & 0x7F];
select_sample(song, i, ip, e->b);
}
song->voice[i].status = VOICE_ON;
song->voice[i].channel = e->channel;
song->voice[i].note = e->a;
song->voice[i].velocity = e->b;
song->voice[i].sample_offset = 0;
song->voice[i].sample_increment = 0; /* make sure it isn't negative */
song->voice[i].tremolo_phase = 0;
song->voice[i].tremolo_phase_increment = song->voice[i].sample->tremolo_phase_increment;
song->voice[i].tremolo_sweep = song->voice[i].sample->tremolo_sweep_increment;
song->voice[i].tremolo_sweep_position = 0;
song->voice[i].vibrato_sweep = song->voice[i].sample->vibrato_sweep_increment;
song->voice[i].vibrato_sweep_position = 0;
song->voice[i].vibrato_control_ratio = song->voice[i].sample->vibrato_control_ratio;
song->voice[i].vibrato_control_counter = song->voice[i].vibrato_phase = 0;
for (j=0; j<MID_VIBRATO_SAMPLE_INCREMENTS; j++)
song->voice[i].vibrato_sample_increment[j] = 0;
if (song->channel[e->channel].panning != NO_PANNING)
song->voice[i].panning = song->channel[e->channel].panning;
else
song->voice[i].panning = song->voice[i].sample->panning;
recompute_freq(song, i);
recompute_amp(song, i);
if (song->voice[i].sample->modes & MODES_ENVELOPE)
{
/* Ramp up from 0 */
song->voice[i].envelope_stage = 0;
song->voice[i].envelope_volume = 0;
song->voice[i].control_counter = 0;
recompute_envelope(song, i);
apply_envelope_to_amp(song, i);
}
else
{
song->voice[i].envelope_increment = 0;
apply_envelope_to_amp(song, i);
}
}
static void kill_note(MidSong *song, int i)
{
song->voice[i].status = VOICE_DIE;
}
/* Only one instance of a note can be playing on a single channel. */
static void note_on(MidSong *song)
{
int i = song->voices, lowest=-1;
sint32 lv=0x7FFFFFFF, v;
MidEvent *e = song->current_event;
while (i--)
{
if (song->voice[i].status == VOICE_FREE)
lowest=i; /* Can't get a lower volume than silence */
else if (song->voice[i].channel==e->channel &&
(song->voice[i].note==e->a || song->channel[song->voice[i].channel].mono))
kill_note(song, i);
}
if (lowest != -1)
{
/* Found a free voice. */
start_note(song,e,lowest);
return;
}
/* Look for the decaying note with the lowest volume */
i = song->voices;
while (i--)
{
if ((song->voice[i].status != VOICE_ON) &&
(song->voice[i].status != VOICE_DIE))
{
v = song->voice[i].left_mix;
if ((song->voice[i].panned == PANNED_MYSTERY)
&& (song->voice[i].right_mix > v))
v = song->voice[i].right_mix;
if (v<lv)
{
lv=v;
lowest=i;
}
}
}
if (lowest != -1)
{
/* This can still cause a click, but if we had a free voice to
spare for ramping down this note, we wouldn't need to kill it
in the first place... Still, this needs to be fixed. Perhaps
we could use a reserve of voices to play dying notes only. */
song->cut_notes++;
song->voice[lowest].status=VOICE_FREE;
start_note(song,e,lowest);
}
else
song->lost_notes++;
}
static void finish_note(MidSong *song, int i)
{
if (song->voice[i].sample->modes & MODES_ENVELOPE)
{
/* We need to get the envelope out of Sustain stage */
song->voice[i].envelope_stage = 3;
song->voice[i].status = VOICE_OFF;
recompute_envelope(song, i);
apply_envelope_to_amp(song, i);
}
else
{
/* Set status to OFF so resample_voice() will let this voice out
of its loop, if any. In any case, this voice dies when it
hits the end of its data (ofs>=data_length). */
song->voice[i].status = VOICE_OFF;
}
}
static void note_off(MidSong *song)
{
int i = song->voices;
MidEvent *e = song->current_event;
while (i--)
if (song->voice[i].status == VOICE_ON &&
song->voice[i].channel == e->channel &&
song->voice[i].note == e->a)
{
if (song->channel[e->channel].sustain)
{
song->voice[i].status = VOICE_SUSTAINED;
}
else
finish_note(song, i);
return;
}
}
/* Process the All Notes Off event */
static void all_notes_off(MidSong *song)
{
int i = song->voices;
int c = song->current_event->channel;
DEBUG_MSG("All notes off on channel %d\n", c);
while (i--)
if (song->voice[i].status == VOICE_ON &&
song->voice[i].channel == c)
{
if (song->channel[c].sustain)
song->voice[i].status = VOICE_SUSTAINED;
else
finish_note(song, i);
}
}
/* Process the All Sounds Off event */
static void all_sounds_off(MidSong *song)
{
int i = song->voices;
int c = song->current_event->channel;
while (i--)
if (song->voice[i].channel == c &&
song->voice[i].status != VOICE_FREE &&
song->voice[i].status != VOICE_DIE)
{
kill_note(song, i);
}
}
static void adjust_pressure(MidSong *song)
{
MidEvent *e = song->current_event;
int i = song->voices;
while (i--)
if (song->voice[i].status == VOICE_ON &&
song->voice[i].channel == e->channel &&
song->voice[i].note == e->a)
{
song->voice[i].velocity = e->b;
recompute_amp(song, i);
apply_envelope_to_amp(song, i);
return;
}
}
static void drop_sustain(MidSong *song)
{
int i = song->voices;
int c = song->current_event->channel;
while (i--)
if (song->voice[i].status == VOICE_SUSTAINED && song->voice[i].channel == c)
finish_note(song, i);
}
static void adjust_pitchbend(MidSong *song)
{
int c = song->current_event->channel;
int i = song->voices;
while (i--)
if (song->voice[i].status != VOICE_FREE && song->voice[i].channel == c)
{
recompute_freq(song, i);
}
}
static void adjust_volume(MidSong *song)
{
int c = song->current_event->channel;
int i = song->voices;
while (i--)
if (song->voice[i].channel == c &&
(song->voice[i].status==VOICE_ON || song->voice[i].status==VOICE_SUSTAINED))
{
recompute_amp(song, i);
apply_envelope_to_amp(song, i);
}
}
static void seek_forward(MidSong *song, sint32 until_time)
{
reset_voices(song);
while (song->current_event->time < until_time)
{
switch(song->current_event->type)
{
/* All notes stay off. Just handle the parameter changes. */
case ME_PITCH_SENS:
song->channel[song->current_event->channel].pitchsens =
song->current_event->a;
song->channel[song->current_event->channel].pitchfactor = 0;
break;
case ME_PITCHWHEEL:
song->channel[song->current_event->channel].pitchbend =
song->current_event->a + song->current_event->b * 128;
song->channel[song->current_event->channel].pitchfactor = 0;
break;
case ME_MAINVOLUME:
song->channel[song->current_event->channel].volume =
song->current_event->a;
break;
case ME_PAN:
song->channel[song->current_event->channel].panning =
song->current_event->a;
break;
case ME_EXPRESSION:
song->channel[song->current_event->channel].expression =
song->current_event->a;
break;
case ME_PROGRAM:
if (ISDRUMCHANNEL(song, song->current_event->channel))
/* Change drum set */
song->channel[song->current_event->channel].bank =
song->current_event->a;
else
song->channel[song->current_event->channel].program =
song->current_event->a;
break;
case ME_SUSTAIN:
song->channel[song->current_event->channel].sustain =
song->current_event->a;
break;
case ME_RESET_CONTROLLERS:
reset_controllers(song, song->current_event->channel);
break;
case ME_TONE_BANK:
song->channel[song->current_event->channel].bank =
song->current_event->a;
break;
case ME_EOT:
song->current_sample = song->current_event->time;
return;
}
song->current_event++;
}
/*song->current_sample=song->current_event->time;*/
if (song->current_event != song->events)
song->current_event--;
song->current_sample=until_time;
}
static void skip_to(MidSong *song, sint32 until_time)
{
if (song->current_sample > until_time)
song->current_sample = 0;
reset_midi(song);
song->current_event = song->events;
if (until_time)
seek_forward(song, until_time);
}
static void do_compute_data(MidSong *song, sint32 count)
{
int i;
memset(song->common_buffer, 0,
(song->encoding & PE_MONO) ? (count * 4) : (count * 8));
for (i = 0; i < song->voices; i++)
{
if(song->voice[i].status != VOICE_FREE)
mix_voice(song, song->common_buffer, i, count);
}
song->current_sample += count;
}
/* count=0 means flush remaining buffered data to output device, then
flush the device itself */
static void compute_data(MidSong *song, sint8 **stream, sint32 count)
{
int channels;
if ( song->encoding & PE_MONO )
channels = 1;
else
channels = 2;
while (count)
{
sint32 block = count;
if (block > song->buffer_size)
block = song->buffer_size;
do_compute_data(song, block);
song->write(*stream, song->common_buffer, channels * block);
*stream += song->bytes_per_sample * block;
count -= block;
}
}
void mid_song_start(MidSong *song)
{
song->playing = 1;
adjust_amplification(song);
skip_to(song, 0);
}
void mid_song_seek(MidSong *song, uint32 ms)
{
skip_to(song, (ms * (song->rate / 100)) / 10);
}
uint32 mid_song_get_total_time(MidSong *song)
{
MidEvent *last_event = &song->events[song->groomed_event_count - 1];
/* We want last_event->time * 1000 / song->rate */
uint32 retvalue = (last_event->time / song->rate) * 1000;
retvalue += (last_event->time % song->rate) * 1000 / song->rate;
return retvalue;
}
uint32 mid_song_get_time(MidSong *song)
{
uint32 retvalue = (song->current_sample / song->rate) * 1000;
retvalue += (song->current_sample % song->rate) * 1000 / song->rate;
return retvalue;
}
char *mid_song_get_meta(MidSong *song, MidSongMetaId what)
{
return song->meta_data[what];
}
size_t mid_song_read_wave(MidSong *song, void *ptr, size_t size)
{
sint32 start_sample, end_sample, samples;
if (!song->playing)
return 0;
samples = size / song->bytes_per_sample;
start_sample = song->current_sample;
end_sample = song->current_sample+samples;
while ( song->current_sample < end_sample ) {
/* Handle all events that should happen at this time */
while (song->current_event->time <= song->current_sample) {
switch(song->current_event->type) {
/* Effects affecting a single note */
case ME_NOTEON:
if (!(song->current_event->b)) /* Velocity 0? */
note_off(song);
else
note_on(song);
break;
case ME_NOTEOFF:
note_off(song);
break;
case ME_KEYPRESSURE:
adjust_pressure(song);
break;
/* Effects affecting a single channel */
case ME_PITCH_SENS:
song->channel[song->current_event->channel].pitchsens =
song->current_event->a;
song->channel[song->current_event->channel].pitchfactor = 0;
break;
case ME_PITCHWHEEL:
song->channel[song->current_event->channel].pitchbend =
song->current_event->a + song->current_event->b * 128;
song->channel[song->current_event->channel].pitchfactor = 0;
/* Adjust pitch for notes already playing */
adjust_pitchbend(song);
break;
case ME_MAINVOLUME:
song->channel[song->current_event->channel].volume =
song->current_event->a;
adjust_volume(song);
break;
case ME_PAN:
song->channel[song->current_event->channel].panning =
song->current_event->a;
break;
case ME_EXPRESSION:
song->channel[song->current_event->channel].expression =
song->current_event->a;
adjust_volume(song);
break;
case ME_PROGRAM:
if (ISDRUMCHANNEL(song, song->current_event->channel)) {
/* Change drum set */
song->channel[song->current_event->channel].bank =
song->current_event->a;
}
else
song->channel[song->current_event->channel].program =
song->current_event->a;
break;
case ME_SUSTAIN:
song->channel[song->current_event->channel].sustain =
song->current_event->a;
if (!song->current_event->a)
drop_sustain(song);
break;
case ME_RESET_CONTROLLERS:
reset_controllers(song, song->current_event->channel);
break;
case ME_ALL_NOTES_OFF:
all_notes_off(song);
break;
case ME_ALL_SOUNDS_OFF:
all_sounds_off(song);
break;
case ME_TONE_BANK:
song->channel[song->current_event->channel].bank =
song->current_event->a;
break;
case ME_EOT:
/* Give the last notes a couple of seconds to decay */
DEBUG_MSG("Playing time: ~%d seconds\n",
song->current_sample/song->rate+2);
DEBUG_MSG("Notes cut: %d\n", song->cut_notes);
DEBUG_MSG("Notes lost totally: %d\n", song->lost_notes);
song->playing = 0;
return (song->current_sample - start_sample) * song->bytes_per_sample;
}
song->current_event++;
}
if (song->current_event->time > end_sample)
compute_data(song, (sint8 **)&ptr, end_sample-song->current_sample);
else
compute_data(song, (sint8 **)&ptr, song->current_event->time-song->current_sample);
}
return samples * song->bytes_per_sample;
}
void mid_song_set_volume(MidSong *song, int volume)
{
int i;
if (volume > MAX_AMPLIFICATION)
song->amplification = MAX_AMPLIFICATION;
else
if (volume < 0)
song->amplification = 0;
else
song->amplification = volume;
adjust_amplification(song);
for (i = 0; i < song->voices; i++)
if (song->voice[i].status != VOICE_FREE)
{
recompute_amp(song, i);
apply_envelope_to_amp(song, i);
}
}

View File

@@ -0,0 +1,64 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
playmidi.h
*/
/* Midi events */
#define ME_NONE 0
#define ME_NOTEON 1
#define ME_NOTEOFF 2
#define ME_KEYPRESSURE 3
#define ME_MAINVOLUME 4
#define ME_PAN 5
#define ME_SUSTAIN 6
#define ME_EXPRESSION 7
#define ME_PITCHWHEEL 8
#define ME_PROGRAM 9
#define ME_TEMPO 10
#define ME_PITCH_SENS 11
#define ME_ALL_SOUNDS_OFF 12
#define ME_RESET_CONTROLLERS 13
#define ME_ALL_NOTES_OFF 14
#define ME_TONE_BANK 15
#define ME_LYRIC 16
#define ME_EOT 99
/* Causes the instrument's default panning to be used. */
#define NO_PANNING -1
/* Voice status options: */
#define VOICE_FREE 0
#define VOICE_ON 1
#define VOICE_SUSTAINED 2
#define VOICE_OFF 3
#define VOICE_DIE 4
/* Voice panned options: */
#define PANNED_MYSTERY 0
#define PANNED_LEFT 1
#define PANNED_RIGHT 2
#define PANNED_CENTER 3
/* Anything but PANNED_MYSTERY only uses the left volume */
#define ISDRUMCHANNEL(s, c) (((s)->drumchannels & (1<<(c))))

View File

@@ -0,0 +1,596 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "timidity.h"
#include "timidity_internal.h"
#include "options.h"
#include "common.h"
#include "instrum.h"
#include "playmidi.h"
/* Computes how many (fractional) samples one MIDI delta-time unit contains */
static void compute_sample_increment(MidSong *song, sint32 tempo,
sint32 divisions)
{
double a;
a = (double) (tempo) * (double) (song->rate) * (65536.0/1000000.0) /
(double)(divisions);
song->sample_correction = (sint32)(a) & 0xFFFF;
song->sample_increment = (sint32)(a) >> 16;
DEBUG_MSG("Samples per delta-t: %d (correction %d)\n",
song->sample_increment, song->sample_correction);
}
/* Read variable-length number (7 bits per byte, MSB first) */
static sint32 getvl(MidIStream *stream)
{
sint32 l=0;
uint8 c;
for (;;)
{
mid_istream_read(stream, &c, 1, 1);
l += (c & 0x7f);
if (!(c & 0x80)) return l;
l<<=7;
}
}
/* Print a string from the file, followed by a newline. Any non-ASCII
or unprintable characters will be converted to periods. */
static int read_meta_data(MidIStream *stream, sint32 len, uint8 type, MidSong *song)
{
char *s=safe_malloc(len+1);
MidSongMetaId id;
static char *label[] = {
"Text event: ", "Text: ", "Copyright: ", "Track name: ",
"Instrument: ", "Lyric: ", "Marker: ", "Cue point: "};
if (len != (sint32) mid_istream_read(stream, s, 1, len))
{
free(s);
return -1;
}
s[len]='\0';
while (len--)
{
if (((unsigned char)s[len])<32)
s[len]='.';
}
DEBUG_MSG("%s%s\n", label[(type > 7) ? 0 : type], s);
switch (type)
{
case 1: id = MID_SONG_TEXT; break;
case 2: id = MID_SONG_COPYRIGHT; break;
default: free(s); s = NULL;
}
if (s)
{
if (song->meta_data[id])
free(song->meta_data[id]);
song->meta_data[id] = s;
}
return 0;
}
#define MIDIEVENT(at,t,ch,pa,pb) \
new=safe_malloc(sizeof(MidEventList)); \
new->event.time=at; new->event.type=t; new->event.channel=ch; \
new->event.a=pa; new->event.b=pb; new->next=0;\
return new;
#define MAGIC_EOT ((MidEventList *)(-1))
/* Read a MIDI event, returning a freshly allocated element that can
be linked to the event list */
static MidEventList *read_midi_event(MidIStream *stream, MidSong *song)
{
static uint8 laststatus, lastchan;
static uint8 nrpn=0, rpn_msb[16], rpn_lsb[16]; /* one per channel */
uint8 me, type, a,b,c;
sint32 len;
MidEventList *new;
for (;;)
{
song->at += getvl(stream);
if (mid_istream_read(stream, &me, 1, 1) != 1)
{
DEBUG_MSG("read_midi_event: mid_istream_read() failure\n");
return 0;
}
if(me==0xF0 || me == 0xF7) /* SysEx event */
{
len=getvl(stream);
mid_istream_skip(stream, len);
}
else if(me==0xFF) /* Meta event */
{
mid_istream_read(stream, &type, 1, 1);
len=getvl(stream);
if (type>0 && type<16)
{
read_meta_data(stream, len, type, song);
}
else
switch(type)
{
case 0x2F: /* End of Track */
return MAGIC_EOT;
case 0x51: /* Tempo */
mid_istream_read(stream, &a, 1, 1);
mid_istream_read(stream, &b, 1, 1);
mid_istream_read(stream, &c, 1, 1);
MIDIEVENT(song->at, ME_TEMPO, c, a, b);
default:
DEBUG_MSG("(Meta event type 0x%02x, length %d)\n", type, len);
mid_istream_skip(stream, len);
break;
}
}
else
{
a=me;
if (a & 0x80) /* status byte */
{
lastchan=a & 0x0F;
laststatus=(a>>4) & 0x07;
mid_istream_read(stream, &a, 1, 1);
a &= 0x7F;
}
switch(laststatus)
{
case 0: /* Note off */
mid_istream_read(stream, &b, 1, 1);
b &= 0x7F;
MIDIEVENT(song->at, ME_NOTEOFF, lastchan, a,b);
case 1: /* Note on */
mid_istream_read(stream, &b, 1, 1);
b &= 0x7F;
MIDIEVENT(song->at, ME_NOTEON, lastchan, a,b);
case 2: /* Key Pressure */
mid_istream_read(stream, &b, 1, 1);
b &= 0x7F;
MIDIEVENT(song->at, ME_KEYPRESSURE, lastchan, a, b);
case 3: /* Control change */
mid_istream_read(stream, &b, 1, 1);
b &= 0x7F;
{
int control=255;
switch(a)
{
case 7: control=ME_MAINVOLUME; break;
case 10: control=ME_PAN; break;
case 11: control=ME_EXPRESSION; break;
case 64: control=ME_SUSTAIN; break;
case 120: control=ME_ALL_SOUNDS_OFF; break;
case 121: control=ME_RESET_CONTROLLERS; break;
case 123: control=ME_ALL_NOTES_OFF; break;
/* These should be the SCC-1 tone bank switch
commands. I don't know why there are two, or
why the latter only allows switching to bank 0.
Also, some MIDI files use 0 as some sort of
continuous controller. This will cause lots of
warnings about undefined tone banks. */
case 0: control=ME_TONE_BANK; break;
case 32:
if (b!=0)
DEBUG_MSG("(Strange: tone bank change 0x20%02x)\n", b);
else
control=ME_TONE_BANK;
break;
case 100: nrpn=0; rpn_msb[lastchan]=b; break;
case 101: nrpn=0; rpn_lsb[lastchan]=b; break;
case 99: nrpn=1; rpn_msb[lastchan]=b; break;
case 98: nrpn=1; rpn_lsb[lastchan]=b; break;
case 6:
if (nrpn)
{
DEBUG_MSG("(Data entry (MSB) for NRPN %02x,%02x: %d)\n",
rpn_msb[lastchan], rpn_lsb[lastchan], b);
break;
}
switch((rpn_msb[lastchan]<<8) | rpn_lsb[lastchan])
{
case 0x0000: /* Pitch bend sensitivity */
control=ME_PITCH_SENS;
break;
case 0x7F7F: /* RPN reset */
/* reset pitch bend sensitivity to 2 */
MIDIEVENT(song->at, ME_PITCH_SENS, lastchan, 2, 0);
default:
DEBUG_MSG("(Data entry (MSB) for RPN %02x,%02x: %d)\n",
rpn_msb[lastchan], rpn_lsb[lastchan], b);
break;
}
break;
default:
DEBUG_MSG("(Control %d: %d)\n", a, b);
break;
}
if (control != 255)
{
MIDIEVENT(song->at, control, lastchan, b, 0);
}
}
break;
case 4: /* Program change */
a &= 0x7f;
MIDIEVENT(song->at, ME_PROGRAM, lastchan, a, 0);
case 5: /* Channel pressure - NOT IMPLEMENTED */
break;
case 6: /* Pitch wheel */
mid_istream_read(stream, &b, 1, 1);
b &= 0x7F;
MIDIEVENT(song->at, ME_PITCHWHEEL, lastchan, a, b);
default:
DEBUG_MSG("*** Can't happen: status 0x%02X, channel 0x%02X\n",
laststatus, lastchan);
break;
}
}
}
return new;
}
#undef MIDIEVENT
/* Read a midi track into the linked list, either merging with any previous
tracks or appending to them. */
static int read_track(MidIStream *stream, MidSong *song, int append)
{
MidEventList *meep;
MidEventList *next, *new;
sint32 len;
char tmp[4];
meep = song->evlist;
if (append && meep)
{
/* find the last event in the list */
for (; meep->next; meep=meep->next)
;
song->at = meep->event.time;
}
else
song->at=0;
/* Check the formalities */
if (mid_istream_read(stream, tmp, 1, 4) != 4 || mid_istream_read(stream, &len, 4, 1) != 1)
{
DEBUG_MSG("Can't read track header.\n");
return -1;
}
len=SWAPBE32(len);
if (memcmp(tmp, "MTrk", 4))
{
DEBUG_MSG("Corrupt MIDI file.\n");
return -2;
}
for (;;)
{
if (!(new=read_midi_event(stream, song))) /* Some kind of error */
return -2;
if (new==MAGIC_EOT) /* End-of-track Hack. */
{
return 0;
}
next=meep->next;
while (next && (next->event.time < new->event.time))
{
meep=next;
next=meep->next;
}
new->next=next;
meep->next=new;
song->event_count++; /* Count the event. (About one?) */
meep=new;
}
}
/* Free the linked event list from memory. */
static void free_midi_list(MidSong *song)
{
MidEventList *meep, *next;
if (!(meep = song->evlist)) return;
while (meep)
{
next=meep->next;
free(meep);
meep=next;
}
song->evlist=0;
}
/* Allocate an array of MidiEvents and fill it from the linked list of
events, marking used instruments for loading. Convert event times to
samples: handle tempo changes. Strip unnecessary events from the list.
Free the linked list. */
static MidEvent *groom_list(MidSong *song, sint32 divisions,sint32 *eventsp,
sint32 *samplesp)
{
MidEvent *groomed_list, *lp;
MidEventList *meep;
sint32 i, our_event_count, tempo, skip_this_event, new_value;
sint32 sample_cum, samples_to_do, at, st, dt, counting_time;
int current_bank[16], current_set[16], current_program[16];
/* Or should each bank have its own current program? */
for (i=0; i<16; i++)
{
current_bank[i]=0;
current_set[i]=0;
current_program[i]=song->default_program;
}
tempo=500000;
compute_sample_increment(song, tempo, divisions);
/* This may allocate a bit more than we need */
groomed_list=lp=safe_malloc(sizeof(MidEvent) * (song->event_count+1));
meep=song->evlist;
our_event_count=0;
st=at=sample_cum=0;
counting_time=2; /* We strip any silence before the first NOTE ON. */
for (i = 0; i < song->event_count; i++)
{
skip_this_event=0;
if (meep->event.type==ME_TEMPO)
{
tempo=
meep->event.channel + meep->event.b * 256 + meep->event.a * 65536;
compute_sample_increment(song, tempo, divisions);
skip_this_event=1;
}
else switch (meep->event.type)
{
case ME_PROGRAM:
if (ISDRUMCHANNEL(song, meep->event.channel))
{
if (song->drumset[meep->event.a]) /* Is this a defined drumset? */
new_value=meep->event.a;
else
{
DEBUG_MSG("Drum set %d is undefined\n", meep->event.a);
new_value=meep->event.a=0;
}
if (current_set[meep->event.channel] != new_value)
current_set[meep->event.channel]=new_value;
else
skip_this_event=1;
}
else
{
new_value=meep->event.a;
if ((current_program[meep->event.channel] != SPECIAL_PROGRAM)
&& (current_program[meep->event.channel] != new_value))
current_program[meep->event.channel] = new_value;
else
skip_this_event=1;
}
break;
case ME_NOTEON:
if (counting_time)
counting_time=1;
if (ISDRUMCHANNEL(song, meep->event.channel))
{
/* Mark this instrument to be loaded */
if (!(song->drumset[current_set[meep->event.channel]]
->instrument[meep->event.a]))
song->drumset[current_set[meep->event.channel]]
->instrument[meep->event.a] = MAGIC_LOAD_INSTRUMENT;
}
else
{
if (current_program[meep->event.channel]==SPECIAL_PROGRAM)
break;
/* Mark this instrument to be loaded */
if (!(song->tonebank[current_bank[meep->event.channel]]
->instrument[current_program[meep->event.channel]]))
song->tonebank[current_bank[meep->event.channel]]
->instrument[current_program[meep->event.channel]] =
MAGIC_LOAD_INSTRUMENT;
}
break;
case ME_TONE_BANK:
if (ISDRUMCHANNEL(song, meep->event.channel))
{
skip_this_event=1;
break;
}
if (song->tonebank[meep->event.a]) /* Is this a defined tone bank? */
new_value=meep->event.a;
else
{
DEBUG_MSG("Tone bank %d is undefined\n", meep->event.a);
new_value=meep->event.a=0;
}
if (current_bank[meep->event.channel]!=new_value)
current_bank[meep->event.channel]=new_value;
else
skip_this_event=1;
break;
}
/* Recompute time in samples*/
if ((dt=meep->event.time - at) && !counting_time)
{
samples_to_do = song->sample_increment * dt;
sample_cum += song->sample_correction * dt;
if (sample_cum & 0xFFFF0000)
{
samples_to_do += ((sample_cum >> 16) & 0xFFFF);
sample_cum &= 0x0000FFFF;
}
st += samples_to_do;
}
else if (counting_time==1) counting_time=0;
if (!skip_this_event)
{
/* Add the event to the list */
*lp=meep->event;
lp->time=st;
lp++;
our_event_count++;
}
at=meep->event.time;
meep=meep->next;
}
/* Add an End-of-Track event */
lp->time=st;
lp->type=ME_EOT;
our_event_count++;
free_midi_list(song);
*eventsp=our_event_count;
*samplesp=st;
return groomed_list;
}
MidEvent *read_midi_file(MidIStream *stream, MidSong *song, sint32 *count, sint32 *sp)
{
sint32 len, divisions;
sint16 format, tracks, divisions_tmp;
int i;
char tmp[4];
song->event_count=0;
song->at=0;
song->evlist=0;
if (mid_istream_read(stream, tmp, 1, 4) != 4 || mid_istream_read(stream, &len, 4, 1) != 1)
{
DEBUG_MSG("Not a MIDI file!\n");
return 0;
}
len=SWAPBE32(len);
if (memcmp(tmp, "MThd", 4) || len < 6)
{
DEBUG_MSG("Not a MIDI file!\n");
return 0;
}
mid_istream_read(stream, &format, 2, 1);
mid_istream_read(stream, &tracks, 2, 1);
mid_istream_read(stream, &divisions_tmp, 2, 1);
format=SWAPBE16(format);
tracks=SWAPBE16(tracks);
divisions_tmp=SWAPBE16(divisions_tmp);
if (divisions_tmp<0)
{
/* SMPTE time -- totally untested. Got a MIDI file that uses this? */
divisions=
(sint32)(-(divisions_tmp/256)) * (sint32)(divisions_tmp & 0xFF);
}
else divisions=(sint32)(divisions_tmp);
if (len > 6)
{
DEBUG_MSG("MIDI file header size %u bytes", len);
mid_istream_skip(stream, len-6); /* skip the excess */
}
if (format<0 || format >2)
{
DEBUG_MSG("Unknown MIDI file format %d\n", format);
return 0;
}
DEBUG_MSG("Format: %d Tracks: %d Divisions: %d\n",
format, tracks, divisions);
/* Put a do-nothing event first in the list for easier processing */
song->evlist=safe_malloc(sizeof(MidEventList));
song->evlist->event.time=0;
song->evlist->event.type=ME_NONE;
song->evlist->next=0;
song->event_count++;
switch(format)
{
case 0:
if (read_track(stream, song, 0))
{
free_midi_list(song);
return 0;
}
break;
case 1:
for (i=0; i<tracks; i++)
if (read_track(stream, song, 0))
{
free_midi_list(song);
return 0;
}
break;
case 2: /* We simply play the tracks sequentially */
for (i=0; i<tracks; i++)
if (read_track(stream, song, 1))
{
free_midi_list(song);
return 0;
}
break;
}
return groom_list(song, divisions, count, sp);
}

View File

@@ -0,0 +1,24 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
readmidi.h
*/
extern MidEvent *read_midi_file(MidIStream *stream, MidSong *song, sint32 *count, sint32 *sp);

View File

@@ -0,0 +1,608 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
resample.c
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "timidity.h"
#include "timidity_internal.h"
#include "options.h"
#include "common.h"
#include "instrum.h"
#include "playmidi.h"
#include "tables.h"
#include "resample.h"
/*************** resampling with fixed increment *****************/
static sample_t *rs_plain(MidSong *song, int v, sint32 *countptr)
{
/* Play sample until end, then free the voice. */
sample_t v1, v2;
MidVoice
*vp=&(song->voice[v]);
sample_t
*dest=song->resample_buffer,
*src=vp->sample->data;
sint32
ofs=vp->sample_offset,
incr=vp->sample_increment,
le=vp->sample->data_length,
count=*countptr;
sint32 i;
if (incr<0) incr = -incr; /* In case we're coming out of a bidir loop */
/* Precalc how many times we should go through the loop.
NOTE: Assumes that incr > 0 and that ofs <= le */
i = (le - ofs) / incr + 1;
if (i > count)
{
i = count;
count = 0;
}
else count -= i;
while (i--)
{
v1 = src[ofs >> FRACTION_BITS];
v2 = src[(ofs >> FRACTION_BITS)+1];
*dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS);
ofs += incr;
}
if (ofs >= le)
{
if (ofs == le)
*dest++ = src[ofs >> FRACTION_BITS];
vp->status=VOICE_FREE;
*countptr-=count+1;
}
vp->sample_offset=ofs; /* Update offset */
return song->resample_buffer;
}
static sample_t *rs_loop(MidSong *song, MidVoice *vp, sint32 count)
{
/* Play sample until end-of-loop, skip back and continue. */
sample_t v1, v2;
sint32
ofs=vp->sample_offset,
incr=vp->sample_increment,
le=vp->sample->loop_end,
ll=le - vp->sample->loop_start;
sample_t
*dest=song->resample_buffer,
*src=vp->sample->data;
sint32 i;
while (count)
{
if (ofs >= le)
/* NOTE: Assumes that ll > incr and that incr > 0. */
ofs -= ll;
/* Precalc how many times we should go through the loop */
i = (le - ofs) / incr + 1;
if (i > count)
{
i = count;
count = 0;
}
else count -= i;
while (i--)
{
v1 = src[ofs >> FRACTION_BITS];
v2 = src[(ofs >> FRACTION_BITS)+1];
*dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS);
ofs += incr;
}
}
vp->sample_offset=ofs; /* Update offset */
return song->resample_buffer;
}
static sample_t *rs_bidir(MidSong *song, MidVoice *vp, sint32 count)
{
sample_t v1, v2;
sint32
ofs=vp->sample_offset,
incr=vp->sample_increment,
le=vp->sample->loop_end,
ls=vp->sample->loop_start;
sample_t
*dest=song->resample_buffer,
*src=vp->sample->data;
sint32
le2 = le<<1,
ls2 = ls<<1,
i;
/* Play normally until inside the loop region */
if (ofs <= ls)
{
/* NOTE: Assumes that incr > 0, which is NOT always the case
when doing bidirectional looping. I have yet to see a case
where both ofs <= ls AND incr < 0, however. */
i = (ls - ofs) / incr + 1;
if (i > count)
{
i = count;
count = 0;
}
else count -= i;
while (i--)
{
v1 = src[ofs >> FRACTION_BITS];
v2 = src[(ofs >> FRACTION_BITS)+1];
*dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS);
ofs += incr;
}
}
/* Then do the bidirectional looping */
while(count)
{
/* Precalc how many times we should go through the loop */
i = ((incr > 0 ? le : ls) - ofs) / incr + 1;
if (i > count)
{
i = count;
count = 0;
}
else count -= i;
while (i--)
{
v1 = src[ofs >> FRACTION_BITS];
v2 = src[(ofs >> FRACTION_BITS)+1];
*dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS);
ofs += incr;
}
if (ofs>=le)
{
/* fold the overshoot back in */
ofs = le2 - ofs;
incr *= -1;
}
else if (ofs <= ls)
{
ofs = ls2 - ofs;
incr *= -1;
}
}
vp->sample_increment=incr;
vp->sample_offset=ofs; /* Update offset */
return song->resample_buffer;
}
/*********************** vibrato versions ***************************/
/* We only need to compute one half of the vibrato sine cycle */
static int vib_phase_to_inc_ptr(int phase)
{
if (phase < MID_VIBRATO_SAMPLE_INCREMENTS/2)
return MID_VIBRATO_SAMPLE_INCREMENTS/2-1-phase;
else if (phase >= 3*MID_VIBRATO_SAMPLE_INCREMENTS/2)
return 5*MID_VIBRATO_SAMPLE_INCREMENTS/2-1-phase;
else
return phase-MID_VIBRATO_SAMPLE_INCREMENTS/2;
}
static sint32 update_vibrato(MidSong *song, MidVoice *vp, int sign)
{
sint32 depth;
int phase, pb;
double a;
if (vp->vibrato_phase++ >= 2*MID_VIBRATO_SAMPLE_INCREMENTS-1)
vp->vibrato_phase=0;
phase=vib_phase_to_inc_ptr(vp->vibrato_phase);
if (vp->vibrato_sample_increment[phase])
{
if (sign)
return -vp->vibrato_sample_increment[phase];
else
return vp->vibrato_sample_increment[phase];
}
/* Need to compute this sample increment. */
depth=vp->sample->vibrato_depth<<7;
if (vp->vibrato_sweep)
{
/* Need to update sweep */
vp->vibrato_sweep_position += vp->vibrato_sweep;
if (vp->vibrato_sweep_position >= (1<<SWEEP_SHIFT))
vp->vibrato_sweep=0;
else
{
/* Adjust depth */
depth *= vp->vibrato_sweep_position;
depth >>= SWEEP_SHIFT;
}
}
a = FSCALE(((double)(vp->sample->sample_rate) *
(double)(vp->frequency)) /
((double)(vp->sample->root_freq) *
(double)(song->rate)),
FRACTION_BITS);
pb=(int)((sine(vp->vibrato_phase *
(SINE_CYCLE_LENGTH/(2*MID_VIBRATO_SAMPLE_INCREMENTS)))
* (double)(depth) * VIBRATO_AMPLITUDE_TUNING));
if (pb<0)
{
pb=-pb;
a /= bend_fine[(pb>>5) & 0xFF] * bend_coarse[pb>>13];
}
else
a *= bend_fine[(pb>>5) & 0xFF] * bend_coarse[pb>>13];
/* If the sweep's over, we can store the newly computed sample_increment */
if (!vp->vibrato_sweep)
vp->vibrato_sample_increment[phase]=(sint32) a;
if (sign)
a = -a; /* need to preserve the loop direction */
return (sint32) a;
}
static sample_t *rs_vib_plain(MidSong *song, int v, sint32 *countptr)
{
/* Play sample until end, then free the voice. */
sample_t v1, v2;
MidVoice *vp=&(song->voice[v]);
sample_t
*dest=song->resample_buffer,
*src=vp->sample->data;
sint32
le=vp->sample->data_length,
ofs=vp->sample_offset,
incr=vp->sample_increment,
count=*countptr;
int
cc=vp->vibrato_control_counter;
/* This has never been tested */
if (incr<0) incr = -incr; /* In case we're coming out of a bidir loop */
while (count--)
{
if (!cc--)
{
cc=vp->vibrato_control_ratio;
incr=update_vibrato(song, vp, 0);
}
v1 = src[ofs >> FRACTION_BITS];
v2 = src[(ofs >> FRACTION_BITS)+1];
*dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS);
ofs += incr;
if (ofs >= le)
{
if (ofs == le)
*dest++ = src[ofs >> FRACTION_BITS];
vp->status=VOICE_FREE;
*countptr-=count+1;
break;
}
}
vp->vibrato_control_counter=cc;
vp->sample_increment=incr;
vp->sample_offset=ofs; /* Update offset */
return song->resample_buffer;
}
static sample_t *rs_vib_loop(MidSong *song, MidVoice *vp, sint32 count)
{
/* Play sample until end-of-loop, skip back and continue. */
sample_t v1, v2;
sint32
ofs=vp->sample_offset,
incr=vp->sample_increment,
le=vp->sample->loop_end,
ll=le - vp->sample->loop_start;
sample_t
*dest=song->resample_buffer,
*src=vp->sample->data;
int
cc=vp->vibrato_control_counter;
sint32 i;
int
vibflag=0;
while (count)
{
/* Hopefully the loop is longer than an increment */
if(ofs >= le)
ofs -= ll;
/* Precalc how many times to go through the loop, taking
the vibrato control ratio into account this time. */
i = (le - ofs) / incr + 1;
if(i > count) i = count;
if(i > cc)
{
i = cc;
vibflag = 1;
}
else cc -= i;
count -= i;
while(i--)
{
v1 = src[ofs >> FRACTION_BITS];
v2 = src[(ofs >> FRACTION_BITS)+1];
*dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS);
ofs += incr;
}
if(vibflag)
{
cc = vp->vibrato_control_ratio;
incr = update_vibrato(song, vp, 0);
vibflag = 0;
}
}
vp->vibrato_control_counter=cc;
vp->sample_increment=incr;
vp->sample_offset=ofs; /* Update offset */
return song->resample_buffer;
}
static sample_t *rs_vib_bidir(MidSong *song, MidVoice *vp, sint32 count)
{
sample_t v1, v2;
sint32
ofs=vp->sample_offset,
incr=vp->sample_increment,
le=vp->sample->loop_end,
ls=vp->sample->loop_start;
sample_t
*dest=song->resample_buffer,
*src=vp->sample->data;
int
cc=vp->vibrato_control_counter;
sint32
le2=le<<1,
ls2=ls<<1,
i;
int
vibflag = 0;
/* Play normally until inside the loop region */
while (count && (ofs <= ls))
{
i = (ls - ofs) / incr + 1;
if (i > count) i = count;
if (i > cc)
{
i = cc;
vibflag = 1;
}
else cc -= i;
count -= i;
while (i--)
{
v1 = src[ofs >> FRACTION_BITS];
v2 = src[(ofs >> FRACTION_BITS)+1];
*dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS);
ofs += incr;
}
if (vibflag)
{
cc = vp->vibrato_control_ratio;
incr = update_vibrato(song, vp, 0);
vibflag = 0;
}
}
/* Then do the bidirectional looping */
while (count)
{
/* Precalc how many times we should go through the loop */
i = ((incr > 0 ? le : ls) - ofs) / incr + 1;
if(i > count) i = count;
if(i > cc)
{
i = cc;
vibflag = 1;
}
else cc -= i;
count -= i;
while (i--)
{
v1 = src[ofs >> FRACTION_BITS];
v2 = src[(ofs >> FRACTION_BITS)+1];
*dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS);
ofs += incr;
}
if (vibflag)
{
cc = vp->vibrato_control_ratio;
incr = update_vibrato(song, vp, (incr < 0));
vibflag = 0;
}
if (ofs >= le)
{
/* fold the overshoot back in */
ofs = le2 - ofs;
incr *= -1;
}
else if (ofs <= ls)
{
ofs = ls2 - ofs;
incr *= -1;
}
}
vp->vibrato_control_counter=cc;
vp->sample_increment=incr;
vp->sample_offset=ofs; /* Update offset */
return song->resample_buffer;
}
sample_t *resample_voice(MidSong *song, int v, sint32 *countptr)
{
sint32 ofs;
uint8 modes;
MidVoice *vp=&(song->voice[v]);
if (!(vp->sample->sample_rate))
{
/* Pre-resampled data -- just update the offset and check if
we're out of data. */
ofs=vp->sample_offset >> FRACTION_BITS; /* Kind of silly to use
FRACTION_BITS here... */
if (*countptr >= (vp->sample->data_length>>FRACTION_BITS) - ofs)
{
/* Note finished. Free the voice. */
vp->status = VOICE_FREE;
/* Let the caller know how much data we had left */
*countptr = (vp->sample->data_length>>FRACTION_BITS) - ofs;
}
else
vp->sample_offset += *countptr << FRACTION_BITS;
return vp->sample->data+ofs;
}
/* Need to resample. Use the proper function. */
modes=vp->sample->modes;
if (vp->vibrato_control_ratio)
{
if ((modes & MODES_LOOPING) &&
((modes & MODES_ENVELOPE) ||
(vp->status==VOICE_ON || vp->status==VOICE_SUSTAINED)))
{
if (modes & MODES_PINGPONG)
return rs_vib_bidir(song, vp, *countptr);
else
return rs_vib_loop(song, vp, *countptr);
}
else
return rs_vib_plain(song, v, countptr);
}
else
{
if ((modes & MODES_LOOPING) &&
((modes & MODES_ENVELOPE) ||
(vp->status==VOICE_ON || vp->status==VOICE_SUSTAINED)))
{
if (modes & MODES_PINGPONG)
return rs_bidir(song, vp, *countptr);
else
return rs_loop(song, vp, *countptr);
}
else
return rs_plain(song, v, countptr);
}
}
void pre_resample(MidSong *song, MidSample *sp)
{
double a, xdiff;
sint32 incr, ofs, newlen, count;
sint16 *newdata, *dest, *src = (sint16 *) sp->data;
sint16 v1, v2, v3, v4, *vptr;
#ifdef DEBUG_CHATTER
static const char note_name[12][3] =
{
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
};
#endif
DEBUG_MSG(" * pre-resampling for note %d (%s%d)\n",
sp->note_to_use,
note_name[sp->note_to_use % 12], (sp->note_to_use & 0x7F) / 12);
a = ((double) (sp->sample_rate) * freq_table[(int) (sp->note_to_use)]) /
((double) (sp->root_freq) * song->rate);
newlen = (sint32)(sp->data_length / a);
dest = newdata = safe_malloc(newlen >> (FRACTION_BITS - 1));
count = (newlen >> FRACTION_BITS) - 1;
ofs = incr = (sp->data_length - (1 << FRACTION_BITS)) / count;
if (--count)
*dest++ = src[0];
/* Since we're pre-processing and this doesn't have to be done in
real-time, we go ahead and do the full sliding cubic interpolation. */
while (--count)
{
vptr = src + (ofs >> FRACTION_BITS);
/*
* Electric Fence to the rescue: Accessing *(vptr - 1) is not a
* good thing to do when vptr <= src. (TiMidity++ has a similar
* safe-guard here.)
*/
v1 = (vptr > src) ? *(vptr - 1) : 0;
v2 = *vptr;
v3 = *(vptr + 1);
v4 = *(vptr + 2);
xdiff = FSCALENEG(ofs & FRACTION_MASK, FRACTION_BITS);
*dest++ = (sint16)(v2 + (xdiff / 6.0) * (-2 * v1 - 3 * v2 + 6 * v3 - v4 +
xdiff * (3 * (v1 - 2 * v2 + v3) + xdiff * (-v1 + 3 * (v2 - v3) + v4))));
ofs += incr;
}
if (ofs & FRACTION_MASK)
{
v1 = src[ofs >> FRACTION_BITS];
v2 = src[(ofs >> FRACTION_BITS) + 1];
*dest++ = v1 + (((v2 - v1) * (ofs & FRACTION_MASK)) >> FRACTION_BITS);
}
else
*dest++ = src[ofs >> FRACTION_BITS];
sp->data_length = newlen;
sp->loop_start = (sint32)(sp->loop_start / a);
sp->loop_end = (sint32)(sp->loop_end / a);
free(sp->data);
sp->data = (sample_t *) newdata;
sp->sample_rate = 0;
}

View File

@@ -0,0 +1,24 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
resample.h
*/
extern sample_t *resample_voice(MidSong *song, int v, sint32 *countptr);
extern void pre_resample(MidSong *song, MidSample *sp);

View File

@@ -0,0 +1,188 @@
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include "string.h"
#include "timidity.h"
#include "timidity_internal.h"
#include "common.h"
struct _MidIStream
{
MidIStreamReadFunc read;
MidIStreamCloseFunc close;
void *ctx;
};
typedef struct StdIOContext
{
FILE *fp;
int autoclose;
} StdIOContext;
size_t
stdio_istream_read (void *ctx, void *ptr, size_t size, size_t nmemb)
{
return fread (ptr, size, nmemb, ((StdIOContext *) ctx)->fp);
}
int
stdio_istream_close (void *ctx)
{
int ret = 0;
if (((StdIOContext *) ctx)->autoclose)
ret = fclose (((StdIOContext *) ctx)->fp);
free (ctx);
return ret;
}
typedef struct MemContext
{
sint8 *base;
sint8 *current;
sint8 *end;
int autofree;
} MemContext;
size_t
mem_istream_read (void *ctx, void *ptr, size_t size, size_t nmemb)
{
MemContext *c;
size_t count;
c = (MemContext *) ctx;
count = nmemb;
if (c->current + count * size > c->end)
count = (c->end - c->current) / size;
memcpy (ptr, c->current, count * size);
c->current += count * size;
return count;
}
int
mem_istream_close (void *ctx)
{
if (((MemContext *) ctx)->autofree)
free (((MemContext *) ctx)->base);
free (ctx);
return 0;
}
MidIStream *
mid_istream_open_fp (FILE * fp, int autoclose)
{
StdIOContext *ctx;
MidIStream *stream;
stream = safe_malloc (sizeof (MidIStream));
if (stream == NULL)
return NULL;
ctx = safe_malloc (sizeof (StdIOContext));
if (ctx == NULL)
{
free (stream);
return NULL;
}
ctx->fp = fp;
ctx->autoclose = autoclose;
stream->ctx = ctx;
stream->read = stdio_istream_read;
stream->close = stdio_istream_close;
return stream;
}
MidIStream *
mid_istream_open_file (const char *file)
{
FILE *fp;
fp = fopen (file, "rb");
if (fp == NULL)
return NULL;
return mid_istream_open_fp (fp, 1);
}
MidIStream *
mid_istream_open_mem (void *mem, size_t size, int autofree)
{
MemContext *ctx;
MidIStream *stream;
stream = safe_malloc (sizeof (MidIStream));
if (stream == NULL)
return NULL;
ctx = safe_malloc (sizeof (MemContext));
if (ctx == NULL)
{
free (stream);
return NULL;
}
ctx->base = mem;
ctx->current = mem;
ctx->end = ((sint8 *) mem) + size;
ctx->autofree = autofree;
stream->ctx = ctx;
stream->read = mem_istream_read;
stream->close = mem_istream_close;
return stream;
}
MidIStream *
mid_istream_open_callbacks (MidIStreamReadFunc read,
MidIStreamCloseFunc close, void *context)
{
MidIStream *stream;
stream = safe_malloc (sizeof (MidIStream));
if (stream == NULL)
return NULL;
stream->ctx = context;
stream->read = read;
stream->close = close;
return stream;
}
size_t
mid_istream_read (MidIStream * stream, void *ptr, size_t size, size_t nmemb)
{
return stream->read (stream->ctx, ptr, size, nmemb);
}
void
mid_istream_skip (MidIStream * stream, size_t len)
{
size_t c;
char tmp[1024];
while (len > 0)
{
c = len;
if (c > 1024)
c = 1024;
len -= c;
if (c != mid_istream_read (stream, tmp, 1, c))
{
DEBUG_MSG ("mid_istream_skip error\n");
}
}
}
int
mid_istream_close (MidIStream * stream)
{
int ret = stream->close (stream->ctx);
free (stream);
return ret;
}

View File

@@ -0,0 +1,214 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include "timidity.h"
#include "tables.h"
const sint32 freq_table[128]=
{
8176, 8662, 9177, 9723,
10301, 10913, 11562, 12250,
12978, 13750, 14568, 15434,
16352, 17324, 18354, 19445,
20602, 21827, 23125, 24500,
25957, 27500, 29135, 30868,
32703, 34648, 36708, 38891,
41203, 43654, 46249, 48999,
51913, 55000, 58270, 61735,
65406, 69296, 73416, 77782,
82407, 87307, 92499, 97999,
103826, 110000, 116541, 123471,
130813, 138591, 146832, 155563,
164814, 174614, 184997, 195998,
207652, 220000, 233082, 246942,
261626, 277183, 293665, 311127,
329628, 349228, 369994, 391995,
415305, 440000, 466164, 493883,
523251, 554365, 587330, 622254,
659255, 698456, 739989, 783991,
830609, 880000, 932328, 987767,
1046502, 1108731, 1174659, 1244508,
1318510, 1396913, 1479978, 1567982,
1661219, 1760000, 1864655, 1975533,
2093005, 2217461, 2349318, 2489016,
2637020, 2793826, 2959955, 3135963,
3322438, 3520000, 3729310, 3951066,
4186009, 4434922, 4698636, 4978032,
5274041, 5587652, 5919911, 6271927,
6644875, 7040000, 7458620, 7902133,
8372018, 8869844, 9397273, 9956063,
10548082, 11175303, 11839822, 12543854
};
/* v=2.^((x/127-1) * 6) */
const double vol_table[128] =
{
0.015625, 0.016145143728351113, 0.016682602624583379, 0.017237953096759438,
0.017811790741104401, 0.01840473098076444, 0.019017409725829021, 0.019650484055324921,
0.020304632921913132, 0.020980557880044631, 0.021678983838355849, 0.02240065983711079,
0.023146359851523596, 0.023916883621822989, 0.024713057510949051, 0.025535735390801884,
0.026385799557992876, 0.027264161680080529, 0.028171763773305786, 0.029109579212875332,
0.030078613776876421, 0.031079906724942836, 0.032114531912828696, 0.033183598944085631,
0.034288254360078256, 0.035429682869614412, 0.036609108619508737, 0.037827796507442342,
0.039087053538526394, 0.040388230227024875, 0.041732722044739302, 0.043121970917609151,
0.044557466772132896, 0.046040749133268132, 0.047573408775524545, 0.049157089429020417,
0.050793489542332405, 0.05248436410402918, 0.054231526524842463, 0.056036850582493913,
0.057902272431264008, 0.059829792678457581, 0.061821478529993396, 0.063879466007418645,
0.066005962238725971, 0.068203247825430205, 0.070473679288442961, 0.072819691595368496,
0.075243800771931268, 0.077748606600335793, 0.080336795407452768, 0.083011142945821612,
0.085774517370559328, 0.088629882315368294, 0.091580300070941839, 0.094628934869176312,
0.097779056276712184, 0.10103404270144323, 0.1043973850157546, 0.1078726903003755,
0.11146368571286204, 0.11517422248485852, 0.11900828005242428, 0.12296997032385605,
0.12706354208958254, 0.13129338557886089, 0.13566403716816194, 0.14018018424629392,
0.14484667024148207, 0.14966849981579558, 0.15465084423249356, 0.15979904690204472,
0.16511862911277009, 0.17061529595225433, 0.17629494242587571, 0.18216365977901747,
0.18822774202974024, 0.19449369271892172, 0.20096823188510385, 0.20765830327152621,
0.21457108177307616, 0.22171398113114205, 0.2290946618846218, 0.23672103958561411,
0.2446012932886038, 0.25274387432224471, 0.26115751535314891, 0.26985123975140174,
0.27883437126784744, 0.28811654403352405, 0.29770771289197112, 0.30761816407549192,
0.31785852623682015, 0.32843978184802081, 0.33937327897885317, 0.3506707434672246,
0.36234429149478936, 0.37440644258117928, 0.38687013301080181, 0.39974872970660535,
0.41305604456569134, 0.42680634927214656, 0.44101439060298442, 0.45569540624360722,
0.47086514112975281, 0.48653986433345225, 0.50273638651110641, 0.51947207793239625,
0.53676488710936021, 0.55463336004561792, 0.57309666012638816, 0.59217458867062556,
0.61188760616732485, 0.63225685421876243, 0.65330417821421161, 0.67505215075844849,
0.69752409588017272, 0.72074411404630734, 0.74473710800900605, 0.76952880951308478,
0.79514580689252357, 0.82161557358563286, 0.84896649759946774, 0.87722791195508854,
0.90643012614631979, 0.93660445864574493, 0.96778327049280244, 1
};
const double bend_fine[256] = {
1, 1.0002256593050698, 1.0004513695322617, 1.0006771306930664,
1.0009029427989777, 1.0011288058614922, 1.0013547198921082, 1.0015806849023274,
1.0018067009036538, 1.002032767907594, 1.0022588859256572, 1.0024850549693551,
1.0027112750502025, 1.0029375461797159, 1.0031638683694153, 1.0033902416308227,
1.0036166659754628, 1.0038431414148634, 1.0040696679605541, 1.0042962456240678,
1.0045228744169397, 1.0047495543507072, 1.0049762854369111, 1.0052030676870944,
1.0054299011128027, 1.0056567857255843, 1.00588372153699, 1.006110708558573,
1.0063377468018897, 1.0065648362784985, 1.0067919769999607, 1.0070191689778405,
1.0072464122237039, 1.0074737067491204, 1.0077010525656616, 1.0079284496849015,
1.0081558981184175, 1.008383397877789, 1.008610948974598, 1.0088385514204294,
1.0090662052268706, 1.0092939104055114, 1.0095216669679448, 1.0097494749257656,
1.009977334290572, 1.0102052450739643, 1.0104332072875455, 1.0106612209429215,
1.0108892860517005, 1.0111174026254934, 1.0113455706759138, 1.0115737902145781,
1.0118020612531047, 1.0120303838031153, 1.0122587578762337, 1.012487183484087,
1.0127156606383041, 1.0129441893505169, 1.0131727696323602, 1.0134014014954713,
1.0136300849514894, 1.0138588200120575, 1.0140876066888203, 1.0143164449934257,
1.0145453349375237, 1.0147742765327674, 1.0150032697908125, 1.0152323147233171,
1.015461411341942, 1.0156905596583505, 1.0159197596842091, 1.0161490114311862,
1.0163783149109531, 1.0166076701351838, 1.0168370771155553, 1.0170665358637463,
1.0172960463914391, 1.0175256087103179, 1.0177552228320703, 1.0179848887683858,
1.0182146065309567, 1.0184443761314785, 1.0186741975816487, 1.0189040708931674,
1.0191339960777379, 1.0193639731470658, 1.0195940021128593, 1.0198240829868295,
1.0200542157806898, 1.0202844005061564, 1.0205146371749483, 1.0207449257987866,
1.0209752663893958, 1.0212056589585028, 1.0214361035178368, 1.0216666000791297,
1.0218971486541166, 1.0221277492545349, 1.0223584018921241, 1.0225891065786274,
1.0228198633257899, 1.0230506721453596, 1.023281533049087, 1.0235124460487257,
1.0237434111560313, 1.0239744283827625, 1.0242054977406807, 1.0244366192415495,
1.0246677928971357, 1.0248990187192082, 1.025130296719539, 1.0253616269099028,
1.0255930093020766, 1.0258244439078401, 1.0260559307389761, 1.0262874698072693,
1.0265190611245079, 1.0267507047024822, 1.0269824005529853, 1.027214148687813,
1.0274459491187637, 1.0276778018576387, 1.0279097069162415, 1.0281416643063788,
1.0283736740398595, 1.0286057361284953, 1.0288378505841009, 1.0290700174184932,
1.0293022366434921, 1.0295345082709197, 1.0297668323126017, 1.0299992087803651,
1.030231637686041, 1.0304641190414621, 1.0306966528584645, 1.0309292391488862,
1.0311618779245688, 1.0313945691973556, 1.0316273129790936, 1.0318601092816313,
1.0320929581168212, 1.0323258594965172, 1.0325588134325767, 1.0327918199368598,
1.0330248790212284, 1.0332579906975481, 1.0334911549776868, 1.033724371873515,
1.0339576413969056, 1.0341909635597348, 1.0344243383738811, 1.0346577658512259,
1.034891246003653, 1.0351247788430489, 1.0353583643813031, 1.0355920026303078,
1.0358256936019572, 1.0360594373081489, 1.0362932337607829, 1.0365270829717617,
1.0367609849529913, 1.0369949397163791, 1.0372289472738365, 1.0374630076372766,
1.0376971208186156, 1.0379312868297725, 1.0381655056826686, 1.0383997773892284,
1.0386341019613787, 1.0388684794110492, 1.0391029097501721, 1.0393373929906822,
1.0395719291445176, 1.0398065182236185, 1.0400411602399278, 1.0402758552053915,
1.0405106031319582, 1.0407454040315787, 1.0409802579162071, 1.0412151647977996,
1.0414501246883161, 1.0416851375997183, 1.0419202035439705, 1.0421553225330404,
1.042390494578898, 1.042625719693516, 1.0428609978888699, 1.043096329176938,
1.0433317135697009, 1.0435671510791424, 1.0438026417172486, 1.0440381854960086,
1.0442737824274138, 1.044509432523459, 1.044745135796141, 1.0449808922574599,
1.0452167019194181, 1.0454525647940205, 1.0456884808932754, 1.0459244502291931,
1.0461604728137874, 1.0463965486590741, 1.046632677777072, 1.0468688601798024,
1.0471050958792898, 1.047341384887561, 1.0475777272166455, 1.047814122878576,
1.048050571885387, 1.0482870742491166, 1.0485236299818055, 1.0487602390954964,
1.0489969016022356, 1.0492336175140715, 1.0494703868430555, 1.0497072096012419,
1.0499440858006872, 1.0501810154534512, 1.050417998571596, 1.0506550351671864,
1.0508921252522903, 1.0511292688389782, 1.0513664659393229, 1.0516037165654004,
1.0518410207292894, 1.0520783784430709, 1.0523157897188296, 1.0525532545686513,
1.0527907730046264, 1.0530283450388465, 1.0532659706834067, 1.0535036499504049,
1.0537413828519411, 1.0539791694001188, 1.0542170096070436, 1.0544549034848243,
1.0546928510455722, 1.0549308523014012, 1.0551689072644284, 1.0554070159467728,
1.0556451783605572, 1.0558833945179062, 1.0561216644309479, 1.0563599881118126,
1.0565983655726334, 1.0568367968255465, 1.0570752818826903, 1.0573138207562065,
1.057552413458239, 1.0577910600009348, 1.0580297603964437, 1.058268514656918,
1.0585073227945128, 1.0587461848213857, 1.058985100749698, 1.0592240705916123
};
const double bend_coarse[128] = {
1, 1.0594630943592953, 1.122462048309373, 1.189207115002721,
1.2599210498948732, 1.3348398541700344, 1.4142135623730951, 1.4983070768766815,
1.5874010519681994, 1.681792830507429, 1.7817974362806785, 1.8877486253633868,
2, 2.1189261887185906, 2.244924096618746, 2.3784142300054421,
2.5198420997897464, 2.6696797083400687, 2.8284271247461903, 2.996614153753363,
3.1748021039363992, 3.363585661014858, 3.5635948725613571, 3.7754972507267741,
4, 4.2378523774371812, 4.4898481932374912, 4.7568284600108841,
5.0396841995794928, 5.3393594166801366, 5.6568542494923806, 5.993228307506727,
6.3496042078727974, 6.727171322029716, 7.1271897451227151, 7.5509945014535473,
8, 8.4757047548743625, 8.9796963864749824, 9.5136569200217682,
10.079368399158986, 10.678718833360273, 11.313708498984761, 11.986456615013454,
12.699208415745595, 13.454342644059432, 14.25437949024543, 15.101989002907095,
16, 16.951409509748721, 17.959392772949972, 19.027313840043536,
20.158736798317967, 21.357437666720553, 22.627416997969522, 23.972913230026901,
25.398416831491197, 26.908685288118864, 28.508758980490853, 30.203978005814196,
32, 33.902819019497443, 35.918785545899944, 38.054627680087073,
40.317473596635935, 42.714875333441107, 45.254833995939045, 47.945826460053802,
50.796833662982394, 53.817370576237728, 57.017517960981706, 60.407956011628393,
64, 67.805638038994886, 71.837571091799887, 76.109255360174146,
80.63494719327187, 85.429750666882214, 90.509667991878089, 95.891652920107603,
101.59366732596479, 107.63474115247546, 114.03503592196341, 120.81591202325679,
128, 135.61127607798977, 143.67514218359977, 152.21851072034829,
161.26989438654374, 170.85950133376443, 181.01933598375618, 191.78330584021521,
203.18733465192958, 215.26948230495091, 228.07007184392683, 241.63182404651357,
256, 271.22255215597971, 287.35028436719938, 304.43702144069658,
322.53978877308765, 341.71900266752868, 362.03867196751236, 383.56661168043064,
406.37466930385892, 430.53896460990183, 456.14014368785394, 483.26364809302686,
512, 542.44510431195943, 574.70056873439876, 608.87404288139317,
645.0795775461753, 683.43800533505737, 724.07734393502471, 767.13322336086128,
812.74933860771785, 861.07792921980365, 912.28028737570787, 966.52729618605372,
1024, 1084.8902086239189, 1149.4011374687975, 1217.7480857627863,
1290.1591550923506, 1366.8760106701147, 1448.1546878700494, 1534.2664467217226
};

View File

@@ -0,0 +1,30 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
tables.h
*/
#include <math.h>
#define sine(x) (sin((2*PI/1024.0) * (x)))
#define SINE_CYCLE_LENGTH 1024
extern const sint32 freq_table[];
extern const double vol_table[];
extern const double bend_fine[];
extern const double bend_coarse[];

View File

@@ -0,0 +1,606 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "timidity.h"
#include "timidity_internal.h"
#include "options.h"
#include "common.h"
#include "instrum.h"
#include "playmidi.h"
#include "readmidi.h"
#include "output.h"
#include "tables.h"
MidToneBank *master_tonebank[128], *master_drumset[128];
static char def_instr_name[256] = "";
#define MAXWORDS 10
/* Quick-and-dirty fgets() replacement. */
static char *__fgets(char *s, int size, FILE *fp)
{
int num_read = 0;
int newline = 0;
while (num_read < size && !newline)
{
if (fread(&s[num_read], 1, 1, fp) != 1)
break;
/* Unlike fgets(), don't store newline. Under Windows/DOS we'll
* probably get an extra blank line for every line that's being
* read, but that should be ok.
*/
if (s[num_read] == '\n' || s[num_read] == '\r')
{
s[num_read] = '\0';
newline = 1;
}
num_read++;
}
s[num_read] = '\0';
return (num_read != 0) ? s : NULL;
}
static int read_config_file(char *name)
{
FILE *fp;
char tmp[1024], *w[MAXWORDS], *cp;
MidToneBank *bank=0;
int i, j, k, line=0, words;
static int rcf_count=0;
if (rcf_count>50)
{
DEBUG_MSG("Probable source loop in configuration files\n");
return (-1);
}
if (!(fp=open_file(name)))
return -1;
while (__fgets(tmp, sizeof(tmp), fp))
{
line++;
w[words=0]=strtok(tmp, " \t\240");
if (!w[0]) continue;
/* Originally the TiMidity++ extensions were prefixed like this */
if (strcmp(w[0], "#extension") == 0)
words = -1;
else if (*w[0] == '#')
continue;
while (w[words] && *w[words] != '#' && (words < MAXWORDS))
w[++words]=strtok(0," \t\240");
/*
* TiMidity++ adds a number of extensions to the config file format.
* Many of them are completely irrelevant to SDL_sound, but at least
* we shouldn't choke on them.
*
* Unfortunately the documentation for these extensions is often quite
* vague, gramatically strange or completely absent.
*/
if (
!strcmp(w[0], "comm") /* "comm" program second */
|| !strcmp(w[0], "HTTPproxy") /* "HTTPproxy" hostname:port */
|| !strcmp(w[0], "FTPproxy") /* "FTPproxy" hostname:port */
|| !strcmp(w[0], "mailaddr") /* "mailaddr" your-mail-address */
|| !strcmp(w[0], "opt") /* "opt" timidity-options */
)
{
/*
* + "comm" sets some kind of comment -- the documentation is too
* vague for me to understand at this time.
* + "HTTPproxy", "FTPproxy" and "mailaddr" are for reading data
* over a network, rather than from the file system.
* + "opt" specifies default options for TiMidity++.
*
* These are all quite useless for our version of TiMidity, so
* they can safely remain no-ops.
*/
} else if (!strcmp(w[0], "timeout")) /* "timeout" program second */
{
/*
* Specifies a timeout value of the program. A number of seconds
* before TiMidity kills the note. This may be useful to implement
* later, but I don't see any urgent need for it.
*/
DEBUG_MSG("FIXME: Implement \"timeout\" in TiMidity config.\n");
} else if (!strcmp(w[0], "copydrumset") /* "copydrumset" drumset */
|| !strcmp(w[0], "copybank")) /* "copybank" bank */
{
/*
* Copies all the settings of the specified drumset or bank to
* the current drumset or bank. May be useful later, but not a
* high priority.
*/
DEBUG_MSG("FIXME: Implement \"%s\" in TiMidity config.\n", w[0]);
} else if (!strcmp(w[0], "undef")) /* "undef" progno */
{
/*
* Undefines the tone "progno" of the current tone bank (or
* drum set?). Not a high priority.
*/
DEBUG_MSG("FIXME: Implement \"undef\" in TiMidity config.\n");
} else if (!strcmp(w[0], "altassign")) /* "altassign" prog1 prog2 ... */
{
/*
* Sets the alternate assign for drum set. Whatever that's
* supposed to mean.
*/
DEBUG_MSG("FIXME: Implement \"altassign\" in TiMidity config.\n");
} else if (!strcmp(w[0], "soundfont")
|| !strcmp(w[0], "font"))
{
/*
* I can't find any documentation for these, but I guess they're
* an alternative way of loading/unloading instruments.
*
* "soundfont" sf_file "remove"
* "soundfont" sf_file ["order=" order] ["cutoff=" cutoff]
* ["reso=" reso] ["amp=" amp]
* "font" "exclude" bank preset keynote
* "font" "order" order bank preset keynote
*/
DEBUG_MSG("FIXME: Implmement \"%s\" in TiMidity config.\n", w[0]);
} else if (!strcmp(w[0], "progbase"))
{
/*
* The documentation for this makes absolutely no sense to me, but
* apparently it sets some sort of base offset for tone numbers.
* Why anyone would want to do this is beyond me.
*/
DEBUG_MSG("FIXME: Implement \"progbase\" in TiMidity config.\n");
} else if (!strcmp(w[0], "map")) /* "map" name set1 elem1 set2 elem2 */
{
/*
* This extension is the one we will need to implement, as it is
* used by the "eawpats". Unfortunately I cannot find any
* documentation whatsoever for it, but it looks like it's used
* for remapping one instrument to another somehow.
*/
DEBUG_MSG("FIXME: Implement \"map\" in TiMidity config.\n");
}
/* Standard TiMidity config */
else if (!strcmp(w[0], "dir"))
{
if (words < 2)
{
DEBUG_MSG("%s: line %d: No directory given\n", name, line);
return -2;
}
for (i=1; i<words; i++)
add_to_pathlist(w[i]);
}
else if (!strcmp(w[0], "source"))
{
if (words < 2)
{
DEBUG_MSG("%s: line %d: No file name given\n", name, line);
return -2;
}
for (i=1; i<words; i++)
{
rcf_count++;
read_config_file(w[i]);
rcf_count--;
}
}
else if (!strcmp(w[0], "default"))
{
if (words != 2)
{
DEBUG_MSG("%s: line %d: Must specify exactly one patch name\n",
name, line);
return -2;
}
strncpy(def_instr_name, w[1], 255);
def_instr_name[255]='\0';
}
else if (!strcmp(w[0], "drumset"))
{
if (words < 2)
{
DEBUG_MSG("%s: line %d: No drum set number given\n", name, line);
return -2;
}
i=atoi(w[1]);
if (i<0 || i>127)
{
DEBUG_MSG("%s: line %d: Drum set must be between 0 and 127\n",
name, line);
return -2;
}
if (!master_drumset[i])
{
master_drumset[i] = safe_malloc(sizeof(MidToneBank));
memset(master_drumset[i], 0, sizeof(MidToneBank));
master_drumset[i]->tone = safe_malloc(128 * sizeof(MidToneBankElement));
memset(master_drumset[i]->tone, 0, 128 * sizeof(MidToneBankElement));
}
bank=master_drumset[i];
}
else if (!strcmp(w[0], "bank"))
{
if (words < 2)
{
DEBUG_MSG("%s: line %d: No bank number given\n", name, line);
return -2;
}
i=atoi(w[1]);
if (i<0 || i>127)
{
DEBUG_MSG("%s: line %d: Tone bank must be between 0 and 127\n",
name, line);
return -2;
}
if (!master_tonebank[i])
{
master_tonebank[i] = safe_malloc(sizeof(MidToneBank));
memset(master_tonebank[i], 0, sizeof(MidToneBank));
master_tonebank[i]->tone = safe_malloc(128 * sizeof(MidToneBankElement));
memset(master_tonebank[i]->tone, 0, 128 * sizeof(MidToneBankElement));
}
bank=master_tonebank[i];
}
else
{
if ((words < 2) || (*w[0] < '0' || *w[0] > '9'))
{
DEBUG_MSG("%s: line %d: syntax error\n", name, line);
return -2;
}
i=atoi(w[0]);
if (i<0 || i>127)
{
DEBUG_MSG("%s: line %d: Program must be between 0 and 127\n",
name, line);
return -2;
}
if (!bank)
{
DEBUG_MSG("%s: line %d: Must specify tone bank or drum set before assignment\n",
name, line);
return -2;
}
if (bank->tone[i].name)
free(bank->tone[i].name);
strcpy((bank->tone[i].name=safe_malloc(strlen(w[1])+1)),w[1]);
bank->tone[i].note=bank->tone[i].amp=bank->tone[i].pan=
bank->tone[i].strip_loop=bank->tone[i].strip_envelope=
bank->tone[i].strip_tail=-1;
for (j=2; j<words; j++)
{
if (!(cp=strchr(w[j], '=')))
{
DEBUG_MSG("%s: line %d: bad patch option %s\n", name, line, w[j]);
return -2;
}
*cp++=0;
if (!strcmp(w[j], "amp"))
{
k=atoi(cp);
if ((k<0 || k>MAX_AMPLIFICATION) || (*cp < '0' || *cp > '9'))
{
DEBUG_MSG("%s: line %d: amplification must be between 0 and %d\n",
name, line, MAX_AMPLIFICATION);
return -2;
}
bank->tone[i].amp=k;
}
else if (!strcmp(w[j], "note"))
{
k=atoi(cp);
if ((k<0 || k>127) || (*cp < '0' || *cp > '9'))
{
DEBUG_MSG("%s: line %d: note must be between 0 and 127\n",
name, line);
return -2;
}
bank->tone[i].note=k;
}
else if (!strcmp(w[j], "pan"))
{
if (!strcmp(cp, "center"))
k=64;
else if (!strcmp(cp, "left"))
k=0;
else if (!strcmp(cp, "right"))
k=127;
else
k=((atoi(cp)+100) * 100) / 157;
if ((k<0 || k>127) || (k==0 && *cp!='-' && (*cp < '0' || *cp > '9')))
{
DEBUG_MSG("%s: line %d: panning must be left, right, center, or between -100 and 100\n",
name, line);
return -2;
}
bank->tone[i].pan=k;
}
else if (!strcmp(w[j], "keep"))
{
if (!strcmp(cp, "env"))
bank->tone[i].strip_envelope=0;
else if (!strcmp(cp, "loop"))
bank->tone[i].strip_loop=0;
else
{
DEBUG_MSG("%s: line %d: keep must be env or loop\n", name, line);
return -2;
}
}
else if (!strcmp(w[j], "strip"))
{
if (!strcmp(cp, "env"))
bank->tone[i].strip_envelope=1;
else if (!strcmp(cp, "loop"))
bank->tone[i].strip_loop=1;
else if (!strcmp(cp, "tail"))
bank->tone[i].strip_tail=1;
else
{
DEBUG_MSG("%s: line %d: strip must be env, loop, or tail\n",
name, line);
return -2;
}
}
else
{
DEBUG_MSG("%s: line %d: bad patch option %s\n", name, line, w[j]);
return -2;
}
}
}
}
fclose(fp);
return 0;
}
int mid_init_no_config()
{
/* Allocate memory for the standard tonebank and drumset */
master_tonebank[0] = safe_malloc(sizeof(MidToneBank));
memset(master_tonebank[0], 0, sizeof(MidToneBank));
master_tonebank[0]->tone = safe_malloc(128 * sizeof(MidToneBankElement));
memset(master_tonebank[0]->tone, 0, 128 * sizeof(MidToneBankElement));
master_drumset[0] = safe_malloc(sizeof(MidToneBank));
memset(master_drumset[0], 0, sizeof(MidToneBank));
master_drumset[0]->tone = safe_malloc(128 * sizeof(MidToneBankElement));
memset(master_drumset[0]->tone, 0, 128 * sizeof(MidToneBankElement));
return 0;
}
int mid_init(char *config_file)
{
/* !!! FIXME: This may be ugly, but slightly less so than requiring the
* default search path to have only one element. I think.
*
* We only need to include the likely locations for the config
* file itself since that file should contain any other directory
* that needs to be added to the search path.
*/
#ifdef WIN32
add_to_pathlist("\\TIMIDITY");
#else
add_to_pathlist("/usr/local/lib/timidity");
add_to_pathlist("/etc");
#endif
mid_init_no_config();
if (config_file == NULL || *config_file == '\0')
config_file = CONFIG_FILE;
return read_config_file(config_file);
}
MidSong *mid_song_load_dls(MidIStream *stream, MidDLSPatches *patches, MidSongOptions *options)
{
MidSong *song;
int i;
if (stream == NULL)
return NULL;
/* Allocate memory for the song */
song = (MidSong *)safe_malloc(sizeof(*song));
memset(song, 0, sizeof(*song));
song->patches = patches;
for (i = 0; i < 128; i++)
{
if (master_tonebank[i])
{
song->tonebank[i] = safe_malloc(sizeof(MidToneBank));
memset(song->tonebank[i], 0, sizeof(MidToneBank));
song->tonebank[i]->tone = master_tonebank[i]->tone;
}
if (master_drumset[i])
{
song->drumset[i] = safe_malloc(sizeof(MidToneBank));
memset(song->drumset[i], 0, sizeof(MidToneBank));
song->drumset[i]->tone = master_drumset[i]->tone;
}
}
song->amplification = DEFAULT_AMPLIFICATION;
song->voices = DEFAULT_VOICES;
song->drumchannels = DEFAULT_DRUMCHANNELS;
song->rate = options->rate;
song->encoding = 0;
if ((options->format & 0xFF) == 16)
song->encoding |= PE_16BIT;
if (options->format & 0x8000)
song->encoding |= PE_SIGNED;
if (options->channels == 1)
song->encoding |= PE_MONO;
switch (options->format) {
case MID_AUDIO_S8:
song->write = s32tos8;
break;
case MID_AUDIO_U8:
song->write = s32tou8;
break;
case MID_AUDIO_S16LSB:
song->write = s32tos16l;
break;
case MID_AUDIO_S16MSB:
song->write = s32tos16b;
break;
case MID_AUDIO_U16LSB:
song->write = s32tou16l;
break;
default:
DEBUG_MSG("Unsupported audio format\n");
song->write = s32tou16l;
break;
}
song->buffer_size = options->buffer_size;
song->resample_buffer = safe_malloc(options->buffer_size * sizeof(sample_t));
song->common_buffer = safe_malloc(options->buffer_size * 2 * sizeof(sint32));
song->bytes_per_sample =
((song->encoding & PE_MONO) ? 1 : 2)
* ((song->encoding & PE_16BIT) ? 2 : 1);
song->control_ratio = options->rate / CONTROLS_PER_SECOND;
if (song->control_ratio < 1)
song->control_ratio = 1;
else if (song->control_ratio > MAX_CONTROL_RATIO)
song->control_ratio = MAX_CONTROL_RATIO;
song->lost_notes = 0;
song->cut_notes = 0;
song->events = read_midi_file(stream, song, &(song->groomed_event_count),
&song->samples);
/* Make sure everything is okay */
if (!song->events) {
free(song);
return(NULL);
}
song->default_instrument = 0;
song->default_program = DEFAULT_PROGRAM;
if (*def_instr_name)
set_default_instrument(song, def_instr_name);
load_missing_instruments(song);
return(song);
}
MidSong *mid_song_load(MidIStream *stream, MidSongOptions *options)
{
return mid_song_load_dls(stream, NULL, options);
}
void mid_song_free(MidSong *song)
{
int i;
free_instruments(song);
for (i = 0; i < 128; i++)
{
if (song->tonebank[i])
free(song->tonebank[i]);
if (song->drumset[i])
free(song->drumset[i]);
}
free(song->common_buffer);
free(song->resample_buffer);
free(song->events);
for (i = 0; i < (sizeof(song->meta_data) / sizeof(song->meta_data[0])); i++)
{
if (song->meta_data[i])
free(song->meta_data[i]);
}
free(song);
}
void mid_exit(void)
{
int i, j;
for (i = 0; i < 128; i++)
{
if (master_tonebank[i])
{
MidToneBankElement *e = master_tonebank[i]->tone;
if (e != NULL)
{
for (j = 0; j < 128; j++)
{
if (e[j].name != NULL)
free(e[j].name);
}
free(e);
}
free(master_tonebank[i]);
}
if (master_drumset[i])
{
MidToneBankElement *e = master_drumset[i]->tone;
if (e != NULL)
{
for (j = 0; j < 128; j++)
{
if (e[j].name != NULL)
free(e[j].name);
}
free(e);
}
free(master_drumset[i]);
}
}
free_pathlist();
}

View File

@@ -0,0 +1,206 @@
/*
libTiMidity -- MIDI to WAVE converter library
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
Copyright (C) 2004 Konstantin Korikov <lostclus@ua.fm>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef TIMIDITY_H
#define TIMIDITY_H
#include <stdio.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C"
{
#endif
#define LIBTIMIDITY_VERSION_MAJOR @LIBTIMIDITY_MAJOR_VERSION@L
#define LIBTIMIDITY_VERSION_MINOR @LIBTIMIDITY_MINOR_VERSION@L
#define LIBTIMIDITY_PATCHLEVEL @LIBTIMIDITY_MICRO_VERSION@L
#define LIBTIMIDITY_VERSION \
((LIBTIMIDITY_VERSION_MAJOR<<16)| \
(LIBTIMIDITY_VERSION_MINOR<< 8)| \
(LIBTIMIDITY_PATCHLEVEL))
/* Audio format flags (defaults to LSB byte order)
*/
#define MID_AUDIO_U8 0x0008 /* Unsigned 8-bit samples */
#define MID_AUDIO_S8 0x8008 /* Signed 8-bit samples */
#define MID_AUDIO_U16LSB 0x0010 /* Unsigned 16-bit samples */
#define MID_AUDIO_S16LSB 0x8010 /* Signed 16-bit samples */
#define MID_AUDIO_U16MSB 0x1010 /* As above, but big-endian byte order */
#define MID_AUDIO_S16MSB 0x9010 /* As above, but big-endian byte order */
#define MID_AUDIO_U16 MID_AUDIO_U16LSB
#define MID_AUDIO_S16 MID_AUDIO_S16LSB
/* Core Library Types
*/
typedef unsigned char uint8;
typedef signed char sint8;
typedef unsigned short uint16;
typedef signed short sint16;
typedef unsigned int uint32;
typedef signed int sint32;
typedef size_t (*MidIStreamReadFunc) (void *ctx, void *ptr, size_t size,
size_t nmemb);
typedef int (*MidIStreamCloseFunc) (void *ctx);
typedef struct _MidIStream MidIStream;
typedef struct _MidDLSPatches MidDLSPatches;
typedef struct _MidSong MidSong;
typedef struct _MidSongOptions MidSongOptions;
struct _MidSongOptions
{
sint32 rate; /* DSP frequency -- samples per second */
uint16 format; /* Audio data format */
uint8 channels; /* Number of channels: 1 mono, 2 stereo */
uint16 buffer_size; /* Sample buffer size in samples */
};
typedef enum
{
MID_SONG_TEXT = 0,
MID_SONG_COPYRIGHT = 1
} MidSongMetaId;
/* Core Library Functions
* ======================
*/
/* Initialize the library. If config_file is NULL
* search for configuratin file in default directories
*/
extern int mid_init (char *config_file);
/* Initialize the library without reading any
* configuratin file
*/
extern int mid_init_no_config (void);
/* Shutdown the library
*/
extern void mid_exit (void);
/* Input Stream Functions
* ======================
*/
/* Create input stream from a file name
*/
extern MidIStream *mid_istream_open_file (const char *file);
/* Create input stream from a file pointer
*/
extern MidIStream *mid_istream_open_fp (FILE * fp, int autoclose);
/* Create input stream from memory
*/
extern MidIStream *mid_istream_open_mem (void *mem, size_t size,
int autofree);
/* Create custom input stream
*/
extern MidIStream *mid_istream_open_callbacks (MidIStreamReadFunc read,
MidIStreamCloseFunc close,
void *context);
/* Read data from input stream
*/
extern size_t mid_istream_read (MidIStream * stream, void *ptr, size_t size,
size_t nmemb);
/* Skip data from input stream
*/
extern void mid_istream_skip (MidIStream * stream, size_t len);
/* Close and destroy input stream
*/
extern int mid_istream_close (MidIStream * stream);
/* DLS Pathes Functions
* ====================
*/
/* Load DLS patches
*/
extern MidDLSPatches *mid_dlspatches_load (MidIStream * stream);
/* Destroy DLS patches
*/
extern void mid_dlspatches_free (MidDLSPatches * patches);
/* MIDI Song Functions
* ===================
*/
/* Load MIDI song
*/
extern MidSong *mid_song_load (MidIStream * stream,
MidSongOptions * options);
/* Load MIDI song with specified DLS pathes
*/
extern MidSong *mid_song_load_dls (MidIStream * stream,
MidDLSPatches * patches,
MidSongOptions * options);
/* Set song amplification value
*/
extern void mid_song_set_volume (MidSong * song, int volume);
/* Seek song to the start position and initialize conversion
*/
extern void mid_song_start (MidSong * song);
/* Read WAVE data
*/
extern size_t mid_song_read_wave (MidSong * song, void *ptr, size_t size);
/* Seek song to specified offset in millseconds
*/
extern void mid_song_seek (MidSong * song, uint32 ms);
/* Get total song time in millseconds
*/
extern uint32 mid_song_get_total_time (MidSong * song);
/* Get current song time in millseconds
*/
extern uint32 mid_song_get_time (MidSong * song);
/* Get song meta data. Return NULL if no meta data found
*/
extern char *mid_song_get_meta (MidSong * song, MidSongMetaId what);
/* Destroy song
*/
extern void mid_song_free (MidSong * song);
#ifdef __cplusplus
}
#endif
#endif /* TIMIDITY_H */

View File

@@ -0,0 +1,192 @@
#ifndef TIMIDITY_INTERNAL_H
#define TIMIDITY_INTERNAL_H
#include "timidity.h"
#if defined(__i386__) || defined(__ia64__) || defined(WIN32) || \
(defined(__alpha__) || defined(__alpha)) || \
defined(__arm__) || \
(defined(__mips__) && defined(__MIPSEL__)) || \
defined(__SYMBIAN32__) || \
defined(__x86_64__) || \
defined(__LITTLE_ENDIAN__)
#ifndef LITTLE_ENDIAN
#define LITTLE_ENDIAN
#endif
#undef BIG_ENDIAN
#else
#ifndef BIG_ENDIAN
#define BIG_ENDIAN
#endif
#undef LITTLE_ENDIAN
#endif
/* Instrument files are little-endian, MIDI files big-endian, so we
need to do some conversions. */
#define XCHG_SHORT(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
#ifdef __i486__
# define XCHG_LONG(x) \
({ sint32 __value; \
asm ("bswap %1; movl %1,%0" : "=g" (__value) : "r" (x)); \
__value; })
#else
# define XCHG_LONG(x) ((((x)&0xFF)<<24) | \
(((x)&0xFF00)<<8) | \
(((x)&0xFF0000)>>8) | \
(((x)>>24)&0xFF))
#endif
#ifdef LITTLE_ENDIAN
#define SWAPLE16(x) x
#define SWAPLE32(x) x
#define SWAPBE16(x) XCHG_SHORT(x)
#define SWAPBE32(x) XCHG_LONG(x)
#else
#define SWAPBE16(x) x
#define SWAPBE32(x) x
#define SWAPLE16(x) XCHG_SHORT(x)
#define SWAPLE32(x) XCHG_LONG(x)
#endif
#ifdef DEBUG
#define DEBUG_MSG(...) fprintf(stderr, __VA_ARGS__)
#else
#define DEBUG_MSG(...)
#endif
#define MID_VIBRATO_SAMPLE_INCREMENTS 32
/* Maximum polyphony. */
#define MID_MAX_VOICES 48
typedef sint16 sample_t;
typedef sint32 final_volume_t;
typedef struct _MidSample MidSample;
struct _MidSample
{
sint32
loop_start, loop_end, data_length,
sample_rate, low_vel, high_vel, low_freq, high_freq, root_freq;
sint32 envelope_rate[6], envelope_offset[6];
float volume;
sample_t *data;
sint32
tremolo_sweep_increment, tremolo_phase_increment,
vibrato_sweep_increment, vibrato_control_ratio;
uint8 tremolo_depth, vibrato_depth, modes;
sint8 panning, note_to_use;
};
typedef struct _MidChannel MidChannel;
struct _MidChannel
{
int bank, program, volume, sustain, panning, pitchbend, expression;
int mono; /* one note only on this channel -- not implemented yet */
int pitchsens;
/* chorus, reverb... Coming soon to a 300-MHz, eight-way superscalar
processor near you */
float pitchfactor; /* precomputed pitch bend factor to save some fdiv's */
};
typedef struct _MidVoice MidVoice;
struct _MidVoice
{
uint8 status, channel, note, velocity;
MidSample *sample;
sint32
orig_frequency, frequency,
sample_offset, sample_increment,
envelope_volume, envelope_target, envelope_increment,
tremolo_sweep, tremolo_sweep_position,
tremolo_phase, tremolo_phase_increment,
vibrato_sweep, vibrato_sweep_position;
final_volume_t left_mix, right_mix;
float left_amp, right_amp, tremolo_volume;
sint32 vibrato_sample_increment[MID_VIBRATO_SAMPLE_INCREMENTS];
int
vibrato_phase, vibrato_control_ratio, vibrato_control_counter,
envelope_stage, control_counter, panning, panned;
};
typedef struct _MidInstrument MidInstrument;
struct _MidInstrument
{
int samples;
MidSample *sample;
};
typedef struct _MidToneBankElement MidToneBankElement;
struct _MidToneBankElement
{
char *name;
int note, amp, pan, strip_loop, strip_envelope, strip_tail;
};
typedef struct _MidToneBank MidToneBank;
struct _MidToneBank
{
MidToneBankElement *tone;
MidInstrument *instrument[128];
};
typedef struct _MidEvent MidEvent;
struct _MidEvent
{
sint32 time;
uint8 channel, type, a, b;
};
typedef struct _MidEventList MidEventList;
struct _MidEventList
{
MidEvent event;
void *next;
};
struct _MidSong
{
int playing;
sint32 rate;
sint32 encoding;
int bytes_per_sample;
float master_volume;
sint32 amplification;
MidDLSPatches *patches;
MidToneBank *tonebank[128];
MidToneBank *drumset[128];
MidInstrument *default_instrument;
int default_program;
void (*write) (void *dp, sint32 * lp, sint32 c);
int buffer_size;
sample_t *resample_buffer;
sint32 *common_buffer;
/* These would both fit into 32 bits, but they are often added in
large multiples, so it's simpler to have two roomy ints */
/* samples per MIDI delta-t */
sint32 sample_increment;
sint32 sample_correction;
MidChannel channel[16];
MidVoice voice[MID_MAX_VOICES];
int voices;
sint32 drumchannels;
sint32 control_ratio;
sint32 lost_notes;
sint32 cut_notes;
sint32 samples;
MidEvent *events;
MidEvent *current_event;
MidEventList *evlist;
sint32 current_sample;
sint32 event_count;
sint32 at;
sint32 groomed_event_count;
char *meta_data[8];
};
#endif /* TIMIDITY_INTERNAL_H */