mirror of https://github.com/PCSX2/pcsx2.git
SPU2-X: Introducing the new SPU2-X! After some talk with Gigaherz, It was decided to branch and rename Playground's mod of SPU2ghz to SPU2-X. This commit isn't just a copy. It includes a series of significant revisions. The most notable features are:
* Working Reverb. Yes, you heard right. Fully and completely implemented reverb effects! * Automatic 5.1 speaker expansion. * Some more bugfixes to volumes. * All new configuration panels. * Improved/Bugfixed XAudio2 drivers. git-svn-id: http://pcsx2.googlecode.com/svn/trunk@497 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
d14d09cab3
commit
042e904a65
|
@ -0,0 +1,26 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 10.00
|
||||
# Visual Studio 2008
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Spu2-X", "spu2-x\src\Win32\Spu2-X_vs2008.vcproj", "{5307BBB7-EBB9-4AA4-8CB6-A94EC473C8C4}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
DebugStrict|Win32 = DebugStrict|Win32
|
||||
Devel|Win32 = Devel|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{5307BBB7-EBB9-4AA4-8CB6-A94EC473C8C4}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{5307BBB7-EBB9-4AA4-8CB6-A94EC473C8C4}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{5307BBB7-EBB9-4AA4-8CB6-A94EC473C8C4}.DebugStrict|Win32.ActiveCfg = DebugStrict|Win32
|
||||
{5307BBB7-EBB9-4AA4-8CB6-A94EC473C8C4}.DebugStrict|Win32.Build.0 = DebugStrict|Win32
|
||||
{5307BBB7-EBB9-4AA4-8CB6-A94EC473C8C4}.Devel|Win32.ActiveCfg = Devel|Win32
|
||||
{5307BBB7-EBB9-4AA4-8CB6-A94EC473C8C4}.Devel|Win32.Build.0 = Devel|Win32
|
||||
{5307BBB7-EBB9-4AA4-8CB6-A94EC473C8C4}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{5307BBB7-EBB9-4AA4-8CB6-A94EC473C8C4}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,503 @@
|
|||
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!
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
[This file contains the template for the SPU2-X code rights license. For the full
|
||||
rant-like preamble of the LGP, see LGPL.TXT.]
|
||||
|
||||
|
||||
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
* Developed and maintained by the Pcsx2 Development Team.
|
||||
*
|
||||
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
||||
*
|
||||
* 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 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
|
||||
* 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
|
||||
*
|
||||
*/
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
-- SPU2-X -- Website Links
|
||||
|
||||
The official Svn for SPU2-X is hosted at GoogleCode, under the Pcsx2 project.
|
||||
You can check it out directly or you can check out Pcsx2 as a whole (which
|
||||
includes this plugin along with many others). The links are as follows:
|
||||
|
||||
Homepage (website):
|
||||
http://code.google.com/p/pcsx2/
|
||||
|
||||
You'll find information on how to check out from svn and how to compile
|
||||
Pcsx2 and the plugins at the above link.
|
||||
|
||||
SPU2-X (svn):
|
||||
http://pcsx2.googlecode.com/svn/trunk/plugins/spu2-x
|
||||
|
||||
Pcsx2 + All Plugins (svn):
|
||||
http://pcsx2.googlecode.com/svn/trunk/
|
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
Binary file not shown.
After Width: | Height: | Size: 88 KiB |
|
@ -0,0 +1,350 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Win32 version of the AMD 3DNow! optimized routines for AMD K6-2/Athlon
|
||||
/// processors. All 3DNow! optimized functions have been gathered into this
|
||||
/// single source code file, regardless to their class or original source code
|
||||
/// file, in order to ease porting the library to other compiler and processor
|
||||
/// platforms.
|
||||
///
|
||||
/// By the way; the performance gain depends heavily on the CPU generation: On
|
||||
/// K6-2 these routines provided speed-up of even 2.4 times, while on Athlon the
|
||||
/// difference to the original routines stayed at unremarkable 8%! Such a small
|
||||
/// improvement on Athlon is due to 3DNow can perform only two operations in
|
||||
/// parallel, and obviously also the Athlon FPU is doing a very good job with
|
||||
/// the standard C floating point routines! Here these routines are anyway,
|
||||
/// although it might not be worth the effort to convert these to GCC platform,
|
||||
/// for Athlon CPU at least. The situation is different regarding the SSE
|
||||
/// optimizations though, thanks to the four parallel operations of SSE that
|
||||
/// already make a difference.
|
||||
///
|
||||
/// This file is to be compiled in Windows platform with Microsoft Visual C++
|
||||
/// Compiler. Please see '3dnow_gcc.cpp' for the gcc compiler version for all
|
||||
/// GNU platforms (if file supplied).
|
||||
///
|
||||
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
|
||||
/// 6.0 processor pack" update to support 3DNow! instruction set. The update is
|
||||
/// available for download at Microsoft Developers Network, see here:
|
||||
/// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx
|
||||
///
|
||||
/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and
|
||||
/// perform a search with keywords "processor pack".
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.10 $
|
||||
//
|
||||
// $Id: 3dnow_win.cpp,v 1.10 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "cpu_detect.h"
|
||||
#include "STTypes.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#error "wrong platform - this source code file is exclusively for Win32 platform"
|
||||
#endif
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
#ifdef ALLOW_3DNOW
|
||||
// 3DNow! routines available only with float sample type
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of 3DNow! optimized functions of class 'TDStretch3DNow'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "TDStretch.h"
|
||||
#include <limits.h>
|
||||
|
||||
// these are declared in 'TDStretch.cpp'
|
||||
extern int scanOffsets[4][24];
|
||||
|
||||
|
||||
// Calculates cross correlation of two buffers
|
||||
double TDStretch3DNow::calcCrossCorrStereo(const float *pV1, const float *pV2) const
|
||||
{
|
||||
uint overlapLengthLocal = overlapLength;
|
||||
float corr;
|
||||
|
||||
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
|
||||
/*
|
||||
c-pseudocode:
|
||||
|
||||
corr = 0;
|
||||
for (i = 0; i < overlapLength / 4; i ++)
|
||||
{
|
||||
corr += pV1[0] * pV2[0];
|
||||
pV1[1] * pV2[1];
|
||||
pV1[2] * pV2[2];
|
||||
pV1[3] * pV2[3];
|
||||
pV1[4] * pV2[4];
|
||||
pV1[5] * pV2[5];
|
||||
pV1[6] * pV2[6];
|
||||
pV1[7] * pV2[7];
|
||||
|
||||
pV1 += 8;
|
||||
pV2 += 8;
|
||||
}
|
||||
*/
|
||||
|
||||
_asm
|
||||
{
|
||||
// give prefetch hints to CPU of what data are to be needed soonish.
|
||||
// give more aggressive hints on pV1 as that changes more between different calls
|
||||
// while pV2 stays the same.
|
||||
prefetch [pV1]
|
||||
prefetch [pV2]
|
||||
prefetch [pV1 + 32]
|
||||
|
||||
mov eax, dword ptr pV2
|
||||
mov ebx, dword ptr pV1
|
||||
|
||||
pxor mm0, mm0
|
||||
|
||||
mov ecx, overlapLengthLocal
|
||||
shr ecx, 2 // div by four
|
||||
|
||||
loop1:
|
||||
movq mm1, [eax]
|
||||
prefetch [eax + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
pfmul mm1, [ebx]
|
||||
prefetch [ebx + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
movq mm2, [eax + 8]
|
||||
pfadd mm0, mm1
|
||||
pfmul mm2, [ebx + 8]
|
||||
|
||||
movq mm3, [eax + 16]
|
||||
pfadd mm0, mm2
|
||||
pfmul mm3, [ebx + 16]
|
||||
|
||||
movq mm4, [eax + 24]
|
||||
pfadd mm0, mm3
|
||||
pfmul mm4, [ebx + 24]
|
||||
|
||||
add eax, 32
|
||||
pfadd mm0, mm4
|
||||
add ebx, 32
|
||||
|
||||
dec ecx
|
||||
jnz loop1
|
||||
|
||||
// add halfs of mm0 together and return the result.
|
||||
// note: mm1 is used as a dummy parameter only, we actually don't care about it's value
|
||||
pfacc mm0, mm1
|
||||
movd corr, mm0
|
||||
femms
|
||||
}
|
||||
|
||||
return corr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of 3DNow! optimized functions of class 'FIRFilter'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "FIRFilter.h"
|
||||
|
||||
FIRFilter3DNow::FIRFilter3DNow() : FIRFilter()
|
||||
{
|
||||
filterCoeffsUnalign = NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilter3DNow::~FIRFilter3DNow()
|
||||
{
|
||||
delete[] filterCoeffsUnalign;
|
||||
}
|
||||
|
||||
|
||||
// (overloaded) Calculates filter coefficients for 3DNow! routine
|
||||
void FIRFilter3DNow::setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor)
|
||||
{
|
||||
uint i;
|
||||
float fDivider;
|
||||
|
||||
FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
|
||||
|
||||
// Scale the filter coefficients so that it won't be necessary to scale the filtering result
|
||||
// also rearrange coefficients suitably for 3DNow!
|
||||
// Ensure that filter coeffs array is aligned to 16-byte boundary
|
||||
delete[] filterCoeffsUnalign;
|
||||
filterCoeffsUnalign = new float[2 * newLength + 4];
|
||||
filterCoeffsAlign = (float *)(((uint)filterCoeffsUnalign + 15) & -16);
|
||||
|
||||
fDivider = (float)resultDivider;
|
||||
|
||||
// rearrange the filter coefficients for mmx routines
|
||||
for (i = 0; i < newLength; i ++)
|
||||
{
|
||||
filterCoeffsAlign[2 * i + 0] =
|
||||
filterCoeffsAlign[2 * i + 1] = coeffs[i + 0] / fDivider;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 3DNow!-optimized version of the filter routine for stereo sound
|
||||
uint FIRFilter3DNow::evaluateFilterStereo(float *dest, const float *src, const uint numSamples) const
|
||||
{
|
||||
float *filterCoeffsLocal = filterCoeffsAlign;
|
||||
uint count = (numSamples - length) & -2;
|
||||
uint lengthLocal = length / 4;
|
||||
|
||||
assert(length != 0);
|
||||
assert(count % 2 == 0);
|
||||
|
||||
/* original code:
|
||||
|
||||
double suml1, suml2;
|
||||
double sumr1, sumr2;
|
||||
uint i, j;
|
||||
|
||||
for (j = 0; j < count; j += 2)
|
||||
{
|
||||
const float *ptr;
|
||||
|
||||
suml1 = sumr1 = 0.0;
|
||||
suml2 = sumr2 = 0.0;
|
||||
ptr = src;
|
||||
filterCoeffsLocal = filterCoeffs;
|
||||
for (i = 0; i < lengthLocal; i ++)
|
||||
{
|
||||
// unroll loop for efficiency.
|
||||
|
||||
suml1 += ptr[0] * filterCoeffsLocal[0] +
|
||||
ptr[2] * filterCoeffsLocal[2] +
|
||||
ptr[4] * filterCoeffsLocal[4] +
|
||||
ptr[6] * filterCoeffsLocal[6];
|
||||
|
||||
sumr1 += ptr[1] * filterCoeffsLocal[1] +
|
||||
ptr[3] * filterCoeffsLocal[3] +
|
||||
ptr[5] * filterCoeffsLocal[5] +
|
||||
ptr[7] * filterCoeffsLocal[7];
|
||||
|
||||
suml2 += ptr[8] * filterCoeffsLocal[0] +
|
||||
ptr[10] * filterCoeffsLocal[2] +
|
||||
ptr[12] * filterCoeffsLocal[4] +
|
||||
ptr[14] * filterCoeffsLocal[6];
|
||||
|
||||
sumr2 += ptr[9] * filterCoeffsLocal[1] +
|
||||
ptr[11] * filterCoeffsLocal[3] +
|
||||
ptr[13] * filterCoeffsLocal[5] +
|
||||
ptr[15] * filterCoeffsLocal[7];
|
||||
|
||||
ptr += 16;
|
||||
filterCoeffsLocal += 8;
|
||||
}
|
||||
dest[0] = (float)suml1;
|
||||
dest[1] = (float)sumr1;
|
||||
dest[2] = (float)suml2;
|
||||
dest[3] = (float)sumr2;
|
||||
|
||||
src += 4;
|
||||
dest += 4;
|
||||
}
|
||||
|
||||
*/
|
||||
_asm
|
||||
{
|
||||
mov eax, dword ptr dest
|
||||
mov ebx, dword ptr src
|
||||
mov edx, count
|
||||
shr edx, 1
|
||||
|
||||
loop1:
|
||||
// "outer loop" : during each round 2*2 output samples are calculated
|
||||
prefetch [ebx] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetch [filterCoeffsLocal] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
mov esi, ebx
|
||||
mov edi, filterCoeffsLocal
|
||||
pxor mm0, mm0
|
||||
pxor mm1, mm1
|
||||
mov ecx, lengthLocal
|
||||
|
||||
loop2:
|
||||
// "inner loop" : during each round four FIR filter taps are evaluated for 2*2 output samples
|
||||
movq mm2, [edi]
|
||||
movq mm3, mm2
|
||||
prefetch [edi + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
pfmul mm2, [esi]
|
||||
prefetch [esi + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
pfmul mm3, [esi + 8]
|
||||
|
||||
movq mm4, [edi + 8]
|
||||
movq mm5, mm4
|
||||
pfadd mm0, mm2
|
||||
pfmul mm4, [esi + 8]
|
||||
pfadd mm1, mm3
|
||||
pfmul mm5, [esi + 16]
|
||||
|
||||
movq mm2, [edi + 16]
|
||||
movq mm6, mm2
|
||||
pfadd mm0, mm4
|
||||
pfmul mm2, [esi + 16]
|
||||
pfadd mm1, mm5
|
||||
pfmul mm6, [esi + 24]
|
||||
|
||||
movq mm3, [edi + 24]
|
||||
movq mm7, mm3
|
||||
pfadd mm0, mm2
|
||||
pfmul mm3, [esi + 24]
|
||||
pfadd mm1, mm6
|
||||
pfmul mm7, [esi + 32]
|
||||
add esi, 32
|
||||
pfadd mm0, mm3
|
||||
add edi, 32
|
||||
pfadd mm1, mm7
|
||||
|
||||
dec ecx
|
||||
jnz loop2
|
||||
|
||||
movq [eax], mm0
|
||||
add ebx, 16
|
||||
movq [eax + 8], mm1
|
||||
add eax, 16
|
||||
|
||||
dec edx
|
||||
jnz loop1
|
||||
|
||||
femms
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
#endif // ALLOW_3DNOW
|
|
@ -0,0 +1,184 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// FIR low-pass (anti-alias) filter with filter coefficient design routine and
|
||||
/// MMX optimization.
|
||||
///
|
||||
/// Anti-alias filter is used to prevent folding of high frequencies when
|
||||
/// transposing the sample rate with interpolation.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.9 $
|
||||
//
|
||||
// $Id: AAFilter.cpp,v 1.9 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <memory.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include "AAFilter.h"
|
||||
#include "FIRFilter.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
#define PI 3.141592655357989
|
||||
#define TWOPI (2 * PI)
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Implementation of the class 'AAFilter'
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
AAFilter::AAFilter(const uint length)
|
||||
{
|
||||
pFIR = FIRFilter::newInstance();
|
||||
cutoffFreq = 0.5;
|
||||
setLength(length);
|
||||
}
|
||||
|
||||
|
||||
|
||||
AAFilter::~AAFilter()
|
||||
{
|
||||
delete pFIR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new anti-alias filter cut-off edge frequency, scaled to
|
||||
// sampling frequency (nyquist frequency = 0.5).
|
||||
// The filter will cut frequencies higher than the given frequency.
|
||||
void AAFilter::setCutoffFreq(const double newCutoffFreq)
|
||||
{
|
||||
cutoffFreq = newCutoffFreq;
|
||||
calculateCoeffs();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets number of FIR filter taps
|
||||
void AAFilter::setLength(const uint newLength)
|
||||
{
|
||||
length = newLength;
|
||||
calculateCoeffs();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Calculates coefficients for a low-pass FIR filter using Hamming window
|
||||
void AAFilter::calculateCoeffs()
|
||||
{
|
||||
uint i;
|
||||
double cntTemp, temp, tempCoeff,h, w;
|
||||
double fc2, wc;
|
||||
double scaleCoeff, sum;
|
||||
double *work;
|
||||
SAMPLETYPE *coeffs;
|
||||
|
||||
assert(length > 0);
|
||||
assert(length % 4 == 0);
|
||||
assert(cutoffFreq >= 0);
|
||||
assert(cutoffFreq <= 0.5);
|
||||
|
||||
work = new double[length];
|
||||
coeffs = new SAMPLETYPE[length];
|
||||
|
||||
fc2 = 2.0 * cutoffFreq;
|
||||
wc = PI * fc2;
|
||||
tempCoeff = TWOPI / (double)length;
|
||||
|
||||
sum = 0;
|
||||
for (i = 0; i < length; i ++)
|
||||
{
|
||||
cntTemp = (double)i - (double)(length / 2);
|
||||
|
||||
temp = cntTemp * wc;
|
||||
if (temp != 0)
|
||||
{
|
||||
h = fc2 * sin(temp) / temp; // sinc function
|
||||
}
|
||||
else
|
||||
{
|
||||
h = 1.0;
|
||||
}
|
||||
w = 0.54 + 0.46 * cos(tempCoeff * cntTemp); // hamming window
|
||||
|
||||
temp = w * h;
|
||||
work[i] = temp;
|
||||
|
||||
// calc net sum of coefficients
|
||||
sum += temp;
|
||||
}
|
||||
|
||||
// ensure the sum of coefficients is larger than zero
|
||||
assert(sum > 0);
|
||||
|
||||
// ensure we've really designed a lowpass filter...
|
||||
assert(work[length/2] > 0);
|
||||
assert(work[length/2 + 1] > -1e-6);
|
||||
assert(work[length/2 - 1] > -1e-6);
|
||||
|
||||
// Calculate a scaling coefficient in such a way that the result can be
|
||||
// divided by 16384
|
||||
scaleCoeff = 16384.0f / sum;
|
||||
|
||||
for (i = 0; i < length; i ++)
|
||||
{
|
||||
// scale & round to nearest integer
|
||||
temp = work[i] * scaleCoeff;
|
||||
temp += (temp >= 0) ? 0.5 : -0.5;
|
||||
// ensure no overfloods
|
||||
assert(temp >= -32768 && temp <= 32767);
|
||||
coeffs[i] = (SAMPLETYPE)temp;
|
||||
}
|
||||
|
||||
// Set coefficients. Use divide factor 14 => divide result by 2^14 = 16384
|
||||
pFIR->setCoefficients(coeffs, length, 14);
|
||||
|
||||
delete[] work;
|
||||
delete[] coeffs;
|
||||
}
|
||||
|
||||
|
||||
// Applies the filter to the given sequence of samples.
|
||||
// Note : The amount of outputted samples is by value of 'filter length'
|
||||
// smaller than the amount of input samples.
|
||||
uint AAFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
|
||||
{
|
||||
return pFIR->evaluate(dest, src, numSamples, numChannels);
|
||||
}
|
||||
|
||||
|
||||
uint AAFilter::getLength() const
|
||||
{
|
||||
return pFIR->getLength();
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||
/// while maintaining the original pitch by using a time domain WSOLA-like method
|
||||
/// with several performance-increasing tweaks.
|
||||
///
|
||||
/// Anti-alias filter is used to prevent folding of high frequencies when
|
||||
/// transposing the sample rate with interpolation.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.10 $
|
||||
//
|
||||
// $Id: AAFilter.h,v 1.10 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef AAFilter_H
|
||||
#define AAFilter_H
|
||||
|
||||
#include "STTypes.h"
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
class AAFilter
|
||||
{
|
||||
protected:
|
||||
class FIRFilter *pFIR;
|
||||
|
||||
/// Low-pass filter cut-off frequency, negative = invalid
|
||||
double cutoffFreq;
|
||||
|
||||
/// num of filter taps
|
||||
uint length;
|
||||
|
||||
/// Calculate the FIR coefficients realizing the given cutoff-frequency
|
||||
void calculateCoeffs();
|
||||
public:
|
||||
AAFilter(uint length);
|
||||
|
||||
~AAFilter();
|
||||
|
||||
/// Sets new anti-alias filter cut-off edge frequency, scaled to sampling
|
||||
/// frequency (nyquist frequency = 0.5). The filter will cut off the
|
||||
/// frequencies than that.
|
||||
void setCutoffFreq(double newCutoffFreq);
|
||||
|
||||
/// Sets number of FIR filter taps, i.e. ~filter complexity
|
||||
void setLength(uint newLength);
|
||||
|
||||
uint getLength() const;
|
||||
|
||||
/// Applies the filter to the given sequence of samples.
|
||||
/// Note : The amount of outputted samples is by value of 'filter length'
|
||||
/// smaller than the amount of input samples.
|
||||
uint evaluate(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples,
|
||||
uint numChannels) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,159 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Beats-per-minute (BPM) detection routine.
|
||||
///
|
||||
/// The beat detection algorithm works as follows:
|
||||
/// - Use function 'inputSamples' to input a chunks of samples to the class for
|
||||
/// analysis. It's a good idea to enter a large sound file or stream in smallish
|
||||
/// chunks of around few kilosamples in order not to extinguish too much RAM memory.
|
||||
/// - Input sound data is decimated to approx 500 Hz to reduce calculation burden,
|
||||
/// which is basically ok as low (bass) frequencies mostly determine the beat rate.
|
||||
/// Simple averaging is used for anti-alias filtering because the resulting signal
|
||||
/// quality isn't of that high importance.
|
||||
/// - Decimated sound data is enveloped, i.e. the amplitude shape is detected by
|
||||
/// taking absolute value that's smoothed by sliding average. Signal levels that
|
||||
/// are below a couple of times the general RMS amplitude level are cut away to
|
||||
/// leave only notable peaks there.
|
||||
/// - Repeating sound patterns (e.g. beats) are detected by calculating short-term
|
||||
/// autocorrelation function of the enveloped signal.
|
||||
/// - After whole sound data file has been analyzed as above, the bpm level is
|
||||
/// detected by function 'getBpm' that finds the highest peak of the autocorrelation
|
||||
/// function, calculates it's precise location and converts this reading to bpm's.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.5 $
|
||||
//
|
||||
// $Id: BPMDetect.h,v 1.5 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _BPMDetect_H_
|
||||
#define _BPMDetect_H_
|
||||
|
||||
#include "STTypes.h"
|
||||
#include "FIFOSampleBuffer.h"
|
||||
|
||||
/// Minimum allowed BPM rate. Used to restrict accepted result above a reasonable limit.
|
||||
#define MIN_BPM 45
|
||||
|
||||
/// Maximum allowed BPM rate. Used to restrict accepted result below a reasonable limit.
|
||||
#define MAX_BPM 230
|
||||
|
||||
|
||||
/// Class for calculating BPM rate for audio data.
|
||||
class BPMDetect
|
||||
{
|
||||
protected:
|
||||
/// Auto-correlation accumulator bins.
|
||||
float *xcorr;
|
||||
|
||||
/// Amplitude envelope sliding average approximation level accumulator
|
||||
float envelopeAccu;
|
||||
|
||||
/// RMS volume sliding average approximation level accumulator
|
||||
float RMSVolumeAccu;
|
||||
|
||||
/// Sample average counter.
|
||||
int decimateCount;
|
||||
|
||||
/// Sample average accumulator for FIFO-like decimation.
|
||||
soundtouch::LONG_SAMPLETYPE decimateSum;
|
||||
|
||||
/// Decimate sound by this coefficient to reach approx. 500 Hz.
|
||||
int decimateBy;
|
||||
|
||||
/// Auto-correlation window length
|
||||
int windowLen;
|
||||
|
||||
/// Number of channels (1 = mono, 2 = stereo)
|
||||
int channels;
|
||||
|
||||
/// sample rate
|
||||
int sampleRate;
|
||||
|
||||
/// Beginning of auto-correlation window: Autocorrelation isn't being updated for
|
||||
/// the first these many correlation bins.
|
||||
int windowStart;
|
||||
|
||||
/// FIFO-buffer for decimated processing samples.
|
||||
soundtouch::FIFOSampleBuffer *buffer;
|
||||
|
||||
/// Initialize the class for processing.
|
||||
void init(int numChannels, int sampleRate);
|
||||
|
||||
/// Updates auto-correlation function for given number of decimated samples that
|
||||
/// are read from the internal 'buffer' pipe (samples aren't removed from the pipe
|
||||
/// though).
|
||||
void updateXCorr(int process_samples /// How many samples are processed.
|
||||
);
|
||||
|
||||
/// Decimates samples to approx. 500 Hz.
|
||||
///
|
||||
/// \return Number of output samples.
|
||||
int decimate(soundtouch::SAMPLETYPE *dest, ///< Destination buffer
|
||||
const soundtouch::SAMPLETYPE *src, ///< Source sample buffer
|
||||
int numsamples ///< Number of source samples.
|
||||
);
|
||||
|
||||
/// Calculates amplitude envelope for the buffer of samples.
|
||||
/// Result is output to 'samples'.
|
||||
void calcEnvelope(soundtouch::SAMPLETYPE *samples, ///< Pointer to input/output data buffer
|
||||
int numsamples ///< Number of samples in buffer
|
||||
);
|
||||
|
||||
public:
|
||||
/// Constructor.
|
||||
BPMDetect(int numChannels, ///< Number of channels in sample data.
|
||||
int sampleRate ///< Sample rate in Hz.
|
||||
);
|
||||
|
||||
/// Destructor.
|
||||
virtual ~BPMDetect();
|
||||
|
||||
/// Inputs a block of samples for analyzing: Envelopes the samples and then
|
||||
/// updates the autocorrelation estimation. When whole song data has been input
|
||||
/// in smaller blocks using this function, read the resulting bpm with 'getBpm'
|
||||
/// function.
|
||||
///
|
||||
/// Notice that data in 'samples' array can be disrupted in processing.
|
||||
void inputSamples(soundtouch::SAMPLETYPE *samples, ///< Pointer to input/working data buffer
|
||||
int numSamples ///< Number of samples in buffer
|
||||
);
|
||||
|
||||
|
||||
/// Analyzes the results and returns the BPM rate. Use this function to read result
|
||||
/// after whole song data has been input to the class by consecutive calls of
|
||||
/// 'inputSamples' function.
|
||||
///
|
||||
/// \return Beats-per-minute rate, or zero if detection failed.
|
||||
float getBpm();
|
||||
};
|
||||
|
||||
#endif // _BPMDetect_H_
|
|
@ -0,0 +1,252 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// A buffer class for temporarily storaging sound samples, operates as a
|
||||
/// first-in-first-out pipe.
|
||||
///
|
||||
/// Samples are added to the end of the sample buffer with the 'putSamples'
|
||||
/// function, and are received from the beginning of the buffer by calling
|
||||
/// the 'receiveSamples' function. The class automatically removes the
|
||||
/// outputted samples from the buffer, as well as grows the buffer size
|
||||
/// whenever necessary.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.11 $
|
||||
//
|
||||
// $Id: FIFOSampleBuffer.cpp,v 1.11 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "FIFOSampleBuffer.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
// Constructor
|
||||
FIFOSampleBuffer::FIFOSampleBuffer(uint numChannels)
|
||||
{
|
||||
sizeInBytes = 0; // reasonable initial value
|
||||
buffer = NULL; //new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE)];
|
||||
bufferUnaligned = NULL;
|
||||
samplesInBuffer = 0;
|
||||
bufferPos = 0;
|
||||
channels = numChannels;
|
||||
}
|
||||
|
||||
|
||||
// destructor
|
||||
FIFOSampleBuffer::~FIFOSampleBuffer()
|
||||
{
|
||||
delete[] bufferUnaligned;
|
||||
}
|
||||
|
||||
|
||||
// Sets number of channels, 1 = mono, 2 = stereo
|
||||
void FIFOSampleBuffer::setChannels(const uint numChannels)
|
||||
{
|
||||
uint usedBytes;
|
||||
|
||||
usedBytes = channels * samplesInBuffer;
|
||||
channels = numChannels;
|
||||
samplesInBuffer = usedBytes / channels;
|
||||
}
|
||||
|
||||
|
||||
// if output location pointer 'bufferPos' isn't zero, 'rewinds' the buffer and
|
||||
// zeroes this pointer by copying samples from the 'bufferPos' pointer
|
||||
// location on to the beginning of the buffer.
|
||||
void FIFOSampleBuffer::rewind()
|
||||
{
|
||||
if (bufferPos)
|
||||
{
|
||||
memmove(buffer, ptrBegin(), sizeof(SAMPLETYPE) * channels * samplesInBuffer);
|
||||
bufferPos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Adds 'numSamples' pcs of samples from the 'samples' memory position to
|
||||
// the sample buffer.
|
||||
void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint numSamples)
|
||||
{
|
||||
memcpy(ptrEnd(numSamples), samples, sizeof(SAMPLETYPE) * numSamples * channels);
|
||||
samplesInBuffer += numSamples;
|
||||
}
|
||||
|
||||
|
||||
// Increases the number of samples in the buffer without copying any actual
|
||||
// samples.
|
||||
//
|
||||
// This function is used to update the number of samples in the sample buffer
|
||||
// when accessing the buffer directly with 'ptrEnd' function. Please be
|
||||
// careful though!
|
||||
void FIFOSampleBuffer::putSamples(uint numSamples)
|
||||
{
|
||||
uint req;
|
||||
|
||||
req = samplesInBuffer + numSamples;
|
||||
ensureCapacity(req);
|
||||
samplesInBuffer += numSamples;
|
||||
}
|
||||
|
||||
|
||||
// Returns a pointer to the end of the used part of the sample buffer (i.e.
|
||||
// where the new samples are to be inserted). This function may be used for
|
||||
// inserting new samples into the sample buffer directly. Please be careful!
|
||||
//
|
||||
// Parameter 'slackCapacity' tells the function how much free capacity (in
|
||||
// terms of samples) there _at least_ should be, in order to the caller to
|
||||
// succesfully insert all the required samples to the buffer. When necessary,
|
||||
// the function grows the buffer size to comply with this requirement.
|
||||
//
|
||||
// When using this function as means for inserting new samples, also remember
|
||||
// to increase the sample count afterwards, by calling the
|
||||
// 'putSamples(numSamples)' function.
|
||||
SAMPLETYPE *FIFOSampleBuffer::ptrEnd(uint slackCapacity)
|
||||
{
|
||||
ensureCapacity(samplesInBuffer + slackCapacity);
|
||||
return buffer + samplesInBuffer * channels;
|
||||
}
|
||||
|
||||
|
||||
// Returns a pointer to the beginning of the currently non-outputted samples.
|
||||
// This function is provided for accessing the output samples directly.
|
||||
// Please be careful!
|
||||
//
|
||||
// When using this function to output samples, also remember to 'remove' the
|
||||
// outputted samples from the buffer by calling the
|
||||
// 'receiveSamples(numSamples)' function
|
||||
SAMPLETYPE *FIFOSampleBuffer::ptrBegin() const
|
||||
{
|
||||
return buffer + bufferPos * channels;
|
||||
}
|
||||
|
||||
|
||||
// Ensures that the buffer has enought capacity, i.e. space for _at least_
|
||||
// 'capacityRequirement' number of samples. The buffer is grown in steps of
|
||||
// 4 kilobytes to eliminate the need for frequently growing up the buffer,
|
||||
// as well as to round the buffer size up to the virtual memory page size.
|
||||
void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement)
|
||||
{
|
||||
SAMPLETYPE *tempUnaligned, *temp;
|
||||
|
||||
if (capacityRequirement > getCapacity())
|
||||
{
|
||||
// enlarge the buffer in 4kbyte steps (round up to next 4k boundary)
|
||||
sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & -4096;
|
||||
assert(sizeInBytes % 2 == 0);
|
||||
tempUnaligned = new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE) + 16 / sizeof(SAMPLETYPE)];
|
||||
if (tempUnaligned == NULL)
|
||||
{
|
||||
throw std::runtime_error("Couldn't allocate memory!\n");
|
||||
}
|
||||
temp = (SAMPLETYPE *)(((ulongptr)tempUnaligned + 15) & -16);
|
||||
memcpy(temp, ptrBegin(), samplesInBuffer * channels * sizeof(SAMPLETYPE));
|
||||
delete[] bufferUnaligned;
|
||||
buffer = temp;
|
||||
bufferUnaligned = tempUnaligned;
|
||||
bufferPos = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// simply rewind the buffer (if necessary)
|
||||
rewind();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Returns the current buffer capacity in terms of samples
|
||||
uint FIFOSampleBuffer::getCapacity() const
|
||||
{
|
||||
return sizeInBytes / (channels * sizeof(SAMPLETYPE));
|
||||
}
|
||||
|
||||
|
||||
// Returns the number of samples currently in the buffer
|
||||
uint FIFOSampleBuffer::numSamples() const
|
||||
{
|
||||
return samplesInBuffer;
|
||||
}
|
||||
|
||||
|
||||
// Output samples from beginning of the sample buffer. Copies demanded number
|
||||
// of samples to output and removes them from the sample buffer. If there
|
||||
// are less than 'numsample' samples in the buffer, returns all available.
|
||||
//
|
||||
// Returns number of samples copied.
|
||||
uint FIFOSampleBuffer::receiveSamples(SAMPLETYPE *output, uint maxSamples)
|
||||
{
|
||||
uint num;
|
||||
|
||||
num = (maxSamples > samplesInBuffer) ? samplesInBuffer : maxSamples;
|
||||
|
||||
memcpy(output, ptrBegin(), channels * sizeof(SAMPLETYPE) * num);
|
||||
return receiveSamples(num);
|
||||
}
|
||||
|
||||
|
||||
// Removes samples from the beginning of the sample buffer without copying them
|
||||
// anywhere. Used to reduce the number of samples in the buffer, when accessing
|
||||
// the sample buffer with the 'ptrBegin' function.
|
||||
uint FIFOSampleBuffer::receiveSamples(uint maxSamples)
|
||||
{
|
||||
if (maxSamples >= samplesInBuffer)
|
||||
{
|
||||
uint temp;
|
||||
|
||||
temp = samplesInBuffer;
|
||||
samplesInBuffer = 0;
|
||||
return temp;
|
||||
}
|
||||
|
||||
samplesInBuffer -= maxSamples;
|
||||
bufferPos += maxSamples;
|
||||
|
||||
return maxSamples;
|
||||
}
|
||||
|
||||
|
||||
// Returns nonzero if the sample buffer is empty
|
||||
int FIFOSampleBuffer::isEmpty() const
|
||||
{
|
||||
return (samplesInBuffer == 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
// Clears the sample buffer
|
||||
void FIFOSampleBuffer::clear()
|
||||
{
|
||||
samplesInBuffer = 0;
|
||||
bufferPos = 0;
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// A buffer class for temporarily storaging sound samples, operates as a
|
||||
/// first-in-first-out pipe.
|
||||
///
|
||||
/// Samples are added to the end of the sample buffer with the 'putSamples'
|
||||
/// function, and are received from the beginning of the buffer by calling
|
||||
/// the 'receiveSamples' function. The class automatically removes the
|
||||
/// output samples from the buffer as well as grows the storage size
|
||||
/// whenever necessary.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.9 $
|
||||
//
|
||||
// $Id: FIFOSampleBuffer.h,v 1.9 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef FIFOSampleBuffer_H
|
||||
#define FIFOSampleBuffer_H
|
||||
|
||||
#include "FIFOSamplePipe.h"
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
/// Sample buffer working in FIFO (first-in-first-out) principle. The class takes
|
||||
/// care of storage size adjustment and data moving during input/output operations.
|
||||
///
|
||||
/// Notice that in case of stereo audio, one sample is considered to consist of
|
||||
/// both channel data.
|
||||
class FIFOSampleBuffer : public FIFOSamplePipe
|
||||
{
|
||||
private:
|
||||
/// Sample buffer.
|
||||
SAMPLETYPE *buffer;
|
||||
|
||||
// Raw unaligned buffer memory. 'buffer' is made aligned by pointing it to first
|
||||
// 16-byte aligned location of this buffer
|
||||
SAMPLETYPE *bufferUnaligned;
|
||||
|
||||
/// Sample buffer size in bytes
|
||||
uint sizeInBytes;
|
||||
|
||||
/// How many samples are currently in buffer.
|
||||
uint samplesInBuffer;
|
||||
|
||||
/// Channels, 1=mono, 2=stereo.
|
||||
uint channels;
|
||||
|
||||
/// Current position pointer to the buffer. This pointer is increased when samples are
|
||||
/// removed from the pipe so that it's necessary to actually rewind buffer (move data)
|
||||
/// only new data when is put to the pipe.
|
||||
uint bufferPos;
|
||||
|
||||
/// Rewind the buffer by moving data from position pointed by 'bufferPos' to real
|
||||
/// beginning of the buffer.
|
||||
void rewind();
|
||||
|
||||
/// Ensures that the buffer has capacity for at least this many samples.
|
||||
void ensureCapacity(const uint capacityRequirement);
|
||||
|
||||
/// Returns current capacity.
|
||||
uint getCapacity() const;
|
||||
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
FIFOSampleBuffer(uint numChannels = 2 ///< Number of channels, 1=mono, 2=stereo.
|
||||
///< Default is stereo.
|
||||
);
|
||||
|
||||
/// destructor
|
||||
~FIFOSampleBuffer();
|
||||
|
||||
/// Returns a pointer to the beginning of the output samples.
|
||||
/// This function is provided for accessing the output samples directly.
|
||||
/// Please be careful for not to corrupt the book-keeping!
|
||||
///
|
||||
/// When using this function to output samples, also remember to 'remove' the
|
||||
/// output samples from the buffer by calling the
|
||||
/// 'receiveSamples(numSamples)' function
|
||||
virtual SAMPLETYPE *ptrBegin() const;
|
||||
|
||||
/// Returns a pointer to the end of the used part of the sample buffer (i.e.
|
||||
/// where the new samples are to be inserted). This function may be used for
|
||||
/// inserting new samples into the sample buffer directly. Please be careful
|
||||
/// not corrupt the book-keeping!
|
||||
///
|
||||
/// When using this function as means for inserting new samples, also remember
|
||||
/// to increase the sample count afterwards, by calling the
|
||||
/// 'putSamples(numSamples)' function.
|
||||
SAMPLETYPE *ptrEnd(
|
||||
uint slackCapacity ///< How much free capacity (in samples) there _at least_
|
||||
///< should be so that the caller can succesfully insert the
|
||||
///< desired samples to the buffer. If necessary, the function
|
||||
///< grows the buffer size to comply with this requirement.
|
||||
);
|
||||
|
||||
/// Adds 'numSamples' pcs of samples from the 'samples' memory position to
|
||||
/// the sample buffer.
|
||||
virtual void putSamples(const SAMPLETYPE *samples, ///< Pointer to samples.
|
||||
uint numSamples ///< Number of samples to insert.
|
||||
);
|
||||
|
||||
/// Adjusts the book-keeping to increase number of samples in the buffer without
|
||||
/// copying any actual samples.
|
||||
///
|
||||
/// This function is used to update the number of samples in the sample buffer
|
||||
/// when accessing the buffer directly with 'ptrEnd' function. Please be
|
||||
/// careful though!
|
||||
virtual void putSamples(uint numSamples ///< Number of samples been inserted.
|
||||
);
|
||||
|
||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||
/// output buffer and removes them from the sample buffer. If there are less than
|
||||
/// 'numsample' samples in the buffer, returns all that available.
|
||||
///
|
||||
/// \return Number of samples returned.
|
||||
virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples.
|
||||
uint maxSamples ///< How many samples to receive at max.
|
||||
);
|
||||
|
||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||
/// sample buffer without copying them anywhere.
|
||||
///
|
||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||
/// with 'ptrBegin' function.
|
||||
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
|
||||
);
|
||||
|
||||
/// Returns number of samples currently available.
|
||||
virtual uint numSamples() const;
|
||||
|
||||
/// Sets number of channels, 1 = mono, 2 = stereo.
|
||||
void setChannels(uint numChannels);
|
||||
|
||||
/// Returns nonzero if there aren't any samples available for outputting.
|
||||
virtual int isEmpty() const;
|
||||
|
||||
/// Clears all the samples.
|
||||
virtual void clear();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,217 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// 'FIFOSamplePipe' : An abstract base class for classes that manipulate sound
|
||||
/// samples by operating like a first-in-first-out pipe: New samples are fed
|
||||
/// into one end of the pipe with the 'putSamples' function, and the processed
|
||||
/// samples are received from the other end with the 'receiveSamples' function.
|
||||
///
|
||||
/// 'FIFOProcessor' : A base class for classes the do signal processing with
|
||||
/// the samples while operating like a first-in-first-out pipe. When samples
|
||||
/// are input with the 'putSamples' function, the class processes them
|
||||
/// and moves the processed samples to the given 'output' pipe object, which
|
||||
/// may be either another processing stage, or a fifo sample buffer object.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.8 $
|
||||
//
|
||||
// $Id: FIFOSamplePipe.h,v 1.8 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef FIFOSamplePipe_H
|
||||
#define FIFOSamplePipe_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "STTypes.h"
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
/// Abstract base class for FIFO (first-in-first-out) sample processing classes.
|
||||
class FIFOSamplePipe
|
||||
{
|
||||
public:
|
||||
/// Returns a pointer to the beginning of the output samples.
|
||||
/// This function is provided for accessing the output samples directly.
|
||||
/// Please be careful for not to corrupt the book-keeping!
|
||||
///
|
||||
/// When using this function to output samples, also remember to 'remove' the
|
||||
/// output samples from the buffer by calling the
|
||||
/// 'receiveSamples(numSamples)' function
|
||||
virtual SAMPLETYPE *ptrBegin() const = 0;
|
||||
|
||||
/// Adds 'numSamples' pcs of samples from the 'samples' memory position to
|
||||
/// the sample buffer.
|
||||
virtual void putSamples(const SAMPLETYPE *samples, ///< Pointer to samples.
|
||||
uint numSamples ///< Number of samples to insert.
|
||||
) = 0;
|
||||
|
||||
|
||||
// Moves samples from the 'other' pipe instance to this instance.
|
||||
void moveSamples(FIFOSamplePipe &other ///< Other pipe instance where from the receive the data.
|
||||
)
|
||||
{
|
||||
int oNumSamples = other.numSamples();
|
||||
|
||||
putSamples(other.ptrBegin(), oNumSamples);
|
||||
other.receiveSamples(oNumSamples);
|
||||
};
|
||||
|
||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||
/// output buffer and removes them from the sample buffer. If there are less than
|
||||
/// 'numsample' samples in the buffer, returns all that available.
|
||||
///
|
||||
/// \return Number of samples returned.
|
||||
virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples.
|
||||
uint maxSamples ///< How many samples to receive at max.
|
||||
) = 0;
|
||||
|
||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||
/// sample buffer without copying them anywhere.
|
||||
///
|
||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||
/// with 'ptrBegin' function.
|
||||
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
|
||||
) = 0;
|
||||
|
||||
/// Returns number of samples currently available.
|
||||
virtual uint numSamples() const = 0;
|
||||
|
||||
// Returns nonzero if there aren't any samples available for outputting.
|
||||
virtual int isEmpty() const = 0;
|
||||
|
||||
/// Clears all the samples.
|
||||
virtual void clear() = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// Base-class for sound processing routines working in FIFO principle. With this base
|
||||
/// class it's easy to implement sound processing stages that can be chained together,
|
||||
/// so that samples that are fed into beginning of the pipe automatically go through
|
||||
/// all the processing stages.
|
||||
///
|
||||
/// When samples are input to this class, they're first processed and then put to
|
||||
/// the FIFO pipe that's defined as output of this class. This output pipe can be
|
||||
/// either other processing stage or a FIFO sample buffer.
|
||||
class FIFOProcessor :public FIFOSamplePipe
|
||||
{
|
||||
protected:
|
||||
/// Internal pipe where processed samples are put.
|
||||
FIFOSamplePipe *output;
|
||||
|
||||
/// Sets output pipe.
|
||||
void setOutPipe(FIFOSamplePipe *pOutput)
|
||||
{
|
||||
assert(output == NULL);
|
||||
assert(pOutput != NULL);
|
||||
output = pOutput;
|
||||
}
|
||||
|
||||
|
||||
/// Constructor. Doesn't define output pipe; it has to be set be
|
||||
/// 'setOutPipe' function.
|
||||
FIFOProcessor()
|
||||
{
|
||||
output = NULL;
|
||||
}
|
||||
|
||||
|
||||
/// Constructor. Configures output pipe.
|
||||
FIFOProcessor(FIFOSamplePipe *pOutput ///< Output pipe.
|
||||
)
|
||||
{
|
||||
output = pOutput;
|
||||
}
|
||||
|
||||
|
||||
/// Destructor.
|
||||
virtual ~FIFOProcessor()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// Returns a pointer to the beginning of the output samples.
|
||||
/// This function is provided for accessing the output samples directly.
|
||||
/// Please be careful for not to corrupt the book-keeping!
|
||||
///
|
||||
/// When using this function to output samples, also remember to 'remove' the
|
||||
/// output samples from the buffer by calling the
|
||||
/// 'receiveSamples(numSamples)' function
|
||||
virtual SAMPLETYPE *ptrBegin() const
|
||||
{
|
||||
return output->ptrBegin();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||
/// output buffer and removes them from the sample buffer. If there are less than
|
||||
/// 'numsample' samples in the buffer, returns all that available.
|
||||
///
|
||||
/// \return Number of samples returned.
|
||||
virtual uint receiveSamples(SAMPLETYPE *outBuffer, ///< Buffer where to copy output samples.
|
||||
uint maxSamples ///< How many samples to receive at max.
|
||||
)
|
||||
{
|
||||
return output->receiveSamples(outBuffer, maxSamples);
|
||||
}
|
||||
|
||||
|
||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||
/// sample buffer without copying them anywhere.
|
||||
///
|
||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||
/// with 'ptrBegin' function.
|
||||
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
|
||||
)
|
||||
{
|
||||
return output->receiveSamples(maxSamples);
|
||||
}
|
||||
|
||||
|
||||
/// Returns number of samples currently available.
|
||||
virtual uint numSamples() const
|
||||
{
|
||||
return output->numSamples();
|
||||
}
|
||||
|
||||
|
||||
/// Returns nonzero if there aren't any samples available for outputting.
|
||||
virtual int isEmpty() const
|
||||
{
|
||||
return output->isEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,272 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// General FIR digital filter routines with MMX optimization.
|
||||
///
|
||||
/// Note : MMX optimized functions reside in a separate, platform-specific file,
|
||||
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.16 $
|
||||
//
|
||||
// $Id: FIRFilter.cpp,v 1.16 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <memory.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdexcept>
|
||||
#include "FIRFilter.h"
|
||||
#include "cpu_detect.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Implementation of the class 'FIRFilter'
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
FIRFilter::FIRFilter()
|
||||
{
|
||||
resultDivFactor = 0;
|
||||
length = 0;
|
||||
lengthDiv8 = 0;
|
||||
filterCoeffs = NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilter::~FIRFilter()
|
||||
{
|
||||
delete[] filterCoeffs;
|
||||
}
|
||||
|
||||
// Usual C-version of the filter routine for stereo sound
|
||||
uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
||||
{
|
||||
uint i, j, end;
|
||||
LONG_SAMPLETYPE suml, sumr;
|
||||
#ifdef FLOAT_SAMPLES
|
||||
// when using floating point samples, use a scaler instead of a divider
|
||||
// because division is much slower operation than multiplying.
|
||||
double dScaler = 1.0 / (double)resultDivider;
|
||||
#endif
|
||||
|
||||
assert(length != 0);
|
||||
|
||||
end = 2 * (numSamples - length);
|
||||
|
||||
for (j = 0; j < end; j += 2)
|
||||
{
|
||||
const SAMPLETYPE *ptr;
|
||||
|
||||
suml = sumr = 0;
|
||||
ptr = src + j;
|
||||
|
||||
for (i = 0; i < length; i += 4)
|
||||
{
|
||||
// loop is unrolled by factor of 4 here for efficiency
|
||||
suml += ptr[2 * i + 0] * filterCoeffs[i + 0] +
|
||||
ptr[2 * i + 2] * filterCoeffs[i + 1] +
|
||||
ptr[2 * i + 4] * filterCoeffs[i + 2] +
|
||||
ptr[2 * i + 6] * filterCoeffs[i + 3];
|
||||
sumr += ptr[2 * i + 1] * filterCoeffs[i + 0] +
|
||||
ptr[2 * i + 3] * filterCoeffs[i + 1] +
|
||||
ptr[2 * i + 5] * filterCoeffs[i + 2] +
|
||||
ptr[2 * i + 7] * filterCoeffs[i + 3];
|
||||
}
|
||||
|
||||
#ifdef INTEGER_SAMPLES
|
||||
suml >>= resultDivFactor;
|
||||
sumr >>= resultDivFactor;
|
||||
// saturate to 16 bit integer limits
|
||||
suml = (suml < -32768) ? -32768 : (suml > 32767) ? 32767 : suml;
|
||||
// saturate to 16 bit integer limits
|
||||
sumr = (sumr < -32768) ? -32768 : (sumr > 32767) ? 32767 : sumr;
|
||||
#else
|
||||
suml *= dScaler;
|
||||
sumr *= dScaler;
|
||||
#endif // INTEGER_SAMPLES
|
||||
dest[j] = (SAMPLETYPE)suml;
|
||||
dest[j + 1] = (SAMPLETYPE)sumr;
|
||||
}
|
||||
return numSamples - length;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Usual C-version of the filter routine for mono sound
|
||||
uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
||||
{
|
||||
uint i, j, end;
|
||||
LONG_SAMPLETYPE sum;
|
||||
#ifdef FLOAT_SAMPLES
|
||||
// when using floating point samples, use a scaler instead of a divider
|
||||
// because division is much slower operation than multiplying.
|
||||
double dScaler = 1.0 / (double)resultDivider;
|
||||
#endif
|
||||
|
||||
|
||||
assert(length != 0);
|
||||
|
||||
end = numSamples - length;
|
||||
for (j = 0; j < end; j ++)
|
||||
{
|
||||
sum = 0;
|
||||
for (i = 0; i < length; i += 4)
|
||||
{
|
||||
// loop is unrolled by factor of 4 here for efficiency
|
||||
sum += src[i + 0] * filterCoeffs[i + 0] +
|
||||
src[i + 1] * filterCoeffs[i + 1] +
|
||||
src[i + 2] * filterCoeffs[i + 2] +
|
||||
src[i + 3] * filterCoeffs[i + 3];
|
||||
}
|
||||
#ifdef INTEGER_SAMPLES
|
||||
sum >>= resultDivFactor;
|
||||
// saturate to 16 bit integer limits
|
||||
sum = (sum < -32768) ? -32768 : (sum > 32767) ? 32767 : sum;
|
||||
#else
|
||||
sum *= dScaler;
|
||||
#endif // INTEGER_SAMPLES
|
||||
dest[j] = (SAMPLETYPE)sum;
|
||||
src ++;
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
|
||||
// Set filter coeffiecients and length.
|
||||
//
|
||||
// Throws an exception if filter length isn't divisible by 8
|
||||
void FIRFilter::setCoefficients(const SAMPLETYPE *coeffs, uint newLength, uint uResultDivFactor)
|
||||
{
|
||||
assert(newLength > 0);
|
||||
if (newLength % 8) throw std::runtime_error("FIR filter length not divisible by 8");
|
||||
|
||||
lengthDiv8 = newLength / 8;
|
||||
length = lengthDiv8 * 8;
|
||||
assert(length == newLength);
|
||||
|
||||
resultDivFactor = uResultDivFactor;
|
||||
#ifdef INTEGER_SAMPLES
|
||||
resultDivider = (SAMPLETYPE)(1<<resultDivFactor);
|
||||
#else
|
||||
resultDivider = (SAMPLETYPE)powf(2, (SAMPLETYPE)resultDivFactor);
|
||||
#endif
|
||||
|
||||
delete[] filterCoeffs;
|
||||
filterCoeffs = new SAMPLETYPE[length];
|
||||
memcpy(filterCoeffs, coeffs, length * sizeof(SAMPLETYPE));
|
||||
}
|
||||
|
||||
|
||||
uint FIRFilter::getLength() const
|
||||
{
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Applies the filter to the given sequence of samples.
|
||||
//
|
||||
// Note : The amount of outputted samples is by value of 'filter_length'
|
||||
// smaller than the amount of input samples.
|
||||
uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
|
||||
{
|
||||
assert(numChannels == 1 || numChannels == 2);
|
||||
|
||||
assert(length > 0);
|
||||
assert(lengthDiv8 * 8 == length);
|
||||
if (numSamples < length) return 0;
|
||||
assert(resultDivFactor >= 0);
|
||||
if (numChannels == 2)
|
||||
{
|
||||
return evaluateFilterStereo(dest, src, numSamples);
|
||||
} else {
|
||||
return evaluateFilterMono(dest, src, numSamples);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
// depending on if we've a MMX-capable CPU available or not.
|
||||
void * FIRFilter::operator new(size_t s)
|
||||
{
|
||||
// Notice! don't use "new FIRFilter" directly, use "newInstance" to create a new instance instead!
|
||||
throw std::runtime_error("Don't use 'new FIRFilter', use 'newInstance' member instead!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilter * FIRFilter::newInstance()
|
||||
{
|
||||
uint uExtensions = 0;
|
||||
|
||||
#if !defined(_MSC_VER) || !defined(__x86_64__)
|
||||
uExtensions = detectCPUextensions();
|
||||
#endif
|
||||
|
||||
// Check if MMX/SSE/3DNow! instruction set extensions supported by CPU
|
||||
|
||||
#ifdef ALLOW_MMX
|
||||
// MMX routines available only with integer sample types
|
||||
if (uExtensions & SUPPORT_MMX)
|
||||
{
|
||||
return ::new FIRFilterMMX;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_MMX
|
||||
|
||||
#ifdef ALLOW_SSE
|
||||
if (uExtensions & SUPPORT_SSE)
|
||||
{
|
||||
// SSE support
|
||||
return ::new FIRFilterSSE;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_SSE
|
||||
|
||||
#ifdef ALLOW_3DNOW
|
||||
if (uExtensions & SUPPORT_3DNOW)
|
||||
{
|
||||
// 3DNow! support
|
||||
return ::new FIRFilter3DNow;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_3DNOW
|
||||
|
||||
{
|
||||
// ISA optimizations not supported, use plain C version
|
||||
return ::new FIRFilter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// General FIR digital filter routines with MMX optimization.
|
||||
///
|
||||
/// Note : MMX optimized functions reside in a separate, platform-specific file,
|
||||
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.17 $
|
||||
//
|
||||
// $Id: FIRFilter.h,v 1.17 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef FIRFilter_H
|
||||
#define FIRFilter_H
|
||||
|
||||
#include "STTypes.h"
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
class FIRFilter
|
||||
{
|
||||
protected:
|
||||
// Number of FIR filter taps
|
||||
uint length;
|
||||
// Number of FIR filter taps divided by 8
|
||||
uint lengthDiv8;
|
||||
|
||||
// Result divider factor in 2^k format
|
||||
uint resultDivFactor;
|
||||
|
||||
// Result divider value.
|
||||
SAMPLETYPE resultDivider;
|
||||
|
||||
// Memory for filter coefficients
|
||||
SAMPLETYPE *filterCoeffs;
|
||||
|
||||
virtual uint evaluateFilterStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples) const;
|
||||
virtual uint evaluateFilterMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples) const;
|
||||
|
||||
public:
|
||||
FIRFilter();
|
||||
virtual ~FIRFilter();
|
||||
|
||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
/// depending on if we've a MMX-capable CPU available or not.
|
||||
void * operator new(size_t s);
|
||||
|
||||
static FIRFilter *newInstance();
|
||||
|
||||
/// Applies the filter to the given sequence of samples.
|
||||
/// Note : The amount of outputted samples is by value of 'filter_length'
|
||||
/// smaller than the amount of input samples.
|
||||
///
|
||||
/// \return Number of samples copied to 'dest'.
|
||||
uint evaluate(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples,
|
||||
uint numChannels) const;
|
||||
|
||||
uint getLength() const;
|
||||
|
||||
virtual void setCoefficients(const SAMPLETYPE *coeffs,
|
||||
uint newLength,
|
||||
uint uResultDivFactor);
|
||||
};
|
||||
|
||||
|
||||
// Optional subclasses that implement CPU-specific optimizations:
|
||||
|
||||
#ifdef ALLOW_MMX
|
||||
|
||||
/// Class that implements MMX optimized functions exclusive for 16bit integer samples type.
|
||||
class FIRFilterMMX : public FIRFilter
|
||||
{
|
||||
protected:
|
||||
short *filterCoeffsUnalign;
|
||||
short *filterCoeffsAlign;
|
||||
|
||||
virtual uint evaluateFilterStereo(short *dest, const short *src, uint numSamples) const;
|
||||
public:
|
||||
FIRFilterMMX();
|
||||
~FIRFilterMMX();
|
||||
|
||||
virtual void setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor);
|
||||
};
|
||||
|
||||
#endif // ALLOW_MMX
|
||||
|
||||
|
||||
#ifdef ALLOW_3DNOW
|
||||
|
||||
/// Class that implements 3DNow! optimized functions exclusive for floating point samples type.
|
||||
class FIRFilter3DNow : public FIRFilter
|
||||
{
|
||||
protected:
|
||||
float *filterCoeffsUnalign;
|
||||
float *filterCoeffsAlign;
|
||||
|
||||
virtual uint evaluateFilterStereo(float *dest, const float *src, uint numSamples) const;
|
||||
public:
|
||||
FIRFilter3DNow();
|
||||
~FIRFilter3DNow();
|
||||
virtual void setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor);
|
||||
};
|
||||
|
||||
#endif // ALLOW_3DNOW
|
||||
|
||||
|
||||
#ifdef ALLOW_SSE
|
||||
/// Class that implements SSE optimized functions exclusive for floating point samples type.
|
||||
class FIRFilterSSE : public FIRFilter
|
||||
{
|
||||
protected:
|
||||
float *filterCoeffsUnalign;
|
||||
float *filterCoeffsAlign;
|
||||
|
||||
virtual uint evaluateFilterStereo(float *dest, const float *src, uint numSamples) const;
|
||||
public:
|
||||
FIRFilterSSE();
|
||||
~FIRFilterSSE();
|
||||
|
||||
virtual void setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor);
|
||||
};
|
||||
|
||||
#endif // ALLOW_SSE
|
||||
|
||||
}
|
||||
|
||||
#endif // FIRFilter_H
|
|
@ -0,0 +1,46 @@
|
|||
## Process this file with automake to create Makefile.in
|
||||
##
|
||||
## $Id: Makefile.am,v 1.3 2006/02/05 18:33:34 Olli Exp $
|
||||
##
|
||||
## Copyright (C) 2003 - David W. Durham
|
||||
##
|
||||
## This file is part of SoundTouch, an audio processing library for pitch/time adjustments
|
||||
##
|
||||
## SoundTouch 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.
|
||||
##
|
||||
## SoundTouch is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
## WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
## A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License along with
|
||||
## this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
## Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
|
||||
AUTOMAKE_OPTIONS = foreign
|
||||
|
||||
noinst_HEADERS=AAFilter.h cpu_detect.h FIRFilter.h RateTransposer.h TDStretch.h cpu_detect_x86_gcc.cpp
|
||||
noinst_LIBRARIES = libSoundTouch.a
|
||||
|
||||
if X86_64
|
||||
libSoundTouch_a_CXXFLAGS = -fPIC
|
||||
libSoundTouch_a_CFLAGS = -fPIC
|
||||
else
|
||||
libSoundTouch_a_CXXFLAGS = -msse -mmmx
|
||||
libSoundTouch_a_CFLAGS = -msse -mmmx
|
||||
endif
|
||||
|
||||
#lib_LTLIBRARIES=libSoundTouch.la
|
||||
# the mmx_gcc.cpp and cpu_detect_x86_gcc.cpp may need to be conditionally included here from things discovered in configure.ac
|
||||
libSoundTouch_a_SOURCES=AAFilter.cpp FIRFilter.cpp FIFOSampleBuffer.cpp mmx_optimized.cpp sse_optimized.cpp \
|
||||
RateTransposer.cpp SoundTouch.cpp TDStretch.cpp WavFile.cpp cpu_detect_x86_gcc.cpp
|
||||
|
||||
# ??? test for -fcheck-new in configure.ac
|
||||
# other compiler flags to add
|
||||
AM_CXXFLAGS=-O3 -msse -fcheck-new -I../../include
|
||||
|
||||
# other linking flags to add
|
||||
#libSoundTouch_la_LIBADD=
|
||||
|
|
@ -0,0 +1,626 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Sample rate transposer. Changes sample rate by using linear interpolation
|
||||
/// together with anti-alias filtering (first order interpolation with anti-
|
||||
/// alias filtering should be quite adequate for this application)
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/03/19 10:05:49 $
|
||||
// File revision : $Revision: 1.13 $
|
||||
//
|
||||
// $Id: RateTransposer.cpp,v 1.13 2006/03/19 10:05:49 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <memory.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include "RateTransposer.h"
|
||||
#include "AAFilter.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
|
||||
/// A linear samplerate transposer class that uses integer arithmetics.
|
||||
/// for the transposing.
|
||||
class RateTransposerInteger : public RateTransposer
|
||||
{
|
||||
protected:
|
||||
int iSlopeCount;
|
||||
uint uRate;
|
||||
SAMPLETYPE sPrevSampleL, sPrevSampleR;
|
||||
|
||||
virtual void resetRegisters();
|
||||
|
||||
virtual uint transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
virtual uint transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
|
||||
public:
|
||||
RateTransposerInteger();
|
||||
virtual ~RateTransposerInteger();
|
||||
|
||||
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
||||
/// rate, larger faster rates.
|
||||
virtual void setRate(float newRate);
|
||||
|
||||
};
|
||||
|
||||
|
||||
/// A linear samplerate transposer class that uses floating point arithmetics
|
||||
/// for the transposing.
|
||||
class RateTransposerFloat : public RateTransposer
|
||||
{
|
||||
protected:
|
||||
float fSlopeCount;
|
||||
float fRateStep;
|
||||
SAMPLETYPE sPrevSampleL, sPrevSampleR;
|
||||
|
||||
virtual void resetRegisters();
|
||||
|
||||
virtual uint transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
virtual uint transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
|
||||
public:
|
||||
RateTransposerFloat();
|
||||
virtual ~RateTransposerFloat();
|
||||
};
|
||||
|
||||
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ((a > b) ? b : a)
|
||||
#define max(a,b) ((a < b) ? b : a)
|
||||
#endif
|
||||
|
||||
|
||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
// depending on if we've a MMX/SSE/etc-capable CPU available or not.
|
||||
void * RateTransposer::operator new(size_t s)
|
||||
{
|
||||
// Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead!
|
||||
assert(FALSE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
RateTransposer *RateTransposer::newInstance()
|
||||
{
|
||||
#ifdef INTEGER_SAMPLES
|
||||
return ::new RateTransposerInteger;
|
||||
#else
|
||||
return ::new RateTransposerFloat;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Constructor
|
||||
RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer)
|
||||
{
|
||||
uChannels = 2;
|
||||
bUseAAFilter = TRUE;
|
||||
|
||||
// Instantiates the anti-alias filter with default tap length
|
||||
// of 32
|
||||
pAAFilter = new AAFilter(32);
|
||||
}
|
||||
|
||||
|
||||
|
||||
RateTransposer::~RateTransposer()
|
||||
{
|
||||
delete pAAFilter;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
|
||||
void RateTransposer::enableAAFilter(const BOOL newMode)
|
||||
{
|
||||
bUseAAFilter = newMode;
|
||||
}
|
||||
|
||||
|
||||
/// Returns nonzero if anti-alias filter is enabled.
|
||||
BOOL RateTransposer::isAAFilterEnabled() const
|
||||
{
|
||||
return bUseAAFilter;
|
||||
}
|
||||
|
||||
|
||||
AAFilter *RateTransposer::getAAFilter() const
|
||||
{
|
||||
return pAAFilter;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new target uRate. Normal uRate = 1.0, smaller values represent slower
|
||||
// uRate, larger faster uRates.
|
||||
void RateTransposer::setRate(float newRate)
|
||||
{
|
||||
float fCutoff;
|
||||
|
||||
fRate = newRate;
|
||||
|
||||
// design a new anti-alias filter
|
||||
if (newRate > 1.0f)
|
||||
{
|
||||
fCutoff = 0.5f / newRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
fCutoff = 0.5f * newRate;
|
||||
}
|
||||
pAAFilter->setCutoffFreq(fCutoff);
|
||||
}
|
||||
|
||||
|
||||
// Outputs as many samples of the 'outputBuffer' as possible, and if there's
|
||||
// any room left, outputs also as many of the incoming samples as possible.
|
||||
// The goal is to drive the outputBuffer empty.
|
||||
//
|
||||
// It's allowed for 'output' and 'input' parameters to point to the same
|
||||
// memory position.
|
||||
void RateTransposer::flushStoreBuffer()
|
||||
{
|
||||
if (storeBuffer.isEmpty()) return;
|
||||
|
||||
outputBuffer.moveSamples(storeBuffer);
|
||||
}
|
||||
|
||||
|
||||
// Adds 'numSamples' pcs of samples from the 'samples' memory position into
|
||||
// the input of the object.
|
||||
void RateTransposer::putSamples(const SAMPLETYPE *samples, uint numSamples)
|
||||
{
|
||||
processSamples(samples, numSamples);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Transposes up the sample rate, causing the observed playback 'rate' of the
|
||||
// sound to decrease
|
||||
void RateTransposer::upsample(const SAMPLETYPE *src, uint numSamples)
|
||||
{
|
||||
int count, sizeTemp, num;
|
||||
|
||||
// If the parameter 'uRate' value is smaller than 'SCALE', first transpose
|
||||
// the samples and then apply the anti-alias filter to remove aliasing.
|
||||
|
||||
// First check that there's enough room in 'storeBuffer'
|
||||
// (+16 is to reserve some slack in the destination buffer)
|
||||
sizeTemp = (int)((float)numSamples / fRate + 16.0f);
|
||||
|
||||
// Transpose the samples, store the result into the end of "storeBuffer"
|
||||
count = transpose(storeBuffer.ptrEnd(sizeTemp), src, numSamples);
|
||||
storeBuffer.putSamples(count);
|
||||
|
||||
// Apply the anti-alias filter to samples in "store output", output the
|
||||
// result to "dest"
|
||||
num = storeBuffer.numSamples();
|
||||
count = pAAFilter->evaluate(outputBuffer.ptrEnd(num),
|
||||
storeBuffer.ptrBegin(), num, uChannels);
|
||||
outputBuffer.putSamples(count);
|
||||
|
||||
// Remove the processed samples from "storeBuffer"
|
||||
storeBuffer.receiveSamples(count);
|
||||
}
|
||||
|
||||
|
||||
// Transposes down the sample rate, causing the observed playback 'rate' of the
|
||||
// sound to increase
|
||||
void RateTransposer::downsample(const SAMPLETYPE *src, uint numSamples)
|
||||
{
|
||||
int count, sizeTemp;
|
||||
|
||||
// If the parameter 'uRate' value is larger than 'SCALE', first apply the
|
||||
// anti-alias filter to remove high frequencies (prevent them from folding
|
||||
// over the lover frequencies), then transpose. */
|
||||
|
||||
// Add the new samples to the end of the storeBuffer */
|
||||
storeBuffer.putSamples(src, numSamples);
|
||||
|
||||
// Anti-alias filter the samples to prevent folding and output the filtered
|
||||
// data to tempBuffer. Note : because of the FIR filter length, the
|
||||
// filtering routine takes in 'filter_length' more samples than it outputs.
|
||||
assert(tempBuffer.isEmpty());
|
||||
sizeTemp = storeBuffer.numSamples();
|
||||
|
||||
count = pAAFilter->evaluate(tempBuffer.ptrEnd(sizeTemp),
|
||||
storeBuffer.ptrBegin(), sizeTemp, uChannels);
|
||||
|
||||
// Remove the filtered samples from 'storeBuffer'
|
||||
storeBuffer.receiveSamples(count);
|
||||
|
||||
// Transpose the samples (+16 is to reserve some slack in the destination buffer)
|
||||
sizeTemp = (int)((float)numSamples / fRate + 16.0f);
|
||||
count = transpose(outputBuffer.ptrEnd(sizeTemp), tempBuffer.ptrBegin(), count);
|
||||
outputBuffer.putSamples(count);
|
||||
}
|
||||
|
||||
|
||||
// Transposes sample rate by applying anti-alias filter to prevent folding.
|
||||
// Returns amount of samples returned in the "dest" buffer.
|
||||
// The maximum amount of samples that can be returned at a time is set by
|
||||
// the 'set_returnBuffer_size' function.
|
||||
void RateTransposer::processSamples(const SAMPLETYPE *src, uint numSamples)
|
||||
{
|
||||
uint count;
|
||||
uint sizeReq;
|
||||
|
||||
if (numSamples == 0) return;
|
||||
assert(pAAFilter);
|
||||
|
||||
// If anti-alias filter is turned off, simply transpose without applying
|
||||
// the filter
|
||||
if (bUseAAFilter == FALSE)
|
||||
{
|
||||
sizeReq = (int)((float)numSamples / fRate + 1.0f);
|
||||
count = transpose(outputBuffer.ptrEnd(sizeReq), src, numSamples);
|
||||
outputBuffer.putSamples(count);
|
||||
return;
|
||||
}
|
||||
|
||||
// Transpose with anti-alias filter
|
||||
if (fRate < 1.0f)
|
||||
{
|
||||
upsample(src, numSamples);
|
||||
}
|
||||
else
|
||||
{
|
||||
downsample(src, numSamples);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// Returns the number of samples returned in the "dest" buffer
|
||||
inline uint RateTransposer::transpose(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples)
|
||||
{
|
||||
if (uChannels == 2)
|
||||
{
|
||||
return transposeStereo(dest, src, numSamples);
|
||||
}
|
||||
else
|
||||
{
|
||||
return transposeMono(dest, src, numSamples);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Sets the number of channels, 1 = mono, 2 = stereo
|
||||
void RateTransposer::setChannels(const uint numchannels)
|
||||
{
|
||||
if (uChannels == numchannels) return;
|
||||
|
||||
assert(numchannels == 1 || numchannels == 2);
|
||||
uChannels = numchannels;
|
||||
|
||||
storeBuffer.setChannels(uChannels);
|
||||
tempBuffer.setChannels(uChannels);
|
||||
outputBuffer.setChannels(uChannels);
|
||||
|
||||
// Inits the linear interpolation registers
|
||||
resetRegisters();
|
||||
}
|
||||
|
||||
|
||||
// Clears all the samples in the object
|
||||
void RateTransposer::clear()
|
||||
{
|
||||
outputBuffer.clear();
|
||||
storeBuffer.clear();
|
||||
}
|
||||
|
||||
|
||||
// Returns nonzero if there aren't any samples available for outputting.
|
||||
uint RateTransposer::isEmpty()
|
||||
{
|
||||
int res;
|
||||
|
||||
res = FIFOProcessor::isEmpty();
|
||||
if (res == 0) return 0;
|
||||
return storeBuffer.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// RateTransposerInteger - integer arithmetic implementation
|
||||
//
|
||||
|
||||
/// fixed-point interpolation routine precision
|
||||
#define SCALE 65536
|
||||
|
||||
// Constructor
|
||||
RateTransposerInteger::RateTransposerInteger() : RateTransposer()
|
||||
{
|
||||
// call these here as these are virtual functions; calling these
|
||||
// from the base class constructor wouldn't execute the overloaded
|
||||
// versions (<master yoda>peculiar C++ can be</my>).
|
||||
resetRegisters();
|
||||
setRate(1.0f);
|
||||
}
|
||||
|
||||
|
||||
RateTransposerInteger::~RateTransposerInteger()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void RateTransposerInteger::resetRegisters()
|
||||
{
|
||||
iSlopeCount = 0;
|
||||
sPrevSampleL =
|
||||
sPrevSampleR = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||
// the "dest" buffer
|
||||
uint RateTransposerInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples)
|
||||
{
|
||||
unsigned int i, used;
|
||||
LONG_SAMPLETYPE temp, vol1;
|
||||
|
||||
used = 0;
|
||||
i = 0;
|
||||
|
||||
// Process the last sample saved from the previous call first...
|
||||
while (iSlopeCount <= SCALE)
|
||||
{
|
||||
vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
|
||||
temp = vol1 * sPrevSampleL + iSlopeCount * src[0];
|
||||
dest[i] = (SAMPLETYPE)(temp / SCALE);
|
||||
i++;
|
||||
iSlopeCount += uRate;
|
||||
}
|
||||
// now always (iSlopeCount > SCALE)
|
||||
iSlopeCount -= SCALE;
|
||||
|
||||
while (1)
|
||||
{
|
||||
while (iSlopeCount > SCALE)
|
||||
{
|
||||
iSlopeCount -= SCALE;
|
||||
used ++;
|
||||
if (used >= numSamples - 1) goto end;
|
||||
}
|
||||
vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
|
||||
temp = src[used] * vol1 + iSlopeCount * src[used + 1];
|
||||
dest[i] = (SAMPLETYPE)(temp / SCALE);
|
||||
|
||||
i++;
|
||||
iSlopeCount += uRate;
|
||||
}
|
||||
end:
|
||||
// Store the last sample for the next round
|
||||
sPrevSampleL = src[numSamples - 1];
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Stereo' version of the routine. Returns the number of samples returned in
|
||||
// the "dest" buffer
|
||||
uint RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples)
|
||||
{
|
||||
unsigned int srcPos, i, used;
|
||||
LONG_SAMPLETYPE temp, vol1;
|
||||
|
||||
if (numSamples == 0) return 0; // no samples, no work
|
||||
|
||||
used = 0;
|
||||
i = 0;
|
||||
|
||||
// Process the last sample saved from the sPrevSampleLious call first...
|
||||
while (iSlopeCount <= SCALE)
|
||||
{
|
||||
vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
|
||||
temp = vol1 * sPrevSampleL + iSlopeCount * src[0];
|
||||
dest[2 * i] = (SAMPLETYPE)(temp / SCALE);
|
||||
temp = vol1 * sPrevSampleR + iSlopeCount * src[1];
|
||||
dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE);
|
||||
i++;
|
||||
iSlopeCount += uRate;
|
||||
}
|
||||
// now always (iSlopeCount > SCALE)
|
||||
iSlopeCount -= SCALE;
|
||||
|
||||
while (1)
|
||||
{
|
||||
while (iSlopeCount > SCALE)
|
||||
{
|
||||
iSlopeCount -= SCALE;
|
||||
used ++;
|
||||
if (used >= numSamples - 1) goto end;
|
||||
}
|
||||
srcPos = 2 * used;
|
||||
vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
|
||||
temp = src[srcPos] * vol1 + iSlopeCount * src[srcPos + 2];
|
||||
dest[2 * i] = (SAMPLETYPE)(temp / SCALE);
|
||||
temp = src[srcPos + 1] * vol1 + iSlopeCount * src[srcPos + 3];
|
||||
dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE);
|
||||
|
||||
i++;
|
||||
iSlopeCount += uRate;
|
||||
}
|
||||
end:
|
||||
// Store the last sample for the next round
|
||||
sPrevSampleL = src[2 * numSamples - 2];
|
||||
sPrevSampleR = src[2 * numSamples - 1];
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
// Sets new target uRate. Normal uRate = 1.0, smaller values represent slower
|
||||
// uRate, larger faster uRates.
|
||||
void RateTransposerInteger::setRate(float newRate)
|
||||
{
|
||||
uRate = (int)(newRate * SCALE + 0.5f);
|
||||
RateTransposer::setRate(newRate);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// RateTransposerFloat - floating point arithmetic implementation
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Constructor
|
||||
RateTransposerFloat::RateTransposerFloat() : RateTransposer()
|
||||
{
|
||||
// call these here as these are virtual functions; calling these
|
||||
// from the base class constructor wouldn't execute the overloaded
|
||||
// versions (<master yoda>peculiar C++ can be</my>).
|
||||
resetRegisters();
|
||||
setRate(1.0f);
|
||||
}
|
||||
|
||||
|
||||
RateTransposerFloat::~RateTransposerFloat()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void RateTransposerFloat::resetRegisters()
|
||||
{
|
||||
fSlopeCount = 0;
|
||||
sPrevSampleL =
|
||||
sPrevSampleR = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||
// the "dest" buffer
|
||||
uint RateTransposerFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples)
|
||||
{
|
||||
unsigned int i, used;
|
||||
|
||||
used = 0;
|
||||
i = 0;
|
||||
|
||||
// Process the last sample saved from the previous call first...
|
||||
while (fSlopeCount <= 1.0f)
|
||||
{
|
||||
dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleL + fSlopeCount * src[0]);
|
||||
i++;
|
||||
fSlopeCount += fRate;
|
||||
}
|
||||
fSlopeCount -= 1.0f;
|
||||
|
||||
if (numSamples == 1) goto end;
|
||||
|
||||
while (1)
|
||||
{
|
||||
while (fSlopeCount > 1.0f)
|
||||
{
|
||||
fSlopeCount -= 1.0f;
|
||||
used ++;
|
||||
if (used >= numSamples - 1) goto end;
|
||||
}
|
||||
dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[used] + fSlopeCount * src[used + 1]);
|
||||
i++;
|
||||
fSlopeCount += fRate;
|
||||
}
|
||||
end:
|
||||
// Store the last sample for the next round
|
||||
sPrevSampleL = src[numSamples - 1];
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||
// the "dest" buffer
|
||||
uint RateTransposerFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples)
|
||||
{
|
||||
unsigned int srcPos, i, used;
|
||||
|
||||
if (numSamples == 0) return 0; // no samples, no work
|
||||
|
||||
used = 0;
|
||||
i = 0;
|
||||
|
||||
// Process the last sample saved from the sPrevSampleLious call first...
|
||||
while (fSlopeCount <= 1.0f)
|
||||
{
|
||||
dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleL + fSlopeCount * src[0]);
|
||||
dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * sPrevSampleR + fSlopeCount * src[1]);
|
||||
i++;
|
||||
fSlopeCount += fRate;
|
||||
}
|
||||
// now always (iSlopeCount > 1.0f)
|
||||
fSlopeCount -= 1.0f;
|
||||
|
||||
if (numSamples == 1) goto end;
|
||||
|
||||
while (1)
|
||||
{
|
||||
while (fSlopeCount > 1.0f)
|
||||
{
|
||||
fSlopeCount -= 1.0f;
|
||||
used ++;
|
||||
if (used >= numSamples - 1) goto end;
|
||||
}
|
||||
srcPos = 2 * used;
|
||||
|
||||
dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos]
|
||||
+ fSlopeCount * src[srcPos + 2]);
|
||||
dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos + 1]
|
||||
+ fSlopeCount * src[srcPos + 3]);
|
||||
|
||||
i++;
|
||||
fSlopeCount += fRate;
|
||||
}
|
||||
end:
|
||||
// Store the last sample for the next round
|
||||
sPrevSampleL = src[2 * numSamples - 2];
|
||||
sPrevSampleR = src[2 * numSamples - 1];
|
||||
|
||||
return i;
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Sample rate transposer. Changes sample rate by using linear interpolation
|
||||
/// together with anti-alias filtering (first order interpolation with anti-
|
||||
/// alias filtering should be quite adequate for this application).
|
||||
///
|
||||
/// Use either of the derived classes of 'RateTransposerInteger' or
|
||||
/// 'RateTransposerFloat' for corresponding integer/floating point tranposing
|
||||
/// algorithm implementation.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.10 $
|
||||
//
|
||||
// $Id: RateTransposer.h,v 1.10 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef RateTransposer_H
|
||||
#define RateTransposer_H
|
||||
|
||||
#include "AAFilter.h"
|
||||
#include "FIFOSamplePipe.h"
|
||||
#include "FIFOSampleBuffer.h"
|
||||
|
||||
#include "STTypes.h"
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
/// A common linear samplerate transposer class.
|
||||
///
|
||||
/// Note: Use function "RateTransposer::newInstance()" to create a new class
|
||||
/// instance instead of the "new" operator; that function automatically
|
||||
/// chooses a correct implementation depending on if integer or floating
|
||||
/// arithmetics are to be used.
|
||||
class RateTransposer : public FIFOProcessor
|
||||
{
|
||||
protected:
|
||||
/// Anti-alias filter object
|
||||
AAFilter *pAAFilter;
|
||||
|
||||
float fRate;
|
||||
|
||||
uint uChannels;
|
||||
|
||||
/// Buffer for collecting samples to feed the anti-alias filter between
|
||||
/// two batches
|
||||
FIFOSampleBuffer storeBuffer;
|
||||
|
||||
/// Buffer for keeping samples between transposing & anti-alias filter
|
||||
FIFOSampleBuffer tempBuffer;
|
||||
|
||||
/// Output sample buffer
|
||||
FIFOSampleBuffer outputBuffer;
|
||||
|
||||
BOOL bUseAAFilter;
|
||||
|
||||
void init();
|
||||
|
||||
virtual void resetRegisters() = 0;
|
||||
|
||||
virtual uint transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples) = 0;
|
||||
virtual uint transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples) = 0;
|
||||
uint transpose(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
|
||||
void flushStoreBuffer();
|
||||
|
||||
void downsample(const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
void upsample(const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
|
||||
/// Transposes sample rate by applying anti-alias filter to prevent folding.
|
||||
/// Returns amount of samples returned in the "dest" buffer.
|
||||
/// The maximum amount of samples that can be returned at a time is set by
|
||||
/// the 'set_returnBuffer_size' function.
|
||||
void processSamples(const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
|
||||
|
||||
public:
|
||||
RateTransposer();
|
||||
virtual ~RateTransposer();
|
||||
|
||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
/// depending on if we're to use integer or floating point arithmetics.
|
||||
void *operator new(size_t s);
|
||||
|
||||
/// Use this function instead of "new" operator to create a new instance of this class.
|
||||
/// This function automatically chooses a correct implementation, depending on if
|
||||
/// integer ot floating point arithmetics are to be used.
|
||||
static RateTransposer *newInstance();
|
||||
|
||||
/// Returns the output buffer object
|
||||
FIFOSamplePipe *getOutput() { return &outputBuffer; };
|
||||
|
||||
/// Returns the store buffer object
|
||||
FIFOSamplePipe *getStore() { return &storeBuffer; };
|
||||
|
||||
/// Return anti-alias filter object
|
||||
AAFilter *getAAFilter() const;
|
||||
|
||||
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
|
||||
void enableAAFilter(BOOL newMode);
|
||||
|
||||
/// Returns nonzero if anti-alias filter is enabled.
|
||||
BOOL isAAFilterEnabled() const;
|
||||
|
||||
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
||||
/// rate, larger faster rates.
|
||||
virtual void setRate(float newRate);
|
||||
|
||||
/// Sets the number of channels, 1 = mono, 2 = stereo
|
||||
void setChannels(uint channels);
|
||||
|
||||
/// Adds 'numSamples' pcs of samples from the 'samples' memory position into
|
||||
/// the input of the object.
|
||||
void putSamples(const SAMPLETYPE *samples, uint numSamples);
|
||||
|
||||
/// Clears all the samples in the object
|
||||
void clear();
|
||||
|
||||
/// Returns nonzero if there aren't any samples available for outputting.
|
||||
uint isEmpty();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,202 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Common type definitions for SoundTouch audio processing library.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.16 $
|
||||
//
|
||||
// $Id: STTypes.h,v 1.16 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef STTypes_H
|
||||
#define STTypes_H
|
||||
|
||||
//#define INTEGER_SAMPLES 1
|
||||
|
||||
typedef unsigned int uint;
|
||||
typedef unsigned long ulong;
|
||||
|
||||
#ifdef __x86_64__
|
||||
typedef unsigned long long ulongptr;
|
||||
#else
|
||||
typedef unsigned long ulongptr;
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __GNUC__
|
||||
// In GCC, include soundtouch_config.h made by config scritps
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
||||
/* Define to 1 if you have the `m' library (-lm). */
|
||||
#define HAVE_LIBM 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 <memory.h> header file. */
|
||||
#define HAVE_MEMORY_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#define HAVE_STDINT_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 <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
|
||||
|
||||
/* Use Integer as Sample type */
|
||||
//#define INTEGER_SAMPLES 1
|
||||
|
||||
/* Define as the return type of signal handlers (`int' or `void'). */
|
||||
#define RETSIGTYPE void
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef _WINDEF_
|
||||
// if these aren't defined already by Windows headers, define now
|
||||
|
||||
typedef int BOOL;
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
#endif // _WINDEF_
|
||||
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
/// Activate these undef's to overrule the possible sampletype
|
||||
/// setting inherited from some other header file:
|
||||
//#undef INTEGER_SAMPLES
|
||||
//#undef FLOAT_SAMPLES
|
||||
|
||||
#if !(INTEGER_SAMPLES || FLOAT_SAMPLES)
|
||||
|
||||
/// Choose either 32bit floating point or 16bit integer sampletype
|
||||
/// by choosing one of the following defines, unless this selection
|
||||
/// has already been done in some other file.
|
||||
////
|
||||
/// Notes:
|
||||
/// - In Windows environment, choose the sample format with the
|
||||
/// following defines.
|
||||
/// - In GNU environment, the floating point samples are used by
|
||||
/// default, but integer samples can be chosen by giving the
|
||||
/// following switch to the configure script:
|
||||
/// ./configure --enable-integer-samples
|
||||
/// However, if you still prefer to select the sample format here
|
||||
/// also in GNU environment, then please #undef the INTEGER_SAMPLE
|
||||
/// and FLOAT_SAMPLE defines first as in comments above.
|
||||
//#define INTEGER_SAMPLES 1 //< 16bit integer samples
|
||||
#define FLOAT_SAMPLES 1 //< 32bit float samples
|
||||
|
||||
#endif
|
||||
|
||||
/// Define this to allow CPU-specific assembler optimizations. Notice that
|
||||
/// having this enabled on non-x86 platforms doesn't matter; the compiler can
|
||||
/// drop unsupported extensions on different platforms automatically.
|
||||
/// However, if you're having difficulties getting the optimized routines
|
||||
/// compiled with your compler (e.g. some gcc compiler versions may be picky),
|
||||
/// you may wish to disable the optimizations to make the library compile.
|
||||
#if !defined(_MSC_VER) || !defined(__x86_64__)
|
||||
#define ALLOW_OPTIMIZATIONS 1
|
||||
#define ALLOW_NONEXACT_SIMD_OPTIMIZATION 1
|
||||
#endif
|
||||
|
||||
|
||||
// If defined, allows the SIMD-optimized routines to take minor shortcuts
|
||||
// for improved performance. Undefine to require faithfully similar SIMD
|
||||
// calculations as in normal C implementation.
|
||||
|
||||
|
||||
|
||||
#ifdef INTEGER_SAMPLES
|
||||
// 16bit integer sample type
|
||||
typedef short SAMPLETYPE;
|
||||
// data type for sample accumulation: Use 32bit integer to prevent overflows
|
||||
typedef long LONG_SAMPLETYPE;
|
||||
|
||||
#ifdef FLOAT_SAMPLES
|
||||
// check that only one sample type is defined
|
||||
#error "conflicting sample types defined"
|
||||
#endif // FLOAT_SAMPLES
|
||||
|
||||
#ifdef ALLOW_OPTIMIZATIONS
|
||||
#if (_WIN32 || __i386__ || __x86_64__)
|
||||
// Allow MMX optimizations
|
||||
#define ALLOW_MMX 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
// floating point samples
|
||||
typedef float SAMPLETYPE;
|
||||
// data type for sample accumulation: Use double to utilize full precision.
|
||||
typedef double LONG_SAMPLETYPE;
|
||||
|
||||
#ifdef ALLOW_OPTIMIZATIONS
|
||||
// Allow 3DNow! and SSE optimizations
|
||||
#if _WIN32
|
||||
// #define ALLOW_3DNOW 1
|
||||
#endif
|
||||
|
||||
#if (_WIN32 || __i386__ || __x86_64__)
|
||||
#define ALLOW_SSE 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // INTEGER_SAMPLES
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,474 @@
|
|||
//////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// SoundTouch - main class for tempo/pitch/rate adjusting routines.
|
||||
///
|
||||
/// Notes:
|
||||
/// - Initialize the SoundTouch object instance by setting up the sound stream
|
||||
/// parameters with functions 'setSampleRate' and 'setChannels', then set
|
||||
/// desired tempo/pitch/rate settings with the corresponding functions.
|
||||
///
|
||||
/// - The SoundTouch class behaves like a first-in-first-out pipeline: The
|
||||
/// samples that are to be processed are fed into one of the pipe by calling
|
||||
/// function 'putSamples', while the ready processed samples can be read
|
||||
/// from the other end of the pipeline with function 'receiveSamples'.
|
||||
///
|
||||
/// - The SoundTouch processing classes require certain sized 'batches' of
|
||||
/// samples in order to process the sound. For this reason the classes buffer
|
||||
/// incoming samples until there are enough of samples available for
|
||||
/// processing, then they carry out the processing step and consequently
|
||||
/// make the processed samples available for outputting.
|
||||
///
|
||||
/// - For the above reason, the processing routines introduce a certain
|
||||
/// 'latency' between the input and output, so that the samples input to
|
||||
/// SoundTouch may not be immediately available in the output, and neither
|
||||
/// the amount of outputtable samples may not immediately be in direct
|
||||
/// relationship with the amount of previously input samples.
|
||||
///
|
||||
/// - The tempo/pitch/rate control parameters can be altered during processing.
|
||||
/// Please notice though that they aren't currently protected by semaphores,
|
||||
/// so in multi-thread application external semaphore protection may be
|
||||
/// required.
|
||||
///
|
||||
/// - This class utilizes classes 'TDStretch' for tempo change (without modifying
|
||||
/// pitch) and 'RateTransposer' for changing the playback rate (that is, both
|
||||
/// tempo and pitch in the same ratio) of the sound. The third available control
|
||||
/// 'pitch' (change pitch but maintain tempo) is produced by a combination of
|
||||
/// combining the two other controls.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.13 $
|
||||
//
|
||||
// $Id: SoundTouch.cpp,v 1.13 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include <math.h>
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "SoundTouch.h"
|
||||
#include "TDStretch.h"
|
||||
#include "RateTransposer.h"
|
||||
#include "cpu_detect.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
/// Print library version string
|
||||
extern "C" void soundtouch_ac_test()
|
||||
{
|
||||
printf("SoundTouch Version: %s\n",SOUNDTOUCH_VERSION);
|
||||
}
|
||||
|
||||
|
||||
SoundTouch::SoundTouch()
|
||||
{
|
||||
// Initialize rate transposer and tempo changer instances
|
||||
|
||||
pRateTransposer = RateTransposer::newInstance();
|
||||
pTDStretch = TDStretch::newInstance();
|
||||
|
||||
setOutPipe(pTDStretch);
|
||||
|
||||
rate = tempo = 0;
|
||||
|
||||
virtualPitch =
|
||||
virtualRate =
|
||||
virtualTempo = 1.0;
|
||||
|
||||
calcEffectiveRateAndTempo();
|
||||
|
||||
channels = 0;
|
||||
bSrateSet = FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
SoundTouch::~SoundTouch()
|
||||
{
|
||||
delete pRateTransposer;
|
||||
delete pTDStretch;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Get SoundTouch library version string
|
||||
const char *SoundTouch::getVersionString()
|
||||
{
|
||||
static const char *_version = SOUNDTOUCH_VERSION;
|
||||
|
||||
return _version;
|
||||
}
|
||||
|
||||
|
||||
/// Get SoundTouch library version Id
|
||||
uint SoundTouch::getVersionId()
|
||||
{
|
||||
return SOUNDTOUCH_VERSION_ID;
|
||||
}
|
||||
|
||||
|
||||
// Sets the number of channels, 1 = mono, 2 = stereo
|
||||
void SoundTouch::setChannels(uint numChannels)
|
||||
{
|
||||
if (numChannels != 1 && numChannels != 2)
|
||||
{
|
||||
throw std::runtime_error("Illegal number of channels");
|
||||
}
|
||||
channels = numChannels;
|
||||
pRateTransposer->setChannels(numChannels);
|
||||
pTDStretch->setChannels(numChannels);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new rate control value. Normal rate = 1.0, smaller values
|
||||
// represent slower rate, larger faster rates.
|
||||
void SoundTouch::setRate(float newRate)
|
||||
{
|
||||
virtualRate = newRate;
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new rate control value as a difference in percents compared
|
||||
// to the original rate (-50 .. +100 %)
|
||||
void SoundTouch::setRateChange(float newRate)
|
||||
{
|
||||
virtualRate = 1.0f + 0.01f * newRate;
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
||||
// represent slower tempo, larger faster tempo.
|
||||
void SoundTouch::setTempo(float newTempo)
|
||||
{
|
||||
virtualTempo = newTempo;
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new tempo control value as a difference in percents compared
|
||||
// to the original tempo (-50 .. +100 %)
|
||||
void SoundTouch::setTempoChange(float newTempo)
|
||||
{
|
||||
virtualTempo = 1.0f + 0.01f * newTempo;
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets new pitch control value. Original pitch = 1.0, smaller values
|
||||
// represent lower pitches, larger values higher pitch.
|
||||
void SoundTouch::setPitch(float newPitch)
|
||||
{
|
||||
virtualPitch = newPitch;
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets pitch change in octaves compared to the original pitch
|
||||
// (-1.00 .. +1.00)
|
||||
void SoundTouch::setPitchOctaves(float newPitch)
|
||||
{
|
||||
virtualPitch = (float)exp(0.69314718056f * newPitch);
|
||||
calcEffectiveRateAndTempo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets pitch change in semi-tones compared to the original pitch
|
||||
// (-12 .. +12)
|
||||
void SoundTouch::setPitchSemiTones(int newPitch)
|
||||
{
|
||||
setPitchOctaves((float)newPitch / 12.0f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SoundTouch::setPitchSemiTones(float newPitch)
|
||||
{
|
||||
setPitchOctaves(newPitch / 12.0f);
|
||||
}
|
||||
|
||||
|
||||
// Calculates 'effective' rate and tempo values from the
|
||||
// nominal control values.
|
||||
void SoundTouch::calcEffectiveRateAndTempo()
|
||||
{
|
||||
float oldTempo = tempo;
|
||||
float oldRate = rate;
|
||||
|
||||
tempo = virtualTempo / virtualPitch;
|
||||
rate = virtualPitch * virtualRate;
|
||||
|
||||
if (rate != oldRate) pRateTransposer->setRate(rate);
|
||||
if (tempo != oldTempo) pTDStretch->setTempo(tempo);
|
||||
|
||||
if (rate > 1.0f)
|
||||
{
|
||||
if (output != pRateTransposer)
|
||||
{
|
||||
FIFOSamplePipe *transOut;
|
||||
|
||||
assert(output == pTDStretch);
|
||||
// move samples in the current output buffer to the output of pRateTransposer
|
||||
transOut = pRateTransposer->getOutput();
|
||||
transOut->moveSamples(*output);
|
||||
// move samples in tempo changer's input to pitch transposer's input
|
||||
pRateTransposer->moveSamples(*pTDStretch->getInput());
|
||||
|
||||
output = pRateTransposer;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (output != pTDStretch)
|
||||
{
|
||||
FIFOSamplePipe *tempoOut;
|
||||
|
||||
assert(output == pRateTransposer);
|
||||
// move samples in the current output buffer to the output of pTDStretch
|
||||
tempoOut = pTDStretch->getOutput();
|
||||
tempoOut->moveSamples(*output);
|
||||
// move samples in pitch transposer's store buffer to tempo changer's input
|
||||
pTDStretch->moveSamples(*pRateTransposer->getStore());
|
||||
|
||||
output = pTDStretch;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Sets sample rate.
|
||||
void SoundTouch::setSampleRate(uint srate)
|
||||
{
|
||||
bSrateSet = TRUE;
|
||||
// set sample rate, leave other tempo changer parameters as they are.
|
||||
pTDStretch->setParameters(srate);
|
||||
}
|
||||
|
||||
|
||||
// Adds 'numSamples' pcs of samples from the 'samples' memory position into
|
||||
// the input of the object.
|
||||
void SoundTouch::putSamples(const SAMPLETYPE *samples, uint numSamples)
|
||||
{
|
||||
if (bSrateSet == FALSE)
|
||||
{
|
||||
throw std::runtime_error("SoundTouch : Sample rate not defined");
|
||||
}
|
||||
else if (channels == 0)
|
||||
{
|
||||
throw std::runtime_error("SoundTouch : Number of channels not defined");
|
||||
}
|
||||
|
||||
// Transpose the rate of the new samples if necessary
|
||||
/* Bypass the nominal setting - can introduce a click in sound when tempo/pitch control crosses the nominal value...
|
||||
if (rate == 1.0f)
|
||||
{
|
||||
// The rate value is same as the original, simply evaluate the tempo changer.
|
||||
assert(output == pTDStretch);
|
||||
if (pRateTransposer->isEmpty() == 0)
|
||||
{
|
||||
// yet flush the last samples in the pitch transposer buffer
|
||||
// (may happen if 'rate' changes from a non-zero value to zero)
|
||||
pTDStretch->moveSamples(*pRateTransposer);
|
||||
}
|
||||
pTDStretch->putSamples(samples, numSamples);
|
||||
}
|
||||
*/
|
||||
else if (rate <= 1.0f)
|
||||
{
|
||||
// transpose the rate down, output the transposed sound to tempo changer buffer
|
||||
assert(output == pTDStretch);
|
||||
pRateTransposer->putSamples(samples, numSamples);
|
||||
pTDStretch->moveSamples(*pRateTransposer);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(rate > 1.0f);
|
||||
// evaluate the tempo changer, then transpose the rate up,
|
||||
assert(output == pRateTransposer);
|
||||
pTDStretch->putSamples(samples, numSamples);
|
||||
pRateTransposer->moveSamples(*pTDStretch);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Flushes the last samples from the processing pipeline to the output.
|
||||
// Clears also the internal processing buffers.
|
||||
//
|
||||
// Note: This function is meant for extracting the last samples of a sound
|
||||
// stream. This function may introduce additional blank samples in the end
|
||||
// of the sound stream, and thus it's not recommended to call this function
|
||||
// in the middle of a sound stream.
|
||||
void SoundTouch::flush()
|
||||
{
|
||||
int i;
|
||||
uint nOut;
|
||||
SAMPLETYPE buff[128];
|
||||
|
||||
nOut = numSamples();
|
||||
|
||||
memset(buff, 0, 128 * sizeof(SAMPLETYPE));
|
||||
// "Push" the last active samples out from the processing pipeline by
|
||||
// feeding blank samples into the processing pipeline until new,
|
||||
// processed samples appear in the output (not however, more than
|
||||
// 8ksamples in any case)
|
||||
for (i = 0; i < 128; i ++)
|
||||
{
|
||||
putSamples(buff, 64);
|
||||
if (numSamples() != nOut) break; // new samples have appeared in the output!
|
||||
}
|
||||
|
||||
// Clear working buffers
|
||||
pRateTransposer->clear();
|
||||
pTDStretch->clearInput();
|
||||
// yet leave the 'tempoChanger' output intouched as that's where the
|
||||
// flushed samples are!
|
||||
}
|
||||
|
||||
|
||||
// Changes a setting controlling the processing system behaviour. See the
|
||||
// 'SETTING_...' defines for available setting ID's.
|
||||
BOOL SoundTouch::setSetting(uint settingId, uint value)
|
||||
{
|
||||
uint sampleRate, sequenceMs, seekWindowMs, overlapMs;
|
||||
|
||||
// read current tdstretch routine parameters
|
||||
pTDStretch->getParameters(&sampleRate, &sequenceMs, &seekWindowMs, &overlapMs);
|
||||
|
||||
switch (settingId)
|
||||
{
|
||||
case SETTING_USE_AA_FILTER :
|
||||
// enables / disabless anti-alias filter
|
||||
pRateTransposer->enableAAFilter((value != 0) ? TRUE : FALSE);
|
||||
return TRUE;
|
||||
|
||||
case SETTING_AA_FILTER_LENGTH :
|
||||
// sets anti-alias filter length
|
||||
pRateTransposer->getAAFilter()->setLength(value);
|
||||
return TRUE;
|
||||
|
||||
case SETTING_USE_QUICKSEEK :
|
||||
// enables / disables tempo routine quick seeking algorithm
|
||||
pTDStretch->enableQuickSeek((value != 0) ? TRUE : FALSE);
|
||||
return TRUE;
|
||||
|
||||
case SETTING_SEQUENCE_MS:
|
||||
// change time-stretch sequence duration parameter
|
||||
pTDStretch->setParameters(sampleRate, value, seekWindowMs, overlapMs);
|
||||
return TRUE;
|
||||
|
||||
case SETTING_SEEKWINDOW_MS:
|
||||
// change time-stretch seek window length parameter
|
||||
pTDStretch->setParameters(sampleRate, sequenceMs, value, overlapMs);
|
||||
return TRUE;
|
||||
|
||||
case SETTING_OVERLAP_MS:
|
||||
// change time-stretch overlap length parameter
|
||||
pTDStretch->setParameters(sampleRate, sequenceMs, seekWindowMs, value);
|
||||
return TRUE;
|
||||
|
||||
default :
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Reads a setting controlling the processing system behaviour. See the
|
||||
// 'SETTING_...' defines for available setting ID's.
|
||||
//
|
||||
// Returns the setting value.
|
||||
uint SoundTouch::getSetting(uint settingId) const
|
||||
{
|
||||
uint temp;
|
||||
|
||||
switch (settingId)
|
||||
{
|
||||
case SETTING_USE_AA_FILTER :
|
||||
return pRateTransposer->isAAFilterEnabled();
|
||||
|
||||
case SETTING_AA_FILTER_LENGTH :
|
||||
return pRateTransposer->getAAFilter()->getLength();
|
||||
|
||||
case SETTING_USE_QUICKSEEK :
|
||||
return pTDStretch->isQuickSeekEnabled();
|
||||
|
||||
case SETTING_SEQUENCE_MS:
|
||||
pTDStretch->getParameters(NULL, &temp, NULL, NULL);
|
||||
return temp;
|
||||
|
||||
case SETTING_SEEKWINDOW_MS:
|
||||
pTDStretch->getParameters(NULL, NULL, &temp, NULL);
|
||||
return temp;
|
||||
|
||||
case SETTING_OVERLAP_MS:
|
||||
pTDStretch->getParameters(NULL, NULL, NULL, &temp);
|
||||
return temp;
|
||||
|
||||
default :
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Clears all the samples in the object's output and internal processing
|
||||
// buffers.
|
||||
void SoundTouch::clear()
|
||||
{
|
||||
pRateTransposer->clear();
|
||||
pTDStretch->clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Returns number of samples currently unprocessed.
|
||||
uint SoundTouch::numUnprocessedSamples() const
|
||||
{
|
||||
FIFOSamplePipe * psp;
|
||||
if (pTDStretch)
|
||||
{
|
||||
psp = pTDStretch->getInput();
|
||||
if (psp)
|
||||
{
|
||||
return psp->numSamples();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,252 @@
|
|||
//////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// SoundTouch - main class for tempo/pitch/rate adjusting routines.
|
||||
///
|
||||
/// Notes:
|
||||
/// - Initialize the SoundTouch object instance by setting up the sound stream
|
||||
/// parameters with functions 'setSampleRate' and 'setChannels', then set
|
||||
/// desired tempo/pitch/rate settings with the corresponding functions.
|
||||
///
|
||||
/// - The SoundTouch class behaves like a first-in-first-out pipeline: The
|
||||
/// samples that are to be processed are fed into one of the pipe by calling
|
||||
/// function 'putSamples', while the ready processed samples can be read
|
||||
/// from the other end of the pipeline with function 'receiveSamples'.
|
||||
///
|
||||
/// - The SoundTouch processing classes require certain sized 'batches' of
|
||||
/// samples in order to process the sound. For this reason the classes buffer
|
||||
/// incoming samples until there are enough of samples available for
|
||||
/// processing, then they carry out the processing step and consequently
|
||||
/// make the processed samples available for outputting.
|
||||
///
|
||||
/// - For the above reason, the processing routines introduce a certain
|
||||
/// 'latency' between the input and output, so that the samples input to
|
||||
/// SoundTouch may not be immediately available in the output, and neither
|
||||
/// the amount of outputtable samples may not immediately be in direct
|
||||
/// relationship with the amount of previously input samples.
|
||||
///
|
||||
/// - The tempo/pitch/rate control parameters can be altered during processing.
|
||||
/// Please notice though that they aren't currently protected by semaphores,
|
||||
/// so in multi-thread application external semaphore protection may be
|
||||
/// required.
|
||||
///
|
||||
/// - This class utilizes classes 'TDStretch' for tempo change (without modifying
|
||||
/// pitch) and 'RateTransposer' for changing the playback rate (that is, both
|
||||
/// tempo and pitch in the same ratio) of the sound. The third available control
|
||||
/// 'pitch' (change pitch but maintain tempo) is produced by a combination of
|
||||
/// combining the two other controls.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.14 $
|
||||
//
|
||||
// $Id: SoundTouch.h,v 1.14 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef SoundTouch_H
|
||||
#define SoundTouch_H
|
||||
|
||||
#include "FIFOSamplePipe.h"
|
||||
#include "STTypes.h"
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
/// Soundtouch library version string
|
||||
#define SOUNDTOUCH_VERSION "1.3.1"
|
||||
|
||||
/// SoundTouch library version id
|
||||
#define SOUNDTOUCH_VERSION_ID 010301
|
||||
|
||||
//
|
||||
// Available setting IDs for the 'setSetting' & 'get_setting' functions:
|
||||
|
||||
/// Enable/disable anti-alias filter in pitch transposer (0 = disable)
|
||||
#define SETTING_USE_AA_FILTER 0
|
||||
|
||||
/// Pitch transposer anti-alias filter length (8 .. 128 taps, default = 32)
|
||||
#define SETTING_AA_FILTER_LENGTH 1
|
||||
|
||||
/// Enable/disable quick seeking algorithm in tempo changer routine
|
||||
/// (enabling quick seeking lowers CPU utilization but causes a minor sound
|
||||
/// quality compromising)
|
||||
#define SETTING_USE_QUICKSEEK 2
|
||||
|
||||
/// Time-stretch algorithm single processing sequence length in milliseconds. This determines
|
||||
/// to how long sequences the original sound is chopped in the time-stretch algorithm.
|
||||
/// See "STTypes.h" or README for more information.
|
||||
#define SETTING_SEQUENCE_MS 3
|
||||
|
||||
/// Time-stretch algorithm seeking window length in milliseconds for algorithm that finds the
|
||||
/// best possible overlapping location. This determines from how wide window the algorithm
|
||||
/// may look for an optimal joining location when mixing the sound sequences back together.
|
||||
/// See "STTypes.h" or README for more information.
|
||||
#define SETTING_SEEKWINDOW_MS 4
|
||||
|
||||
/// Time-stretch algorithm overlap length in milliseconds. When the chopped sound sequences
|
||||
/// are mixed back together, to form a continuous sound stream, this parameter defines over
|
||||
/// how long period the two consecutive sequences are let to overlap each other.
|
||||
/// See "STTypes.h" or README for more information.
|
||||
#define SETTING_OVERLAP_MS 5
|
||||
|
||||
|
||||
class SoundTouch : public FIFOProcessor
|
||||
{
|
||||
private:
|
||||
/// Rate transposer class instance
|
||||
class RateTransposer *pRateTransposer;
|
||||
|
||||
/// Time-stretch class instance
|
||||
class TDStretch *pTDStretch;
|
||||
|
||||
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
||||
float virtualRate;
|
||||
|
||||
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
||||
float virtualTempo;
|
||||
|
||||
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
||||
float virtualPitch;
|
||||
|
||||
/// Flag: Has sample rate been set?
|
||||
BOOL bSrateSet;
|
||||
|
||||
/// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and
|
||||
/// 'virtualPitch' parameters.
|
||||
void calcEffectiveRateAndTempo();
|
||||
|
||||
protected :
|
||||
/// Number of channels
|
||||
uint channels;
|
||||
|
||||
/// Effective 'rate' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
|
||||
float rate;
|
||||
|
||||
/// Effective 'tempo' value calculated from 'virtualRate', 'virtualTempo' and 'virtualPitch'
|
||||
float tempo;
|
||||
|
||||
public:
|
||||
SoundTouch();
|
||||
virtual ~SoundTouch();
|
||||
|
||||
/// Get SoundTouch library version string
|
||||
static const char *getVersionString();
|
||||
|
||||
/// Get SoundTouch library version Id
|
||||
static uint getVersionId();
|
||||
|
||||
/// Sets new rate control value. Normal rate = 1.0, smaller values
|
||||
/// represent slower rate, larger faster rates.
|
||||
void setRate(float newRate);
|
||||
|
||||
/// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
||||
/// represent slower tempo, larger faster tempo.
|
||||
void setTempo(float newTempo);
|
||||
|
||||
/// Sets new rate control value as a difference in percents compared
|
||||
/// to the original rate (-50 .. +100 %)
|
||||
void setRateChange(float newRate);
|
||||
|
||||
/// Sets new tempo control value as a difference in percents compared
|
||||
/// to the original tempo (-50 .. +100 %)
|
||||
void setTempoChange(float newTempo);
|
||||
|
||||
/// Sets new pitch control value. Original pitch = 1.0, smaller values
|
||||
/// represent lower pitches, larger values higher pitch.
|
||||
void setPitch(float newPitch);
|
||||
|
||||
/// Sets pitch change in octaves compared to the original pitch
|
||||
/// (-1.00 .. +1.00)
|
||||
void setPitchOctaves(float newPitch);
|
||||
|
||||
/// Sets pitch change in semi-tones compared to the original pitch
|
||||
/// (-12 .. +12)
|
||||
void setPitchSemiTones(int newPitch);
|
||||
void setPitchSemiTones(float newPitch);
|
||||
|
||||
/// Sets the number of channels, 1 = mono, 2 = stereo
|
||||
void setChannels(uint numChannels);
|
||||
|
||||
/// Sets sample rate.
|
||||
void setSampleRate(uint srate);
|
||||
|
||||
/// Flushes the last samples from the processing pipeline to the output.
|
||||
/// Clears also the internal processing buffers.
|
||||
//
|
||||
/// Note: This function is meant for extracting the last samples of a sound
|
||||
/// stream. This function may introduce additional blank samples in the end
|
||||
/// of the sound stream, and thus it's not recommended to call this function
|
||||
/// in the middle of a sound stream.
|
||||
void flush();
|
||||
|
||||
/// Adds 'numSamples' pcs of samples from the 'samples' memory position into
|
||||
/// the input of the object. Notice that sample rate _has_to_ be set before
|
||||
/// calling this function, otherwise throws a runtime_error exception.
|
||||
virtual void putSamples(
|
||||
const SAMPLETYPE *samples, ///< Pointer to sample buffer.
|
||||
uint numSamples ///< Number of samples in buffer. Notice
|
||||
///< that in case of stereo-sound a single sample
|
||||
///< contains data for both channels.
|
||||
);
|
||||
|
||||
/// Clears all the samples in the object's output and internal processing
|
||||
/// buffers.
|
||||
virtual void clear();
|
||||
|
||||
/// Changes a setting controlling the processing system behaviour. See the
|
||||
/// 'SETTING_...' defines for available setting ID's.
|
||||
///
|
||||
/// \return 'TRUE' if the setting was succesfully changed
|
||||
BOOL setSetting(uint settingId, ///< Setting ID number. see SETTING_... defines.
|
||||
uint value ///< New setting value.
|
||||
);
|
||||
|
||||
/// Reads a setting controlling the processing system behaviour. See the
|
||||
/// 'SETTING_...' defines for available setting ID's.
|
||||
///
|
||||
/// \return the setting value.
|
||||
uint getSetting(uint settingId ///< Setting ID number, see SETTING_... defines.
|
||||
) const;
|
||||
|
||||
/// Returns number of samples currently unprocessed.
|
||||
virtual uint numUnprocessedSamples() const;
|
||||
|
||||
|
||||
/// Other handy functions that are implemented in the ancestor classes (see
|
||||
/// classes 'FIFOProcessor' and 'FIFOSamplePipe')
|
||||
///
|
||||
/// - receiveSamples() : Use this function to receive 'ready' processed samples from SoundTouch.
|
||||
/// - numSamples() : Get number of 'ready' samples that can be received with
|
||||
/// function 'receiveSamples()'
|
||||
/// - isEmpty() : Returns nonzero if there aren't any 'ready' samples.
|
||||
/// - clear() : Clears all samples from ready/processing buffers.
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,940 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||
/// while maintaining the original pitch by using a time domain WSOLA-like
|
||||
/// method with several performance-increasing tweaks.
|
||||
///
|
||||
/// Note : MMX optimized functions reside in a separate, platform-specific
|
||||
/// file, e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.24 $
|
||||
//
|
||||
// $Id: TDStretch.cpp,v 1.24 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "STTypes.h"
|
||||
#include "cpu_detect.h"
|
||||
#include "TDStretch.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ((a > b) ? b : a)
|
||||
#define max(a,b) ((a < b) ? b : a)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Constant definitions
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
// Table for the hierarchical mixing position seeking algorithm
|
||||
int scanOffsets[4][24]={
|
||||
{ 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806,
|
||||
868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0},
|
||||
{-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{ -20, -15, -10, -5, 5, 10, 15, 20, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{ -4, -3, -2, -1, 1, 2, 3, 4, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Implementation of the class 'TDStretch'
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
|
||||
{
|
||||
bQuickseek = FALSE;
|
||||
channels = 2;
|
||||
bMidBufferDirty = FALSE;
|
||||
|
||||
pMidBuffer = NULL;
|
||||
pRefMidBufferUnaligned = NULL;
|
||||
overlapLength = 0;
|
||||
|
||||
setParameters(48000, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS);
|
||||
|
||||
setTempo(1.0f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
TDStretch::~TDStretch()
|
||||
{
|
||||
delete[] pMidBuffer;
|
||||
delete[] pRefMidBufferUnaligned;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Calculates the x having the closest 2^x value for the given value
|
||||
static int _getClosest2Power(double value)
|
||||
{
|
||||
return (int)(log(value) / log(2.0) + 0.5);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets routine control parameters. These control are certain time constants
|
||||
// defining how the sound is stretched to the desired duration.
|
||||
//
|
||||
// 'sampleRate' = sample rate of the sound
|
||||
// 'sequenceMS' = one processing sequence length in milliseconds (default = 82 ms)
|
||||
// 'seekwindowMS' = seeking window length for scanning the best overlapping
|
||||
// position (default = 28 ms)
|
||||
// 'overlapMS' = overlapping length (default = 12 ms)
|
||||
|
||||
void TDStretch::setParameters(uint aSampleRate, uint aSequenceMS,
|
||||
uint aSeekWindowMS, uint aOverlapMS)
|
||||
{
|
||||
this->sampleRate = aSampleRate;
|
||||
this->sequenceMs = aSequenceMS;
|
||||
this->seekWindowMs = aSeekWindowMS;
|
||||
this->overlapMs = aOverlapMS;
|
||||
|
||||
seekLength = (sampleRate * seekWindowMs) / 1000;
|
||||
seekWindowLength = (sampleRate * sequenceMs) / 1000;
|
||||
|
||||
maxOffset = seekLength;
|
||||
|
||||
calculateOverlapLength(overlapMs);
|
||||
|
||||
// set tempo to recalculate 'sampleReq'
|
||||
setTempo(tempo);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Get routine control parameters, see setParameters() function.
|
||||
/// Any of the parameters to this function can be NULL, in such case corresponding parameter
|
||||
/// value isn't returned.
|
||||
void TDStretch::getParameters(uint *pSampleRate, uint *pSequenceMs, uint *pSeekWindowMs, uint *pOverlapMs)
|
||||
{
|
||||
if (pSampleRate)
|
||||
{
|
||||
*pSampleRate = sampleRate;
|
||||
}
|
||||
|
||||
if (pSequenceMs)
|
||||
{
|
||||
*pSequenceMs = sequenceMs;
|
||||
}
|
||||
|
||||
if (pSeekWindowMs)
|
||||
{
|
||||
*pSeekWindowMs = seekWindowMs;
|
||||
}
|
||||
|
||||
if (pOverlapMs)
|
||||
{
|
||||
*pOverlapMs = overlapMs;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Overlaps samples in 'midBuffer' with the samples in 'input'
|
||||
void TDStretch::overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const
|
||||
{
|
||||
int i, itemp;
|
||||
|
||||
for (i = 0; i < (int)overlapLength ; i ++)
|
||||
{
|
||||
itemp = overlapLength - i;
|
||||
output[i] = (input[i] * i + pMidBuffer[i] * itemp ) / overlapLength; // >> overlapDividerBits;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TDStretch::clearMidBuffer()
|
||||
{
|
||||
if (bMidBufferDirty)
|
||||
{
|
||||
memset(pMidBuffer, 0, 2 * sizeof(SAMPLETYPE) * overlapLength);
|
||||
bMidBufferDirty = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TDStretch::clearInput()
|
||||
{
|
||||
inputBuffer.clear();
|
||||
clearMidBuffer();
|
||||
}
|
||||
|
||||
|
||||
// Clears the sample buffers
|
||||
void TDStretch::clear()
|
||||
{
|
||||
outputBuffer.clear();
|
||||
inputBuffer.clear();
|
||||
clearMidBuffer();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Enables/disables the quick position seeking algorithm. Zero to disable, nonzero
|
||||
// to enable
|
||||
void TDStretch::enableQuickSeek(BOOL enable)
|
||||
{
|
||||
bQuickseek = enable;
|
||||
}
|
||||
|
||||
|
||||
// Returns nonzero if the quick seeking algorithm is enabled.
|
||||
BOOL TDStretch::isQuickSeekEnabled() const
|
||||
{
|
||||
return bQuickseek;
|
||||
}
|
||||
|
||||
|
||||
// Seeks for the optimal overlap-mixing position.
|
||||
uint TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos)
|
||||
{
|
||||
if (channels == 2)
|
||||
{
|
||||
// stereo sound
|
||||
if (bQuickseek)
|
||||
{
|
||||
return seekBestOverlapPositionStereoQuick(refPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
return seekBestOverlapPositionStereo(refPos);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// mono sound
|
||||
if (bQuickseek)
|
||||
{
|
||||
return seekBestOverlapPositionMonoQuick(refPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
return seekBestOverlapPositionMono(refPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Overlaps samples in 'midBuffer' with the samples in 'inputBuffer' at position
|
||||
// of 'ovlPos'.
|
||||
inline void TDStretch::overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const
|
||||
{
|
||||
if (channels == 2)
|
||||
{
|
||||
// stereo sound
|
||||
overlapStereo(output, input + 2 * ovlPos);
|
||||
} else {
|
||||
// mono sound.
|
||||
overlapMono(output, input + ovlPos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
|
||||
// routine
|
||||
//
|
||||
// The best position is determined as the position where the two overlapped
|
||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
||||
// value over the overlapping period
|
||||
uint TDStretch::seekBestOverlapPositionStereo(const SAMPLETYPE *refPos)
|
||||
{
|
||||
uint bestOffs;
|
||||
LONG_SAMPLETYPE bestCorr, corr;
|
||||
uint i;
|
||||
|
||||
// Slopes the amplitudes of the 'midBuffer' samples
|
||||
precalcCorrReferenceStereo();
|
||||
|
||||
bestCorr = INT_MIN;
|
||||
bestOffs = 0;
|
||||
|
||||
// Scans for the best correlation value by testing each possible position
|
||||
// over the permitted range.
|
||||
for (i = 0; i < seekLength; i ++)
|
||||
{
|
||||
// Calculates correlation value for the mixing position corresponding
|
||||
// to 'i'
|
||||
corr = calcCrossCorrStereo(refPos + 2 * i, pRefMidBuffer);
|
||||
|
||||
// Checks for the highest correlation value
|
||||
if (corr > bestCorr)
|
||||
{
|
||||
bestCorr = corr;
|
||||
bestOffs = i;
|
||||
}
|
||||
}
|
||||
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
||||
clearCrossCorrState();
|
||||
|
||||
return bestOffs;
|
||||
}
|
||||
|
||||
|
||||
// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
|
||||
// routine
|
||||
//
|
||||
// The best position is determined as the position where the two overlapped
|
||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
||||
// value over the overlapping period
|
||||
uint TDStretch::seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos)
|
||||
{
|
||||
uint j;
|
||||
uint bestOffs;
|
||||
LONG_SAMPLETYPE bestCorr, corr;
|
||||
uint scanCount, corrOffset, tempOffset;
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples
|
||||
precalcCorrReferenceStereo();
|
||||
|
||||
bestCorr = INT_MIN;
|
||||
bestOffs = 0;
|
||||
corrOffset = 0;
|
||||
tempOffset = 0;
|
||||
|
||||
// Scans for the best correlation value using four-pass hierarchical search.
|
||||
//
|
||||
// The look-up table 'scans' has hierarchical position adjusting steps.
|
||||
// In first pass the routine searhes for the highest correlation with
|
||||
// relatively coarse steps, then rescans the neighbourhood of the highest
|
||||
// correlation with better resolution and so on.
|
||||
for (scanCount = 0;scanCount < 4; scanCount ++)
|
||||
{
|
||||
j = 0;
|
||||
while (scanOffsets[scanCount][j])
|
||||
{
|
||||
tempOffset = corrOffset + scanOffsets[scanCount][j];
|
||||
if (tempOffset >= seekLength) break;
|
||||
|
||||
// Calculates correlation value for the mixing position corresponding
|
||||
// to 'tempOffset'
|
||||
corr = calcCrossCorrStereo(refPos + 2 * tempOffset, pRefMidBuffer);
|
||||
|
||||
// Checks for the highest correlation value
|
||||
if (corr > bestCorr)
|
||||
{
|
||||
bestCorr = corr;
|
||||
bestOffs = tempOffset;
|
||||
}
|
||||
j ++;
|
||||
}
|
||||
corrOffset = bestOffs;
|
||||
}
|
||||
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
||||
clearCrossCorrState();
|
||||
|
||||
return bestOffs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Seeks for the optimal overlap-mixing position. The 'mono' version of the
|
||||
// routine
|
||||
//
|
||||
// The best position is determined as the position where the two overlapped
|
||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
||||
// value over the overlapping period
|
||||
uint TDStretch::seekBestOverlapPositionMono(const SAMPLETYPE *refPos)
|
||||
{
|
||||
uint bestOffs;
|
||||
LONG_SAMPLETYPE bestCorr, corr;
|
||||
uint tempOffset;
|
||||
const SAMPLETYPE *compare;
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples
|
||||
precalcCorrReferenceMono();
|
||||
|
||||
bestCorr = INT_MIN;
|
||||
bestOffs = 0;
|
||||
|
||||
// Scans for the best correlation value by testing each possible position
|
||||
// over the permitted range.
|
||||
for (tempOffset = 0; tempOffset < seekLength; tempOffset ++)
|
||||
{
|
||||
compare = refPos + tempOffset;
|
||||
|
||||
// Calculates correlation value for the mixing position corresponding
|
||||
// to 'tempOffset'
|
||||
corr = calcCrossCorrMono(pRefMidBuffer, compare);
|
||||
|
||||
// Checks for the highest correlation value
|
||||
if (corr > bestCorr)
|
||||
{
|
||||
bestCorr = corr;
|
||||
bestOffs = tempOffset;
|
||||
}
|
||||
}
|
||||
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
||||
clearCrossCorrState();
|
||||
|
||||
return bestOffs;
|
||||
}
|
||||
|
||||
|
||||
// Seeks for the optimal overlap-mixing position. The 'mono' version of the
|
||||
// routine
|
||||
//
|
||||
// The best position is determined as the position where the two overlapped
|
||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
||||
// value over the overlapping period
|
||||
uint TDStretch::seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos)
|
||||
{
|
||||
uint j;
|
||||
uint bestOffs;
|
||||
LONG_SAMPLETYPE bestCorr, corr;
|
||||
uint scanCount, corrOffset, tempOffset;
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples
|
||||
precalcCorrReferenceMono();
|
||||
|
||||
bestCorr = INT_MIN;
|
||||
bestOffs = 0;
|
||||
corrOffset = 0;
|
||||
tempOffset = 0;
|
||||
|
||||
// Scans for the best correlation value using four-pass hierarchical search.
|
||||
//
|
||||
// The look-up table 'scans' has hierarchical position adjusting steps.
|
||||
// In first pass the routine searhes for the highest correlation with
|
||||
// relatively coarse steps, then rescans the neighbourhood of the highest
|
||||
// correlation with better resolution and so on.
|
||||
for (scanCount = 0;scanCount < 4; scanCount ++)
|
||||
{
|
||||
j = 0;
|
||||
while (scanOffsets[scanCount][j])
|
||||
{
|
||||
tempOffset = corrOffset + scanOffsets[scanCount][j];
|
||||
if (tempOffset >= seekLength) break;
|
||||
|
||||
// Calculates correlation value for the mixing position corresponding
|
||||
// to 'tempOffset'
|
||||
corr = calcCrossCorrMono(refPos + tempOffset, pRefMidBuffer);
|
||||
|
||||
// Checks for the highest correlation value
|
||||
if (corr > bestCorr)
|
||||
{
|
||||
bestCorr = corr;
|
||||
bestOffs = tempOffset;
|
||||
}
|
||||
j ++;
|
||||
}
|
||||
corrOffset = bestOffs;
|
||||
}
|
||||
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
||||
clearCrossCorrState();
|
||||
|
||||
return bestOffs;
|
||||
}
|
||||
|
||||
|
||||
/// clear cross correlation routine state if necessary
|
||||
void TDStretch::clearCrossCorrState()
|
||||
{
|
||||
// default implementation is empty.
|
||||
}
|
||||
|
||||
|
||||
// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
||||
// tempo, larger faster tempo.
|
||||
void TDStretch::setTempo(float newTempo)
|
||||
{
|
||||
uint intskip;
|
||||
|
||||
tempo = newTempo;
|
||||
|
||||
// Calculate ideal skip length (according to tempo value)
|
||||
nominalSkip = tempo * (seekWindowLength - overlapLength);
|
||||
skipFract = 0;
|
||||
intskip = (int)(nominalSkip + 0.5f);
|
||||
|
||||
// Calculate how many samples are needed in the 'inputBuffer' to
|
||||
// process another batch of samples
|
||||
sampleReq = max(intskip + overlapLength, seekWindowLength) + maxOffset;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Sets the number of channels, 1 = mono, 2 = stereo
|
||||
void TDStretch::setChannels(uint numChannels)
|
||||
{
|
||||
if (channels == numChannels) return;
|
||||
assert(numChannels == 1 || numChannels == 2);
|
||||
|
||||
channels = numChannels;
|
||||
inputBuffer.setChannels(channels);
|
||||
outputBuffer.setChannels(channels);
|
||||
}
|
||||
|
||||
|
||||
// nominal tempo, no need for processing, just pass the samples through
|
||||
// to outputBuffer
|
||||
void TDStretch::processNominalTempo()
|
||||
{
|
||||
assert(tempo == 1.0f);
|
||||
|
||||
if (bMidBufferDirty)
|
||||
{
|
||||
// If there are samples in pMidBuffer waiting for overlapping,
|
||||
// do a single sliding overlapping with them in order to prevent a
|
||||
// clicking distortion in the output sound
|
||||
if (inputBuffer.numSamples() < overlapLength)
|
||||
{
|
||||
// wait until we've got overlapLength input samples
|
||||
return;
|
||||
}
|
||||
// Mix the samples in the beginning of 'inputBuffer' with the
|
||||
// samples in 'midBuffer' using sliding overlapping
|
||||
overlap(outputBuffer.ptrEnd(overlapLength), inputBuffer.ptrBegin(), 0);
|
||||
outputBuffer.putSamples(overlapLength);
|
||||
inputBuffer.receiveSamples(overlapLength);
|
||||
clearMidBuffer();
|
||||
// now we've caught the nominal sample flow and may switch to
|
||||
// bypass mode
|
||||
}
|
||||
|
||||
// Simply bypass samples from input to output
|
||||
outputBuffer.moveSamples(inputBuffer);
|
||||
}
|
||||
|
||||
|
||||
// Processes as many processing frames of the samples 'inputBuffer', store
|
||||
// the result into 'outputBuffer'
|
||||
void TDStretch::processSamples()
|
||||
{
|
||||
uint ovlSkip, offset;
|
||||
int temp;
|
||||
|
||||
/* Removed this small optimization - can introduce a click to sound when tempo setting
|
||||
crosses the nominal value
|
||||
if (tempo == 1.0f)
|
||||
{
|
||||
// tempo not changed from the original, so bypass the processing
|
||||
processNominalTempo();
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
if (bMidBufferDirty == FALSE)
|
||||
{
|
||||
// if midBuffer is empty, move the first samples of the input stream
|
||||
// into it
|
||||
if (inputBuffer.numSamples() < overlapLength)
|
||||
{
|
||||
// wait until we've got overlapLength samples
|
||||
return;
|
||||
}
|
||||
memcpy(pMidBuffer, inputBuffer.ptrBegin(), channels * overlapLength * sizeof(SAMPLETYPE));
|
||||
inputBuffer.receiveSamples(overlapLength);
|
||||
bMidBufferDirty = TRUE;
|
||||
}
|
||||
|
||||
// Process samples as long as there are enough samples in 'inputBuffer'
|
||||
// to form a processing frame.
|
||||
while (inputBuffer.numSamples() >= sampleReq)
|
||||
{
|
||||
// If tempo differs from the normal ('SCALE'), scan for the best overlapping
|
||||
// position
|
||||
offset = seekBestOverlapPosition(inputBuffer.ptrBegin());
|
||||
|
||||
// Mix the samples in the 'inputBuffer' at position of 'offset' with the
|
||||
// samples in 'midBuffer' using sliding overlapping
|
||||
// ... first partially overlap with the end of the previous sequence
|
||||
// (that's in 'midBuffer')
|
||||
overlap(outputBuffer.ptrEnd(overlapLength), inputBuffer.ptrBegin(), offset);
|
||||
outputBuffer.putSamples(overlapLength);
|
||||
|
||||
// ... then copy sequence samples from 'inputBuffer' to output
|
||||
temp = (seekWindowLength - 2 * overlapLength);// & 0xfffffffe;
|
||||
if (temp > 0)
|
||||
{
|
||||
outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * (offset + overlapLength), temp);
|
||||
}
|
||||
|
||||
// Copies the end of the current sequence from 'inputBuffer' to
|
||||
// 'midBuffer' for being mixed with the beginning of the next
|
||||
// processing sequence and so on
|
||||
assert(offset + seekWindowLength <= inputBuffer.numSamples());
|
||||
memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + seekWindowLength - overlapLength),
|
||||
channels * sizeof(SAMPLETYPE) * overlapLength);
|
||||
bMidBufferDirty = TRUE;
|
||||
|
||||
// Remove the processed samples from the input buffer. Update
|
||||
// the difference between integer & nominal skip step to 'skipFract'
|
||||
// in order to prevent the error from accumulating over time.
|
||||
skipFract += nominalSkip; // real skip size
|
||||
ovlSkip = (int)skipFract; // rounded to integer skip
|
||||
skipFract -= ovlSkip; // maintain the fraction part, i.e. real vs. integer skip
|
||||
inputBuffer.receiveSamples(ovlSkip);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Adds 'numsamples' pcs of samples from the 'samples' memory position into
|
||||
// the input of the object.
|
||||
void TDStretch::putSamples(const SAMPLETYPE *samples, uint numSamples)
|
||||
{
|
||||
// Add the samples into the input buffer
|
||||
inputBuffer.putSamples(samples, numSamples);
|
||||
// Process the samples in input buffer
|
||||
processSamples();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Set new overlap length parameter & reallocate RefMidBuffer if necessary.
|
||||
void TDStretch::acceptNewOverlapLength(uint newOverlapLength)
|
||||
{
|
||||
uint prevOvl;
|
||||
|
||||
prevOvl = overlapLength;
|
||||
overlapLength = newOverlapLength;
|
||||
|
||||
if (overlapLength > prevOvl)
|
||||
{
|
||||
delete[] pMidBuffer;
|
||||
delete[] pRefMidBufferUnaligned;
|
||||
|
||||
pMidBuffer = new SAMPLETYPE[overlapLength * 2];
|
||||
bMidBufferDirty = TRUE;
|
||||
clearMidBuffer();
|
||||
|
||||
pRefMidBufferUnaligned = new SAMPLETYPE[2 * overlapLength + 16 / sizeof(SAMPLETYPE)];
|
||||
// ensure that 'pRefMidBuffer' is aligned to 16 byte boundary for efficiency
|
||||
pRefMidBuffer = (SAMPLETYPE *)((((ulongptr)pRefMidBufferUnaligned) + 15) & -16);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
// depending on if we've a MMX/SSE/etc-capable CPU available or not.
|
||||
void * TDStretch::operator new(size_t s)
|
||||
{
|
||||
// Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead!
|
||||
assert(FALSE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
TDStretch * TDStretch::newInstance()
|
||||
{
|
||||
uint uExtensions = 0;
|
||||
|
||||
#if !defined(_MSC_VER) || !defined(__x86_64__)
|
||||
uExtensions = detectCPUextensions();
|
||||
#endif
|
||||
|
||||
// Check if MMX/SSE/3DNow! instruction set extensions supported by CPU
|
||||
|
||||
#ifdef ALLOW_MMX
|
||||
// MMX routines available only with integer sample types
|
||||
if (uExtensions & SUPPORT_MMX)
|
||||
{
|
||||
return ::new TDStretchMMX;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_MMX
|
||||
|
||||
|
||||
#ifdef ALLOW_SSE
|
||||
if (uExtensions & SUPPORT_SSE)
|
||||
{
|
||||
// SSE support
|
||||
return ::new TDStretchSSE;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_SSE
|
||||
|
||||
|
||||
#ifdef ALLOW_3DNOW
|
||||
if (uExtensions & SUPPORT_3DNOW)
|
||||
{
|
||||
// 3DNow! support
|
||||
return ::new TDStretch3DNow;
|
||||
}
|
||||
else
|
||||
#endif // ALLOW_3DNOW
|
||||
|
||||
{
|
||||
// ISA optimizations not supported, use plain C version
|
||||
return ::new TDStretch;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Integer arithmetics specific algorithm implementations.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef INTEGER_SAMPLES
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples so that cross correlation
|
||||
// is faster to calculate
|
||||
void TDStretch::precalcCorrReferenceStereo()
|
||||
{
|
||||
int i, cnt2;
|
||||
int temp, temp2;
|
||||
|
||||
for (i=0 ; i < (int)overlapLength ;i ++)
|
||||
{
|
||||
temp = i * (overlapLength - i);
|
||||
cnt2 = i * 2;
|
||||
|
||||
temp2 = (pMidBuffer[cnt2] * temp) / slopingDivider;
|
||||
pRefMidBuffer[cnt2] = (short)(temp2);
|
||||
temp2 = (pMidBuffer[cnt2 + 1] * temp) / slopingDivider;
|
||||
pRefMidBuffer[cnt2 + 1] = (short)(temp2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples so that cross correlation
|
||||
// is faster to calculate
|
||||
void TDStretch::precalcCorrReferenceMono()
|
||||
{
|
||||
int i;
|
||||
long temp;
|
||||
long temp2;
|
||||
|
||||
for (i=0 ; i < (int)overlapLength ;i ++)
|
||||
{
|
||||
temp = i * (overlapLength - i);
|
||||
temp2 = (pMidBuffer[i] * temp) / slopingDivider;
|
||||
pRefMidBuffer[i] = (short)temp2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Stereo'
|
||||
// version of the routine.
|
||||
void TDStretch::overlapStereo(short *output, const short *input) const
|
||||
{
|
||||
int i;
|
||||
short temp;
|
||||
uint cnt2;
|
||||
|
||||
for (i = 0; i < (int)overlapLength ; i ++)
|
||||
{
|
||||
temp = (short)(overlapLength - i);
|
||||
cnt2 = 2 * i;
|
||||
output[cnt2] = (input[cnt2] * i + pMidBuffer[cnt2] * temp ) / overlapLength;
|
||||
output[cnt2 + 1] = (input[cnt2 + 1] * i + pMidBuffer[cnt2 + 1] * temp ) / overlapLength;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Calculates overlap period length in samples.
|
||||
/// Integer version rounds overlap length to closest power of 2
|
||||
/// for a divide scaling operation.
|
||||
void TDStretch::calculateOverlapLength(uint overlapMs)
|
||||
{
|
||||
uint newOvl;
|
||||
|
||||
overlapDividerBits = _getClosest2Power((sampleRate * overlapMs) / 1000.0);
|
||||
if (overlapDividerBits > 9) overlapDividerBits = 9;
|
||||
if (overlapDividerBits < 4) overlapDividerBits = 4;
|
||||
newOvl = 1<<overlapDividerBits;
|
||||
|
||||
acceptNewOverlapLength(newOvl);
|
||||
|
||||
// calculate sloping divider so that crosscorrelation operation won't
|
||||
// overflow 32-bit register. Max. sum of the crosscorrelation sum without
|
||||
// divider would be 2^30*(N^3-N)/3, where N = overlap length
|
||||
slopingDivider = (newOvl * newOvl - 1) / 3;
|
||||
}
|
||||
|
||||
|
||||
long TDStretch::calcCrossCorrMono(const short *mixingPos, const short *compare) const
|
||||
{
|
||||
long corr;
|
||||
uint i;
|
||||
|
||||
corr = 0;
|
||||
for (i = 1; i < overlapLength; i ++)
|
||||
{
|
||||
corr += (mixingPos[i] * compare[i]) >> overlapDividerBits;
|
||||
}
|
||||
|
||||
return corr;
|
||||
}
|
||||
|
||||
|
||||
long TDStretch::calcCrossCorrStereo(const short *mixingPos, const short *compare) const
|
||||
{
|
||||
long corr;
|
||||
uint i;
|
||||
|
||||
corr = 0;
|
||||
for (i = 2; i < 2 * overlapLength; i += 2)
|
||||
{
|
||||
corr += (mixingPos[i] * compare[i] +
|
||||
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits;
|
||||
}
|
||||
|
||||
return corr;
|
||||
}
|
||||
|
||||
#endif // INTEGER_SAMPLES
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Floating point arithmetics specific algorithm implementations.
|
||||
//
|
||||
|
||||
#ifdef FLOAT_SAMPLES
|
||||
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples so that cross correlation
|
||||
// is faster to calculate
|
||||
void TDStretch::precalcCorrReferenceStereo()
|
||||
{
|
||||
int i, cnt2;
|
||||
float temp;
|
||||
|
||||
for (i=0 ; i < (int)overlapLength ;i ++)
|
||||
{
|
||||
temp = (float)i * (float)(overlapLength - i);
|
||||
cnt2 = i * 2;
|
||||
pRefMidBuffer[cnt2] = (float)(pMidBuffer[cnt2] * temp);
|
||||
pRefMidBuffer[cnt2 + 1] = (float)(pMidBuffer[cnt2 + 1] * temp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Slopes the amplitude of the 'midBuffer' samples so that cross correlation
|
||||
// is faster to calculate
|
||||
void TDStretch::precalcCorrReferenceMono()
|
||||
{
|
||||
int i;
|
||||
float temp;
|
||||
|
||||
for (i=0 ; i < (int)overlapLength ;i ++)
|
||||
{
|
||||
temp = (float)i * (float)(overlapLength - i);
|
||||
pRefMidBuffer[i] = (float)(pMidBuffer[i] * temp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// SSE-optimized version of the function overlapStereo
|
||||
void TDStretch::overlapStereo(float *output, const float *input) const
|
||||
{
|
||||
int i;
|
||||
uint cnt2;
|
||||
float fTemp;
|
||||
float fScale;
|
||||
float fi;
|
||||
|
||||
fScale = 1.0f / (float)overlapLength;
|
||||
|
||||
for (i = 0; i < (int)overlapLength ; i ++)
|
||||
{
|
||||
fTemp = (float)(overlapLength - i) * fScale;
|
||||
fi = (float)i * fScale;
|
||||
cnt2 = 2 * i;
|
||||
output[cnt2 + 0] = input[cnt2 + 0] * fi + pMidBuffer[cnt2 + 0] * fTemp;
|
||||
output[cnt2 + 1] = input[cnt2 + 1] * fi + pMidBuffer[cnt2 + 1] * fTemp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Calculates overlap period length in samples.
|
||||
void TDStretch::calculateOverlapLength(uint overlapMs)
|
||||
{
|
||||
uint newOvl;
|
||||
|
||||
newOvl = (sampleRate * overlapMs) / 1000;
|
||||
if (newOvl < 16) newOvl = 16;
|
||||
|
||||
// must be divisible by 8
|
||||
newOvl -= newOvl % 8;
|
||||
|
||||
acceptNewOverlapLength(newOvl);
|
||||
}
|
||||
|
||||
|
||||
|
||||
double TDStretch::calcCrossCorrMono(const float *mixingPos, const float *compare) const
|
||||
{
|
||||
double corr;
|
||||
uint i;
|
||||
|
||||
corr = 0;
|
||||
for (i = 1; i < overlapLength; i ++)
|
||||
{
|
||||
corr += mixingPos[i] * compare[i];
|
||||
}
|
||||
|
||||
return corr;
|
||||
}
|
||||
|
||||
|
||||
double TDStretch::calcCrossCorrStereo(const float *mixingPos, const float *compare) const
|
||||
{
|
||||
double corr;
|
||||
uint i;
|
||||
|
||||
corr = 0;
|
||||
for (i = 2; i < 2 * overlapLength; i += 2)
|
||||
{
|
||||
corr += mixingPos[i] * compare[i] +
|
||||
mixingPos[i + 1] * compare[i + 1];
|
||||
}
|
||||
|
||||
return corr;
|
||||
}
|
||||
|
||||
#endif // FLOAT_SAMPLES
|
|
@ -0,0 +1,236 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||
/// while maintaining the original pitch by using a time domain WSOLA-like method
|
||||
/// with several performance-increasing tweaks.
|
||||
///
|
||||
/// Note : MMX optimized functions reside in a separate, platform-specific file,
|
||||
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.16 $
|
||||
//
|
||||
// $Id: TDStretch.h,v 1.16 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef TDStretch_H
|
||||
#define TDStretch_H
|
||||
|
||||
#include "STTypes.h"
|
||||
#include "RateTransposer.h"
|
||||
#include "FIFOSamplePipe.h"
|
||||
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
// Default values for sound processing parameters:
|
||||
|
||||
/// Default length of a single processing sequence, in milliseconds. This determines to how
|
||||
/// long sequences the original sound is chopped in the time-stretch algorithm.
|
||||
///
|
||||
/// The larger this value is, the lesser sequences are used in processing. In principle
|
||||
/// a bigger value sounds better when slowing down tempo, but worse when increasing tempo
|
||||
/// and vice versa.
|
||||
///
|
||||
/// Increasing this value reduces computational burden & vice versa.
|
||||
#define DEFAULT_SEQUENCE_MS 63
|
||||
|
||||
#define DEFAULT_SEEKWINDOW_MS 17
|
||||
|
||||
#define DEFAULT_OVERLAP_MS 7
|
||||
|
||||
|
||||
/// Class that does the time-stretch (tempo change) effect for the processed
|
||||
/// sound.
|
||||
class TDStretch : public FIFOProcessor
|
||||
{
|
||||
protected:
|
||||
uint channels;
|
||||
uint sampleReq;
|
||||
float tempo;
|
||||
|
||||
SAMPLETYPE *pMidBuffer;
|
||||
SAMPLETYPE *pRefMidBuffer;
|
||||
SAMPLETYPE *pRefMidBufferUnaligned;
|
||||
uint overlapLength;
|
||||
uint overlapDividerBits;
|
||||
uint slopingDivider;
|
||||
uint seekLength;
|
||||
uint seekWindowLength;
|
||||
uint maxOffset;
|
||||
float nominalSkip;
|
||||
float skipFract;
|
||||
FIFOSampleBuffer outputBuffer;
|
||||
FIFOSampleBuffer inputBuffer;
|
||||
BOOL bQuickseek;
|
||||
BOOL bMidBufferDirty;
|
||||
|
||||
uint sampleRate;
|
||||
uint sequenceMs;
|
||||
uint seekWindowMs;
|
||||
uint overlapMs;
|
||||
|
||||
void acceptNewOverlapLength(uint newOverlapLength);
|
||||
|
||||
virtual void clearCrossCorrState();
|
||||
void calculateOverlapLength(uint overlapMs);
|
||||
|
||||
virtual LONG_SAMPLETYPE calcCrossCorrStereo(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare) const;
|
||||
virtual LONG_SAMPLETYPE calcCrossCorrMono(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare) const;
|
||||
|
||||
virtual uint seekBestOverlapPositionStereo(const SAMPLETYPE *refPos);
|
||||
virtual uint seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos);
|
||||
virtual uint seekBestOverlapPositionMono(const SAMPLETYPE *refPos);
|
||||
virtual uint seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos);
|
||||
uint seekBestOverlapPosition(const SAMPLETYPE *refPos);
|
||||
|
||||
virtual void overlapStereo(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
||||
virtual void overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
||||
|
||||
void clearMidBuffer();
|
||||
void overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const;
|
||||
|
||||
void precalcCorrReferenceMono();
|
||||
void precalcCorrReferenceStereo();
|
||||
|
||||
void processNominalTempo();
|
||||
|
||||
/// Changes the tempo of the given sound samples.
|
||||
/// Returns amount of samples returned in the "output" buffer.
|
||||
/// The maximum amount of samples that can be returned at a time is set by
|
||||
/// the 'set_returnBuffer_size' function.
|
||||
void processSamples();
|
||||
|
||||
public:
|
||||
TDStretch();
|
||||
virtual ~TDStretch();
|
||||
|
||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
/// depending on if we've a MMX/SSE/etc-capable CPU available or not.
|
||||
void *operator new(size_t s);
|
||||
|
||||
/// Use this function instead of "new" operator to create a new instance of this class.
|
||||
/// This function automatically chooses a correct feature set depending on if the CPU
|
||||
/// supports MMX/SSE/etc extensions.
|
||||
static TDStretch *newInstance();
|
||||
|
||||
/// Returns the output buffer object
|
||||
FIFOSamplePipe *getOutput() { return &outputBuffer; };
|
||||
|
||||
/// Returns the input buffer object
|
||||
FIFOSamplePipe *getInput() { return &inputBuffer; };
|
||||
|
||||
/// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
||||
/// tempo, larger faster tempo.
|
||||
void setTempo(float newTempo);
|
||||
|
||||
/// Returns nonzero if there aren't any samples available for outputting.
|
||||
virtual void clear();
|
||||
|
||||
/// Clears the input buffer
|
||||
void clearInput();
|
||||
|
||||
/// Sets the number of channels, 1 = mono, 2 = stereo
|
||||
void setChannels(uint numChannels);
|
||||
|
||||
/// Enables/disables the quick position seeking algorithm. Zero to disable,
|
||||
/// nonzero to enable
|
||||
void enableQuickSeek(BOOL enable);
|
||||
|
||||
/// Returns nonzero if the quick seeking algorithm is enabled.
|
||||
BOOL isQuickSeekEnabled() const;
|
||||
|
||||
/// Sets routine control parameters. These control are certain time constants
|
||||
/// defining how the sound is stretched to the desired duration.
|
||||
//
|
||||
/// 'sampleRate' = sample rate of the sound
|
||||
/// 'sequenceMS' = one processing sequence length in milliseconds
|
||||
/// 'seekwindowMS' = seeking window length for scanning the best overlapping
|
||||
/// position
|
||||
/// 'overlapMS' = overlapping length
|
||||
void setParameters(uint sampleRate, ///< Samplerate of sound being processed (Hz)
|
||||
uint sequenceMS = DEFAULT_SEQUENCE_MS, ///< Single processing sequence length (ms)
|
||||
uint seekwindowMS = DEFAULT_SEEKWINDOW_MS, ///< Offset seeking window length (ms)
|
||||
uint overlapMS = DEFAULT_OVERLAP_MS ///< Sequence overlapping length (ms)
|
||||
);
|
||||
|
||||
/// Get routine control parameters, see setParameters() function.
|
||||
/// Any of the parameters to this function can be NULL, in such case corresponding parameter
|
||||
/// value isn't returned.
|
||||
void getParameters(uint *pSampleRate, uint *pSequenceMs, uint *pSeekWindowMs, uint *pOverlapMs);
|
||||
|
||||
/// Adds 'numsamples' pcs of samples from the 'samples' memory position into
|
||||
/// the input of the object.
|
||||
virtual void putSamples(
|
||||
const SAMPLETYPE *samples, ///< Input sample data
|
||||
uint numSamples ///< Number of samples in 'samples' so that one sample
|
||||
///< contains both channels if stereo
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Implementation-specific class declarations:
|
||||
|
||||
//#ifdef ALLOW_MMX
|
||||
// /// Class that implements MMX optimized routines for 16bit integer samples type.
|
||||
// class TDStretchMMX : public TDStretch
|
||||
// {
|
||||
// protected:
|
||||
// long calcCrossCorrStereo(const short *mixingPos, const short *compare) const;
|
||||
// virtual void overlapStereo(short *output, const short *input) const;
|
||||
// virtual void clearCrossCorrState();
|
||||
// };
|
||||
//#endif /// ALLOW_MMX
|
||||
//
|
||||
//
|
||||
//#ifdef ALLOW_3DNOW
|
||||
// /// Class that implements 3DNow! optimized routines for floating point samples type.
|
||||
// class TDStretch3DNow : public TDStretch
|
||||
// {
|
||||
// protected:
|
||||
// double calcCrossCorrStereo(const float *mixingPos, const float *compare) const;
|
||||
// };
|
||||
//#endif /// ALLOW_3DNOW
|
||||
|
||||
|
||||
#ifdef ALLOW_SSE
|
||||
/// Class that implements SSE optimized routines for floating point samples type.
|
||||
class TDStretchSSE : public TDStretch
|
||||
{
|
||||
protected:
|
||||
double calcCrossCorrStereo(const float *mixingPos, const float *compare) const;
|
||||
};
|
||||
|
||||
#endif /// ALLOW_SSE
|
||||
|
||||
}
|
||||
#endif /// TDStretch_H
|
|
@ -0,0 +1,728 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Classes for easy reading & writing of WAV sound files.
|
||||
///
|
||||
/// For big-endian CPU, define _BIG_ENDIAN_ during compile-time to correctly
|
||||
/// parse the WAV files with such processors.
|
||||
///
|
||||
/// Admittingly, more complete WAV reader routines may exist in public domain,
|
||||
/// but the reason for 'yet another' one is that those generic WAV reader
|
||||
/// libraries are exhaustingly large and cumbersome! Wanted to have something
|
||||
/// simpler here, i.e. something that's not already larger than rest of the
|
||||
/// SoundTouch/SoundStretch program...
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.15 $
|
||||
//
|
||||
// $Id: WavFile.cpp,v 1.15 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "WavFile.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
const static char riffStr[] = "RIFF";
|
||||
const static char waveStr[] = "WAVE";
|
||||
const static char fmtStr[] = "fmt ";
|
||||
const static char dataStr[] = "data";
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Helper functions for swapping byte order to correctly read/write WAV files
|
||||
// with big-endian CPU's: Define compile-time definition _BIG_ENDIAN_ to
|
||||
// turn-on the conversion if it appears necessary.
|
||||
//
|
||||
// For example, Intel x86 is little-endian and doesn't require conversion,
|
||||
// while PowerPC of Mac's and many other RISC cpu's are big-endian.
|
||||
|
||||
#ifdef BYTE_ORDER
|
||||
// In gcc compiler detect the byte order automatically
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
// big-endian platform.
|
||||
#define _BIG_ENDIAN_
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _BIG_ENDIAN_
|
||||
// big-endian CPU, swap bytes in 16 & 32 bit words
|
||||
|
||||
// helper-function to swap byte-order of 32bit integer
|
||||
static inline void _swap32(unsigned int &dwData)
|
||||
{
|
||||
dwData = ((dwData >> 24) & 0x000000FF) |
|
||||
((dwData >> 8) & 0x0000FF00) |
|
||||
((dwData << 8) & 0x00FF0000) |
|
||||
((dwData << 24) & 0xFF000000);
|
||||
}
|
||||
|
||||
// helper-function to swap byte-order of 16bit integer
|
||||
static inline void _swap16(unsigned short &wData)
|
||||
{
|
||||
wData = ((wData >> 8) & 0x00FF) |
|
||||
((wData << 8) & 0xFF00);
|
||||
}
|
||||
|
||||
// helper-function to swap byte-order of buffer of 16bit integers
|
||||
static inline void _swap16Buffer(unsigned short *pData, unsigned int dwNumWords)
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
for (i = 0; i < dwNumWords; i ++)
|
||||
{
|
||||
_swap16(pData[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#else // BIG_ENDIAN
|
||||
// little-endian CPU, WAV file is ok as such
|
||||
|
||||
// dummy helper-function
|
||||
static inline void _swap32(unsigned int &dwData)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
// dummy helper-function
|
||||
static inline void _swap16(unsigned short &wData)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
// dummy helper-function
|
||||
static inline void _swap16Buffer(unsigned short *pData, unsigned int dwNumBytes)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
#endif // BIG_ENDIAN
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Class WavInFile
|
||||
//
|
||||
|
||||
WavInFile::WavInFile(const char *fileName)
|
||||
{
|
||||
int hdrsOk;
|
||||
|
||||
// Try to open the file for reading
|
||||
fptr = fopen(fileName, "rb");
|
||||
if (fptr == NULL)
|
||||
{
|
||||
// didn't succeed
|
||||
string msg = "Error : Unable to open file \"";
|
||||
msg += fileName;
|
||||
msg += "\" for reading.";
|
||||
throw runtime_error(msg);
|
||||
}
|
||||
|
||||
// Read the file headers
|
||||
hdrsOk = readWavHeaders();
|
||||
if (hdrsOk != 0)
|
||||
{
|
||||
// Something didn't match in the wav file headers
|
||||
string msg = "File \"";
|
||||
msg += fileName;
|
||||
msg += "\" is corrupt or not a WAV file";
|
||||
throw runtime_error(msg);
|
||||
}
|
||||
|
||||
if (header.format.fixed != 1)
|
||||
{
|
||||
string msg = "File \"";
|
||||
msg += fileName;
|
||||
msg += "\" uses unsupported encoding.";
|
||||
throw runtime_error(msg);
|
||||
}
|
||||
|
||||
dataRead = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
WavInFile::~WavInFile()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void WavInFile::rewind()
|
||||
{
|
||||
int hdrsOk;
|
||||
|
||||
fseek(fptr, 0, SEEK_SET);
|
||||
hdrsOk = readWavHeaders();
|
||||
assert(hdrsOk == 0);
|
||||
dataRead = 0;
|
||||
}
|
||||
|
||||
|
||||
int WavInFile::checkCharTags()
|
||||
{
|
||||
// header.format.fmt should equal to 'fmt '
|
||||
if (memcmp(fmtStr, header.format.fmt, 4) != 0) return -1;
|
||||
// header.data.data_field should equal to 'data'
|
||||
if (memcmp(dataStr, header.data.data_field, 4) != 0) return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int WavInFile::read(char *buffer, int maxElems)
|
||||
{
|
||||
int numBytes;
|
||||
uint afterDataRead;
|
||||
|
||||
// ensure it's 8 bit format
|
||||
if (header.format.bits_per_sample != 8)
|
||||
{
|
||||
throw runtime_error("Error: WavInFile::read(char*, int) works only with 8bit samples.");
|
||||
}
|
||||
assert(sizeof(char) == 1);
|
||||
|
||||
numBytes = maxElems;
|
||||
afterDataRead = dataRead + numBytes;
|
||||
if (afterDataRead > header.data.data_len)
|
||||
{
|
||||
// Don't read more samples than are marked available in header
|
||||
numBytes = header.data.data_len - dataRead;
|
||||
assert(numBytes >= 0);
|
||||
}
|
||||
|
||||
numBytes = fread(buffer, 1, numBytes, fptr);
|
||||
dataRead += numBytes;
|
||||
|
||||
return numBytes;
|
||||
}
|
||||
|
||||
|
||||
int WavInFile::read(short *buffer, int maxElems)
|
||||
{
|
||||
unsigned int afterDataRead;
|
||||
int numBytes;
|
||||
int numElems;
|
||||
|
||||
if (header.format.bits_per_sample == 8)
|
||||
{
|
||||
// 8 bit format
|
||||
char *temp = new char[maxElems];
|
||||
int i;
|
||||
|
||||
numElems = read(temp, maxElems);
|
||||
// convert from 8 to 16 bit
|
||||
for (i = 0; i < numElems; i ++)
|
||||
{
|
||||
buffer[i] = temp[i] << 8;
|
||||
}
|
||||
delete[] temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 16 bit format
|
||||
assert(header.format.bits_per_sample == 16);
|
||||
assert(sizeof(short) == 2);
|
||||
|
||||
numBytes = maxElems * 2;
|
||||
afterDataRead = dataRead + numBytes;
|
||||
if (afterDataRead > header.data.data_len)
|
||||
{
|
||||
// Don't read more samples than are marked available in header
|
||||
numBytes = header.data.data_len - dataRead;
|
||||
assert(numBytes >= 0);
|
||||
}
|
||||
|
||||
numBytes = fread(buffer, 1, numBytes, fptr);
|
||||
dataRead += numBytes;
|
||||
numElems = numBytes / 2;
|
||||
|
||||
// 16bit samples, swap byte order if necessary
|
||||
_swap16Buffer((unsigned short *)buffer, numElems);
|
||||
}
|
||||
|
||||
return numElems;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int WavInFile::read(float *buffer, int maxElems)
|
||||
{
|
||||
short *temp = new short[maxElems];
|
||||
int num;
|
||||
int i;
|
||||
double fscale;
|
||||
|
||||
num = read(temp, maxElems);
|
||||
|
||||
fscale = 1.0 / 32768.0;
|
||||
// convert to floats, scale to range [-1..+1[
|
||||
for (i = 0; i < num; i ++)
|
||||
{
|
||||
buffer[i] = (float)(fscale * (double)temp[i]);
|
||||
}
|
||||
|
||||
delete[] temp;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
|
||||
int WavInFile::eof() const
|
||||
{
|
||||
// return true if all data has been read or file eof has reached
|
||||
return (dataRead == header.data.data_len || feof(fptr));
|
||||
}
|
||||
|
||||
|
||||
void WavInFile::close()
|
||||
{
|
||||
fclose(fptr);
|
||||
fptr = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// test if character code is between a white space ' ' and little 'z'
|
||||
static int isAlpha(char c)
|
||||
{
|
||||
return (c >= ' ' && c <= 'z') ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
// test if all characters are between a white space ' ' and little 'z'
|
||||
static int isAlphaStr(char *str)
|
||||
{
|
||||
int c;
|
||||
|
||||
c = str[0];
|
||||
while (c)
|
||||
{
|
||||
if (isAlpha(c) == 0) return 0;
|
||||
str ++;
|
||||
c = str[0];
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int WavInFile::readRIFFBlock()
|
||||
{
|
||||
fread(&(header.riff), sizeof(WavRiff), 1, fptr);
|
||||
|
||||
// swap 32bit data byte order if necessary
|
||||
_swap32((unsigned int &)header.riff.package_len);
|
||||
|
||||
// header.riff.riff_char should equal to 'RIFF');
|
||||
if (memcmp(riffStr, header.riff.riff_char, 4) != 0) return -1;
|
||||
// header.riff.wave should equal to 'WAVE'
|
||||
if (memcmp(waveStr, header.riff.wave, 4) != 0) return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int WavInFile::readHeaderBlock()
|
||||
{
|
||||
char label[5];
|
||||
string sLabel;
|
||||
|
||||
// lead label string
|
||||
fread(label, 1, 4, fptr);
|
||||
label[4] = 0;
|
||||
|
||||
if (isAlphaStr(label) == 0) return -1; // not a valid label
|
||||
|
||||
// Decode blocks according to their label
|
||||
if (strcmp(label, fmtStr) == 0)
|
||||
{
|
||||
int nLen, nDump;
|
||||
|
||||
// 'fmt ' block
|
||||
memcpy(header.format.fmt, fmtStr, 4);
|
||||
|
||||
// read length of the format field
|
||||
fread(&nLen, sizeof(int), 1, fptr);
|
||||
// swap byte order if necessary
|
||||
_swap32((unsigned int &)nLen); // int format_len;
|
||||
header.format.format_len = nLen;
|
||||
|
||||
// calculate how much length differs from expected
|
||||
nDump = nLen - (sizeof(header.format) - 8);
|
||||
|
||||
// if format_len is larger than expected, read only as much data as we've space for
|
||||
if (nDump > 0)
|
||||
{
|
||||
nLen = sizeof(header.format) - 8;
|
||||
}
|
||||
|
||||
// read data
|
||||
fread(&(header.format.fixed), nLen, 1, fptr);
|
||||
|
||||
// swap byte order if necessary
|
||||
_swap16((unsigned short &)header.format.fixed); // short int fixed;
|
||||
_swap16((unsigned short &)header.format.channel_number); // short int channel_number;
|
||||
_swap32((unsigned int &)header.format.sample_rate); // int sample_rate;
|
||||
_swap32((unsigned int &)header.format.byte_rate); // int byte_rate;
|
||||
_swap16((unsigned short &)header.format.byte_per_sample); // short int byte_per_sample;
|
||||
_swap16((unsigned short &)header.format.bits_per_sample); // short int bits_per_sample;
|
||||
|
||||
// if format_len is larger than expected, skip the extra data
|
||||
if (nDump > 0)
|
||||
{
|
||||
fseek(fptr, nDump, SEEK_CUR);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp(label, dataStr) == 0)
|
||||
{
|
||||
// 'data' block
|
||||
memcpy(header.data.data_field, dataStr, 4);
|
||||
fread(&(header.data.data_len), sizeof(uint), 1, fptr);
|
||||
|
||||
// swap byte order if necessary
|
||||
_swap32((unsigned int &)header.data.data_len);
|
||||
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint len, i;
|
||||
uint temp;
|
||||
// unknown block
|
||||
|
||||
// read length
|
||||
fread(&len, sizeof(len), 1, fptr);
|
||||
// scan through the block
|
||||
for (i = 0; i < len; i ++)
|
||||
{
|
||||
fread(&temp, 1, 1, fptr);
|
||||
if (feof(fptr)) return -1; // unexpected eof
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int WavInFile::readWavHeaders()
|
||||
{
|
||||
int res;
|
||||
|
||||
memset(&header, 0, sizeof(header));
|
||||
|
||||
res = readRIFFBlock();
|
||||
if (res) return 1;
|
||||
// read header blocks until data block is found
|
||||
do
|
||||
{
|
||||
// read header blocks
|
||||
res = readHeaderBlock();
|
||||
if (res < 0) return 1; // error in file structure
|
||||
} while (res == 0);
|
||||
// check that all required tags are legal
|
||||
return checkCharTags();
|
||||
}
|
||||
|
||||
|
||||
uint WavInFile::getNumChannels() const
|
||||
{
|
||||
return header.format.channel_number;
|
||||
}
|
||||
|
||||
|
||||
uint WavInFile::getNumBits() const
|
||||
{
|
||||
return header.format.bits_per_sample;
|
||||
}
|
||||
|
||||
|
||||
uint WavInFile::getBytesPerSample() const
|
||||
{
|
||||
return getNumChannels() * getNumBits() / 8;
|
||||
}
|
||||
|
||||
|
||||
uint WavInFile::getSampleRate() const
|
||||
{
|
||||
return header.format.sample_rate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint WavInFile::getDataSizeInBytes() const
|
||||
{
|
||||
return header.data.data_len;
|
||||
}
|
||||
|
||||
|
||||
uint WavInFile::getNumSamples() const
|
||||
{
|
||||
return header.data.data_len / header.format.byte_per_sample;
|
||||
}
|
||||
|
||||
|
||||
uint WavInFile::getLengthMS() const
|
||||
{
|
||||
uint numSamples;
|
||||
uint sampleRate;
|
||||
|
||||
numSamples = getNumSamples();
|
||||
sampleRate = getSampleRate();
|
||||
|
||||
assert(numSamples < UINT_MAX / 1000);
|
||||
return (1000 * numSamples / sampleRate);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Class WavOutFile
|
||||
//
|
||||
|
||||
WavOutFile::WavOutFile(const char *fileName, int sampleRate, int bits, int channels)
|
||||
{
|
||||
bytesWritten = 0;
|
||||
fptr = fopen(fileName, "wb");
|
||||
if (fptr == NULL)
|
||||
{
|
||||
string msg = "Error : Unable to open file \"";
|
||||
msg += fileName;
|
||||
msg += "\" for writing.";
|
||||
//pmsg = msg.c_str;
|
||||
throw runtime_error(msg);
|
||||
}
|
||||
|
||||
fillInHeader(sampleRate, bits, channels);
|
||||
writeHeader();
|
||||
|
||||
flushTime = flushRate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
WavOutFile::~WavOutFile()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void WavOutFile::fillInHeader(uint sampleRate, uint bits, uint channels)
|
||||
{
|
||||
// fill in the 'riff' part..
|
||||
|
||||
// copy string 'RIFF' to riff_char
|
||||
memcpy(&(header.riff.riff_char), riffStr, 4);
|
||||
// package_len unknown so far
|
||||
header.riff.package_len = 0;
|
||||
// copy string 'WAVE' to wave
|
||||
memcpy(&(header.riff.wave), waveStr, 4);
|
||||
|
||||
|
||||
// fill in the 'format' part..
|
||||
|
||||
// copy string 'fmt ' to fmt
|
||||
memcpy(&(header.format.fmt), fmtStr, 4);
|
||||
|
||||
header.format.format_len = 0x10;
|
||||
header.format.fixed = 1;
|
||||
header.format.channel_number = (short)channels;
|
||||
header.format.sample_rate = sampleRate;
|
||||
header.format.bits_per_sample = (short)bits;
|
||||
header.format.byte_per_sample = (short)(bits * channels / 8);
|
||||
header.format.byte_rate = header.format.byte_per_sample * sampleRate;
|
||||
header.format.sample_rate = sampleRate;
|
||||
|
||||
// fill in the 'data' part..
|
||||
|
||||
// copy string 'data' to data_field
|
||||
memcpy(&(header.data.data_field), dataStr, 4);
|
||||
// data_len unknown so far
|
||||
header.data.data_len = 0;
|
||||
}
|
||||
|
||||
|
||||
void WavOutFile::finishHeader()
|
||||
{
|
||||
// supplement the file length into the header structure
|
||||
header.riff.package_len = bytesWritten + 36;
|
||||
header.data.data_len = bytesWritten;
|
||||
|
||||
writeHeader();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void WavOutFile::writeHeader()
|
||||
{
|
||||
WavHeader hdrTemp;
|
||||
|
||||
// swap byte order if necessary
|
||||
hdrTemp = header;
|
||||
_swap32((unsigned int &)hdrTemp.riff.package_len);
|
||||
_swap32((unsigned int &)hdrTemp.format.format_len);
|
||||
_swap16((unsigned short &)hdrTemp.format.fixed);
|
||||
_swap16((unsigned short &)hdrTemp.format.channel_number);
|
||||
_swap32((unsigned int &)hdrTemp.format.sample_rate);
|
||||
_swap32((unsigned int &)hdrTemp.format.byte_rate);
|
||||
_swap16((unsigned short &)hdrTemp.format.byte_per_sample);
|
||||
_swap16((unsigned short &)hdrTemp.format.bits_per_sample);
|
||||
_swap32((unsigned int &)hdrTemp.data.data_len);
|
||||
|
||||
// write the supplemented header in the beginning of the file
|
||||
fseek(fptr, 0, SEEK_SET);
|
||||
fwrite(&hdrTemp, sizeof(hdrTemp), 1, fptr);
|
||||
// jump back to the end of the file
|
||||
fseek(fptr, 0, SEEK_END);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void WavOutFile::close()
|
||||
{
|
||||
finishHeader();
|
||||
fclose(fptr);
|
||||
fptr = NULL;
|
||||
}
|
||||
|
||||
void WavOutFile::flush( int numElems )
|
||||
{
|
||||
flushTime -= numElems;
|
||||
if( flushTime < 0 )
|
||||
{
|
||||
flushTime += flushRate;
|
||||
finishHeader();
|
||||
}
|
||||
}
|
||||
|
||||
void WavOutFile::write(const char *buffer, int numElems)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (header.format.bits_per_sample != 8)
|
||||
{
|
||||
throw runtime_error("Error: WavOutFile::write(const char*, int) accepts only 8bit samples.");
|
||||
}
|
||||
assert(sizeof(char) == 1);
|
||||
|
||||
res = fwrite(buffer, 1, numElems, fptr);
|
||||
if (res != numElems)
|
||||
{
|
||||
throw runtime_error("Error while writing to a wav file.");
|
||||
}
|
||||
|
||||
bytesWritten += numElems;
|
||||
flush( numElems );
|
||||
}
|
||||
|
||||
|
||||
void WavOutFile::write(const short *buffer, int numElems)
|
||||
{
|
||||
int res;
|
||||
|
||||
// 16 bit samples
|
||||
if (numElems < 1) return; // nothing to do
|
||||
|
||||
if (header.format.bits_per_sample == 8)
|
||||
{
|
||||
int i;
|
||||
char *temp = new char[numElems];
|
||||
// convert from 16bit format to 8bit format
|
||||
for (i = 0; i < numElems; i ++)
|
||||
{
|
||||
temp[i] = buffer[i] >> 8;
|
||||
}
|
||||
// write in 8bit format
|
||||
write(temp, numElems);
|
||||
delete[] temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 16bit format
|
||||
unsigned short *pTemp = new unsigned short[numElems];
|
||||
|
||||
assert(header.format.bits_per_sample == 16);
|
||||
|
||||
// allocate temp buffer to swap byte order if necessary
|
||||
memcpy(pTemp, buffer, numElems * 2);
|
||||
_swap16Buffer(pTemp, numElems);
|
||||
|
||||
res = fwrite(pTemp, 2, numElems, fptr);
|
||||
|
||||
delete[] pTemp;
|
||||
|
||||
if (res != numElems)
|
||||
{
|
||||
throw runtime_error("Error while writing to a wav file.");
|
||||
}
|
||||
bytesWritten += 2 * numElems;
|
||||
flush( numElems*2 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WavOutFile::write(const float *buffer, int numElems)
|
||||
{
|
||||
int i;
|
||||
short *temp = new short[numElems];
|
||||
int iTemp;
|
||||
|
||||
// convert to 16 bit integer
|
||||
for (i = 0; i < numElems; i ++)
|
||||
{
|
||||
// convert to integer
|
||||
iTemp = (int)(32768.0f * buffer[i]);
|
||||
|
||||
// saturate
|
||||
if (iTemp < -32768) iTemp = -32768;
|
||||
if (iTemp > 32767) iTemp = 32767;
|
||||
temp[i] = (short)iTemp;
|
||||
}
|
||||
|
||||
write(temp, numElems);
|
||||
flush( numElems );
|
||||
|
||||
delete[] temp;
|
||||
}
|
|
@ -0,0 +1,264 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Classes for easy reading & writing of WAV sound files.
|
||||
///
|
||||
/// For big-endian CPU, define BIG_ENDIAN during compile-time to correctly
|
||||
/// parse the WAV files with such processors.
|
||||
///
|
||||
/// Admittingly, more complete WAV reader routines may exist in public domain, but
|
||||
/// the reason for 'yet another' one is that those generic WAV reader libraries are
|
||||
/// exhaustingly large and cumbersome! Wanted to have something simpler here, i.e.
|
||||
/// something that's not already larger than rest of the SoundTouch/SoundStretch program...
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.7 $
|
||||
//
|
||||
// $Id: WavFile.h,v 1.7 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef WAVFILE_H
|
||||
#define WAVFILE_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef uint
|
||||
typedef unsigned int uint;
|
||||
#endif
|
||||
|
||||
|
||||
/// WAV audio file 'riff' section header
|
||||
typedef struct
|
||||
{
|
||||
char riff_char[4];
|
||||
int package_len;
|
||||
char wave[4];
|
||||
} WavRiff;
|
||||
|
||||
/// WAV audio file 'format' section header
|
||||
typedef struct
|
||||
{
|
||||
char fmt[4];
|
||||
int format_len;
|
||||
short fixed;
|
||||
short channel_number;
|
||||
int sample_rate;
|
||||
int byte_rate;
|
||||
short byte_per_sample;
|
||||
short bits_per_sample;
|
||||
} WavFormat;
|
||||
|
||||
/// WAV audio file 'data' section header
|
||||
typedef struct
|
||||
{
|
||||
char data_field[4];
|
||||
uint data_len;
|
||||
} WavData;
|
||||
|
||||
|
||||
/// WAV audio file header
|
||||
typedef struct
|
||||
{
|
||||
WavRiff riff;
|
||||
WavFormat format;
|
||||
WavData data;
|
||||
} WavHeader;
|
||||
|
||||
|
||||
/// Class for reading WAV audio files.
|
||||
class WavInFile
|
||||
{
|
||||
private:
|
||||
/// File pointer.
|
||||
FILE *fptr;
|
||||
|
||||
/// Counter of how many bytes of sample data have been read from the file.
|
||||
uint dataRead;
|
||||
|
||||
/// WAV header information
|
||||
WavHeader header;
|
||||
|
||||
/// Read WAV file headers.
|
||||
/// \return zero if all ok, nonzero if file format is invalid.
|
||||
int readWavHeaders();
|
||||
|
||||
/// Checks WAV file header tags.
|
||||
/// \return zero if all ok, nonzero if file format is invalid.
|
||||
int checkCharTags();
|
||||
|
||||
/// Reads a single WAV file header block.
|
||||
/// \return zero if all ok, nonzero if file format is invalid.
|
||||
int readHeaderBlock();
|
||||
|
||||
/// Reads WAV file 'riff' block
|
||||
int readRIFFBlock();
|
||||
|
||||
public:
|
||||
/// Constructor: Opens the given WAV file. If the file can't be opened,
|
||||
/// throws 'runtime_error' exception.
|
||||
WavInFile(const char *filename);
|
||||
|
||||
/// Destructor: Closes the file.
|
||||
~WavInFile();
|
||||
|
||||
/// Close the file. Notice that file is automatically closed also when the
|
||||
/// class instance is deleted.
|
||||
void close();
|
||||
|
||||
/// Rewind to beginning of the file
|
||||
void rewind();
|
||||
|
||||
/// Get sample rate.
|
||||
uint getSampleRate() const;
|
||||
|
||||
/// Get number of bits per sample, i.e. 8 or 16.
|
||||
uint getNumBits() const;
|
||||
|
||||
/// Get sample data size in bytes. Ahem, this should return same information as
|
||||
/// 'getBytesPerSample'...
|
||||
uint getDataSizeInBytes() const;
|
||||
|
||||
/// Get total number of samples in file.
|
||||
uint getNumSamples() const;
|
||||
|
||||
/// Get number of bytes per audio sample (e.g. 16bit stereo = 4 bytes/sample)
|
||||
uint getBytesPerSample() const;
|
||||
|
||||
/// Get number of audio channels in the file (1=mono, 2=stereo)
|
||||
uint getNumChannels() const;
|
||||
|
||||
/// Get the audio file length in milliseconds
|
||||
uint getLengthMS() const;
|
||||
|
||||
/// Reads audio samples from the WAV file. This routine works only for 8 bit samples.
|
||||
/// Reads given number of elements from the file or if end-of-file reached, as many
|
||||
/// elements as are left in the file.
|
||||
///
|
||||
/// \return Number of 8-bit integers read from the file.
|
||||
int read(char *buffer, int maxElems);
|
||||
|
||||
/// Reads audio samples from the WAV file to 16 bit integer format. Reads given number
|
||||
/// of elements from the file or if end-of-file reached, as many elements as are
|
||||
/// left in the file.
|
||||
///
|
||||
/// \return Number of 16-bit integers read from the file.
|
||||
int read(short *buffer, ///< Pointer to buffer where to read data.
|
||||
int maxElems ///< Size of 'buffer' array (number of array elements).
|
||||
);
|
||||
|
||||
/// Reads audio samples from the WAV file to floating point format, converting
|
||||
/// sample values to range [-1,1[. Reads given number of elements from the file
|
||||
/// or if end-of-file reached, as many elements as are left in the file.
|
||||
///
|
||||
/// \return Number of elements read from the file.
|
||||
int read(float *buffer, ///< Pointer to buffer where to read data.
|
||||
int maxElems ///< Size of 'buffer' array (number of array elements).
|
||||
);
|
||||
|
||||
/// Check end-of-file.
|
||||
///
|
||||
/// \return Nonzero if end-of-file reached.
|
||||
int eof() const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// Class for writing WAV audio files.
|
||||
class WavOutFile
|
||||
{
|
||||
private:
|
||||
/// Pointer to the WAV file
|
||||
FILE *fptr;
|
||||
|
||||
/// WAV file header data.
|
||||
WavHeader header;
|
||||
|
||||
/// Counter of how many bytes have been written to the file so far.
|
||||
int bytesWritten;
|
||||
|
||||
/// number of bytes to be written before next flush.
|
||||
int flushTime;
|
||||
|
||||
/// Fills in WAV file header information.
|
||||
void fillInHeader(const uint sampleRate, const uint bits, const uint channels);
|
||||
|
||||
/// Finishes the WAV file header by supplementing information of amount of
|
||||
/// data written to file etc
|
||||
void finishHeader();
|
||||
|
||||
/// Writes the WAV file header.
|
||||
void writeHeader();
|
||||
|
||||
/// Flushes the WAV file every so often -- writes header info for the current
|
||||
/// data length and then returns the seek position to the end of the WAV for
|
||||
/// continued writing. This method is called from each write() method.
|
||||
void flush( int numElems );
|
||||
|
||||
/// Flush the WAVheader every 32kb written
|
||||
static const int flushRate = 0x8000;
|
||||
|
||||
public:
|
||||
/// Constructor: Creates a new WAV file. Throws a 'runtime_error' exception
|
||||
/// if file creation fails.
|
||||
WavOutFile(const char *fileName, ///< Filename
|
||||
int sampleRate, ///< Sample rate (e.g. 44100 etc)
|
||||
int bits, ///< Bits per sample (8 or 16 bits)
|
||||
int channels ///< Number of channels (1=mono, 2=stereo)
|
||||
);
|
||||
|
||||
/// Destructor: Finalizes & closes the WAV file.
|
||||
~WavOutFile();
|
||||
|
||||
/// Write data to WAV file. This function works only with 8bit samples.
|
||||
/// Throws a 'runtime_error' exception if writing to file fails.
|
||||
void write(const char *buffer, ///< Pointer to sample data buffer.
|
||||
int numElems ///< How many array items are to be written to file.
|
||||
);
|
||||
|
||||
/// Write data to WAV file. Throws a 'runtime_error' exception if writing to
|
||||
/// file fails.
|
||||
void write(const short *buffer, ///< Pointer to sample data buffer.
|
||||
int numElems ///< How many array items are to be written to file.
|
||||
);
|
||||
|
||||
/// Write data to WAV file in floating point format, saturating sample values to range
|
||||
/// [-1..+1[. Throws a 'runtime_error' exception if writing to file fails.
|
||||
void write(const float *buffer, ///< Pointer to sample data buffer.
|
||||
int numElems ///< How many array items are to be written to file.
|
||||
);
|
||||
|
||||
/// Finalize & close the WAV file. Automatically supplements the WAV file header
|
||||
/// information according to written data etc.
|
||||
///
|
||||
/// Notice that file is automatically closed also when the class instance is deleted.
|
||||
void close();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,62 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// A header file for detecting the Intel MMX instructions set extension.
|
||||
///
|
||||
/// Please see 'mmx_win.cpp', 'mmx_cpp.cpp' and 'mmx_non_x86.cpp' for the
|
||||
/// routine implementations for x86 Windows, x86 gnu version and non-x86
|
||||
/// platforms, respectively.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.4 $
|
||||
//
|
||||
// $Id: cpu_detect.h,v 1.4 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _CPU_DETECT_H_
|
||||
#define _CPU_DETECT_H_
|
||||
|
||||
#include "STTypes.h"
|
||||
|
||||
#define SUPPORT_MMX 0x0001
|
||||
#define SUPPORT_3DNOW 0x0002
|
||||
#define SUPPORT_ALTIVEC 0x0004
|
||||
#define SUPPORT_SSE 0x0008
|
||||
#define SUPPORT_SSE2 0x0010
|
||||
|
||||
/// Checks which instruction set extensions are supported by the CPU.
|
||||
///
|
||||
/// \return A bitmask of supported extensions, see SUPPORT_... defines.
|
||||
uint detectCPUextensions(void);
|
||||
|
||||
/// Disables given set of instruction extensions. See SUPPORT_... defines.
|
||||
void disableExtensions(uint wDisableMask);
|
||||
|
||||
#endif // _CPU_DETECT_H_
|
|
@ -0,0 +1,138 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// gcc version of the x86 CPU detect routine.
|
||||
///
|
||||
/// This file is to be compiled on any platform with the GNU C compiler.
|
||||
/// Compiler. Please see 'cpu_detect_x86_win.cpp' for the x86 Windows version
|
||||
/// of this file.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.6 $
|
||||
//
|
||||
// $Id: cpu_detect_x86_gcc.cpp,v 1.6 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include "cpu_detect.h"
|
||||
|
||||
#ifndef __GNUC__
|
||||
#error wrong platform - this source code file is for the GNU C compiler.
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <stdio.h>
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// processor instructions extension detection routines
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// Flag variable indicating whick ISA extensions are disabled (for debugging)
|
||||
static uint _dwDisabledISA = 0x00; // 0xffffffff; //<- use this to disable all extensions
|
||||
|
||||
// Disables given set of instruction extensions. See SUPPORT_... defines.
|
||||
void disableExtensions(uint dwDisableMask)
|
||||
{
|
||||
_dwDisabledISA = dwDisableMask;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Checks which instruction set extensions are supported by the CPU.
|
||||
uint detectCPUextensions(void)
|
||||
{
|
||||
#ifndef __i386__
|
||||
return 0; // always disable extensions on non-x86 platforms.
|
||||
#else
|
||||
uint res = 0;
|
||||
|
||||
if (_dwDisabledISA == 0xffffffff) return 0;
|
||||
|
||||
asm volatile(
|
||||
"\n\txor %%esi, %%esi" // clear %%esi = result register
|
||||
// check if 'cpuid' instructions is available by toggling eflags bit 21
|
||||
|
||||
"\n\tpushf" // save eflags to stack
|
||||
"\n\tpop %%eax" // load eax from stack (with eflags)
|
||||
"\n\tmovl %%eax, %%ecx" // save the original eflags values to ecx
|
||||
"\n\txor $0x00200000, %%eax" // toggle bit 21
|
||||
"\n\tpush %%eax" // store toggled eflags to stack
|
||||
"\n\tpopf" // load eflags from stack
|
||||
"\n\tpushf" // save updated eflags to stack
|
||||
"\n\tpop %%eax" // load from stack
|
||||
"\n\txor %%edx, %%edx" // clear edx for defaulting no mmx
|
||||
"\n\tcmp %%ecx, %%eax" // compare to original eflags values
|
||||
"\n\tjz end" // jumps to 'end' if cpuid not present
|
||||
|
||||
// cpuid instruction available, test for presence of mmx instructions
|
||||
|
||||
"\n\tmovl $1, %%eax"
|
||||
"\n\tcpuid"
|
||||
// movl $0x00800000, %edx // force enable MMX
|
||||
"\n\ttest $0x00800000, %%edx"
|
||||
"\n\tjz end" // branch if MMX not available
|
||||
|
||||
"\n\tor $0x01, %%esi" // otherwise add MMX support bit
|
||||
|
||||
"\n\ttest $0x02000000, %%edx"
|
||||
"\n\tjz test3DNow" // branch if SSE not available
|
||||
|
||||
"\n\tor $0x08, %%esi" // otherwise add SSE support bit
|
||||
|
||||
"\n\ttest3DNow:"
|
||||
// test for precense of AMD extensions
|
||||
"\n\tmov $0x80000000, %%eax"
|
||||
"\n\tcpuid"
|
||||
"\n\tcmp $0x80000000, %%eax"
|
||||
"\n\tjbe end" // branch if no AMD extensions detected
|
||||
|
||||
// test for precense of 3DNow! extension
|
||||
"\n\tmov $0x80000001, %%eax"
|
||||
"\n\tcpuid"
|
||||
"\n\ttest $0x80000000, %%edx"
|
||||
"\n\tjz end" // branch if 3DNow! not detected
|
||||
|
||||
"\n\tor $0x02, %%esi" // otherwise add 3DNow support bit
|
||||
|
||||
"\n\tend:"
|
||||
|
||||
"\n\tmov %%esi, %0"
|
||||
|
||||
: "=r" (res)
|
||||
: /* no inputs */
|
||||
: "%edx", "%eax", "%ecx", "%esi" );
|
||||
|
||||
return res & ~_dwDisabledISA;
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Win32 version of the x86 CPU detect routine.
|
||||
///
|
||||
/// This file is to be compiled in Windows platform with Microsoft Visual C++
|
||||
/// Compiler. Please see 'cpu_detect_x86_gcc.cpp' for the gcc compiler version
|
||||
/// for all GNU platforms.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.10 $
|
||||
//
|
||||
// $Id: cpu_detect_x86_win.cpp,v 1.10 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "cpu_detect.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#error wrong platform - this source code file is exclusively for Win32 platform
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// processor instructions extension detection routines
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Flag variable indicating whick ISA extensions are disabled (for debugging)
|
||||
static uint _dwDisabledISA = 0x00; // 0xffffffff; //<- use this to disable all extensions
|
||||
|
||||
|
||||
// Disables given set of instruction extensions. See SUPPORT_... defines.
|
||||
void disableExtensions(uint dwDisableMask)
|
||||
{
|
||||
_dwDisabledISA = dwDisableMask;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Checks which instruction set extensions are supported by the CPU.
|
||||
uint detectCPUextensions(void)
|
||||
{
|
||||
uint res = 0;
|
||||
|
||||
if (_dwDisabledISA == 0xffffffff) return 0;
|
||||
|
||||
_asm
|
||||
{
|
||||
; check if 'cpuid' instructions is available by toggling eflags bit 21
|
||||
;
|
||||
xor esi, esi ; clear esi = result register
|
||||
|
||||
pushfd ; save eflags to stack
|
||||
pop eax ; load eax from stack (with eflags)
|
||||
mov ecx, eax ; save the original eflags values to ecx
|
||||
xor eax, 0x00200000 ; toggle bit 21
|
||||
push eax ; store toggled eflags to stack
|
||||
popfd ; load eflags from stack
|
||||
pushfd ; save updated eflags to stack
|
||||
pop eax ; load from stack
|
||||
xor edx, edx ; clear edx for defaulting no mmx
|
||||
cmp eax, ecx ; compare to original eflags values
|
||||
jz end ; jumps to 'end' if cpuid not present
|
||||
|
||||
; cpuid instruction available, test for presence of mmx instructions
|
||||
mov eax, 1
|
||||
cpuid
|
||||
test edx, 0x00800000
|
||||
jz end ; branch if MMX not available
|
||||
|
||||
or esi, SUPPORT_MMX ; otherwise add MMX support bit
|
||||
|
||||
test edx, 0x02000000
|
||||
jz test3DNow ; branch if SSE not available
|
||||
|
||||
or esi, SUPPORT_SSE ; otherwise add SSE support bit
|
||||
|
||||
test3DNow:
|
||||
; test for precense of AMD extensions
|
||||
mov eax, 0x80000000
|
||||
cpuid
|
||||
cmp eax, 0x80000000
|
||||
jbe end ; branch if no AMD extensions detected
|
||||
|
||||
; test for precense of 3DNow! extension
|
||||
mov eax, 0x80000001
|
||||
cpuid
|
||||
test edx, 0x80000000
|
||||
jz end ; branch if 3DNow! not detected
|
||||
|
||||
or esi, SUPPORT_3DNOW ; otherwise add 3DNow support bit
|
||||
|
||||
end:
|
||||
|
||||
mov res, esi
|
||||
}
|
||||
|
||||
return res & ~_dwDisabledISA;
|
||||
}
|
|
@ -0,0 +1,305 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// MMX optimized routines. All MMX optimized functions have been gathered into
|
||||
/// this single source code file, regardless to their class or original source
|
||||
/// code file, in order to ease porting the library to other compiler and
|
||||
/// processor platforms.
|
||||
///
|
||||
/// The MMX-optimizations are programmed using MMX compiler intrinsics that
|
||||
/// are supported both by Microsoft Visual C++ and GCC compilers, so this file
|
||||
/// should compile with both toolsets.
|
||||
///
|
||||
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
|
||||
/// 6.0 processor pack" update to support compiler intrinsic syntax. The update
|
||||
/// is available for download at Microsoft Developers Network, see here:
|
||||
/// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/06 18:52:43 $
|
||||
// File revision : $Revision: 1.1 $
|
||||
//
|
||||
// $Id: mmx_optimized.cpp,v 1.1 2006/02/06 18:52:43 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "STTypes.h"
|
||||
|
||||
#ifdef ALLOW_MMX
|
||||
// MMX routines available only with integer sample type
|
||||
|
||||
#if !(_WIN32 || __i386__ || __x86_64__)
|
||||
#error "wrong platform - this source code file is exclusively for x86 platforms"
|
||||
#endif
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of MMX optimized functions of class 'TDStretchMMX'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "TDStretch.h"
|
||||
#include <mmintrin.h>
|
||||
#include <limits.h>
|
||||
|
||||
|
||||
// Calculates cross correlation of two buffers
|
||||
long TDStretchMMX::calcCrossCorrStereo(const short *pV1, const short *pV2) const
|
||||
{
|
||||
const __m64 *pVec1, *pVec2;
|
||||
__m64 shifter;
|
||||
__m64 accu;
|
||||
long corr;
|
||||
uint i;
|
||||
|
||||
pVec1 = (__m64*)pV1;
|
||||
pVec2 = (__m64*)pV2;
|
||||
|
||||
shifter = _m_from_int(overlapDividerBits);
|
||||
accu = _mm_setzero_si64();
|
||||
|
||||
// Process 4 parallel sets of 2 * stereo samples each during each
|
||||
// round to improve CPU-level parallellization.
|
||||
for (i = 0; i < overlapLength / 8; i ++)
|
||||
{
|
||||
__m64 temp;
|
||||
|
||||
// dictionary of instructions:
|
||||
// _m_pmaddwd : 4*16bit multiply-add, resulting two 32bits = [a0*b0+a1*b1 ; a2*b2+a3*b3]
|
||||
// _mm_add_pi32 : 2*32bit add
|
||||
// _m_psrad : 32bit right-shift
|
||||
|
||||
temp = _mm_add_pi32(_mm_madd_pi16(pVec1[0], pVec2[0]),
|
||||
_mm_madd_pi16(pVec1[1], pVec2[1]));
|
||||
accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));
|
||||
|
||||
temp = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec2[2]),
|
||||
_mm_madd_pi16(pVec1[3], pVec2[3]));
|
||||
accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));
|
||||
|
||||
pVec1 += 4;
|
||||
pVec2 += 4;
|
||||
}
|
||||
|
||||
// copy hi-dword of mm0 to lo-dword of mm1, then sum mmo+mm1
|
||||
// and finally store the result into the variable "corr"
|
||||
|
||||
accu = _mm_add_pi32(accu, _mm_srli_si64(accu, 32));
|
||||
corr = _m_to_int(accu);
|
||||
|
||||
// Clear MMS state
|
||||
_m_empty();
|
||||
|
||||
return corr;
|
||||
// Note: Warning about the missing EMMS instruction is harmless
|
||||
// as it'll be called elsewhere.
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TDStretchMMX::clearCrossCorrState()
|
||||
{
|
||||
// Clear MMS state
|
||||
_m_empty();
|
||||
//_asm EMMS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MMX-optimized version of the function overlapStereo
|
||||
void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
||||
{
|
||||
const __m64 *pVinput, *pVMidBuf;
|
||||
__m64 *pVdest;
|
||||
__m64 mix1, mix2, adder, shifter;
|
||||
uint i;
|
||||
|
||||
pVinput = (const __m64*)input;
|
||||
pVMidBuf = (const __m64*)pMidBuffer;
|
||||
pVdest = (__m64*)output;
|
||||
|
||||
// mix1 = mixer values for 1st stereo sample
|
||||
// mix1 = mixer values for 2nd stereo sample
|
||||
// adder = adder for updating mixer values after each round
|
||||
|
||||
mix1 = _mm_set_pi16(0, overlapLength, 0, overlapLength);
|
||||
adder = _mm_set_pi16(1, -1, 1, -1);
|
||||
mix2 = _mm_add_pi16(mix1, adder);
|
||||
adder = _mm_add_pi16(adder, adder);
|
||||
|
||||
shifter = _m_from_int(overlapDividerBits);
|
||||
|
||||
for (i = 0; i < overlapLength / 4; i ++)
|
||||
{
|
||||
__m64 temp1, temp2;
|
||||
|
||||
// load & shuffle data so that input & mixbuffer data samples are paired
|
||||
temp1 = _mm_unpacklo_pi16(pVMidBuf[0], pVinput[0]); // = i0l m0l i0r m0r
|
||||
temp2 = _mm_unpackhi_pi16(pVMidBuf[0], pVinput[0]); // = i1l m1l i1r m1r
|
||||
|
||||
// temp = (temp .* mix) >> shifter
|
||||
temp1 = _mm_sra_pi32(_mm_madd_pi16(temp1, mix1), shifter);
|
||||
temp2 = _mm_sra_pi32(_mm_madd_pi16(temp2, mix2), shifter);
|
||||
pVdest[0] = _mm_packs_pi32(temp1, temp2); // pack 2*2*32bit => 4*16bit
|
||||
|
||||
// update mix += adder
|
||||
mix1 = _mm_add_pi16(mix1, adder);
|
||||
mix2 = _mm_add_pi16(mix2, adder);
|
||||
|
||||
// --- second round begins here ---
|
||||
|
||||
// load & shuffle data so that input & mixbuffer data samples are paired
|
||||
temp1 = _mm_unpacklo_pi16(pVMidBuf[1], pVinput[1]); // = i2l m2l i2r m2r
|
||||
temp2 = _mm_unpackhi_pi16(pVMidBuf[1], pVinput[1]); // = i3l m3l i3r m3r
|
||||
|
||||
// temp = (temp .* mix) >> shifter
|
||||
temp1 = _mm_sra_pi32(_mm_madd_pi16(temp1, mix1), shifter);
|
||||
temp2 = _mm_sra_pi32(_mm_madd_pi16(temp2, mix2), shifter);
|
||||
pVdest[1] = _mm_packs_pi32(temp1, temp2); // pack 2*2*32bit => 4*16bit
|
||||
|
||||
// update mix += adder
|
||||
mix1 = _mm_add_pi16(mix1, adder);
|
||||
mix2 = _mm_add_pi16(mix2, adder);
|
||||
|
||||
pVinput += 2;
|
||||
pVMidBuf += 2;
|
||||
pVdest += 2;
|
||||
}
|
||||
|
||||
_m_empty(); // clear MMS state
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of MMX optimized functions of class 'FIRFilter'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "FIRFilter.h"
|
||||
|
||||
|
||||
FIRFilterMMX::FIRFilterMMX() : FIRFilter()
|
||||
{
|
||||
filterCoeffsUnalign = NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilterMMX::~FIRFilterMMX()
|
||||
{
|
||||
delete[] filterCoeffsUnalign;
|
||||
}
|
||||
|
||||
|
||||
// (overloaded) Calculates filter coefficients for MMX routine
|
||||
void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor)
|
||||
{
|
||||
uint i;
|
||||
FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
|
||||
|
||||
// Ensure that filter coeffs array is aligned to 16-byte boundary
|
||||
delete[] filterCoeffsUnalign;
|
||||
filterCoeffsUnalign = new short[2 * newLength + 8];
|
||||
filterCoeffsAlign = (short *)(((ulongptr)filterCoeffsUnalign + 15) & -16);
|
||||
|
||||
// rearrange the filter coefficients for mmx routines
|
||||
for (i = 0;i < length; i += 4)
|
||||
{
|
||||
filterCoeffsAlign[2 * i + 0] = coeffs[i + 0];
|
||||
filterCoeffsAlign[2 * i + 1] = coeffs[i + 2];
|
||||
filterCoeffsAlign[2 * i + 2] = coeffs[i + 0];
|
||||
filterCoeffsAlign[2 * i + 3] = coeffs[i + 2];
|
||||
|
||||
filterCoeffsAlign[2 * i + 4] = coeffs[i + 1];
|
||||
filterCoeffsAlign[2 * i + 5] = coeffs[i + 3];
|
||||
filterCoeffsAlign[2 * i + 6] = coeffs[i + 1];
|
||||
filterCoeffsAlign[2 * i + 7] = coeffs[i + 3];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// mmx-optimized version of the filter routine for stereo sound
|
||||
uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, const uint numSamples) const
|
||||
{
|
||||
// Create stack copies of the needed member variables for asm routines :
|
||||
uint i, j;
|
||||
__m64 *pVdest = (__m64*)dest;
|
||||
|
||||
if (length < 2) return 0;
|
||||
|
||||
for (i = 0; i < numSamples / 2; i ++)
|
||||
{
|
||||
__m64 accu1;
|
||||
__m64 accu2;
|
||||
const __m64 *pVsrc = (const __m64*)src;
|
||||
const __m64 *pVfilter = (const __m64*)filterCoeffsAlign;
|
||||
|
||||
accu1 = accu2 = _mm_setzero_si64();
|
||||
for (j = 0; j < lengthDiv8 * 2; j ++)
|
||||
{
|
||||
__m64 temp1, temp2;
|
||||
|
||||
temp1 = _mm_unpacklo_pi16(pVsrc[0], pVsrc[1]); // = l2 l0 r2 r0
|
||||
temp2 = _mm_unpackhi_pi16(pVsrc[0], pVsrc[1]); // = l3 l1 r3 r1
|
||||
|
||||
accu1 = _mm_add_pi32(accu1, _mm_madd_pi16(temp1, pVfilter[0])); // += l2*f2+l0*f0 r2*f2+r0*f0
|
||||
accu1 = _mm_add_pi32(accu1, _mm_madd_pi16(temp2, pVfilter[1])); // += l3*f3+l1*f1 r3*f3+r1*f1
|
||||
|
||||
temp1 = _mm_unpacklo_pi16(pVsrc[1], pVsrc[2]); // = l4 l2 r4 r2
|
||||
|
||||
accu2 = _mm_add_pi32(accu2, _mm_madd_pi16(temp2, pVfilter[0])); // += l3*f2+l1*f0 r3*f2+r1*f0
|
||||
accu2 = _mm_add_pi32(accu2, _mm_madd_pi16(temp1, pVfilter[1])); // += l4*f3+l2*f1 r4*f3+r2*f1
|
||||
|
||||
// accu1 += l2*f2+l0*f0 r2*f2+r0*f0
|
||||
// += l3*f3+l1*f1 r3*f3+r1*f1
|
||||
|
||||
// accu2 += l3*f2+l1*f0 r3*f2+r1*f0
|
||||
// l4*f3+l2*f1 r4*f3+r2*f1
|
||||
|
||||
pVfilter += 2;
|
||||
pVsrc += 2;
|
||||
}
|
||||
// accu >>= resultDivFactor
|
||||
accu1 = _mm_srai_pi32(accu1, resultDivFactor);
|
||||
accu2 = _mm_srai_pi32(accu2, resultDivFactor);
|
||||
|
||||
// pack 2*2*32bits => 4*16 bits
|
||||
pVdest[0] = _mm_packs_pi32(accu1, accu2);
|
||||
src += 4;
|
||||
pVdest ++;
|
||||
}
|
||||
|
||||
_m_empty(); // clear emms state
|
||||
|
||||
return (numSamples & 0xfffffffe) - length;
|
||||
}
|
||||
|
||||
#endif // ALLOW_MMX
|
|
@ -0,0 +1,484 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// SSE optimized routines for Pentium-III, Athlon-XP and later CPUs. All SSE
|
||||
/// optimized functions have been gathered into this single source
|
||||
/// code file, regardless to their class or original source code file, in order
|
||||
/// to ease porting the library to other compiler and processor platforms.
|
||||
///
|
||||
/// The SSE-optimizations are programmed using SSE compiler intrinsics that
|
||||
/// are supported both by Microsoft Visual C++ and GCC compilers, so this file
|
||||
/// should compile with both toolsets.
|
||||
///
|
||||
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
|
||||
/// 6.0 processor pack" update to support SSE instruction set. The update is
|
||||
/// available for download at Microsoft Developers Network, see here:
|
||||
/// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx
|
||||
///
|
||||
/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and
|
||||
/// perform a search with keywords "processor pack".
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Last changed : $Date: 2006/02/05 16:44:06 $
|
||||
// File revision : $Revision: 1.2 $
|
||||
//
|
||||
// $Id: sse_optimized.cpp,v 1.2 2006/02/05 16:44:06 Olli Exp $
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "cpu_detect.h"
|
||||
#include "STTypes.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
#ifdef ALLOW_SSE
|
||||
|
||||
// SSE routines available only with float sample type
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of SSE optimized functions of class 'TDStretchSSE'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "TDStretch.h"
|
||||
#include <xmmintrin.h>
|
||||
|
||||
// Calculates cross correlation of two buffers
|
||||
double TDStretchSSE::calcCrossCorrStereo(const float *pV1, const float *pV2) const
|
||||
{
|
||||
uint i;
|
||||
__m128 vSum, *pVec2;
|
||||
|
||||
// Note. It means a major slow-down if the routine needs to tolerate
|
||||
// unaligned __m128 memory accesses. It's way faster if we can skip
|
||||
// unaligned slots and use _mm_load_ps instruction instead of _mm_loadu_ps.
|
||||
// This can mean up to ~ 10-fold difference (incl. part of which is
|
||||
// due to skipping every second round for stereo sound though).
|
||||
//
|
||||
// Compile-time define ALLOW_NONEXACT_SIMD_OPTIMIZATION is provided
|
||||
// for choosing if this little cheating is allowed.
|
||||
|
||||
#ifdef ALLOW_NONEXACT_SIMD_OPTIMIZATION
|
||||
// Little cheating allowed, return valid correlation only for
|
||||
// aligned locations, meaning every second round for stereo sound.
|
||||
|
||||
#define _MM_LOAD _mm_load_ps
|
||||
|
||||
if (((ulong)pV1) & 15) return -1e50; // skip unaligned locations
|
||||
|
||||
#else
|
||||
// No cheating allowed, use unaligned load & take the resulting
|
||||
// performance hit.
|
||||
#define _MM_LOAD _mm_loadu_ps
|
||||
#endif
|
||||
|
||||
// ensure overlapLength is divisible by 8
|
||||
assert((overlapLength % 8) == 0);
|
||||
|
||||
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
|
||||
// Note: pV2 _must_ be aligned to 16-bit boundary, pV1 need not.
|
||||
pVec2 = (__m128*)pV2;
|
||||
vSum = _mm_setzero_ps();
|
||||
|
||||
// Unroll the loop by factor of 4 * 4 operations
|
||||
for (i = 0; i < overlapLength / 8; i ++)
|
||||
{
|
||||
// vSum += pV1[0..3] * pV2[0..3]
|
||||
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1),pVec2[0]));
|
||||
|
||||
// vSum += pV1[4..7] * pV2[4..7]
|
||||
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1 + 4), pVec2[1]));
|
||||
|
||||
// vSum += pV1[8..11] * pV2[8..11]
|
||||
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1 + 8), pVec2[2]));
|
||||
|
||||
// vSum += pV1[12..15] * pV2[12..15]
|
||||
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1 + 12), pVec2[3]));
|
||||
|
||||
pV1 += 16;
|
||||
pVec2 += 4;
|
||||
}
|
||||
|
||||
// return value = vSum[0] + vSum[1] + vSum[2] + vSum[3]
|
||||
float *pvSum = (float*)&vSum;
|
||||
return (double)(pvSum[0] + pvSum[1] + pvSum[2] + pvSum[3]);
|
||||
|
||||
/* This is approximately corresponding routine in C-language:
|
||||
double corr;
|
||||
uint i;
|
||||
|
||||
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
|
||||
corr = 0.0;
|
||||
for (i = 0; i < overlapLength / 8; i ++)
|
||||
{
|
||||
corr += pV1[0] * pV2[0] +
|
||||
pV1[1] * pV2[1] +
|
||||
pV1[2] * pV2[2] +
|
||||
pV1[3] * pV2[3] +
|
||||
pV1[4] * pV2[4] +
|
||||
pV1[5] * pV2[5] +
|
||||
pV1[6] * pV2[6] +
|
||||
pV1[7] * pV2[7] +
|
||||
pV1[8] * pV2[8] +
|
||||
pV1[9] * pV2[9] +
|
||||
pV1[10] * pV2[10] +
|
||||
pV1[11] * pV2[11] +
|
||||
pV1[12] * pV2[12] +
|
||||
pV1[13] * pV2[13] +
|
||||
pV1[14] * pV2[14] +
|
||||
pV1[15] * pV2[15];
|
||||
|
||||
pV1 += 16;
|
||||
pV2 += 16;
|
||||
}
|
||||
*/
|
||||
|
||||
/* This is corresponding routine in assembler. This may be teeny-weeny bit faster
|
||||
than intrinsic version, but more difficult to maintain & get compiled on multiple
|
||||
platforms.
|
||||
|
||||
uint overlapLengthLocal = overlapLength;
|
||||
float corr;
|
||||
|
||||
_asm
|
||||
{
|
||||
// Very important note: data in 'pV2' _must_ be aligned to
|
||||
// 16-byte boundary!
|
||||
|
||||
// give prefetch hints to CPU of what data are to be needed soonish
|
||||
// give more aggressive hints on pV1 as that changes while pV2 stays
|
||||
// same between runs
|
||||
prefetcht0 [pV1]
|
||||
prefetcht0 [pV2]
|
||||
prefetcht0 [pV1 + 32]
|
||||
|
||||
mov eax, dword ptr pV1
|
||||
mov ebx, dword ptr pV2
|
||||
|
||||
xorps xmm0, xmm0
|
||||
|
||||
mov ecx, overlapLengthLocal
|
||||
shr ecx, 3 // div by eight
|
||||
|
||||
loop1:
|
||||
prefetcht0 [eax + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetcht0 [ebx + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
movups xmm1, [eax]
|
||||
mulps xmm1, [ebx]
|
||||
addps xmm0, xmm1
|
||||
|
||||
movups xmm2, [eax + 16]
|
||||
mulps xmm2, [ebx + 16]
|
||||
addps xmm0, xmm2
|
||||
|
||||
prefetcht0 [eax + 96] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetcht0 [ebx + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
movups xmm3, [eax + 32]
|
||||
mulps xmm3, [ebx + 32]
|
||||
addps xmm0, xmm3
|
||||
|
||||
movups xmm4, [eax + 48]
|
||||
mulps xmm4, [ebx + 48]
|
||||
addps xmm0, xmm4
|
||||
|
||||
add eax, 64
|
||||
add ebx, 64
|
||||
|
||||
dec ecx
|
||||
jnz loop1
|
||||
|
||||
// add the four floats of xmm0 together and return the result.
|
||||
|
||||
movhlps xmm1, xmm0 // move 3 & 4 of xmm0 to 1 & 2 of xmm1
|
||||
addps xmm1, xmm0
|
||||
movaps xmm2, xmm1
|
||||
shufps xmm2, xmm2, 0x01 // move 2 of xmm2 as 1 of xmm2
|
||||
addss xmm2, xmm1
|
||||
movss corr, xmm2
|
||||
}
|
||||
|
||||
return (double)corr;
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation of SSE optimized functions of class 'FIRFilter'
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "FIRFilter.h"
|
||||
|
||||
FIRFilterSSE::FIRFilterSSE() : FIRFilter()
|
||||
{
|
||||
filterCoeffsUnalign = NULL;
|
||||
}
|
||||
|
||||
|
||||
FIRFilterSSE::~FIRFilterSSE()
|
||||
{
|
||||
delete[] filterCoeffsUnalign;
|
||||
}
|
||||
|
||||
|
||||
// (overloaded) Calculates filter coefficients for SSE routine
|
||||
void FIRFilterSSE::setCoefficients(const float *coeffs, uint newLength, uint uResultDivFactor)
|
||||
{
|
||||
uint i;
|
||||
float fDivider;
|
||||
|
||||
FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);
|
||||
|
||||
// Scale the filter coefficients so that it won't be necessary to scale the filtering result
|
||||
// also rearrange coefficients suitably for 3DNow!
|
||||
// Ensure that filter coeffs array is aligned to 16-byte boundary
|
||||
delete[] filterCoeffsUnalign;
|
||||
filterCoeffsUnalign = new float[2 * newLength + 4];
|
||||
filterCoeffsAlign = (float *)(((unsigned long)filterCoeffsUnalign + 15) & -16);
|
||||
|
||||
fDivider = (float)resultDivider;
|
||||
|
||||
// rearrange the filter coefficients for mmx routines
|
||||
for (i = 0; i < newLength; i ++)
|
||||
{
|
||||
filterCoeffsAlign[2 * i + 0] =
|
||||
filterCoeffsAlign[2 * i + 1] = coeffs[i + 0] / fDivider;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// SSE-optimized version of the filter routine for stereo sound
|
||||
uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint numSamples) const
|
||||
{
|
||||
int count = (numSamples - length) & -2;
|
||||
int j;
|
||||
|
||||
assert(count % 2 == 0);
|
||||
|
||||
if (count < 2) return 0;
|
||||
|
||||
assert((length % 8) == 0);
|
||||
assert(((unsigned long)filterCoeffsAlign) % 16 == 0);
|
||||
|
||||
// filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'
|
||||
for (j = 0; j < count; j += 2)
|
||||
{
|
||||
const float *pSrc;
|
||||
const __m128 *pFil;
|
||||
__m128 sum1, sum2;
|
||||
uint i;
|
||||
|
||||
pSrc = source; // source audio data
|
||||
pFil = (__m128*)filterCoeffsAlign; // filter coefficients. NOTE: Assumes coefficients
|
||||
// are aligned to 16-byte boundary
|
||||
sum1 = sum2 = _mm_setzero_ps();
|
||||
|
||||
for (i = 0; i < length / 8; i ++)
|
||||
{
|
||||
// Unroll loop for efficiency & calculate filter for 2*2 stereo samples
|
||||
// at each pass
|
||||
|
||||
// sum1 is accu for 2*2 filtered stereo sound data at the primary sound data offset
|
||||
// sum2 is accu for 2*2 filtered stereo sound data for the next sound sample offset.
|
||||
|
||||
sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc) , pFil[0]));
|
||||
sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 2), pFil[0]));
|
||||
|
||||
sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 4), pFil[1]));
|
||||
sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 6), pFil[1]));
|
||||
|
||||
sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 8) , pFil[2]));
|
||||
sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 10), pFil[2]));
|
||||
|
||||
sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_loadu_ps(pSrc + 12), pFil[3]));
|
||||
sum2 = _mm_add_ps(sum2, _mm_mul_ps(_mm_loadu_ps(pSrc + 14), pFil[3]));
|
||||
|
||||
pSrc += 16;
|
||||
pFil += 4;
|
||||
}
|
||||
|
||||
// Now sum1 and sum2 both have a filtered 2-channel sample each, but we still need
|
||||
// to sum the two hi- and lo-floats of these registers together.
|
||||
|
||||
// post-shuffle & add the filtered values and store to dest.
|
||||
_mm_storeu_ps(dest, _mm_add_ps(
|
||||
_mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(1,0,3,2)), // s2_1 s2_0 s1_3 s1_2
|
||||
_mm_shuffle_ps(sum1, sum2, _MM_SHUFFLE(3,2,1,0)) // s2_3 s2_2 s1_1 s1_0
|
||||
));
|
||||
source += 4;
|
||||
dest += 4;
|
||||
}
|
||||
|
||||
// Ideas for further improvement:
|
||||
// 1. If it could be guaranteed that 'source' were always aligned to 16-byte
|
||||
// boundary, a faster aligned '_mm_load_ps' instruction could be used.
|
||||
// 2. If it could be guaranteed that 'dest' were always aligned to 16-byte
|
||||
// boundary, a faster '_mm_store_ps' instruction could be used.
|
||||
|
||||
return (uint)count;
|
||||
|
||||
/* original routine in C-language. please notice the C-version has differently
|
||||
organized coefficients though.
|
||||
double suml1, suml2;
|
||||
double sumr1, sumr2;
|
||||
uint i, j;
|
||||
|
||||
for (j = 0; j < count; j += 2)
|
||||
{
|
||||
const float *ptr;
|
||||
const float *pFil;
|
||||
|
||||
suml1 = sumr1 = 0.0;
|
||||
suml2 = sumr2 = 0.0;
|
||||
ptr = src;
|
||||
pFil = filterCoeffs;
|
||||
for (i = 0; i < lengthLocal; i ++)
|
||||
{
|
||||
// unroll loop for efficiency.
|
||||
|
||||
suml1 += ptr[0] * pFil[0] +
|
||||
ptr[2] * pFil[2] +
|
||||
ptr[4] * pFil[4] +
|
||||
ptr[6] * pFil[6];
|
||||
|
||||
sumr1 += ptr[1] * pFil[1] +
|
||||
ptr[3] * pFil[3] +
|
||||
ptr[5] * pFil[5] +
|
||||
ptr[7] * pFil[7];
|
||||
|
||||
suml2 += ptr[8] * pFil[0] +
|
||||
ptr[10] * pFil[2] +
|
||||
ptr[12] * pFil[4] +
|
||||
ptr[14] * pFil[6];
|
||||
|
||||
sumr2 += ptr[9] * pFil[1] +
|
||||
ptr[11] * pFil[3] +
|
||||
ptr[13] * pFil[5] +
|
||||
ptr[15] * pFil[7];
|
||||
|
||||
ptr += 16;
|
||||
pFil += 8;
|
||||
}
|
||||
dest[0] = (float)suml1;
|
||||
dest[1] = (float)sumr1;
|
||||
dest[2] = (float)suml2;
|
||||
dest[3] = (float)sumr2;
|
||||
|
||||
src += 4;
|
||||
dest += 4;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/* Similar routine in assembly, again obsoleted due to maintainability
|
||||
_asm
|
||||
{
|
||||
// Very important note: data in 'src' _must_ be aligned to
|
||||
// 16-byte boundary!
|
||||
mov edx, count
|
||||
mov ebx, dword ptr src
|
||||
mov eax, dword ptr dest
|
||||
shr edx, 1
|
||||
|
||||
loop1:
|
||||
// "outer loop" : during each round 2*2 output samples are calculated
|
||||
|
||||
// give prefetch hints to CPU of what data are to be needed soonish
|
||||
prefetcht0 [ebx]
|
||||
prefetcht0 [filterCoeffsLocal]
|
||||
|
||||
mov esi, ebx
|
||||
mov edi, filterCoeffsLocal
|
||||
xorps xmm0, xmm0
|
||||
xorps xmm1, xmm1
|
||||
mov ecx, lengthLocal
|
||||
|
||||
loop2:
|
||||
// "inner loop" : during each round eight FIR filter taps are evaluated for 2*2 samples
|
||||
prefetcht0 [esi + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetcht0 [edi + 32] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
movups xmm2, [esi] // possibly unaligned load
|
||||
movups xmm3, [esi + 8] // possibly unaligned load
|
||||
mulps xmm2, [edi]
|
||||
mulps xmm3, [edi]
|
||||
addps xmm0, xmm2
|
||||
addps xmm1, xmm3
|
||||
|
||||
movups xmm4, [esi + 16] // possibly unaligned load
|
||||
movups xmm5, [esi + 24] // possibly unaligned load
|
||||
mulps xmm4, [edi + 16]
|
||||
mulps xmm5, [edi + 16]
|
||||
addps xmm0, xmm4
|
||||
addps xmm1, xmm5
|
||||
|
||||
prefetcht0 [esi + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
prefetcht0 [edi + 64] // give a prefetch hint to CPU what data are to be needed soonish
|
||||
|
||||
movups xmm6, [esi + 32] // possibly unaligned load
|
||||
movups xmm7, [esi + 40] // possibly unaligned load
|
||||
mulps xmm6, [edi + 32]
|
||||
mulps xmm7, [edi + 32]
|
||||
addps xmm0, xmm6
|
||||
addps xmm1, xmm7
|
||||
|
||||
movups xmm4, [esi + 48] // possibly unaligned load
|
||||
movups xmm5, [esi + 56] // possibly unaligned load
|
||||
mulps xmm4, [edi + 48]
|
||||
mulps xmm5, [edi + 48]
|
||||
addps xmm0, xmm4
|
||||
addps xmm1, xmm5
|
||||
|
||||
add esi, 64
|
||||
add edi, 64
|
||||
dec ecx
|
||||
jnz loop2
|
||||
|
||||
// Now xmm0 and xmm1 both have a filtered 2-channel sample each, but we still need
|
||||
// to sum the two hi- and lo-floats of these registers together.
|
||||
|
||||
movhlps xmm2, xmm0 // xmm2 = xmm2_3 xmm2_2 xmm0_3 xmm0_2
|
||||
movlhps xmm2, xmm1 // xmm2 = xmm1_1 xmm1_0 xmm0_3 xmm0_2
|
||||
shufps xmm0, xmm1, 0xe4 // xmm0 = xmm1_3 xmm1_2 xmm0_1 xmm0_0
|
||||
addps xmm0, xmm2
|
||||
|
||||
movaps [eax], xmm0
|
||||
add ebx, 16
|
||||
add eax, 16
|
||||
|
||||
dec edx
|
||||
jnz loop1
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
#endif // ALLOW_SSE
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* a52.h
|
||||
* Copyright (C) 2000-2002 Michel Lespinasse <walken@zoy.org>
|
||||
* Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
|
||||
*
|
||||
* This file is part of a52dec, a free ATSC A-52 stream decoder.
|
||||
* See http://liba52.sourceforge.net/ for updates.
|
||||
*
|
||||
* a52dec 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.
|
||||
*
|
||||
* a52dec is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef A52_H
|
||||
#define A52_H
|
||||
|
||||
#ifndef LIBA52_DOUBLE
|
||||
typedef float sample_t;
|
||||
#else
|
||||
typedef double sample_t;
|
||||
#endif
|
||||
|
||||
typedef struct a52_state_s a52_state_t;
|
||||
|
||||
#define A52_CHANNEL 0
|
||||
#define A52_MONO 1
|
||||
#define A52_STEREO 2
|
||||
#define A52_3F 3
|
||||
#define A52_2F1R 4
|
||||
#define A52_3F1R 5
|
||||
#define A52_2F2R 6
|
||||
#define A52_3F2R 7
|
||||
#define A52_CHANNEL1 8
|
||||
#define A52_CHANNEL2 9
|
||||
#define A52_DOLBY 10
|
||||
#define A52_CHANNEL_MASK 15
|
||||
|
||||
#define A52_LFE 16
|
||||
#define A52_ADJUST_LEVEL 32
|
||||
|
||||
a52_state_t * a52_init (uint32_t mm_accel);
|
||||
sample_t * a52_samples (a52_state_t * state);
|
||||
int a52_syncinfo (uint8_t * buf, int * flags,
|
||||
int * sample_rate, int * bit_rate);
|
||||
int a52_frame (a52_state_t * state, uint8_t * buf, int * flags,
|
||||
sample_t * level, sample_t bias);
|
||||
void a52_dynrng (a52_state_t * state,
|
||||
sample_t (* call) (sample_t, void *), void * data);
|
||||
int a52_block (a52_state_t * state);
|
||||
void a52_free (a52_state_t * state);
|
||||
|
||||
#endif /* A52_H */
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* a52_internal.h
|
||||
* Copyright (C) 2000-2002 Michel Lespinasse <walken@zoy.org>
|
||||
* Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
|
||||
*
|
||||
* This file is part of a52dec, a free ATSC A-52 stream decoder.
|
||||
* See http://liba52.sourceforge.net/ for updates.
|
||||
*
|
||||
* a52dec 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.
|
||||
*
|
||||
* a52dec is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
uint8_t bai; /* fine SNR offset, fast gain */
|
||||
uint8_t deltbae; /* delta bit allocation exists */
|
||||
int8_t deltba[50]; /* per-band delta bit allocation */
|
||||
} ba_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t exp[256]; /* decoded channel exponents */
|
||||
int8_t bap[256]; /* derived channel bit allocation */
|
||||
} expbap_t;
|
||||
|
||||
struct a52_state_s {
|
||||
uint8_t fscod; /* sample rate */
|
||||
uint8_t halfrate; /* halfrate factor */
|
||||
uint8_t acmod; /* coded channels */
|
||||
uint8_t lfeon; /* coded lfe channel */
|
||||
sample_t clev; /* centre channel mix level */
|
||||
sample_t slev; /* surround channels mix level */
|
||||
|
||||
int output; /* type of output */
|
||||
sample_t level; /* output level */
|
||||
sample_t bias; /* output bias */
|
||||
|
||||
int dynrnge; /* apply dynamic range */
|
||||
sample_t dynrng; /* dynamic range */
|
||||
void * dynrngdata; /* dynamic range callback funtion and data */
|
||||
sample_t (* dynrngcall) (sample_t range, void * dynrngdata);
|
||||
|
||||
uint8_t chincpl; /* channel coupled */
|
||||
uint8_t phsflginu; /* phase flags in use (stereo only) */
|
||||
uint8_t cplstrtmant; /* coupling channel start mantissa */
|
||||
uint8_t cplendmant; /* coupling channel end mantissa */
|
||||
uint32_t cplbndstrc; /* coupling band structure */
|
||||
sample_t cplco[5][18]; /* coupling coordinates */
|
||||
|
||||
/* derived information */
|
||||
uint8_t cplstrtbnd; /* coupling start band (for bit allocation) */
|
||||
uint8_t ncplbnd; /* number of coupling bands */
|
||||
|
||||
uint8_t rematflg; /* stereo rematrixing */
|
||||
|
||||
uint8_t endmant[5]; /* channel end mantissa */
|
||||
|
||||
uint16_t bai; /* bit allocation information */
|
||||
|
||||
uint32_t * buffer_start;
|
||||
uint16_t lfsr_state; /* dither state */
|
||||
uint32_t bits_left;
|
||||
uint32_t current_word;
|
||||
|
||||
uint8_t csnroffst; /* coarse SNR offset */
|
||||
ba_t cplba; /* coupling bit allocation parameters */
|
||||
ba_t ba[5]; /* channel bit allocation parameters */
|
||||
ba_t lfeba; /* lfe bit allocation parameters */
|
||||
|
||||
uint8_t cplfleak; /* coupling fast leak init */
|
||||
uint8_t cplsleak; /* coupling slow leak init */
|
||||
|
||||
expbap_t cpl_expbap;
|
||||
expbap_t fbw_expbap[5];
|
||||
expbap_t lfe_expbap;
|
||||
|
||||
sample_t * samples;
|
||||
int downmixed;
|
||||
};
|
||||
|
||||
// [Air]: Added sample_t casts here.
|
||||
// Improves SSE2 optimization efficiency under Visual Studio 2008
|
||||
|
||||
#define LEVEL_PLUS6DB ((sample_t)2.0)
|
||||
#define LEVEL_PLUS3DB ((sample_t)1.4142135623730951)
|
||||
#define LEVEL_3DB ((sample_t)0.7071067811865476)
|
||||
#define LEVEL_45DB ((sample_t)0.5946035575013605)
|
||||
#define LEVEL_6DB ((sample_t)0.5)
|
||||
|
||||
#define EXP_REUSE (0)
|
||||
#define EXP_D15 (1)
|
||||
#define EXP_D25 (2)
|
||||
#define EXP_D45 (3)
|
||||
|
||||
#define DELTA_BIT_REUSE (0)
|
||||
#define DELTA_BIT_NEW (1)
|
||||
#define DELTA_BIT_NONE (2)
|
||||
#define DELTA_BIT_RESERVED (3)
|
||||
|
||||
void a52_bit_allocate (a52_state_t * state, ba_t * ba, int bndstart,
|
||||
int start, int end, int fastleak, int slowleak,
|
||||
expbap_t * expbap);
|
||||
|
||||
int a52_downmix_init (int input, int flags, sample_t * level,
|
||||
sample_t clev, sample_t slev);
|
||||
int a52_downmix_coeff (sample_t * coeff, int acmod, int output, sample_t level,
|
||||
sample_t clev, sample_t slev);
|
||||
void a52_downmix (sample_t * samples, int acmod, int output, sample_t bias,
|
||||
sample_t clev, sample_t slev);
|
||||
void a52_upmix (sample_t * samples, int acmod, int output);
|
||||
|
||||
void a52_imdct_init (uint32_t mm_accel);
|
||||
void a52_imdct_256 (sample_t * data, sample_t * delay, sample_t bias);
|
||||
void a52_imdct_512 (sample_t * data, sample_t * delay, sample_t bias);
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* attributes.h
|
||||
* Copyright (C) 2000-2002 Michel Lespinasse <walken@zoy.org>
|
||||
* Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
|
||||
*
|
||||
* This file is part of a52dec, a free ATSC A-52 stream decoder.
|
||||
* See http://liba52.sourceforge.net/ for updates.
|
||||
*
|
||||
* a52dec 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.
|
||||
*
|
||||
* a52dec is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/* use gcc attribs to align critical data structures */
|
||||
#ifdef ATTRIBUTE_ALIGNED_MAX
|
||||
#define ATTR_ALIGN(align) __attribute__ ((__aligned__ ((ATTRIBUTE_ALIGNED_MAX < align) ? ATTRIBUTE_ALIGNED_MAX : align)))
|
||||
#else
|
||||
#define ATTR_ALIGN(align)
|
||||
#endif
|
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
* bit_allocate.c
|
||||
* Copyright (C) 2000-2002 Michel Lespinasse <walken@zoy.org>
|
||||
* Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
|
||||
*
|
||||
* This file is part of a52dec, a free ATSC A-52 stream decoder.
|
||||
* See http://liba52.sourceforge.net/ for updates.
|
||||
*
|
||||
* a52dec 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.
|
||||
*
|
||||
* a52dec is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "inttypes.h"
|
||||
|
||||
#include "a52.h"
|
||||
#include "a52_internal.h"
|
||||
|
||||
static int hthtab[3][50] = {
|
||||
{0x730, 0x730, 0x7c0, 0x800, 0x820, 0x840, 0x850, 0x850, 0x860, 0x860,
|
||||
0x860, 0x860, 0x860, 0x870, 0x870, 0x870, 0x880, 0x880, 0x890, 0x890,
|
||||
0x8a0, 0x8a0, 0x8b0, 0x8b0, 0x8c0, 0x8c0, 0x8d0, 0x8e0, 0x8f0, 0x900,
|
||||
0x910, 0x910, 0x910, 0x910, 0x900, 0x8f0, 0x8c0, 0x870, 0x820, 0x7e0,
|
||||
0x7a0, 0x770, 0x760, 0x7a0, 0x7c0, 0x7c0, 0x6e0, 0x400, 0x3c0, 0x3c0},
|
||||
{0x710, 0x710, 0x7a0, 0x7f0, 0x820, 0x830, 0x840, 0x850, 0x850, 0x860,
|
||||
0x860, 0x860, 0x860, 0x860, 0x870, 0x870, 0x870, 0x880, 0x880, 0x880,
|
||||
0x890, 0x890, 0x8a0, 0x8a0, 0x8b0, 0x8b0, 0x8c0, 0x8c0, 0x8e0, 0x8f0,
|
||||
0x900, 0x910, 0x910, 0x910, 0x910, 0x900, 0x8e0, 0x8b0, 0x870, 0x820,
|
||||
0x7e0, 0x7b0, 0x760, 0x770, 0x7a0, 0x7c0, 0x780, 0x5d0, 0x3c0, 0x3c0},
|
||||
{0x680, 0x680, 0x750, 0x7b0, 0x7e0, 0x810, 0x820, 0x830, 0x840, 0x850,
|
||||
0x850, 0x850, 0x860, 0x860, 0x860, 0x860, 0x860, 0x860, 0x860, 0x860,
|
||||
0x870, 0x870, 0x870, 0x870, 0x880, 0x880, 0x880, 0x890, 0x8a0, 0x8b0,
|
||||
0x8c0, 0x8d0, 0x8e0, 0x8f0, 0x900, 0x910, 0x910, 0x910, 0x900, 0x8f0,
|
||||
0x8d0, 0x8b0, 0x840, 0x7f0, 0x790, 0x760, 0x7a0, 0x7c0, 0x7b0, 0x720}
|
||||
};
|
||||
|
||||
static int8_t baptab[305] = {
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, /* 93 padding elems */
|
||||
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 14, 14, 14, 14, 14, 14, 14,
|
||||
14, 12, 12, 12, 12, 11, 11, 11, 11, 10, 10, 10, 10, 9, 9, 9,
|
||||
9, 8, 8, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5,
|
||||
5, 4, 4, -3, -3, 3, 3, 3, -2, -2, -1, -1, -1, -1, -1, 0,
|
||||
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0 /* 148 padding elems */
|
||||
};
|
||||
|
||||
static int bndtab[30] = {21, 22, 23, 24, 25, 26, 27, 28, 31, 34,
|
||||
37, 40, 43, 46, 49, 55, 61, 67, 73, 79,
|
||||
85, 97, 109, 121, 133, 157, 181, 205, 229, 253};
|
||||
|
||||
static int8_t latab[256] = {
|
||||
-64, -63, -62, -61, -60, -59, -58, -57, -56, -55, -54, -53,
|
||||
-52, -52, -51, -50, -49, -48, -47, -47, -46, -45, -44, -44,
|
||||
-43, -42, -41, -41, -40, -39, -38, -38, -37, -36, -36, -35,
|
||||
-35, -34, -33, -33, -32, -32, -31, -30, -30, -29, -29, -28,
|
||||
-28, -27, -27, -26, -26, -25, -25, -24, -24, -23, -23, -22,
|
||||
-22, -21, -21, -21, -20, -20, -19, -19, -19, -18, -18, -18,
|
||||
-17, -17, -17, -16, -16, -16, -15, -15, -15, -14, -14, -14,
|
||||
-13, -13, -13, -13, -12, -12, -12, -12, -11, -11, -11, -11,
|
||||
-10, -10, -10, -10, -10, -9, -9, -9, -9, -9, -8, -8,
|
||||
-8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -6, -6,
|
||||
-6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5,
|
||||
-5, -5, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4,
|
||||
-4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
|
||||
-3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2,
|
||||
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0
|
||||
};
|
||||
|
||||
#define UPDATE_LEAK() \
|
||||
do { \
|
||||
fastleak += fdecay; \
|
||||
if (fastleak > psd + fgain) \
|
||||
fastleak = psd + fgain; \
|
||||
slowleak += sdecay; \
|
||||
if (slowleak > psd + sgain) \
|
||||
slowleak = psd + sgain; \
|
||||
} while (0)
|
||||
|
||||
#define COMPUTE_MASK() \
|
||||
do { \
|
||||
if (psd > dbknee) \
|
||||
mask -= (psd - dbknee) >> 2; \
|
||||
if (mask > hth [i >> halfrate]) \
|
||||
mask = hth [i >> halfrate]; \
|
||||
mask -= snroffset + 128 * deltba[i]; \
|
||||
mask = (mask > 0) ? 0 : ((-mask) >> 5); \
|
||||
mask -= floor; \
|
||||
} while (0)
|
||||
|
||||
void a52_bit_allocate (a52_state_t * state, ba_t * ba, int bndstart,
|
||||
int start, int end, int fastleak, int slowleak,
|
||||
expbap_t * expbap)
|
||||
{
|
||||
static int slowgain[4] = {0x540, 0x4d8, 0x478, 0x410};
|
||||
static int dbpbtab[4] = {0xc00, 0x500, 0x300, 0x100};
|
||||
static int floortab[8] = {0x910, 0x950, 0x990, 0x9d0,
|
||||
0xa10, 0xa90, 0xb10, 0x1400};
|
||||
|
||||
int i, j;
|
||||
uint8_t * exp;
|
||||
int8_t * bap;
|
||||
int fdecay, fgain, sdecay, sgain, dbknee, floor, snroffset;
|
||||
int psd, mask;
|
||||
int8_t * deltba;
|
||||
int * hth;
|
||||
int halfrate;
|
||||
|
||||
halfrate = state->halfrate;
|
||||
fdecay = (63 + 20 * ((state->bai >> 7) & 3)) >> halfrate; /* fdcycod */
|
||||
fgain = 128 + 128 * (ba->bai & 7); /* fgaincod */
|
||||
sdecay = (15 + 2 * (state->bai >> 9)) >> halfrate; /* sdcycod */
|
||||
sgain = slowgain[(state->bai >> 5) & 3]; /* sgaincod */
|
||||
dbknee = dbpbtab[(state->bai >> 3) & 3]; /* dbpbcod */
|
||||
hth = hthtab[state->fscod];
|
||||
/*
|
||||
* if there is no delta bit allocation, make deltba point to an area
|
||||
* known to contain zeroes. baptab+156 here.
|
||||
*/
|
||||
deltba = (ba->deltbae == DELTA_BIT_NONE) ? baptab + 156 : ba->deltba;
|
||||
floor = floortab[state->bai & 7]; /* floorcod */
|
||||
snroffset = 960 - 64 * state->csnroffst - 4 * (ba->bai >> 3) + floor;
|
||||
floor >>= 5;
|
||||
|
||||
exp = expbap->exp;
|
||||
bap = expbap->bap;
|
||||
|
||||
i = bndstart;
|
||||
j = start;
|
||||
if (start == 0) { /* not the coupling channel */
|
||||
int lowcomp;
|
||||
|
||||
lowcomp = 0;
|
||||
j = end - 1;
|
||||
do {
|
||||
if (i < j) {
|
||||
if (exp[i+1] == exp[i] - 2)
|
||||
lowcomp = 384;
|
||||
else if (lowcomp && (exp[i+1] > exp[i]))
|
||||
lowcomp -= 64;
|
||||
}
|
||||
psd = 128 * exp[i];
|
||||
mask = psd + fgain + lowcomp;
|
||||
COMPUTE_MASK ();
|
||||
bap[i] = (baptab+156)[mask + 4 * exp[i]];
|
||||
i++;
|
||||
} while ((i < 3) || ((i < 7) && (exp[i] > exp[i-1])));
|
||||
fastleak = psd + fgain;
|
||||
slowleak = psd + sgain;
|
||||
|
||||
while (i < 7) {
|
||||
if (i < j) {
|
||||
if (exp[i+1] == exp[i] - 2)
|
||||
lowcomp = 384;
|
||||
else if (lowcomp && (exp[i+1] > exp[i]))
|
||||
lowcomp -= 64;
|
||||
}
|
||||
psd = 128 * exp[i];
|
||||
UPDATE_LEAK ();
|
||||
mask = ((fastleak + lowcomp < slowleak) ?
|
||||
fastleak + lowcomp : slowleak);
|
||||
COMPUTE_MASK ();
|
||||
bap[i] = (baptab+156)[mask + 4 * exp[i]];
|
||||
i++;
|
||||
}
|
||||
|
||||
if (end == 7) /* lfe channel */
|
||||
return;
|
||||
|
||||
do {
|
||||
if (exp[i+1] == exp[i] - 2)
|
||||
lowcomp = 320;
|
||||
else if (lowcomp && (exp[i+1] > exp[i]))
|
||||
lowcomp -= 64;
|
||||
psd = 128 * exp[i];
|
||||
UPDATE_LEAK ();
|
||||
mask = ((fastleak + lowcomp < slowleak) ?
|
||||
fastleak + lowcomp : slowleak);
|
||||
COMPUTE_MASK ();
|
||||
bap[i] = (baptab+156)[mask + 4 * exp[i]];
|
||||
i++;
|
||||
} while (i < 20);
|
||||
|
||||
while (lowcomp > 128) { /* two iterations maximum */
|
||||
lowcomp -= 128;
|
||||
psd = 128 * exp[i];
|
||||
UPDATE_LEAK ();
|
||||
mask = ((fastleak + lowcomp < slowleak) ?
|
||||
fastleak + lowcomp : slowleak);
|
||||
COMPUTE_MASK ();
|
||||
bap[i] = (baptab+156)[mask + 4 * exp[i]];
|
||||
i++;
|
||||
}
|
||||
j = i;
|
||||
}
|
||||
|
||||
do {
|
||||
int startband, endband;
|
||||
|
||||
startband = j;
|
||||
endband = ((bndtab-20)[i] < end) ? (bndtab-20)[i] : end;
|
||||
psd = 128 * exp[j++];
|
||||
while (j < endband) {
|
||||
int next, delta;
|
||||
|
||||
next = 128 * exp[j++];
|
||||
delta = next - psd;
|
||||
switch (delta >> 9) {
|
||||
case -6: case -5: case -4: case -3: case -2:
|
||||
psd = next;
|
||||
break;
|
||||
case -1:
|
||||
psd = next + latab[(-delta) >> 1];
|
||||
break;
|
||||
case 0:
|
||||
psd += latab[delta >> 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* minpsd = -289 */
|
||||
UPDATE_LEAK ();
|
||||
mask = (fastleak < slowleak) ? fastleak : slowleak;
|
||||
COMPUTE_MASK ();
|
||||
i++;
|
||||
j = startband;
|
||||
do {
|
||||
/* max(mask+4*exp)=147=-(minpsd+fgain-deltba-snroffset)>>5+4*exp */
|
||||
/* min(mask+4*exp)=-156=-(sgain-deltba-snroffset)>>5 */
|
||||
bap[j] = (baptab+156)[mask + 4 * exp[j]];
|
||||
} while (++j < endband);
|
||||
} while (j < end);
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* bitstream.c
|
||||
* Copyright (C) 2000-2002 Michel Lespinasse <walken@zoy.org>
|
||||
* Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
|
||||
*
|
||||
* This file is part of a52dec, a free ATSC A-52 stream decoder.
|
||||
* See http://liba52.sourceforge.net/ for updates.
|
||||
*
|
||||
* a52dec 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.
|
||||
*
|
||||
* a52dec is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "inttypes.h"
|
||||
|
||||
#include "a52.h"
|
||||
#include "a52_internal.h"
|
||||
#include "bitstream.h"
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
void a52_bitstream_set_ptr (a52_state_t * state, uint8_t * buf)
|
||||
{
|
||||
int align;
|
||||
|
||||
align = (long)buf & 3;
|
||||
state->buffer_start = (uint32_t *) (buf - align);
|
||||
state->bits_left = 0;
|
||||
bitstream_get (state, align * 8);
|
||||
}
|
||||
|
||||
static inline void bitstream_fill_current (a52_state_t * state)
|
||||
{
|
||||
uint32_t tmp;
|
||||
|
||||
tmp = *(state->buffer_start++);
|
||||
state->current_word = swab32 (tmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* The fast paths for _get is in the
|
||||
* bitstream.h header file so it can be inlined.
|
||||
*
|
||||
* The "bottom half" of this routine is suffixed _bh
|
||||
*
|
||||
* -ah
|
||||
*/
|
||||
|
||||
uint32_t a52_bitstream_get_bh (a52_state_t * state, uint32_t num_bits)
|
||||
{
|
||||
uint32_t result;
|
||||
|
||||
num_bits -= state->bits_left;
|
||||
result = ((state->current_word << (32 - state->bits_left)) >>
|
||||
(32 - state->bits_left));
|
||||
|
||||
bitstream_fill_current (state);
|
||||
|
||||
if (num_bits != 0)
|
||||
result = (result << num_bits) | (state->current_word >> (32 - num_bits));
|
||||
|
||||
state->bits_left = 32 - num_bits;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t a52_bitstream_get_bh_2 (a52_state_t * state, uint32_t num_bits)
|
||||
{
|
||||
int32_t result;
|
||||
|
||||
num_bits -= state->bits_left;
|
||||
result = ((((int32_t)state->current_word) << (32 - state->bits_left)) >>
|
||||
(32 - state->bits_left));
|
||||
|
||||
bitstream_fill_current(state);
|
||||
|
||||
if (num_bits != 0)
|
||||
result = (result << num_bits) | (state->current_word >> (32 - num_bits));
|
||||
|
||||
state->bits_left = 32 - num_bits;
|
||||
|
||||
return result;
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* bitstream.h
|
||||
* Copyright (C) 2000-2002 Michel Lespinasse <walken@zoy.org>
|
||||
* Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
|
||||
*
|
||||
* This file is part of a52dec, a free ATSC A-52 stream decoder.
|
||||
* See http://liba52.sourceforge.net/ for updates.
|
||||
*
|
||||
* a52dec 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.
|
||||
*
|
||||
* a52dec is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/* (stolen from the kernel) */
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
|
||||
# define swab32(x) (x)
|
||||
|
||||
#else
|
||||
|
||||
# if 0 && defined (__i386__)
|
||||
|
||||
# define swab32(x) __i386_swab32(x)
|
||||
static inline const uint32_t __i386_swab32(uint32_t x)
|
||||
{
|
||||
__asm__("bswap %0" : "=r" (x) : "0" (x));
|
||||
return x;
|
||||
}
|
||||
|
||||
# else
|
||||
|
||||
# define swab32(x)\
|
||||
((((uint8_t*)&x)[0] << 24) | (((uint8_t*)&x)[1] << 16) | \
|
||||
(((uint8_t*)&x)[2] << 8) | (((uint8_t*)&x)[3]))
|
||||
|
||||
# endif
|
||||
#endif
|
||||
|
||||
void a52_bitstream_set_ptr (a52_state_t * state, uint8_t * buf);
|
||||
uint32_t a52_bitstream_get_bh (a52_state_t * state, uint32_t num_bits);
|
||||
int32_t a52_bitstream_get_bh_2 (a52_state_t * state, uint32_t num_bits);
|
||||
|
||||
static inline uint32_t bitstream_get (a52_state_t * state, uint32_t num_bits)
|
||||
{
|
||||
uint32_t result;
|
||||
|
||||
if (num_bits < state->bits_left) {
|
||||
result = (state->current_word << (32 - state->bits_left)) >> (32 - num_bits);
|
||||
state->bits_left -= num_bits;
|
||||
return result;
|
||||
}
|
||||
|
||||
return a52_bitstream_get_bh (state, num_bits);
|
||||
}
|
||||
|
||||
static inline int32_t bitstream_get_2 (a52_state_t * state, uint32_t num_bits)
|
||||
{
|
||||
int32_t result;
|
||||
|
||||
if (num_bits < state->bits_left) {
|
||||
result = (((int32_t)state->current_word) << (32 - state->bits_left)) >> (32 - num_bits);
|
||||
state->bits_left -= num_bits;
|
||||
return result;
|
||||
}
|
||||
|
||||
return a52_bitstream_get_bh_2 (state, num_bits);
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
/* vc++/config.h - manually adapted from include/config.h.in */
|
||||
|
||||
/* maximum supported data alignment */
|
||||
/* #undef ATTRIBUTE_ALIGNED_MAX */
|
||||
|
||||
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||
/* #undef HAVE_DLFCN_H */
|
||||
|
||||
/* Define to 1 if you have the `ftime' function. */
|
||||
#define HAVE_FTIME 1
|
||||
|
||||
/* Define to 1 if you have the `gettimeofday' function. */
|
||||
/* #undef HAVE_GETTIMEOFDAY */
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
/* #undef HAVE_INTTYPES_H */
|
||||
|
||||
/* Define to 1 if you have the <io.h> header file. */
|
||||
#define HAVE_IO_H 1
|
||||
|
||||
/* Define to 1 if you have the `memalign' function. */
|
||||
/* #undef HAVE_MEMALIGN */
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#define HAVE_MEMORY_H 1
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
/* #undef HAVE_STDINT_H */
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#define HAVE_STDLIB_H 1
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
/* #undef HAVE_STRINGS_H */
|
||||
|
||||
/* 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/timeb.h> header file. */
|
||||
#define HAVE_SYS_TIMEB_H 1
|
||||
|
||||
/* Define to 1 if you have the <sys/time.h> header file. */
|
||||
/* #undef HAVE_SYS_TIME_H */
|
||||
|
||||
/* 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. */
|
||||
/* #undef HAVE_UNISTD_H */
|
||||
|
||||
/* liba52 djbfft support */
|
||||
/* #undef LIBA52_DJBFFT */
|
||||
|
||||
/* a52 sample precision */
|
||||
/* #undef LIBA52_DOUBLE */
|
||||
|
||||
/* libao al support */
|
||||
/* #undef LIBAO_AL */
|
||||
|
||||
/* libao OSS support */
|
||||
/* #undef LIBAO_OSS */
|
||||
|
||||
/* libao solaris support */
|
||||
/* #undef LIBAO_SOLARIS */
|
||||
|
||||
/* libao win support */
|
||||
#define LIBAO_WIN
|
||||
|
||||
/* Name of package */
|
||||
#define PACKAGE "a52dec"
|
||||
|
||||
/* 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 as the return type of signal handlers (`int' or `void'). */
|
||||
#define RETSIGTYPE void
|
||||
|
||||
/* The size of a `char', as computed by sizeof. */
|
||||
#define SIZEOF_CHAR 1
|
||||
|
||||
/* The size of a `int', as computed by sizeof. */
|
||||
#define SIZEOF_INT 4
|
||||
|
||||
/* The size of a `short', as computed by sizeof. */
|
||||
#define SIZEOF_SHORT 2
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
/* Version number of package */
|
||||
#define VERSION "0.7.4-cvs"
|
||||
|
||||
/* Define to 1 if your processor stores words with the most significant byte
|
||||
first (like Motorola and SPARC, unlike Intel and VAX). */
|
||||
/* #undef WORDS_BIGENDIAN */
|
||||
|
||||
/* Define to empty if `const' does not conform to ANSI C. */
|
||||
/* #undef const */
|
||||
|
||||
/* Define as `__inline' if that's what the C compiler calls it, or to nothing
|
||||
if it is not supported. */
|
||||
#define inline __inline
|
||||
|
||||
/* Define as `__restrict' if that's what the C compiler calls it, or to
|
||||
nothing if it is not supported. */
|
||||
#define restrict /*__restrict*/
|
||||
|
||||
/* Define to `unsigned' if <sys/types.h> does not define. */
|
||||
/* #undef size_t */
|
|
@ -0,0 +1,19 @@
|
|||
AC_SUBST([LIBA52_CFLAGS])
|
||||
AC_SUBST([LIBA52_LIBS])
|
||||
|
||||
dnl avoid -fPIC when possible
|
||||
LIBA52_CFLAGS="$LIBA52_CFLAGS -prefer-non-pic"
|
||||
|
||||
AC_ARG_ENABLE([double],
|
||||
[ --enable-double use double-precision samples])
|
||||
if test x"$enable_double" = x"yes"; then
|
||||
AC_DEFINE([LIBA52_DOUBLE],,[a52 sample precision])
|
||||
fi
|
||||
|
||||
dnl check for djbttf
|
||||
AC_ARG_ENABLE([djbfft],
|
||||
[ --enable-djbfft make a version using djbfft])
|
||||
if test x"$enable_djbfft" = x"yes"; then
|
||||
AC_DEFINE([LIBA52_DJBFFT],,[liba52 djbfft support])
|
||||
LIBA52_LIBS="$LIBA52_LIBS -ldjbfft"
|
||||
fi
|
|
@ -0,0 +1,655 @@
|
|||
/*
|
||||
* downmix.c
|
||||
* Copyright (C) 2000-2002 Michel Lespinasse <walken@zoy.org>
|
||||
* Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
|
||||
*
|
||||
* This file is part of a52dec, a free ATSC A-52 stream decoder.
|
||||
* See http://liba52.sourceforge.net/ for updates.
|
||||
*
|
||||
* a52dec 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.
|
||||
*
|
||||
* a52dec is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#pragma warning(disable:4244)
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include "inttypes.h"
|
||||
|
||||
#include "a52.h"
|
||||
#include "a52_internal.h"
|
||||
|
||||
#define CONVERT(acmod,output) (((output) << 3) + (acmod))
|
||||
|
||||
int a52_downmix_init (int input, int flags, sample_t * level,
|
||||
sample_t clev, sample_t slev)
|
||||
{
|
||||
static uint8_t table[11][8] = {
|
||||
{A52_CHANNEL, A52_DOLBY, A52_STEREO, A52_STEREO,
|
||||
A52_STEREO, A52_STEREO, A52_STEREO, A52_STEREO},
|
||||
{A52_MONO, A52_MONO, A52_MONO, A52_MONO,
|
||||
A52_MONO, A52_MONO, A52_MONO, A52_MONO},
|
||||
{A52_CHANNEL, A52_DOLBY, A52_STEREO, A52_STEREO,
|
||||
A52_STEREO, A52_STEREO, A52_STEREO, A52_STEREO},
|
||||
{A52_CHANNEL, A52_DOLBY, A52_STEREO, A52_3F,
|
||||
A52_STEREO, A52_3F, A52_STEREO, A52_3F},
|
||||
{A52_CHANNEL, A52_DOLBY, A52_STEREO, A52_STEREO,
|
||||
A52_2F1R, A52_2F1R, A52_2F1R, A52_2F1R},
|
||||
{A52_CHANNEL, A52_DOLBY, A52_STEREO, A52_STEREO,
|
||||
A52_2F1R, A52_3F1R, A52_2F1R, A52_3F1R},
|
||||
{A52_CHANNEL, A52_DOLBY, A52_STEREO, A52_3F,
|
||||
A52_2F2R, A52_2F2R, A52_2F2R, A52_2F2R},
|
||||
{A52_CHANNEL, A52_DOLBY, A52_STEREO, A52_3F,
|
||||
A52_2F2R, A52_3F2R, A52_2F2R, A52_3F2R},
|
||||
{A52_CHANNEL1, A52_MONO, A52_MONO, A52_MONO,
|
||||
A52_MONO, A52_MONO, A52_MONO, A52_MONO},
|
||||
{A52_CHANNEL2, A52_MONO, A52_MONO, A52_MONO,
|
||||
A52_MONO, A52_MONO, A52_MONO, A52_MONO},
|
||||
{A52_CHANNEL, A52_DOLBY, A52_STEREO, A52_DOLBY,
|
||||
A52_DOLBY, A52_DOLBY, A52_DOLBY, A52_DOLBY}
|
||||
};
|
||||
int output;
|
||||
|
||||
output = flags & A52_CHANNEL_MASK;
|
||||
if (output > A52_DOLBY)
|
||||
return -1;
|
||||
|
||||
output = table[output][input & 7];
|
||||
|
||||
if ((output == A52_STEREO) &&
|
||||
((input == A52_DOLBY) || ((input == A52_3F) && (clev == LEVEL_3DB))))
|
||||
output = A52_DOLBY;
|
||||
|
||||
if (flags & A52_ADJUST_LEVEL)
|
||||
switch (CONVERT (input & 7, output)) {
|
||||
|
||||
case CONVERT (A52_3F, A52_MONO):
|
||||
*level *= LEVEL_3DB / (1 + clev);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_STEREO, A52_MONO):
|
||||
case CONVERT (A52_2F2R, A52_2F1R):
|
||||
case CONVERT (A52_3F2R, A52_3F1R):
|
||||
level_3db:
|
||||
*level *= LEVEL_3DB;
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F2R, A52_2F1R):
|
||||
if (clev < LEVEL_PLUS3DB - 1)
|
||||
goto level_3db;
|
||||
/* break thru */
|
||||
case CONVERT (A52_3F, A52_STEREO):
|
||||
case CONVERT (A52_3F1R, A52_2F1R):
|
||||
case CONVERT (A52_3F1R, A52_2F2R):
|
||||
case CONVERT (A52_3F2R, A52_2F2R):
|
||||
*level /= 1 + clev;
|
||||
break;
|
||||
|
||||
case CONVERT (A52_2F1R, A52_MONO):
|
||||
*level *= LEVEL_PLUS3DB / (2 + slev);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_2F1R, A52_STEREO):
|
||||
case CONVERT (A52_3F1R, A52_3F):
|
||||
*level /= 1 + slev * LEVEL_3DB;
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F1R, A52_MONO):
|
||||
*level *= LEVEL_3DB / (1 + clev + 0.5 * slev);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F1R, A52_STEREO):
|
||||
*level /= 1 + clev + slev * LEVEL_3DB;
|
||||
break;
|
||||
|
||||
case CONVERT (A52_2F2R, A52_MONO):
|
||||
*level *= LEVEL_3DB / (1 + slev);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_2F2R, A52_STEREO):
|
||||
case CONVERT (A52_3F2R, A52_3F):
|
||||
*level /= 1 + slev;
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F2R, A52_MONO):
|
||||
*level *= LEVEL_3DB / (1 + clev + slev);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F2R, A52_STEREO):
|
||||
*level /= 1 + clev + slev;
|
||||
break;
|
||||
|
||||
case CONVERT (A52_MONO, A52_DOLBY):
|
||||
*level *= LEVEL_PLUS3DB;
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F, A52_DOLBY):
|
||||
case CONVERT (A52_2F1R, A52_DOLBY):
|
||||
*level *= 1 / (1 + LEVEL_3DB);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F1R, A52_DOLBY):
|
||||
case CONVERT (A52_2F2R, A52_DOLBY):
|
||||
*level *= 1 / (1 + 2 * LEVEL_3DB);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F2R, A52_DOLBY):
|
||||
*level *= 1 / (1 + 3 * LEVEL_3DB);
|
||||
break;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
int a52_downmix_coeff (sample_t * coeff, int acmod, int output, sample_t level,
|
||||
sample_t clev, sample_t slev)
|
||||
{
|
||||
switch (CONVERT (acmod, output & A52_CHANNEL_MASK)) {
|
||||
|
||||
case CONVERT (A52_CHANNEL, A52_CHANNEL):
|
||||
case CONVERT (A52_MONO, A52_MONO):
|
||||
case CONVERT (A52_STEREO, A52_STEREO):
|
||||
case CONVERT (A52_3F, A52_3F):
|
||||
case CONVERT (A52_2F1R, A52_2F1R):
|
||||
case CONVERT (A52_3F1R, A52_3F1R):
|
||||
case CONVERT (A52_2F2R, A52_2F2R):
|
||||
case CONVERT (A52_3F2R, A52_3F2R):
|
||||
case CONVERT (A52_STEREO, A52_DOLBY):
|
||||
coeff[0] = coeff[1] = coeff[2] = coeff[3] = coeff[4] = level;
|
||||
return 0;
|
||||
|
||||
case CONVERT (A52_CHANNEL, A52_MONO):
|
||||
coeff[0] = coeff[1] = level * LEVEL_6DB;
|
||||
return 3;
|
||||
|
||||
case CONVERT (A52_STEREO, A52_MONO):
|
||||
coeff[0] = coeff[1] = level * LEVEL_3DB;
|
||||
return 3;
|
||||
|
||||
case CONVERT (A52_3F, A52_MONO):
|
||||
coeff[0] = coeff[2] = level * LEVEL_3DB;
|
||||
coeff[1] = level * clev * LEVEL_PLUS3DB;
|
||||
return 7;
|
||||
|
||||
case CONVERT (A52_2F1R, A52_MONO):
|
||||
coeff[0] = coeff[1] = level * LEVEL_3DB;
|
||||
coeff[2] = level * slev * LEVEL_3DB;
|
||||
return 7;
|
||||
|
||||
case CONVERT (A52_2F2R, A52_MONO):
|
||||
coeff[0] = coeff[1] = level * LEVEL_3DB;
|
||||
coeff[2] = coeff[3] = level * slev * LEVEL_3DB;
|
||||
return 15;
|
||||
|
||||
case CONVERT (A52_3F1R, A52_MONO):
|
||||
coeff[0] = coeff[2] = level * LEVEL_3DB;
|
||||
coeff[1] = level * clev * LEVEL_PLUS3DB;
|
||||
coeff[3] = level * slev * LEVEL_3DB;
|
||||
return 15;
|
||||
|
||||
case CONVERT (A52_3F2R, A52_MONO):
|
||||
coeff[0] = coeff[2] = level * LEVEL_3DB;
|
||||
coeff[1] = level * clev * LEVEL_PLUS3DB;
|
||||
coeff[3] = coeff[4] = level * slev * LEVEL_3DB;
|
||||
return 31;
|
||||
|
||||
case CONVERT (A52_MONO, A52_DOLBY):
|
||||
coeff[0] = level * LEVEL_3DB;
|
||||
return 0;
|
||||
|
||||
case CONVERT (A52_3F, A52_DOLBY):
|
||||
clev = LEVEL_3DB;
|
||||
case CONVERT (A52_3F, A52_STEREO):
|
||||
case CONVERT (A52_3F1R, A52_2F1R):
|
||||
case CONVERT (A52_3F2R, A52_2F2R):
|
||||
coeff[0] = coeff[2] = coeff[3] = coeff[4] = level;
|
||||
coeff[1] = level * clev;
|
||||
return 7;
|
||||
|
||||
case CONVERT (A52_2F1R, A52_DOLBY):
|
||||
slev = 1;
|
||||
case CONVERT (A52_2F1R, A52_STEREO):
|
||||
coeff[0] = coeff[1] = level;
|
||||
coeff[2] = level * slev * LEVEL_3DB;
|
||||
return 7;
|
||||
|
||||
case CONVERT (A52_3F1R, A52_DOLBY):
|
||||
clev = LEVEL_3DB;
|
||||
slev = 1;
|
||||
case CONVERT (A52_3F1R, A52_STEREO):
|
||||
coeff[0] = coeff[2] = level;
|
||||
coeff[1] = level * clev;
|
||||
coeff[3] = level * slev * LEVEL_3DB;
|
||||
return 15;
|
||||
|
||||
case CONVERT (A52_2F2R, A52_DOLBY):
|
||||
slev = LEVEL_3DB;
|
||||
case CONVERT (A52_2F2R, A52_STEREO):
|
||||
coeff[0] = coeff[1] = level;
|
||||
coeff[2] = coeff[3] = level * slev;
|
||||
return 15;
|
||||
|
||||
case CONVERT (A52_3F2R, A52_DOLBY):
|
||||
clev = LEVEL_3DB;
|
||||
case CONVERT (A52_3F2R, A52_2F1R):
|
||||
slev = LEVEL_3DB;
|
||||
case CONVERT (A52_3F2R, A52_STEREO):
|
||||
coeff[0] = coeff[2] = level;
|
||||
coeff[1] = level * clev;
|
||||
coeff[3] = coeff[4] = level * slev;
|
||||
return 31;
|
||||
|
||||
case CONVERT (A52_3F1R, A52_3F):
|
||||
coeff[0] = coeff[1] = coeff[2] = level;
|
||||
coeff[3] = level * slev * LEVEL_3DB;
|
||||
return 13;
|
||||
|
||||
case CONVERT (A52_3F2R, A52_3F):
|
||||
coeff[0] = coeff[1] = coeff[2] = level;
|
||||
coeff[3] = coeff[4] = level * slev;
|
||||
return 29;
|
||||
|
||||
case CONVERT (A52_2F2R, A52_2F1R):
|
||||
coeff[0] = coeff[1] = level;
|
||||
coeff[2] = coeff[3] = level * LEVEL_3DB;
|
||||
return 12;
|
||||
|
||||
case CONVERT (A52_3F2R, A52_3F1R):
|
||||
coeff[0] = coeff[1] = coeff[2] = level;
|
||||
coeff[3] = coeff[4] = level * LEVEL_3DB;
|
||||
return 24;
|
||||
|
||||
case CONVERT (A52_2F1R, A52_2F2R):
|
||||
coeff[0] = coeff[1] = level;
|
||||
coeff[2] = level * LEVEL_3DB;
|
||||
return 0;
|
||||
|
||||
case CONVERT (A52_3F1R, A52_2F2R):
|
||||
coeff[0] = coeff[2] = level;
|
||||
coeff[1] = level * clev;
|
||||
coeff[3] = level * LEVEL_3DB;
|
||||
return 7;
|
||||
|
||||
case CONVERT (A52_3F1R, A52_3F2R):
|
||||
coeff[0] = coeff[1] = coeff[2] = level;
|
||||
coeff[3] = level * LEVEL_3DB;
|
||||
return 0;
|
||||
|
||||
case CONVERT (A52_CHANNEL, A52_CHANNEL1):
|
||||
coeff[0] = level;
|
||||
coeff[1] = 0;
|
||||
return 0;
|
||||
|
||||
case CONVERT (A52_CHANNEL, A52_CHANNEL2):
|
||||
coeff[0] = 0;
|
||||
coeff[1] = level;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1; /* NOTREACHED */
|
||||
}
|
||||
|
||||
static void mix2to1 (sample_t * dest, sample_t * src, sample_t bias)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
dest[i] += src[i] + bias;
|
||||
}
|
||||
|
||||
static void mix3to1 (sample_t * samples, sample_t bias)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
samples[i] += samples[i + 256] + samples[i + 512] + bias;
|
||||
}
|
||||
|
||||
static void mix4to1 (sample_t * samples, sample_t bias)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
samples[i] += (samples[i + 256] + samples[i + 512] +
|
||||
samples[i + 768] + bias);
|
||||
}
|
||||
|
||||
static void mix5to1 (sample_t * samples, sample_t bias)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
samples[i] += (samples[i + 256] + samples[i + 512] +
|
||||
samples[i + 768] + samples[i + 1024] + bias);
|
||||
}
|
||||
|
||||
static void mix3to2 (sample_t * samples, sample_t bias)
|
||||
{
|
||||
int i;
|
||||
sample_t common;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
common = samples[i + 256] + bias;
|
||||
samples[i] += common;
|
||||
samples[i + 256] = samples[i + 512] + common;
|
||||
}
|
||||
}
|
||||
|
||||
static void mix21to2 (sample_t * left, sample_t * right, sample_t bias)
|
||||
{
|
||||
int i;
|
||||
sample_t common;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
common = right[i + 256] + bias;
|
||||
left[i] += common;
|
||||
right[i] += common;
|
||||
}
|
||||
}
|
||||
|
||||
static void mix21toS (sample_t * samples, sample_t bias)
|
||||
{
|
||||
int i;
|
||||
sample_t surround;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
surround = samples[i + 512];
|
||||
samples[i] += bias - surround;
|
||||
samples[i + 256] += bias + surround;
|
||||
}
|
||||
}
|
||||
|
||||
static void mix31to2 (sample_t * samples, sample_t bias)
|
||||
{
|
||||
int i;
|
||||
sample_t common;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
common = samples[i + 256] + samples[i + 768] + bias;
|
||||
samples[i] += common;
|
||||
samples[i + 256] = samples[i + 512] + common;
|
||||
}
|
||||
}
|
||||
|
||||
static void mix31toS (sample_t * samples, sample_t bias)
|
||||
{
|
||||
int i;
|
||||
sample_t common, surround;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
common = samples[i + 256] + bias;
|
||||
surround = samples[i + 768];
|
||||
samples[i] += common - surround;
|
||||
samples[i + 256] = samples[i + 512] + common + surround;
|
||||
}
|
||||
}
|
||||
|
||||
static void mix22toS (sample_t * samples, sample_t bias)
|
||||
{
|
||||
int i;
|
||||
sample_t surround;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
surround = samples[i + 512] + samples[i + 768];
|
||||
samples[i] += bias - surround;
|
||||
samples[i + 256] += bias + surround;
|
||||
}
|
||||
}
|
||||
|
||||
static void mix32to2 (sample_t * samples, sample_t bias)
|
||||
{
|
||||
int i;
|
||||
sample_t common;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
common = samples[i + 256] + bias;
|
||||
samples[i] += common + samples[i + 768];
|
||||
samples[i + 256] = common + samples[i + 512] + samples[i + 1024];
|
||||
}
|
||||
}
|
||||
|
||||
static void mix32toS (sample_t * samples, sample_t bias)
|
||||
{
|
||||
int i;
|
||||
sample_t common, surround;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
common = samples[i + 256] + bias;
|
||||
surround = samples[i + 768] + samples[i + 1024];
|
||||
samples[i] += common - surround;
|
||||
samples[i + 256] = samples[i + 512] + common + surround;
|
||||
}
|
||||
}
|
||||
|
||||
static void move2to1 (sample_t * src, sample_t * dest, sample_t bias)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
dest[i] = src[i] + src[i + 256] + bias;
|
||||
}
|
||||
|
||||
static void zero (sample_t * samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
samples[i] = 0;
|
||||
}
|
||||
|
||||
void a52_downmix (sample_t * samples, int acmod, int output, sample_t bias,
|
||||
sample_t clev, sample_t slev)
|
||||
{
|
||||
switch (CONVERT (acmod, output & A52_CHANNEL_MASK)) {
|
||||
|
||||
case CONVERT (A52_CHANNEL, A52_CHANNEL2):
|
||||
memcpy (samples, samples + 256, 256 * sizeof (sample_t));
|
||||
break;
|
||||
|
||||
case CONVERT (A52_CHANNEL, A52_MONO):
|
||||
case CONVERT (A52_STEREO, A52_MONO):
|
||||
mix_2to1:
|
||||
mix2to1 (samples, samples + 256, bias);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_2F1R, A52_MONO):
|
||||
if (slev == 0)
|
||||
goto mix_2to1;
|
||||
case CONVERT (A52_3F, A52_MONO):
|
||||
mix_3to1:
|
||||
mix3to1 (samples, bias);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F1R, A52_MONO):
|
||||
if (slev == 0)
|
||||
goto mix_3to1;
|
||||
case CONVERT (A52_2F2R, A52_MONO):
|
||||
if (slev == 0)
|
||||
goto mix_2to1;
|
||||
mix4to1 (samples, bias);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F2R, A52_MONO):
|
||||
if (slev == 0)
|
||||
goto mix_3to1;
|
||||
mix5to1 (samples, bias);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_MONO, A52_DOLBY):
|
||||
memcpy (samples + 256, samples, 256 * sizeof (sample_t));
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F, A52_STEREO):
|
||||
case CONVERT (A52_3F, A52_DOLBY):
|
||||
mix_3to2:
|
||||
mix3to2 (samples, bias);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_2F1R, A52_STEREO):
|
||||
if (slev == 0)
|
||||
break;
|
||||
mix21to2 (samples, samples + 256, bias);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_2F1R, A52_DOLBY):
|
||||
mix21toS (samples, bias);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F1R, A52_STEREO):
|
||||
if (slev == 0)
|
||||
goto mix_3to2;
|
||||
mix31to2 (samples, bias);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F1R, A52_DOLBY):
|
||||
mix31toS (samples, bias);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_2F2R, A52_STEREO):
|
||||
if (slev == 0)
|
||||
break;
|
||||
mix2to1 (samples, samples + 512, bias);
|
||||
mix2to1 (samples + 256, samples + 768, bias);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_2F2R, A52_DOLBY):
|
||||
mix22toS (samples, bias);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F2R, A52_STEREO):
|
||||
if (slev == 0)
|
||||
goto mix_3to2;
|
||||
mix32to2 (samples, bias);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F2R, A52_DOLBY):
|
||||
mix32toS (samples, bias);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F1R, A52_3F):
|
||||
if (slev == 0)
|
||||
break;
|
||||
mix21to2 (samples, samples + 512, bias);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F2R, A52_3F):
|
||||
if (slev == 0)
|
||||
break;
|
||||
mix2to1 (samples, samples + 768, bias);
|
||||
mix2to1 (samples + 512, samples + 1024, bias);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F1R, A52_2F1R):
|
||||
mix3to2 (samples, bias);
|
||||
memcpy (samples + 512, samples + 768, 256 * sizeof (sample_t));
|
||||
break;
|
||||
|
||||
case CONVERT (A52_2F2R, A52_2F1R):
|
||||
mix2to1 (samples + 512, samples + 768, bias);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F2R, A52_2F1R):
|
||||
mix3to2 (samples, bias);
|
||||
move2to1 (samples + 768, samples + 512, bias);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F2R, A52_3F1R):
|
||||
mix2to1 (samples + 768, samples + 1024, bias);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_2F1R, A52_2F2R):
|
||||
memcpy (samples + 768, samples + 512, 256 * sizeof (sample_t));
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F1R, A52_2F2R):
|
||||
mix3to2 (samples, bias);
|
||||
memcpy (samples + 512, samples + 768, 256 * sizeof (sample_t));
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F2R, A52_2F2R):
|
||||
mix3to2 (samples, bias);
|
||||
memcpy (samples + 512, samples + 768, 256 * sizeof (sample_t));
|
||||
memcpy (samples + 768, samples + 1024, 256 * sizeof (sample_t));
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F1R, A52_3F2R):
|
||||
memcpy (samples + 1027, samples + 768, 256 * sizeof (sample_t));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void a52_upmix (sample_t * samples, int acmod, int output)
|
||||
{
|
||||
switch (CONVERT (acmod, output & A52_CHANNEL_MASK)) {
|
||||
|
||||
case CONVERT (A52_CHANNEL, A52_CHANNEL2):
|
||||
memcpy (samples + 256, samples, 256 * sizeof (sample_t));
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F2R, A52_MONO):
|
||||
zero (samples + 1024);
|
||||
case CONVERT (A52_3F1R, A52_MONO):
|
||||
case CONVERT (A52_2F2R, A52_MONO):
|
||||
zero (samples + 768);
|
||||
case CONVERT (A52_3F, A52_MONO):
|
||||
case CONVERT (A52_2F1R, A52_MONO):
|
||||
zero (samples + 512);
|
||||
case CONVERT (A52_CHANNEL, A52_MONO):
|
||||
case CONVERT (A52_STEREO, A52_MONO):
|
||||
zero (samples + 256);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F2R, A52_STEREO):
|
||||
case CONVERT (A52_3F2R, A52_DOLBY):
|
||||
zero (samples + 1024);
|
||||
case CONVERT (A52_3F1R, A52_STEREO):
|
||||
case CONVERT (A52_3F1R, A52_DOLBY):
|
||||
zero (samples + 768);
|
||||
case CONVERT (A52_3F, A52_STEREO):
|
||||
case CONVERT (A52_3F, A52_DOLBY):
|
||||
mix_3to2:
|
||||
memcpy (samples + 512, samples + 256, 256 * sizeof (sample_t));
|
||||
zero (samples + 256);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_2F2R, A52_STEREO):
|
||||
case CONVERT (A52_2F2R, A52_DOLBY):
|
||||
zero (samples + 768);
|
||||
case CONVERT (A52_2F1R, A52_STEREO):
|
||||
case CONVERT (A52_2F1R, A52_DOLBY):
|
||||
zero (samples + 512);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F2R, A52_3F):
|
||||
zero (samples + 1024);
|
||||
case CONVERT (A52_3F1R, A52_3F):
|
||||
case CONVERT (A52_2F2R, A52_2F1R):
|
||||
zero (samples + 768);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F2R, A52_3F1R):
|
||||
zero (samples + 1024);
|
||||
break;
|
||||
|
||||
case CONVERT (A52_3F2R, A52_2F1R):
|
||||
zero (samples + 1024);
|
||||
case CONVERT (A52_3F1R, A52_2F1R):
|
||||
mix_31to21:
|
||||
memcpy (samples + 768, samples + 512, 256 * sizeof (sample_t));
|
||||
goto mix_3to2;
|
||||
|
||||
case CONVERT (A52_3F2R, A52_2F2R):
|
||||
memcpy (samples + 1024, samples + 768, 256 * sizeof (sample_t));
|
||||
goto mix_31to21;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,432 @@
|
|||
/*
|
||||
* imdct.c
|
||||
* Copyright (C) 2000-2002 Michel Lespinasse <walken@zoy.org>
|
||||
* Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
|
||||
*
|
||||
* The ifft algorithms in this file have been largely inspired by Dan
|
||||
* Bernstein's work, djbfft, available at http://cr.yp.to/djbfft.html
|
||||
*
|
||||
* This file is part of a52dec, a free ATSC A-52 stream decoder.
|
||||
* See http://liba52.sourceforge.net/ for updates.
|
||||
*
|
||||
* a52dec 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.
|
||||
*
|
||||
* a52dec is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#pragma warning(disable:4244)
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#ifdef LIBA52_DJBFFT
|
||||
#include <fftc4.h>
|
||||
#endif
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.1415926535897932384626433832795029
|
||||
#endif
|
||||
#include "inttypes.h"
|
||||
|
||||
#include "a52.h"
|
||||
#include "a52_internal.h"
|
||||
#include "mm_accel.h"
|
||||
|
||||
typedef struct complex_s {
|
||||
sample_t real;
|
||||
sample_t imag;
|
||||
} complex_t;
|
||||
|
||||
static uint8_t fftorder[] = {
|
||||
0,128, 64,192, 32,160,224, 96, 16,144, 80,208,240,112, 48,176,
|
||||
8,136, 72,200, 40,168,232,104,248,120, 56,184, 24,152,216, 88,
|
||||
4,132, 68,196, 36,164,228,100, 20,148, 84,212,244,116, 52,180,
|
||||
252,124, 60,188, 28,156,220, 92, 12,140, 76,204,236,108, 44,172,
|
||||
2,130, 66,194, 34,162,226, 98, 18,146, 82,210,242,114, 50,178,
|
||||
10,138, 74,202, 42,170,234,106,250,122, 58,186, 26,154,218, 90,
|
||||
254,126, 62,190, 30,158,222, 94, 14,142, 78,206,238,110, 46,174,
|
||||
6,134, 70,198, 38,166,230,102,246,118, 54,182, 22,150,214, 86
|
||||
};
|
||||
|
||||
/* Root values for IFFT */
|
||||
static sample_t roots16[3];
|
||||
static sample_t roots32[7];
|
||||
static sample_t roots64[15];
|
||||
static sample_t roots128[31];
|
||||
|
||||
/* Twiddle factors for IMDCT */
|
||||
static complex_t pre1[128];
|
||||
static complex_t post1[64];
|
||||
static complex_t pre2[64];
|
||||
static complex_t post2[32];
|
||||
|
||||
static sample_t a52_imdct_window[256];
|
||||
|
||||
static void (* ifft128) (complex_t * buf);
|
||||
static void (* ifft64) (complex_t * buf);
|
||||
|
||||
static inline void ifft2 (complex_t * buf)
|
||||
{
|
||||
double r, i;
|
||||
|
||||
r = buf[0].real;
|
||||
i = buf[0].imag;
|
||||
buf[0].real += buf[1].real;
|
||||
buf[0].imag += buf[1].imag;
|
||||
buf[1].real = r - buf[1].real;
|
||||
buf[1].imag = i - buf[1].imag;
|
||||
}
|
||||
|
||||
static inline void ifft4 (complex_t * buf)
|
||||
{
|
||||
double tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8;
|
||||
|
||||
tmp1 = buf[0].real + buf[1].real;
|
||||
tmp2 = buf[3].real + buf[2].real;
|
||||
tmp3 = buf[0].imag + buf[1].imag;
|
||||
tmp4 = buf[2].imag + buf[3].imag;
|
||||
tmp5 = buf[0].real - buf[1].real;
|
||||
tmp6 = buf[0].imag - buf[1].imag;
|
||||
tmp7 = buf[2].imag - buf[3].imag;
|
||||
tmp8 = buf[3].real - buf[2].real;
|
||||
|
||||
buf[0].real = tmp1 + tmp2;
|
||||
buf[0].imag = tmp3 + tmp4;
|
||||
buf[2].real = tmp1 - tmp2;
|
||||
buf[2].imag = tmp3 - tmp4;
|
||||
buf[1].real = tmp5 + tmp7;
|
||||
buf[1].imag = tmp6 + tmp8;
|
||||
buf[3].real = tmp5 - tmp7;
|
||||
buf[3].imag = tmp6 - tmp8;
|
||||
}
|
||||
|
||||
/* the basic split-radix ifft butterfly */
|
||||
|
||||
#define BUTTERFLY(a0,a1,a2,a3,wr,wi) do { \
|
||||
tmp5 = a2.real * wr + a2.imag * wi; \
|
||||
tmp6 = a2.imag * wr - a2.real * wi; \
|
||||
tmp7 = a3.real * wr - a3.imag * wi; \
|
||||
tmp8 = a3.imag * wr + a3.real * wi; \
|
||||
tmp1 = tmp5 + tmp7; \
|
||||
tmp2 = tmp6 + tmp8; \
|
||||
tmp3 = tmp6 - tmp8; \
|
||||
tmp4 = tmp7 - tmp5; \
|
||||
a2.real = a0.real - tmp1; \
|
||||
a2.imag = a0.imag - tmp2; \
|
||||
a3.real = a1.real - tmp3; \
|
||||
a3.imag = a1.imag - tmp4; \
|
||||
a0.real += tmp1; \
|
||||
a0.imag += tmp2; \
|
||||
a1.real += tmp3; \
|
||||
a1.imag += tmp4; \
|
||||
} while (0)
|
||||
|
||||
/* split-radix ifft butterfly, specialized for wr=1 wi=0 */
|
||||
|
||||
#define BUTTERFLY_ZERO(a0,a1,a2,a3) do { \
|
||||
tmp1 = a2.real + a3.real; \
|
||||
tmp2 = a2.imag + a3.imag; \
|
||||
tmp3 = a2.imag - a3.imag; \
|
||||
tmp4 = a3.real - a2.real; \
|
||||
a2.real = a0.real - tmp1; \
|
||||
a2.imag = a0.imag - tmp2; \
|
||||
a3.real = a1.real - tmp3; \
|
||||
a3.imag = a1.imag - tmp4; \
|
||||
a0.real += tmp1; \
|
||||
a0.imag += tmp2; \
|
||||
a1.real += tmp3; \
|
||||
a1.imag += tmp4; \
|
||||
} while (0)
|
||||
|
||||
/* split-radix ifft butterfly, specialized for wr=wi */
|
||||
|
||||
#define BUTTERFLY_HALF(a0,a1,a2,a3,w) do { \
|
||||
tmp5 = (a2.real + a2.imag) * w; \
|
||||
tmp6 = (a2.imag - a2.real) * w; \
|
||||
tmp7 = (a3.real - a3.imag) * w; \
|
||||
tmp8 = (a3.imag + a3.real) * w; \
|
||||
tmp1 = tmp5 + tmp7; \
|
||||
tmp2 = tmp6 + tmp8; \
|
||||
tmp3 = tmp6 - tmp8; \
|
||||
tmp4 = tmp7 - tmp5; \
|
||||
a2.real = a0.real - tmp1; \
|
||||
a2.imag = a0.imag - tmp2; \
|
||||
a3.real = a1.real - tmp3; \
|
||||
a3.imag = a1.imag - tmp4; \
|
||||
a0.real += tmp1; \
|
||||
a0.imag += tmp2; \
|
||||
a1.real += tmp3; \
|
||||
a1.imag += tmp4; \
|
||||
} while (0)
|
||||
|
||||
static inline void ifft8 (complex_t * buf)
|
||||
{
|
||||
double tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8;
|
||||
|
||||
ifft4 (buf);
|
||||
ifft2 (buf + 4);
|
||||
ifft2 (buf + 6);
|
||||
BUTTERFLY_ZERO (buf[0], buf[2], buf[4], buf[6]);
|
||||
BUTTERFLY_HALF (buf[1], buf[3], buf[5], buf[7], roots16[1]);
|
||||
}
|
||||
|
||||
static void ifft_pass (complex_t * buf, sample_t * weight, int n)
|
||||
{
|
||||
complex_t * buf1;
|
||||
complex_t * buf2;
|
||||
complex_t * buf3;
|
||||
double tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8;
|
||||
int i;
|
||||
|
||||
buf++;
|
||||
buf1 = buf + n;
|
||||
buf2 = buf + 2 * n;
|
||||
buf3 = buf + 3 * n;
|
||||
|
||||
BUTTERFLY_ZERO (buf[-1], buf1[-1], buf2[-1], buf3[-1]);
|
||||
|
||||
i = n - 1;
|
||||
|
||||
do {
|
||||
BUTTERFLY (buf[0], buf1[0], buf2[0], buf3[0], weight[n], weight[2*i]);
|
||||
buf++;
|
||||
buf1++;
|
||||
buf2++;
|
||||
buf3++;
|
||||
weight++;
|
||||
} while (--i);
|
||||
}
|
||||
|
||||
static void ifft16 (complex_t * buf)
|
||||
{
|
||||
ifft8 (buf);
|
||||
ifft4 (buf + 8);
|
||||
ifft4 (buf + 12);
|
||||
ifft_pass (buf, roots16 - 4, 4);
|
||||
}
|
||||
|
||||
static void ifft32 (complex_t * buf)
|
||||
{
|
||||
ifft16 (buf);
|
||||
ifft8 (buf + 16);
|
||||
ifft8 (buf + 24);
|
||||
ifft_pass (buf, roots32 - 8, 8);
|
||||
}
|
||||
|
||||
static void ifft64_c (complex_t * buf)
|
||||
{
|
||||
ifft32 (buf);
|
||||
ifft16 (buf + 32);
|
||||
ifft16 (buf + 48);
|
||||
ifft_pass (buf, roots64 - 16, 16);
|
||||
}
|
||||
|
||||
static void ifft128_c (complex_t * buf)
|
||||
{
|
||||
ifft32 (buf);
|
||||
ifft16 (buf + 32);
|
||||
ifft16 (buf + 48);
|
||||
ifft_pass (buf, roots64 - 16, 16);
|
||||
|
||||
ifft32 (buf + 64);
|
||||
ifft32 (buf + 96);
|
||||
ifft_pass (buf, roots128 - 32, 32);
|
||||
}
|
||||
|
||||
void a52_imdct_512 (sample_t * data, sample_t * delay, sample_t bias)
|
||||
{
|
||||
int i, k;
|
||||
sample_t t_r, t_i, a_r, a_i, b_r, b_i, w_1, w_2;
|
||||
const sample_t * window = a52_imdct_window;
|
||||
complex_t buf[128];
|
||||
|
||||
for (i = 0; i < 128; i++) {
|
||||
k = fftorder[i];
|
||||
t_r = pre1[i].real;
|
||||
t_i = pre1[i].imag;
|
||||
|
||||
buf[i].real = t_i * data[255-k] + t_r * data[k];
|
||||
buf[i].imag = t_r * data[255-k] - t_i * data[k];
|
||||
}
|
||||
|
||||
ifft128 (buf);
|
||||
|
||||
/* Post IFFT complex multiply plus IFFT complex conjugate*/
|
||||
/* Window and convert to real valued signal */
|
||||
for (i = 0; i < 64; i++) {
|
||||
/* y[n] = z[n] * (xcos1[n] + j * xsin1[n]) ; */
|
||||
t_r = post1[i].real;
|
||||
t_i = post1[i].imag;
|
||||
|
||||
a_r = t_r * buf[i].real + t_i * buf[i].imag;
|
||||
a_i = t_i * buf[i].real - t_r * buf[i].imag;
|
||||
b_r = t_i * buf[127-i].real + t_r * buf[127-i].imag;
|
||||
b_i = t_r * buf[127-i].real - t_i * buf[127-i].imag;
|
||||
|
||||
w_1 = window[2*i];
|
||||
w_2 = window[255-2*i];
|
||||
data[2*i] = delay[2*i] * w_2 - a_r * w_1 + bias;
|
||||
data[255-2*i] = delay[2*i] * w_1 + a_r * w_2 + bias;
|
||||
delay[2*i] = a_i;
|
||||
|
||||
w_1 = window[2*i+1];
|
||||
w_2 = window[254-2*i];
|
||||
data[2*i+1] = delay[2*i+1] * w_2 + b_r * w_1 + bias;
|
||||
data[254-2*i] = delay[2*i+1] * w_1 - b_r * w_2 + bias;
|
||||
delay[2*i+1] = b_i;
|
||||
}
|
||||
}
|
||||
|
||||
void a52_imdct_256(sample_t * data, sample_t * delay, sample_t bias)
|
||||
{
|
||||
int i, k;
|
||||
sample_t t_r, t_i, a_r, a_i, b_r, b_i, c_r, c_i, d_r, d_i, w_1, w_2;
|
||||
const sample_t * window = a52_imdct_window;
|
||||
complex_t buf1[64], buf2[64];
|
||||
|
||||
/* Pre IFFT complex multiply plus IFFT cmplx conjugate */
|
||||
for (i = 0; i < 64; i++) {
|
||||
k = fftorder[i];
|
||||
t_r = pre2[i].real;
|
||||
t_i = pre2[i].imag;
|
||||
|
||||
buf1[i].real = t_i * data[254-k] + t_r * data[k];
|
||||
buf1[i].imag = t_r * data[254-k] - t_i * data[k];
|
||||
|
||||
buf2[i].real = t_i * data[255-k] + t_r * data[k+1];
|
||||
buf2[i].imag = t_r * data[255-k] - t_i * data[k+1];
|
||||
}
|
||||
|
||||
ifft64 (buf1);
|
||||
ifft64 (buf2);
|
||||
|
||||
/* Post IFFT complex multiply */
|
||||
/* Window and convert to real valued signal */
|
||||
for (i = 0; i < 32; i++) {
|
||||
/* y1[n] = z1[n] * (xcos2[n] + j * xs in2[n]) ; */
|
||||
t_r = post2[i].real;
|
||||
t_i = post2[i].imag;
|
||||
|
||||
a_r = t_r * buf1[i].real + t_i * buf1[i].imag;
|
||||
a_i = t_i * buf1[i].real - t_r * buf1[i].imag;
|
||||
b_r = t_i * buf1[63-i].real + t_r * buf1[63-i].imag;
|
||||
b_i = t_r * buf1[63-i].real - t_i * buf1[63-i].imag;
|
||||
|
||||
c_r = t_r * buf2[i].real + t_i * buf2[i].imag;
|
||||
c_i = t_i * buf2[i].real - t_r * buf2[i].imag;
|
||||
d_r = t_i * buf2[63-i].real + t_r * buf2[63-i].imag;
|
||||
d_i = t_r * buf2[63-i].real - t_i * buf2[63-i].imag;
|
||||
|
||||
w_1 = window[2*i];
|
||||
w_2 = window[255-2*i];
|
||||
data[2*i] = delay[2*i] * w_2 - a_r * w_1 + bias;
|
||||
data[255-2*i] = delay[2*i] * w_1 + a_r * w_2 + bias;
|
||||
delay[2*i] = c_i;
|
||||
|
||||
w_1 = window[128+2*i];
|
||||
w_2 = window[127-2*i];
|
||||
data[128+2*i] = delay[127-2*i] * w_2 + a_i * w_1 + bias;
|
||||
data[127-2*i] = delay[127-2*i] * w_1 - a_i * w_2 + bias;
|
||||
delay[127-2*i] = c_r;
|
||||
|
||||
w_1 = window[2*i+1];
|
||||
w_2 = window[254-2*i];
|
||||
data[2*i+1] = delay[2*i+1] * w_2 - b_i * w_1 + bias;
|
||||
data[254-2*i] = delay[2*i+1] * w_1 + b_i * w_2 + bias;
|
||||
delay[2*i+1] = d_r;
|
||||
|
||||
w_1 = window[129+2*i];
|
||||
w_2 = window[126-2*i];
|
||||
data[129+2*i] = delay[126-2*i] * w_2 + b_r * w_1 + bias;
|
||||
data[126-2*i] = delay[126-2*i] * w_1 - b_r * w_2 + bias;
|
||||
delay[126-2*i] = d_i;
|
||||
}
|
||||
}
|
||||
|
||||
static double besselI0 (double x)
|
||||
{
|
||||
double bessel = 1;
|
||||
int i = 100;
|
||||
|
||||
do
|
||||
bessel = bessel * x / (i * i) + 1;
|
||||
while (--i);
|
||||
return bessel;
|
||||
}
|
||||
|
||||
void a52_imdct_init (uint32_t mm_accel)
|
||||
{
|
||||
int i, k;
|
||||
double sum;
|
||||
|
||||
/* compute imdct window - kaiser-bessel derived window, alpha = 5.0 */
|
||||
sum = 0;
|
||||
for (i = 0; i < 256; i++) {
|
||||
sum += besselI0 (i * (256 - i) * (5 * M_PI / 256) * (5 * M_PI / 256));
|
||||
a52_imdct_window[i] = sum;
|
||||
}
|
||||
sum++;
|
||||
for (i = 0; i < 256; i++)
|
||||
a52_imdct_window[i] = sqrt (a52_imdct_window[i] / sum);
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
roots16[i] = cos ((M_PI / 8) * (i + 1));
|
||||
|
||||
for (i = 0; i < 7; i++)
|
||||
roots32[i] = cos ((M_PI / 16) * (i + 1));
|
||||
|
||||
for (i = 0; i < 15; i++)
|
||||
roots64[i] = cos ((M_PI / 32) * (i + 1));
|
||||
|
||||
for (i = 0; i < 31; i++)
|
||||
roots128[i] = cos ((M_PI / 64) * (i + 1));
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
k = fftorder[i] / 2 + 64;
|
||||
pre1[i].real = cos ((M_PI / 256) * (k - 0.25));
|
||||
pre1[i].imag = sin ((M_PI / 256) * (k - 0.25));
|
||||
}
|
||||
|
||||
for (i = 64; i < 128; i++) {
|
||||
k = fftorder[i] / 2 + 64;
|
||||
pre1[i].real = -cos ((M_PI / 256) * (k - 0.25));
|
||||
pre1[i].imag = -sin ((M_PI / 256) * (k - 0.25));
|
||||
}
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
post1[i].real = cos ((M_PI / 256) * (i + 0.5));
|
||||
post1[i].imag = sin ((M_PI / 256) * (i + 0.5));
|
||||
}
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
k = fftorder[i] / 4;
|
||||
pre2[i].real = cos ((M_PI / 128) * (k - 0.25));
|
||||
pre2[i].imag = sin ((M_PI / 128) * (k - 0.25));
|
||||
}
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
post2[i].real = cos ((M_PI / 128) * (i + 0.5));
|
||||
post2[i].imag = sin ((M_PI / 128) * (i + 0.5));
|
||||
}
|
||||
|
||||
#ifdef LIBA52_DJBFFT
|
||||
if (mm_accel & MM_ACCEL_DJBFFT) {
|
||||
fprintf (stderr, "Using djbfft for IMDCT transform\n");
|
||||
ifft128 = (void (*) (complex_t *)) fftc4_un128;
|
||||
ifft64 = (void (*) (complex_t *)) fftc4_un64;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
fprintf (stderr, "No accelerated IMDCT transform found\n");
|
||||
ifft128 = ifft128_c;
|
||||
ifft64 = ifft64_c;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
typedef signed char int8_t;
|
||||
typedef signed short int16_t;
|
||||
typedef signed int int32_t;
|
||||
#ifdef ARCH_X86
|
||||
typedef signed long long int64_t;
|
||||
#endif
|
||||
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
#ifdef ARCH_X86
|
||||
typedef unsigned long long uint64_t;
|
||||
#endif
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* mm_accel.h
|
||||
* Copyright (C) 2000-2002 Michel Lespinasse <walken@zoy.org>
|
||||
* Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
|
||||
*
|
||||
* This file is part of a52dec, a free ATSC A-52 stream decoder.
|
||||
* See http://liba52.sourceforge.net/ for updates.
|
||||
*
|
||||
* a52dec 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.
|
||||
*
|
||||
* a52dec is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef MM_ACCEL_H
|
||||
#define MM_ACCEL_H
|
||||
|
||||
/* generic accelerations */
|
||||
#define MM_ACCEL_DJBFFT 0x00000001
|
||||
|
||||
/* x86 accelerations */
|
||||
#define MM_ACCEL_X86_MMX 0x80000000
|
||||
#define MM_ACCEL_X86_3DNOW 0x40000000
|
||||
#define MM_ACCEL_X86_MMXEXT 0x20000000
|
||||
|
||||
uint32_t mm_accel (void);
|
||||
|
||||
#endif /* MM_ACCEL_H */
|
|
@ -0,0 +1,903 @@
|
|||
/*
|
||||
* parse.c
|
||||
* Copyright (C) 2000-2002 Michel Lespinasse <walken@zoy.org>
|
||||
* Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
|
||||
*
|
||||
* This file is part of a52dec, a free ATSC A-52 stream decoder.
|
||||
* See http://liba52.sourceforge.net/ for updates.
|
||||
*
|
||||
* a52dec 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.
|
||||
*
|
||||
* a52dec is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#pragma warning(disable:4305)
|
||||
#pragma warning(disable:4244)
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "inttypes.h"
|
||||
|
||||
#include "a52.h"
|
||||
#include "a52_internal.h"
|
||||
#include "bitstream.h"
|
||||
#include "tables.h"
|
||||
|
||||
#ifdef HAVE_MEMALIGN
|
||||
/* some systems have memalign() but no declaration for it */
|
||||
void * memalign (size_t align, size_t size);
|
||||
#else
|
||||
/* assume malloc alignment is sufficient */
|
||||
#define memalign(align,size) malloc (size)
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
sample_t q1[2];
|
||||
sample_t q2[2];
|
||||
sample_t q4;
|
||||
int q1_ptr;
|
||||
int q2_ptr;
|
||||
int q4_ptr;
|
||||
} quantizer_t;
|
||||
|
||||
static uint8_t halfrate[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3};
|
||||
|
||||
a52_state_t * a52_init (uint32_t mm_accel)
|
||||
{
|
||||
a52_state_t * state;
|
||||
int i;
|
||||
|
||||
state = malloc (sizeof (a52_state_t));
|
||||
if (state == NULL)
|
||||
return NULL;
|
||||
|
||||
state->samples = memalign (16, 256 * 12 * sizeof (sample_t));
|
||||
if (state->samples == NULL) {
|
||||
free (state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < 256 * 12; i++)
|
||||
state->samples[i] = 0;
|
||||
|
||||
state->downmixed = 1;
|
||||
|
||||
state->lfsr_state = 1;
|
||||
|
||||
a52_imdct_init (mm_accel);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
sample_t * a52_samples (a52_state_t * state)
|
||||
{
|
||||
return state->samples;
|
||||
}
|
||||
|
||||
int a52_syncinfo (uint8_t * buf, int * flags,
|
||||
int * sample_rate, int * bit_rate)
|
||||
{
|
||||
static int rate[] = { 32, 40, 48, 56, 64, 80, 96, 112,
|
||||
128, 160, 192, 224, 256, 320, 384, 448,
|
||||
512, 576, 640};
|
||||
static uint8_t lfeon[8] = {0x10, 0x10, 0x04, 0x04, 0x04, 0x01, 0x04, 0x01};
|
||||
int frmsizecod;
|
||||
int bitrate;
|
||||
int half;
|
||||
int acmod;
|
||||
|
||||
if ((buf[0] != 0x0b) || (buf[1] != 0x77)) /* syncword */
|
||||
return 0;
|
||||
|
||||
if (buf[5] >= 0x60) /* bsid >= 12 */
|
||||
return 0;
|
||||
half = halfrate[buf[5] >> 3];
|
||||
|
||||
/* acmod, dsurmod and lfeon */
|
||||
acmod = buf[6] >> 5;
|
||||
*flags = ((((buf[6] & 0xf8) == 0x50) ? A52_DOLBY : acmod) |
|
||||
((buf[6] & lfeon[acmod]) ? A52_LFE : 0));
|
||||
|
||||
frmsizecod = buf[4] & 63;
|
||||
if (frmsizecod >= 38)
|
||||
return 0;
|
||||
bitrate = rate [frmsizecod >> 1];
|
||||
*bit_rate = (bitrate * 1000) >> half;
|
||||
|
||||
switch (buf[4] & 0xc0) {
|
||||
case 0:
|
||||
*sample_rate = 48000 >> half;
|
||||
return 4 * bitrate;
|
||||
case 0x40:
|
||||
*sample_rate = 44100 >> half;
|
||||
return 2 * (320 * bitrate / 147 + (frmsizecod & 1));
|
||||
case 0x80:
|
||||
*sample_rate = 32000 >> half;
|
||||
return 6 * bitrate;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int a52_frame (a52_state_t * state, uint8_t * buf, int * flags,
|
||||
sample_t * level, sample_t bias)
|
||||
{
|
||||
static sample_t clev[4] = {LEVEL_3DB, LEVEL_45DB, LEVEL_6DB, LEVEL_45DB};
|
||||
static sample_t slev[4] = {LEVEL_3DB, LEVEL_6DB, 0, LEVEL_6DB};
|
||||
int chaninfo;
|
||||
int acmod;
|
||||
|
||||
state->fscod = buf[4] >> 6;
|
||||
state->halfrate = halfrate[buf[5] >> 3];
|
||||
state->acmod = acmod = buf[6] >> 5;
|
||||
|
||||
a52_bitstream_set_ptr (state, buf + 6);
|
||||
bitstream_get (state, 3); /* skip acmod we already parsed */
|
||||
|
||||
if ((acmod == 2) && (bitstream_get (state, 2) == 2)) /* dsurmod */
|
||||
acmod = A52_DOLBY;
|
||||
|
||||
if ((acmod & 1) && (acmod != 1))
|
||||
state->clev = clev[bitstream_get (state, 2)]; /* cmixlev */
|
||||
|
||||
if (acmod & 4)
|
||||
state->slev = slev[bitstream_get (state, 2)]; /* surmixlev */
|
||||
|
||||
state->lfeon = bitstream_get (state, 1);
|
||||
|
||||
state->output = a52_downmix_init (acmod, *flags, level,
|
||||
state->clev, state->slev);
|
||||
if (state->output < 0)
|
||||
return 1;
|
||||
if (state->lfeon && (*flags & A52_LFE))
|
||||
state->output |= A52_LFE;
|
||||
*flags = state->output;
|
||||
/* the 2* compensates for differences in imdct */
|
||||
state->dynrng = state->level = 2 * *level;
|
||||
state->bias = bias;
|
||||
state->dynrnge = 1;
|
||||
state->dynrngcall = NULL;
|
||||
state->cplba.deltbae = DELTA_BIT_NONE;
|
||||
state->ba[0].deltbae = state->ba[1].deltbae = state->ba[2].deltbae =
|
||||
state->ba[3].deltbae = state->ba[4].deltbae = DELTA_BIT_NONE;
|
||||
|
||||
chaninfo = !acmod;
|
||||
do {
|
||||
bitstream_get (state, 5); /* dialnorm */
|
||||
if (bitstream_get (state, 1)) /* compre */
|
||||
bitstream_get (state, 8); /* compr */
|
||||
if (bitstream_get (state, 1)) /* langcode */
|
||||
bitstream_get (state, 8); /* langcod */
|
||||
if (bitstream_get (state, 1)) /* audprodie */
|
||||
bitstream_get (state, 7); /* mixlevel + roomtyp */
|
||||
} while (chaninfo--);
|
||||
|
||||
bitstream_get (state, 2); /* copyrightb + origbs */
|
||||
|
||||
if (bitstream_get (state, 1)) /* timecod1e */
|
||||
bitstream_get (state, 14); /* timecod1 */
|
||||
if (bitstream_get (state, 1)) /* timecod2e */
|
||||
bitstream_get (state, 14); /* timecod2 */
|
||||
|
||||
if (bitstream_get (state, 1)) { /* addbsie */
|
||||
int addbsil;
|
||||
|
||||
addbsil = bitstream_get (state, 6);
|
||||
do {
|
||||
bitstream_get (state, 8); /* addbsi */
|
||||
} while (addbsil--);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void a52_dynrng (a52_state_t * state,
|
||||
sample_t (* call) (sample_t, void *), void * data)
|
||||
{
|
||||
state->dynrnge = 0;
|
||||
if (call) {
|
||||
state->dynrnge = 1;
|
||||
state->dynrngcall = call;
|
||||
state->dynrngdata = data;
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_exponents (a52_state_t * state, int expstr, int ngrps,
|
||||
uint8_t exponent, uint8_t * dest)
|
||||
{
|
||||
int exps;
|
||||
|
||||
while (ngrps--) {
|
||||
exps = bitstream_get (state, 7);
|
||||
|
||||
exponent += exp_1[exps];
|
||||
if (exponent > 24)
|
||||
return 1;
|
||||
|
||||
switch (expstr) {
|
||||
case EXP_D45:
|
||||
*(dest++) = exponent;
|
||||
*(dest++) = exponent;
|
||||
case EXP_D25:
|
||||
*(dest++) = exponent;
|
||||
case EXP_D15:
|
||||
*(dest++) = exponent;
|
||||
}
|
||||
|
||||
exponent += exp_2[exps];
|
||||
if (exponent > 24)
|
||||
return 1;
|
||||
|
||||
switch (expstr) {
|
||||
case EXP_D45:
|
||||
*(dest++) = exponent;
|
||||
*(dest++) = exponent;
|
||||
case EXP_D25:
|
||||
*(dest++) = exponent;
|
||||
case EXP_D15:
|
||||
*(dest++) = exponent;
|
||||
}
|
||||
|
||||
exponent += exp_3[exps];
|
||||
if (exponent > 24)
|
||||
return 1;
|
||||
|
||||
switch (expstr) {
|
||||
case EXP_D45:
|
||||
*(dest++) = exponent;
|
||||
*(dest++) = exponent;
|
||||
case EXP_D25:
|
||||
*(dest++) = exponent;
|
||||
case EXP_D15:
|
||||
*(dest++) = exponent;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_deltba (a52_state_t * state, int8_t * deltba)
|
||||
{
|
||||
int deltnseg, deltlen, delta, j;
|
||||
|
||||
memset (deltba, 0, 50);
|
||||
|
||||
deltnseg = bitstream_get (state, 3);
|
||||
j = 0;
|
||||
do {
|
||||
j += bitstream_get (state, 5);
|
||||
deltlen = bitstream_get (state, 4);
|
||||
delta = bitstream_get (state, 3);
|
||||
delta -= (delta >= 4) ? 3 : 4;
|
||||
if (!deltlen)
|
||||
continue;
|
||||
if (j + deltlen >= 50)
|
||||
return 1;
|
||||
while (deltlen--)
|
||||
deltba[j++] = delta;
|
||||
} while (deltnseg--);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int zero_snr_offsets (int nfchans, a52_state_t * state)
|
||||
{
|
||||
int i;
|
||||
|
||||
if ((state->csnroffst) ||
|
||||
(state->chincpl && state->cplba.bai >> 3) || /* cplinu, fsnroffst */
|
||||
(state->lfeon && state->lfeba.bai >> 3)) /* fsnroffst */
|
||||
return 0;
|
||||
for (i = 0; i < nfchans; i++)
|
||||
if (state->ba[i].bai >> 3) /* fsnroffst */
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int16_t dither_gen (a52_state_t * state)
|
||||
{
|
||||
int16_t nstate;
|
||||
|
||||
nstate = dither_lut[state->lfsr_state >> 8] ^ (state->lfsr_state << 8);
|
||||
|
||||
state->lfsr_state = (uint16_t) nstate;
|
||||
|
||||
return nstate;
|
||||
}
|
||||
|
||||
static void coeff_get (a52_state_t * state, sample_t * coeff,
|
||||
expbap_t * expbap, quantizer_t * quantizer,
|
||||
sample_t level, int dither, int end)
|
||||
{
|
||||
int i;
|
||||
uint8_t * exp;
|
||||
int8_t * bap;
|
||||
sample_t factor[25];
|
||||
|
||||
for (i = 0; i <= 24; i++)
|
||||
factor[i] = scale_factor[i] * level;
|
||||
|
||||
exp = expbap->exp;
|
||||
bap = expbap->bap;
|
||||
|
||||
for (i = 0; i < end; i++) {
|
||||
int bapi;
|
||||
|
||||
bapi = bap[i];
|
||||
switch (bapi) {
|
||||
case 0:
|
||||
if (dither) {
|
||||
coeff[i] = dither_gen (state) * LEVEL_3DB * factor[exp[i]];
|
||||
continue;
|
||||
} else {
|
||||
coeff[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
case -1:
|
||||
if (quantizer->q1_ptr >= 0) {
|
||||
coeff[i] = quantizer->q1[quantizer->q1_ptr--] * factor[exp[i]];
|
||||
continue;
|
||||
} else {
|
||||
int code;
|
||||
|
||||
code = bitstream_get (state, 5);
|
||||
|
||||
quantizer->q1_ptr = 1;
|
||||
quantizer->q1[0] = q_1_2[code];
|
||||
quantizer->q1[1] = q_1_1[code];
|
||||
coeff[i] = q_1_0[code] * factor[exp[i]];
|
||||
continue;
|
||||
}
|
||||
|
||||
case -2:
|
||||
if (quantizer->q2_ptr >= 0) {
|
||||
coeff[i] = quantizer->q2[quantizer->q2_ptr--] * factor[exp[i]];
|
||||
continue;
|
||||
} else {
|
||||
int code;
|
||||
|
||||
code = bitstream_get (state, 7);
|
||||
|
||||
quantizer->q2_ptr = 1;
|
||||
quantizer->q2[0] = q_2_2[code];
|
||||
quantizer->q2[1] = q_2_1[code];
|
||||
coeff[i] = q_2_0[code] * factor[exp[i]];
|
||||
continue;
|
||||
}
|
||||
|
||||
case 3:
|
||||
coeff[i] = q_3[bitstream_get (state, 3)] * factor[exp[i]];
|
||||
continue;
|
||||
|
||||
case -3:
|
||||
if (quantizer->q4_ptr == 0) {
|
||||
quantizer->q4_ptr = -1;
|
||||
coeff[i] = quantizer->q4 * factor[exp[i]];
|
||||
continue;
|
||||
} else {
|
||||
int code;
|
||||
|
||||
code = bitstream_get (state, 7);
|
||||
|
||||
quantizer->q4_ptr = 0;
|
||||
quantizer->q4 = q_4_1[code];
|
||||
coeff[i] = q_4_0[code] * factor[exp[i]];
|
||||
continue;
|
||||
}
|
||||
|
||||
case 4:
|
||||
coeff[i] = q_5[bitstream_get (state, 4)] * factor[exp[i]];
|
||||
continue;
|
||||
|
||||
default:
|
||||
coeff[i] = ((bitstream_get_2 (state, bapi) << (16 - bapi)) *
|
||||
factor[exp[i]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void coeff_get_coupling (a52_state_t * state, int nfchans,
|
||||
sample_t * coeff, sample_t (* samples)[256],
|
||||
quantizer_t * quantizer, uint8_t dithflag[5])
|
||||
{
|
||||
int cplbndstrc, bnd, i, i_end, ch;
|
||||
uint8_t * exp;
|
||||
int8_t * bap;
|
||||
sample_t cplco[5];
|
||||
|
||||
exp = state->cpl_expbap.exp;
|
||||
bap = state->cpl_expbap.bap;
|
||||
bnd = 0;
|
||||
cplbndstrc = state->cplbndstrc;
|
||||
i = state->cplstrtmant;
|
||||
while (i < state->cplendmant) {
|
||||
i_end = i + 12;
|
||||
while (cplbndstrc & 1) {
|
||||
cplbndstrc >>= 1;
|
||||
i_end += 12;
|
||||
}
|
||||
cplbndstrc >>= 1;
|
||||
for (ch = 0; ch < nfchans; ch++)
|
||||
cplco[ch] = state->cplco[ch][bnd] * coeff[ch];
|
||||
bnd++;
|
||||
|
||||
while (i < i_end) {
|
||||
sample_t cplcoeff;
|
||||
int bapi;
|
||||
|
||||
bapi = bap[i];
|
||||
switch (bapi) {
|
||||
case 0:
|
||||
cplcoeff = LEVEL_3DB * scale_factor[exp[i]];
|
||||
for (ch = 0; ch < nfchans; ch++)
|
||||
if ((state->chincpl >> ch) & 1) {
|
||||
if (dithflag[ch])
|
||||
samples[ch][i] = (cplcoeff * cplco[ch] *
|
||||
dither_gen (state));
|
||||
else
|
||||
samples[ch][i] = 0;
|
||||
}
|
||||
i++;
|
||||
continue;
|
||||
|
||||
case -1:
|
||||
if (quantizer->q1_ptr >= 0) {
|
||||
cplcoeff = quantizer->q1[quantizer->q1_ptr--];
|
||||
break;
|
||||
} else {
|
||||
int code;
|
||||
|
||||
code = bitstream_get (state, 5);
|
||||
|
||||
quantizer->q1_ptr = 1;
|
||||
quantizer->q1[0] = q_1_2[code];
|
||||
quantizer->q1[1] = q_1_1[code];
|
||||
cplcoeff = q_1_0[code];
|
||||
break;
|
||||
}
|
||||
|
||||
case -2:
|
||||
if (quantizer->q2_ptr >= 0) {
|
||||
cplcoeff = quantizer->q2[quantizer->q2_ptr--];
|
||||
break;
|
||||
} else {
|
||||
int code;
|
||||
|
||||
code = bitstream_get (state, 7);
|
||||
|
||||
quantizer->q2_ptr = 1;
|
||||
quantizer->q2[0] = q_2_2[code];
|
||||
quantizer->q2[1] = q_2_1[code];
|
||||
cplcoeff = q_2_0[code];
|
||||
break;
|
||||
}
|
||||
|
||||
case 3:
|
||||
cplcoeff = q_3[bitstream_get (state, 3)];
|
||||
break;
|
||||
|
||||
case -3:
|
||||
if (quantizer->q4_ptr == 0) {
|
||||
quantizer->q4_ptr = -1;
|
||||
cplcoeff = quantizer->q4;
|
||||
break;
|
||||
} else {
|
||||
int code;
|
||||
|
||||
code = bitstream_get (state, 7);
|
||||
|
||||
quantizer->q4_ptr = 0;
|
||||
quantizer->q4 = q_4_1[code];
|
||||
cplcoeff = q_4_0[code];
|
||||
break;
|
||||
}
|
||||
|
||||
case 4:
|
||||
cplcoeff = q_5[bitstream_get (state, 4)];
|
||||
break;
|
||||
|
||||
default:
|
||||
cplcoeff = bitstream_get_2 (state, bapi) << (16 - bapi);
|
||||
}
|
||||
|
||||
cplcoeff *= scale_factor[exp[i]];
|
||||
for (ch = 0; ch < nfchans; ch++)
|
||||
if ((state->chincpl >> ch) & 1)
|
||||
samples[ch][i] = cplcoeff * cplco[ch];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int a52_block (a52_state_t * state)
|
||||
{
|
||||
static const uint8_t nfchans_tbl[] = {2, 1, 2, 3, 3, 4, 4, 5, 1, 1, 2};
|
||||
static int rematrix_band[4] = {25, 37, 61, 253};
|
||||
int i, nfchans, chaninfo;
|
||||
uint8_t cplexpstr, chexpstr[5], lfeexpstr, do_bit_alloc, done_cpl;
|
||||
uint8_t blksw[5], dithflag[5];
|
||||
sample_t coeff[5];
|
||||
int chanbias;
|
||||
quantizer_t quantizer;
|
||||
sample_t * samples;
|
||||
|
||||
nfchans = nfchans_tbl[state->acmod];
|
||||
|
||||
for (i = 0; i < nfchans; i++)
|
||||
blksw[i] = bitstream_get (state, 1);
|
||||
|
||||
for (i = 0; i < nfchans; i++)
|
||||
dithflag[i] = bitstream_get (state, 1);
|
||||
|
||||
chaninfo = !state->acmod;
|
||||
do {
|
||||
if (bitstream_get (state, 1)) { /* dynrnge */
|
||||
int dynrng;
|
||||
|
||||
dynrng = bitstream_get_2 (state, 8);
|
||||
if (state->dynrnge) {
|
||||
sample_t range;
|
||||
|
||||
range = ((((dynrng & 0x1f) | 0x20) << 13) *
|
||||
scale_factor[3 - (dynrng >> 5)]);
|
||||
if (state->dynrngcall)
|
||||
range = state->dynrngcall (range, state->dynrngdata);
|
||||
state->dynrng = state->level * range;
|
||||
}
|
||||
}
|
||||
} while (chaninfo--);
|
||||
|
||||
if (bitstream_get (state, 1)) { /* cplstre */
|
||||
state->chincpl = 0;
|
||||
if (bitstream_get (state, 1)) { /* cplinu */
|
||||
static uint8_t bndtab[16] = {31, 35, 37, 39, 41, 42, 43, 44,
|
||||
45, 45, 46, 46, 47, 47, 48, 48};
|
||||
int cplbegf;
|
||||
int cplendf;
|
||||
int ncplsubnd;
|
||||
|
||||
for (i = 0; i < nfchans; i++)
|
||||
state->chincpl |= bitstream_get (state, 1) << i;
|
||||
switch (state->acmod) {
|
||||
case 0: case 1:
|
||||
return 1;
|
||||
case 2:
|
||||
state->phsflginu = bitstream_get (state, 1);
|
||||
}
|
||||
cplbegf = bitstream_get (state, 4);
|
||||
cplendf = bitstream_get (state, 4);
|
||||
|
||||
if (cplendf + 3 - cplbegf < 0)
|
||||
return 1;
|
||||
state->ncplbnd = ncplsubnd = cplendf + 3 - cplbegf;
|
||||
state->cplstrtbnd = bndtab[cplbegf];
|
||||
state->cplstrtmant = cplbegf * 12 + 37;
|
||||
state->cplendmant = cplendf * 12 + 73;
|
||||
|
||||
state->cplbndstrc = 0;
|
||||
for (i = 0; i < ncplsubnd - 1; i++)
|
||||
if (bitstream_get (state, 1)) {
|
||||
state->cplbndstrc |= 1 << i;
|
||||
state->ncplbnd--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state->chincpl) { /* cplinu */
|
||||
int j, cplcoe;
|
||||
|
||||
cplcoe = 0;
|
||||
for (i = 0; i < nfchans; i++)
|
||||
if ((state->chincpl) >> i & 1)
|
||||
if (bitstream_get (state, 1)) { /* cplcoe */
|
||||
int mstrcplco, cplcoexp, cplcomant;
|
||||
|
||||
cplcoe = 1;
|
||||
mstrcplco = 3 * bitstream_get (state, 2);
|
||||
for (j = 0; j < state->ncplbnd; j++) {
|
||||
cplcoexp = bitstream_get (state, 4);
|
||||
cplcomant = bitstream_get (state, 4);
|
||||
if (cplcoexp == 15)
|
||||
cplcomant <<= 14;
|
||||
else
|
||||
cplcomant = (cplcomant | 0x10) << 13;
|
||||
state->cplco[i][j] =
|
||||
cplcomant * scale_factor[cplcoexp + mstrcplco];
|
||||
}
|
||||
}
|
||||
if ((state->acmod == 2) && state->phsflginu && cplcoe)
|
||||
for (j = 0; j < state->ncplbnd; j++)
|
||||
if (bitstream_get (state, 1)) /* phsflg */
|
||||
state->cplco[1][j] = -state->cplco[1][j];
|
||||
}
|
||||
|
||||
if ((state->acmod == 2) && (bitstream_get (state, 1))) { /* rematstr */
|
||||
int end;
|
||||
|
||||
state->rematflg = 0;
|
||||
end = (state->chincpl) ? state->cplstrtmant : 253; /* cplinu */
|
||||
i = 0;
|
||||
do
|
||||
state->rematflg |= bitstream_get (state, 1) << i;
|
||||
while (rematrix_band[i++] < end);
|
||||
}
|
||||
|
||||
cplexpstr = EXP_REUSE;
|
||||
lfeexpstr = EXP_REUSE;
|
||||
if (state->chincpl) /* cplinu */
|
||||
cplexpstr = bitstream_get (state, 2);
|
||||
for (i = 0; i < nfchans; i++)
|
||||
chexpstr[i] = bitstream_get (state, 2);
|
||||
if (state->lfeon)
|
||||
lfeexpstr = bitstream_get (state, 1);
|
||||
|
||||
for (i = 0; i < nfchans; i++)
|
||||
if (chexpstr[i] != EXP_REUSE) {
|
||||
if ((state->chincpl >> i) & 1)
|
||||
state->endmant[i] = state->cplstrtmant;
|
||||
else {
|
||||
int chbwcod;
|
||||
|
||||
chbwcod = bitstream_get (state, 6);
|
||||
if (chbwcod > 60)
|
||||
return 1;
|
||||
state->endmant[i] = chbwcod * 3 + 73;
|
||||
}
|
||||
}
|
||||
|
||||
do_bit_alloc = 0;
|
||||
|
||||
if (cplexpstr != EXP_REUSE) {
|
||||
int cplabsexp, ncplgrps;
|
||||
|
||||
do_bit_alloc = 64;
|
||||
ncplgrps = ((state->cplendmant - state->cplstrtmant) /
|
||||
(3 << (cplexpstr - 1)));
|
||||
cplabsexp = bitstream_get (state, 4) << 1;
|
||||
if (parse_exponents (state, cplexpstr, ncplgrps, cplabsexp,
|
||||
state->cpl_expbap.exp + state->cplstrtmant))
|
||||
return 1;
|
||||
}
|
||||
for (i = 0; i < nfchans; i++)
|
||||
if (chexpstr[i] != EXP_REUSE) {
|
||||
int grp_size, nchgrps;
|
||||
|
||||
do_bit_alloc |= 1 << i;
|
||||
grp_size = 3 << (chexpstr[i] - 1);
|
||||
nchgrps = (state->endmant[i] + grp_size - 4) / grp_size;
|
||||
state->fbw_expbap[i].exp[0] = bitstream_get (state, 4);
|
||||
if (parse_exponents (state, chexpstr[i], nchgrps,
|
||||
state->fbw_expbap[i].exp[0],
|
||||
state->fbw_expbap[i].exp + 1))
|
||||
return 1;
|
||||
bitstream_get (state, 2); /* gainrng */
|
||||
}
|
||||
if (lfeexpstr != EXP_REUSE) {
|
||||
do_bit_alloc |= 32;
|
||||
state->lfe_expbap.exp[0] = bitstream_get (state, 4);
|
||||
if (parse_exponents (state, lfeexpstr, 2, state->lfe_expbap.exp[0],
|
||||
state->lfe_expbap.exp + 1))
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (bitstream_get (state, 1)) { /* baie */
|
||||
do_bit_alloc = -1;
|
||||
state->bai = bitstream_get (state, 11);
|
||||
}
|
||||
if (bitstream_get (state, 1)) { /* snroffste */
|
||||
do_bit_alloc = -1;
|
||||
state->csnroffst = bitstream_get (state, 6);
|
||||
if (state->chincpl) /* cplinu */
|
||||
state->cplba.bai = bitstream_get (state, 7);
|
||||
for (i = 0; i < nfchans; i++)
|
||||
state->ba[i].bai = bitstream_get (state, 7);
|
||||
if (state->lfeon)
|
||||
state->lfeba.bai = bitstream_get (state, 7);
|
||||
}
|
||||
if ((state->chincpl) && (bitstream_get (state, 1))) { /* cplleake */
|
||||
do_bit_alloc |= 64;
|
||||
state->cplfleak = 9 - bitstream_get (state, 3);
|
||||
state->cplsleak = 9 - bitstream_get (state, 3);
|
||||
}
|
||||
|
||||
if (bitstream_get (state, 1)) { /* deltbaie */
|
||||
do_bit_alloc = -1;
|
||||
if (state->chincpl) /* cplinu */
|
||||
state->cplba.deltbae = bitstream_get (state, 2);
|
||||
for (i = 0; i < nfchans; i++)
|
||||
state->ba[i].deltbae = bitstream_get (state, 2);
|
||||
if (state->chincpl && /* cplinu */
|
||||
(state->cplba.deltbae == DELTA_BIT_NEW) &&
|
||||
parse_deltba (state, state->cplba.deltba))
|
||||
return 1;
|
||||
for (i = 0; i < nfchans; i++)
|
||||
if ((state->ba[i].deltbae == DELTA_BIT_NEW) &&
|
||||
parse_deltba (state, state->ba[i].deltba))
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (do_bit_alloc) {
|
||||
if (zero_snr_offsets (nfchans, state)) {
|
||||
memset (state->cpl_expbap.bap, 0, sizeof (state->cpl_expbap.bap));
|
||||
for (i = 0; i < nfchans; i++)
|
||||
memset (state->fbw_expbap[i].bap, 0,
|
||||
sizeof (state->fbw_expbap[i].bap));
|
||||
memset (state->lfe_expbap.bap, 0, sizeof (state->lfe_expbap.bap));
|
||||
} else {
|
||||
if (state->chincpl && (do_bit_alloc & 64)) /* cplinu */
|
||||
a52_bit_allocate (state, &state->cplba, state->cplstrtbnd,
|
||||
state->cplstrtmant, state->cplendmant,
|
||||
state->cplfleak << 8, state->cplsleak << 8,
|
||||
&state->cpl_expbap);
|
||||
for (i = 0; i < nfchans; i++)
|
||||
if (do_bit_alloc & (1 << i))
|
||||
a52_bit_allocate (state, state->ba + i, 0, 0,
|
||||
state->endmant[i], 0, 0,
|
||||
state->fbw_expbap +i);
|
||||
if (state->lfeon && (do_bit_alloc & 32)) {
|
||||
state->lfeba.deltbae = DELTA_BIT_NONE;
|
||||
a52_bit_allocate (state, &state->lfeba, 0, 0, 7, 0, 0,
|
||||
&state->lfe_expbap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bitstream_get (state, 1)) { /* skiple */
|
||||
i = bitstream_get (state, 9); /* skipl */
|
||||
while (i--)
|
||||
bitstream_get (state, 8);
|
||||
}
|
||||
|
||||
samples = state->samples;
|
||||
if (state->output & A52_LFE)
|
||||
samples += 256; /* shift for LFE channel */
|
||||
|
||||
chanbias = a52_downmix_coeff (coeff, state->acmod, state->output,
|
||||
state->dynrng, state->clev, state->slev);
|
||||
|
||||
quantizer.q1_ptr = quantizer.q2_ptr = quantizer.q4_ptr = -1;
|
||||
done_cpl = 0;
|
||||
|
||||
for (i = 0; i < nfchans; i++) {
|
||||
int j;
|
||||
|
||||
coeff_get (state, samples + 256 * i, state->fbw_expbap +i, &quantizer,
|
||||
coeff[i], dithflag[i], state->endmant[i]);
|
||||
|
||||
if ((state->chincpl >> i) & 1) {
|
||||
if (!done_cpl) {
|
||||
done_cpl = 1;
|
||||
coeff_get_coupling (state, nfchans, coeff,
|
||||
(sample_t (*)[256])samples, &quantizer,
|
||||
dithflag);
|
||||
}
|
||||
j = state->cplendmant;
|
||||
} else
|
||||
j = state->endmant[i];
|
||||
do
|
||||
(samples + 256 * i)[j] = 0;
|
||||
while (++j < 256);
|
||||
}
|
||||
|
||||
if (state->acmod == 2) {
|
||||
int j, end, band, rematflg;
|
||||
|
||||
end = ((state->endmant[0] < state->endmant[1]) ?
|
||||
state->endmant[0] : state->endmant[1]);
|
||||
|
||||
i = 0;
|
||||
j = 13;
|
||||
rematflg = state->rematflg;
|
||||
do {
|
||||
if (! (rematflg & 1)) {
|
||||
rematflg >>= 1;
|
||||
j = rematrix_band[i++];
|
||||
continue;
|
||||
}
|
||||
rematflg >>= 1;
|
||||
band = rematrix_band[i++];
|
||||
if (band > end)
|
||||
band = end;
|
||||
do {
|
||||
sample_t tmp0, tmp1;
|
||||
|
||||
tmp0 = samples[j];
|
||||
tmp1 = (samples+256)[j];
|
||||
samples[j] = tmp0 + tmp1;
|
||||
(samples+256)[j] = tmp0 - tmp1;
|
||||
} while (++j < band);
|
||||
} while (j < end);
|
||||
}
|
||||
|
||||
if (state->lfeon) {
|
||||
if (state->output & A52_LFE) {
|
||||
coeff_get (state, samples - 256, &state->lfe_expbap, &quantizer,
|
||||
state->dynrng, 0, 7);
|
||||
for (i = 7; i < 256; i++)
|
||||
(samples-256)[i] = 0;
|
||||
a52_imdct_512 (samples - 256, samples + 1536 - 256, state->bias);
|
||||
} else {
|
||||
/* just skip the LFE coefficients */
|
||||
coeff_get (state, samples + 1280, &state->lfe_expbap, &quantizer,
|
||||
0, 0, 7);
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
if (nfchans_tbl[state->output & A52_CHANNEL_MASK] < nfchans)
|
||||
for (i = 1; i < nfchans; i++)
|
||||
if (blksw[i] != blksw[0])
|
||||
break;
|
||||
|
||||
if (i < nfchans) {
|
||||
if (state->downmixed) {
|
||||
state->downmixed = 0;
|
||||
a52_upmix (samples + 1536, state->acmod, state->output);
|
||||
}
|
||||
|
||||
for (i = 0; i < nfchans; i++) {
|
||||
sample_t bias;
|
||||
|
||||
bias = 0;
|
||||
if (!(chanbias & (1 << i)))
|
||||
bias = state->bias;
|
||||
|
||||
if (coeff[i]) {
|
||||
if (blksw[i])
|
||||
a52_imdct_256 (samples + 256 * i, samples + 1536 + 256 * i,
|
||||
bias);
|
||||
else
|
||||
a52_imdct_512 (samples + 256 * i, samples + 1536 + 256 * i,
|
||||
bias);
|
||||
} else {
|
||||
int j;
|
||||
|
||||
for (j = 0; j < 256; j++)
|
||||
(samples + 256 * i)[j] = bias;
|
||||
}
|
||||
}
|
||||
|
||||
a52_downmix (samples, state->acmod, state->output, state->bias,
|
||||
state->clev, state->slev);
|
||||
} else {
|
||||
nfchans = nfchans_tbl[state->output & A52_CHANNEL_MASK];
|
||||
|
||||
a52_downmix (samples, state->acmod, state->output, 0,
|
||||
state->clev, state->slev);
|
||||
|
||||
if (!state->downmixed) {
|
||||
state->downmixed = 1;
|
||||
a52_downmix (samples + 1536, state->acmod, state->output, 0,
|
||||
state->clev, state->slev);
|
||||
}
|
||||
|
||||
if (blksw[0])
|
||||
for (i = 0; i < nfchans; i++)
|
||||
a52_imdct_256 (samples + 256 * i, samples + 1536 + 256 * i,
|
||||
state->bias);
|
||||
else
|
||||
for (i = 0; i < nfchans; i++)
|
||||
a52_imdct_512 (samples + 256 * i, samples + 1536 + 256 * i,
|
||||
state->bias);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void a52_free (a52_state_t * state)
|
||||
{
|
||||
free (state->samples);
|
||||
free (state);
|
||||
}
|
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* tables.h
|
||||
* Copyright (C) 2000-2002 Michel Lespinasse <walken@zoy.org>
|
||||
* Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
|
||||
*
|
||||
* This file is part of a52dec, a free ATSC A-52 stream decoder.
|
||||
* See http://liba52.sourceforge.net/ for updates.
|
||||
*
|
||||
* a52dec 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.
|
||||
*
|
||||
* a52dec is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
static const int8_t exp_1[128] = {
|
||||
-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
25,25,25
|
||||
};
|
||||
static const int8_t exp_2[128] = {
|
||||
-2,-2,-2,-2,-2,-1,-1,-1,-1,-1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
|
||||
-2,-2,-2,-2,-2,-1,-1,-1,-1,-1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
|
||||
-2,-2,-2,-2,-2,-1,-1,-1,-1,-1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
|
||||
-2,-2,-2,-2,-2,-1,-1,-1,-1,-1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
|
||||
-2,-2,-2,-2,-2,-1,-1,-1,-1,-1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
|
||||
25,25,25
|
||||
};
|
||||
static const int8_t exp_3[128] = {
|
||||
-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,
|
||||
-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,
|
||||
-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,
|
||||
-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,
|
||||
-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,
|
||||
25,25,25
|
||||
};
|
||||
|
||||
#define Q0 ((-2 << 15) / 3.0)
|
||||
#define Q1 (0)
|
||||
#define Q2 ((2 << 15) / 3.0)
|
||||
|
||||
static const sample_t q_1_0[32] = {
|
||||
Q0,Q0,Q0,Q0,Q0,Q0,Q0,Q0,Q0,
|
||||
Q1,Q1,Q1,Q1,Q1,Q1,Q1,Q1,Q1,
|
||||
Q2,Q2,Q2,Q2,Q2,Q2,Q2,Q2,Q2,
|
||||
0,0,0,0,0
|
||||
};
|
||||
|
||||
static const sample_t q_1_1[32] = {
|
||||
Q0,Q0,Q0,Q1,Q1,Q1,Q2,Q2,Q2,
|
||||
Q0,Q0,Q0,Q1,Q1,Q1,Q2,Q2,Q2,
|
||||
Q0,Q0,Q0,Q1,Q1,Q1,Q2,Q2,Q2,
|
||||
0,0,0,0,0
|
||||
};
|
||||
|
||||
static const sample_t q_1_2[32] = {
|
||||
Q0,Q1,Q2,Q0,Q1,Q2,Q0,Q1,Q2,
|
||||
Q0,Q1,Q2,Q0,Q1,Q2,Q0,Q1,Q2,
|
||||
Q0,Q1,Q2,Q0,Q1,Q2,Q0,Q1,Q2,
|
||||
0,0,0,0,0
|
||||
};
|
||||
|
||||
#undef Q0
|
||||
#undef Q1
|
||||
#undef Q2
|
||||
|
||||
#define Q0 ((-4 << 15) / 5.0)
|
||||
#define Q1 ((-2 << 15) / 5.0)
|
||||
#define Q2 (0)
|
||||
#define Q3 ((2 << 15) / 5.0)
|
||||
#define Q4 ((4 << 15) / 5.0)
|
||||
|
||||
static const sample_t q_2_0[128] = {
|
||||
Q0,Q0,Q0,Q0,Q0,Q0,Q0,Q0,Q0,Q0,Q0,Q0,Q0,Q0,Q0,Q0,Q0,Q0,Q0,Q0,Q0,Q0,Q0,Q0,Q0,
|
||||
Q1,Q1,Q1,Q1,Q1,Q1,Q1,Q1,Q1,Q1,Q1,Q1,Q1,Q1,Q1,Q1,Q1,Q1,Q1,Q1,Q1,Q1,Q1,Q1,Q1,
|
||||
Q2,Q2,Q2,Q2,Q2,Q2,Q2,Q2,Q2,Q2,Q2,Q2,Q2,Q2,Q2,Q2,Q2,Q2,Q2,Q2,Q2,Q2,Q2,Q2,Q2,
|
||||
Q3,Q3,Q3,Q3,Q3,Q3,Q3,Q3,Q3,Q3,Q3,Q3,Q3,Q3,Q3,Q3,Q3,Q3,Q3,Q3,Q3,Q3,Q3,Q3,Q3,
|
||||
Q4,Q4,Q4,Q4,Q4,Q4,Q4,Q4,Q4,Q4,Q4,Q4,Q4,Q4,Q4,Q4,Q4,Q4,Q4,Q4,Q4,Q4,Q4,Q4,Q4,
|
||||
0,0,0
|
||||
};
|
||||
|
||||
static const sample_t q_2_1[128] = {
|
||||
Q0,Q0,Q0,Q0,Q0,Q1,Q1,Q1,Q1,Q1,Q2,Q2,Q2,Q2,Q2,Q3,Q3,Q3,Q3,Q3,Q4,Q4,Q4,Q4,Q4,
|
||||
Q0,Q0,Q0,Q0,Q0,Q1,Q1,Q1,Q1,Q1,Q2,Q2,Q2,Q2,Q2,Q3,Q3,Q3,Q3,Q3,Q4,Q4,Q4,Q4,Q4,
|
||||
Q0,Q0,Q0,Q0,Q0,Q1,Q1,Q1,Q1,Q1,Q2,Q2,Q2,Q2,Q2,Q3,Q3,Q3,Q3,Q3,Q4,Q4,Q4,Q4,Q4,
|
||||
Q0,Q0,Q0,Q0,Q0,Q1,Q1,Q1,Q1,Q1,Q2,Q2,Q2,Q2,Q2,Q3,Q3,Q3,Q3,Q3,Q4,Q4,Q4,Q4,Q4,
|
||||
Q0,Q0,Q0,Q0,Q0,Q1,Q1,Q1,Q1,Q1,Q2,Q2,Q2,Q2,Q2,Q3,Q3,Q3,Q3,Q3,Q4,Q4,Q4,Q4,Q4,
|
||||
0,0,0
|
||||
};
|
||||
|
||||
static const sample_t q_2_2[128] = {
|
||||
Q0,Q1,Q2,Q3,Q4,Q0,Q1,Q2,Q3,Q4,Q0,Q1,Q2,Q3,Q4,Q0,Q1,Q2,Q3,Q4,Q0,Q1,Q2,Q3,Q4,
|
||||
Q0,Q1,Q2,Q3,Q4,Q0,Q1,Q2,Q3,Q4,Q0,Q1,Q2,Q3,Q4,Q0,Q1,Q2,Q3,Q4,Q0,Q1,Q2,Q3,Q4,
|
||||
Q0,Q1,Q2,Q3,Q4,Q0,Q1,Q2,Q3,Q4,Q0,Q1,Q2,Q3,Q4,Q0,Q1,Q2,Q3,Q4,Q0,Q1,Q2,Q3,Q4,
|
||||
Q0,Q1,Q2,Q3,Q4,Q0,Q1,Q2,Q3,Q4,Q0,Q1,Q2,Q3,Q4,Q0,Q1,Q2,Q3,Q4,Q0,Q1,Q2,Q3,Q4,
|
||||
Q0,Q1,Q2,Q3,Q4,Q0,Q1,Q2,Q3,Q4,Q0,Q1,Q2,Q3,Q4,Q0,Q1,Q2,Q3,Q4,Q0,Q1,Q2,Q3,Q4,
|
||||
0,0,0
|
||||
};
|
||||
|
||||
#undef Q0
|
||||
#undef Q1
|
||||
#undef Q2
|
||||
#undef Q3
|
||||
#undef Q4
|
||||
|
||||
static const sample_t q_3[8] = {
|
||||
(-6 << 15)/7.0, (-4 << 15)/7.0, (-2 << 15)/7.0, 0,
|
||||
( 2 << 15)/7.0, ( 4 << 15)/7.0, ( 6 << 15)/7.0, 0
|
||||
};
|
||||
|
||||
#define Q0 ((-10 << 15) / 11.0)
|
||||
#define Q1 ((-8 << 15) / 11.0)
|
||||
#define Q2 ((-6 << 15) / 11.0)
|
||||
#define Q3 ((-4 << 15) / 11.0)
|
||||
#define Q4 ((-2 << 15) / 11.0)
|
||||
#define Q5 (0)
|
||||
#define Q6 ((2 << 15) / 11.0)
|
||||
#define Q7 ((4 << 15) / 11.0)
|
||||
#define Q8 ((6 << 15) / 11.0)
|
||||
#define Q9 ((8 << 15) / 11.0)
|
||||
#define QA ((10 << 15) / 11.0)
|
||||
|
||||
static const sample_t q_4_0[128] = {
|
||||
Q0, Q0, Q0, Q0, Q0, Q0, Q0, Q0, Q0, Q0, Q0,
|
||||
Q1, Q1, Q1, Q1, Q1, Q1, Q1, Q1, Q1, Q1, Q1,
|
||||
Q2, Q2, Q2, Q2, Q2, Q2, Q2, Q2, Q2, Q2, Q2,
|
||||
Q3, Q3, Q3, Q3, Q3, Q3, Q3, Q3, Q3, Q3, Q3,
|
||||
Q4, Q4, Q4, Q4, Q4, Q4, Q4, Q4, Q4, Q4, Q4,
|
||||
Q5, Q5, Q5, Q5, Q5, Q5, Q5, Q5, Q5, Q5, Q5,
|
||||
Q6, Q6, Q6, Q6, Q6, Q6, Q6, Q6, Q6, Q6, Q6,
|
||||
Q7, Q7, Q7, Q7, Q7, Q7, Q7, Q7, Q7, Q7, Q7,
|
||||
Q8, Q8, Q8, Q8, Q8, Q8, Q8, Q8, Q8, Q8, Q8,
|
||||
Q9, Q9, Q9, Q9, Q9, Q9, Q9, Q9, Q9, Q9, Q9,
|
||||
QA, QA, QA, QA, QA, QA, QA, QA, QA, QA, QA,
|
||||
0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
static const sample_t q_4_1[128] = {
|
||||
Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, QA,
|
||||
Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, QA,
|
||||
Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, QA,
|
||||
Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, QA,
|
||||
Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, QA,
|
||||
Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, QA,
|
||||
Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, QA,
|
||||
Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, QA,
|
||||
Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, QA,
|
||||
Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, QA,
|
||||
Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, QA,
|
||||
0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
#undef Q0
|
||||
#undef Q1
|
||||
#undef Q2
|
||||
#undef Q3
|
||||
#undef Q4
|
||||
#undef Q5
|
||||
#undef Q6
|
||||
#undef Q7
|
||||
#undef Q8
|
||||
#undef Q9
|
||||
#undef QA
|
||||
|
||||
static const sample_t q_5[16] = {
|
||||
(-14 << 15)/15.0,(-12 << 15)/15.0,(-10 << 15)/15.0,
|
||||
( -8 << 15)/15.0,( -6 << 15)/15.0,( -4 << 15)/15.0,
|
||||
( -2 << 15)/15.0, 0 ,( 2 << 15)/15.0,
|
||||
( 4 << 15)/15.0,( 6 << 15)/15.0,( 8 << 15)/15.0,
|
||||
( 10 << 15)/15.0,( 12 << 15)/15.0,( 14 << 15)/15.0,
|
||||
0
|
||||
};
|
||||
|
||||
static const sample_t scale_factor[25] = {
|
||||
0.000030517578125,
|
||||
0.0000152587890625,
|
||||
0.00000762939453125,
|
||||
0.000003814697265625,
|
||||
0.0000019073486328125,
|
||||
0.00000095367431640625,
|
||||
0.000000476837158203125,
|
||||
0.0000002384185791015625,
|
||||
0.00000011920928955078125,
|
||||
0.000000059604644775390625,
|
||||
0.0000000298023223876953125,
|
||||
0.00000001490116119384765625,
|
||||
0.000000007450580596923828125,
|
||||
0.0000000037252902984619140625,
|
||||
0.00000000186264514923095703125,
|
||||
0.000000000931322574615478515625,
|
||||
0.0000000004656612873077392578125,
|
||||
0.00000000023283064365386962890625,
|
||||
0.000000000116415321826934814453125,
|
||||
0.0000000000582076609134674072265625,
|
||||
0.00000000002910383045673370361328125,
|
||||
0.000000000014551915228366851806640625,
|
||||
0.0000000000072759576141834259033203125,
|
||||
0.00000000000363797880709171295166015625,
|
||||
0.000000000001818989403545856475830078125
|
||||
};
|
||||
|
||||
static const uint16_t dither_lut[256] = {
|
||||
0x0000, 0xa011, 0xe033, 0x4022, 0x6077, 0xc066, 0x8044, 0x2055,
|
||||
0xc0ee, 0x60ff, 0x20dd, 0x80cc, 0xa099, 0x0088, 0x40aa, 0xe0bb,
|
||||
0x21cd, 0x81dc, 0xc1fe, 0x61ef, 0x41ba, 0xe1ab, 0xa189, 0x0198,
|
||||
0xe123, 0x4132, 0x0110, 0xa101, 0x8154, 0x2145, 0x6167, 0xc176,
|
||||
0x439a, 0xe38b, 0xa3a9, 0x03b8, 0x23ed, 0x83fc, 0xc3de, 0x63cf,
|
||||
0x8374, 0x2365, 0x6347, 0xc356, 0xe303, 0x4312, 0x0330, 0xa321,
|
||||
0x6257, 0xc246, 0x8264, 0x2275, 0x0220, 0xa231, 0xe213, 0x4202,
|
||||
0xa2b9, 0x02a8, 0x428a, 0xe29b, 0xc2ce, 0x62df, 0x22fd, 0x82ec,
|
||||
0x8734, 0x2725, 0x6707, 0xc716, 0xe743, 0x4752, 0x0770, 0xa761,
|
||||
0x47da, 0xe7cb, 0xa7e9, 0x07f8, 0x27ad, 0x87bc, 0xc79e, 0x678f,
|
||||
0xa6f9, 0x06e8, 0x46ca, 0xe6db, 0xc68e, 0x669f, 0x26bd, 0x86ac,
|
||||
0x6617, 0xc606, 0x8624, 0x2635, 0x0660, 0xa671, 0xe653, 0x4642,
|
||||
0xc4ae, 0x64bf, 0x249d, 0x848c, 0xa4d9, 0x04c8, 0x44ea, 0xe4fb,
|
||||
0x0440, 0xa451, 0xe473, 0x4462, 0x6437, 0xc426, 0x8404, 0x2415,
|
||||
0xe563, 0x4572, 0x0550, 0xa541, 0x8514, 0x2505, 0x6527, 0xc536,
|
||||
0x258d, 0x859c, 0xc5be, 0x65af, 0x45fa, 0xe5eb, 0xa5c9, 0x05d8,
|
||||
0xae79, 0x0e68, 0x4e4a, 0xee5b, 0xce0e, 0x6e1f, 0x2e3d, 0x8e2c,
|
||||
0x6e97, 0xce86, 0x8ea4, 0x2eb5, 0x0ee0, 0xaef1, 0xeed3, 0x4ec2,
|
||||
0x8fb4, 0x2fa5, 0x6f87, 0xcf96, 0xefc3, 0x4fd2, 0x0ff0, 0xafe1,
|
||||
0x4f5a, 0xef4b, 0xaf69, 0x0f78, 0x2f2d, 0x8f3c, 0xcf1e, 0x6f0f,
|
||||
0xede3, 0x4df2, 0x0dd0, 0xadc1, 0x8d94, 0x2d85, 0x6da7, 0xcdb6,
|
||||
0x2d0d, 0x8d1c, 0xcd3e, 0x6d2f, 0x4d7a, 0xed6b, 0xad49, 0x0d58,
|
||||
0xcc2e, 0x6c3f, 0x2c1d, 0x8c0c, 0xac59, 0x0c48, 0x4c6a, 0xec7b,
|
||||
0x0cc0, 0xacd1, 0xecf3, 0x4ce2, 0x6cb7, 0xcca6, 0x8c84, 0x2c95,
|
||||
0x294d, 0x895c, 0xc97e, 0x696f, 0x493a, 0xe92b, 0xa909, 0x0918,
|
||||
0xe9a3, 0x49b2, 0x0990, 0xa981, 0x89d4, 0x29c5, 0x69e7, 0xc9f6,
|
||||
0x0880, 0xa891, 0xe8b3, 0x48a2, 0x68f7, 0xc8e6, 0x88c4, 0x28d5,
|
||||
0xc86e, 0x687f, 0x285d, 0x884c, 0xa819, 0x0808, 0x482a, 0xe83b,
|
||||
0x6ad7, 0xcac6, 0x8ae4, 0x2af5, 0x0aa0, 0xaab1, 0xea93, 0x4a82,
|
||||
0xaa39, 0x0a28, 0x4a0a, 0xea1b, 0xca4e, 0x6a5f, 0x2a7d, 0x8a6c,
|
||||
0x4b1a, 0xeb0b, 0xab29, 0x0b38, 0x2b6d, 0x8b7c, 0xcb5e, 0x6b4f,
|
||||
0x8bf4, 0x2be5, 0x6bc7, 0xcbd6, 0xeb83, 0x4b92, 0x0bb0, 0xaba1
|
||||
};
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* tendra.h
|
||||
* Copyright (C) 2000-2002 Michel Lespinasse <walken@zoy.org>
|
||||
* Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
|
||||
*
|
||||
* This file is part of a52dec, a free ATSC A-52 stream decoder.
|
||||
* See http://liba52.sourceforge.net/ for updates.
|
||||
*
|
||||
* a52dec 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.
|
||||
*
|
||||
* a52dec is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma TenDRA begin
|
||||
#pragma TenDRA longlong type warning
|
||||
|
||||
#ifdef TenDRA_check
|
||||
|
||||
#pragma TenDRA conversion analysis (pointer-int explicit) off
|
||||
#pragma TenDRA implicit function declaration off
|
||||
|
||||
/* avoid the "No declarations in translation unit" problem */
|
||||
int TenDRA;
|
||||
|
||||
#endif /* TenDRA_check */
|
|
@ -0,0 +1,57 @@
|
|||
|
||||
#ifndef _BASETYPES_H_
|
||||
#define _BASETYPES_H_
|
||||
|
||||
//system defines
|
||||
#ifdef __LINUX__
|
||||
#include <gtk/gtk.h>
|
||||
#else
|
||||
# define WINVER 0x0501
|
||||
# define _WIN32_WINNT 0x0501
|
||||
# include <windows.h>
|
||||
# include <mmsystem.h>
|
||||
# include <tchar.h>
|
||||
# include "resource.h"
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
using std::string;
|
||||
using std::wstring;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Override Win32 min/max macros with the STL's type safe and macro
|
||||
// free varieties (much safer!)
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using std::min;
|
||||
using std::max;
|
||||
|
||||
template< typename T >
|
||||
static __forceinline void Clampify( T& src, T min, T max )
|
||||
{
|
||||
src = std::min( std::max( src, min ), max );
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
static __forceinline T GetClamped( T src, T min, T max )
|
||||
{
|
||||
return std::min( std::max( src, min ), max );
|
||||
}
|
||||
|
||||
|
||||
extern void SysMessage(const char *fmt, ...);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,52 @@
|
|||
/* SPU2-X
|
||||
* A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#ifndef _DPLII_H_
|
||||
#define _DPLII_H_
|
||||
|
||||
#include "PS2Etypes.h"
|
||||
#include "lowpass.h"
|
||||
|
||||
class DPLII
|
||||
{
|
||||
public:
|
||||
static const bool UseAveraging = false;
|
||||
|
||||
protected:
|
||||
s32 LAccum;
|
||||
s32 RAccum;
|
||||
s32 ANum;
|
||||
|
||||
LPF_data lpf_l;
|
||||
LPF_data lpf_r;
|
||||
|
||||
u8 bufdone;
|
||||
s32 Gfl,Gfr;
|
||||
|
||||
s32 spdif_data[6];
|
||||
s32 LMax,RMax;
|
||||
|
||||
s32 LBuff[128];
|
||||
s32 RBuff[128];
|
||||
|
||||
public:
|
||||
DPLII( s32 lowpass_freq, s32 samplerate );
|
||||
void Convert( s16 *obuffer, s32 ValL, s32 ValR );
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,266 @@
|
|||
//GiGaHeRz's SPU2 Driver
|
||||
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
|
||||
//
|
||||
//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
|
||||
//
|
||||
|
||||
#include "spu2.h"
|
||||
|
||||
int crazy_debug=0;
|
||||
|
||||
char s[4096];
|
||||
|
||||
FILE *spu2Log;
|
||||
|
||||
void FileLog(const char *fmt, ...) {
|
||||
#ifdef SPU2_LOG
|
||||
int n;
|
||||
va_list list;
|
||||
|
||||
if(!AccessLog()) return;
|
||||
if(!spu2Log) return;
|
||||
|
||||
va_start(list, fmt);
|
||||
n=vsprintf(s,fmt, list);
|
||||
va_end(list);
|
||||
|
||||
fputs(s,spu2Log);
|
||||
fflush(spu2Log);
|
||||
|
||||
#if 0
|
||||
if(crazy_debug)
|
||||
{
|
||||
fputs(s,stderr);
|
||||
fflush(stderr);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void ConLog(const char *fmt, ...) {
|
||||
#ifdef SPU2_LOG
|
||||
int n;
|
||||
va_list list;
|
||||
|
||||
if(!MsgToConsole()) return;
|
||||
|
||||
va_start(list, fmt);
|
||||
n=vsprintf(s,fmt, list);
|
||||
va_end(list);
|
||||
|
||||
fputs(s,stderr);
|
||||
fflush(stderr);
|
||||
|
||||
if(spu2Log)
|
||||
{
|
||||
fputs(s,spu2Log);
|
||||
fflush(spu2Log);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void DoFullDump()
|
||||
{
|
||||
#ifdef SPU2_LOG
|
||||
FILE *dump;
|
||||
u8 c=0, v=0;
|
||||
|
||||
if(MemDump())
|
||||
{
|
||||
dump = _wfopen( MemDumpFileName, _T("wb") );
|
||||
if (dump)
|
||||
{
|
||||
fwrite(_spu2mem,0x200000,1,dump);
|
||||
fclose(dump);
|
||||
}
|
||||
}
|
||||
if(RegDump())
|
||||
{
|
||||
dump = _wfopen( RegDumpFileName, _T("wb") );
|
||||
if (dump)
|
||||
{
|
||||
fwrite(spu2regs,0x2000,1,dump);
|
||||
fclose(dump);
|
||||
}
|
||||
}
|
||||
|
||||
if(!CoresDump()) return;
|
||||
dump = _wfopen( CoresDumpFileName, _T("wt") );
|
||||
if (dump) {
|
||||
for(c=0;c<2;c++)
|
||||
{
|
||||
fprintf(dump,"#### CORE %d DUMP.\n",c);
|
||||
fprintf(dump,"Master Volume for Left Channel: %x\n"
|
||||
" - Value: %x\n"
|
||||
" - Mode: %x\n"
|
||||
" - Increment: %x\n",
|
||||
Cores[c].MasterL.Reg_VOL,
|
||||
Cores[c].MasterL.Value,
|
||||
Cores[c].MasterL.Mode,
|
||||
Cores[c].MasterL.Increment);
|
||||
fprintf(dump,"Master Volume for Right Channel: %x\n"
|
||||
" - Value: %x\n"
|
||||
" - Mode: %x\n"
|
||||
" - Increment: %x\n",
|
||||
Cores[c].MasterR.Reg_VOL,
|
||||
Cores[c].MasterR.Value,
|
||||
Cores[c].MasterR.Mode,
|
||||
Cores[c].MasterR.Increment);
|
||||
fprintf(dump,"Volume for External Data Input (Left Channel): %x\n",Cores[c].ExtL);
|
||||
fprintf(dump,"Volume for External Data Input (Right Channel): %x\n",Cores[c].ExtR);
|
||||
fprintf(dump,"Volume for Sound Data Input (Left Channel): %x\n",Cores[c].InpL);
|
||||
fprintf(dump,"Volume for Sound Data Input (Right Channel): %x\n",Cores[c].InpR);
|
||||
fprintf(dump,"Volume for Output from Effects (Left Channel): %x\n",Cores[c].FxL);
|
||||
fprintf(dump,"Volume for Output from Effects (Right Channel): %x\n",Cores[c].FxR);
|
||||
fprintf(dump,"Interrupt Address: %x\n",Cores[c].IRQA);
|
||||
fprintf(dump,"DMA Transfer Start Address: %x\n",Cores[c].TSA);
|
||||
fprintf(dump,"External Input to Direct Output (Left): %s\n",Cores[c].ExtDryL?"Yes":"No");
|
||||
fprintf(dump,"External Input to Direct Output (Right): %s\n",Cores[c].ExtDryR?"Yes":"No");
|
||||
fprintf(dump,"External Input to Effects (Left): %s\n",Cores[c].ExtWetL?"Yes":"No");
|
||||
fprintf(dump,"External Input to Effects (Right): %s\n",Cores[c].ExtWetR?"Yes":"No");
|
||||
fprintf(dump,"Sound Data Input to Direct Output (Left): %s\n",Cores[c].SndDryL?"Yes":"No");
|
||||
fprintf(dump,"Sound Data Input to Direct Output (Right): %s\n",Cores[c].SndDryR?"Yes":"No");
|
||||
fprintf(dump,"Sound Data Input to Effects (Left): %s\n",Cores[c].SndWetL?"Yes":"No");
|
||||
fprintf(dump,"Sound Data Input to Effects (Right): %s\n",Cores[c].SndWetR?"Yes":"No");
|
||||
fprintf(dump,"Voice Data Input to Direct Output (Left): %s\n",Cores[c].InpDryL?"Yes":"No");
|
||||
fprintf(dump,"Voice Data Input to Direct Output (Right): %s\n",Cores[c].InpDryR?"Yes":"No");
|
||||
fprintf(dump,"Voice Data Input to Effects (Left): %s\n",Cores[c].InpWetL?"Yes":"No");
|
||||
fprintf(dump,"Voice Data Input to Effects (Right): %s\n",Cores[c].InpWetR?"Yes":"No");
|
||||
fprintf(dump,"IRQ Enabled: %s\n",Cores[c].IRQEnable?"Yes":"No");
|
||||
fprintf(dump,"Effects Enabled: %s\n",Cores[c].FxEnable?"Yes":"No");
|
||||
fprintf(dump,"Mute Enabled: %s\n",Cores[c].Mute?"Yes":"No");
|
||||
fprintf(dump,"Noise Clock: %d\n",Cores[c].NoiseClk);
|
||||
fprintf(dump,"DMA Bits: %d\n",Cores[c].DMABits);
|
||||
fprintf(dump,"Effects Start: %x\n",Cores[c].EffectsStartA);
|
||||
fprintf(dump,"Effects End: %x\n",Cores[c].EffectsEndA);
|
||||
fprintf(dump,"Registers:\n");
|
||||
fprintf(dump," - PMON: %x\n",Cores[c].Regs.PMON);
|
||||
fprintf(dump," - NON: %x\n",Cores[c].Regs.NON);
|
||||
fprintf(dump," - VMIXL: %x\n",Cores[c].Regs.VMIXL);
|
||||
fprintf(dump," - VMIXR: %x\n",Cores[c].Regs.VMIXR);
|
||||
fprintf(dump," - VMIXEL: %x\n",Cores[c].Regs.VMIXEL);
|
||||
fprintf(dump," - VMIXER: %x\n",Cores[c].Regs.VMIXER);
|
||||
fprintf(dump," - MMIX: %x\n",Cores[c].Regs.VMIXEL);
|
||||
fprintf(dump," - ENDX: %x\n",Cores[c].Regs.VMIXER);
|
||||
fprintf(dump," - STATX: %x\n",Cores[c].Regs.VMIXEL);
|
||||
fprintf(dump," - ATTR: %x\n",Cores[c].Regs.VMIXER);
|
||||
for(v=0;v<24;v++) {
|
||||
fprintf(dump,"Voice %d:\n",v);
|
||||
fprintf(dump," - Volume for Left Channel: %x\n"
|
||||
" - Value: %x\n"
|
||||
" - Mode: %x\n"
|
||||
" - Increment: %x\n",
|
||||
Cores[c].Voices[v].VolumeL.Reg_VOL,
|
||||
Cores[c].Voices[v].VolumeL.Value,
|
||||
Cores[c].Voices[v].VolumeL.Mode,
|
||||
Cores[c].Voices[v].VolumeL.Increment);
|
||||
fprintf(dump," - Volume for Right Channel: %x\n"
|
||||
" - Value: %x\n"
|
||||
" - Mode: %x\n"
|
||||
" - Increment: %x\n",
|
||||
Cores[c].Voices[v].VolumeR.Reg_VOL,
|
||||
Cores[c].Voices[v].VolumeR.Value,
|
||||
Cores[c].Voices[v].VolumeR.Mode,
|
||||
Cores[c].Voices[v].VolumeR.Increment);
|
||||
fprintf(dump," - ADSR Envelope: %x & %x\n"
|
||||
" - Ar: %x\n"
|
||||
" - Am: %x\n"
|
||||
" - Dr: %x\n"
|
||||
" - Sl: %x\n"
|
||||
" - Sr: %x\n"
|
||||
" - Sm: %x\n"
|
||||
" - Rr: %x\n"
|
||||
" - Rm: %x\n"
|
||||
" - Phase: %x\n"
|
||||
" - Value: %x\n",
|
||||
Cores[c].Voices[v].ADSR.Reg_ADSR1,
|
||||
Cores[c].Voices[v].ADSR.Reg_ADSR2,
|
||||
Cores[c].Voices[v].ADSR.Ar,
|
||||
Cores[c].Voices[v].ADSR.Am,
|
||||
Cores[c].Voices[v].ADSR.Dr,
|
||||
Cores[c].Voices[v].ADSR.Sl,
|
||||
Cores[c].Voices[v].ADSR.Sr,
|
||||
Cores[c].Voices[v].ADSR.Sm,
|
||||
Cores[c].Voices[v].ADSR.Rr,
|
||||
Cores[c].Voices[v].ADSR.Rm,
|
||||
Cores[c].Voices[v].ADSR.Phase,
|
||||
Cores[c].Voices[v].ADSR.Value);
|
||||
fprintf(dump," - Pitch: %x\n",Cores[c].Voices[v].Pitch);
|
||||
fprintf(dump," - Modulated: %s\n",Cores[c].Voices[v].Modulated?"Yes":"No");
|
||||
fprintf(dump," - Source: %s\n",Cores[c].Voices[v].Noise?"Noise":"Wave");
|
||||
fprintf(dump," - Direct Output for Left Channel: %s\n",Cores[c].Voices[v].DryL?"Yes":"No");
|
||||
fprintf(dump," - Direct Output for Right Channel: %s\n",Cores[c].Voices[v].DryR?"Yes":"No");
|
||||
fprintf(dump," - Effects Output for Left Channel: %s\n",Cores[c].Voices[v].WetL?"Yes":"No");
|
||||
fprintf(dump," - Effects Output for Right Channel: %s\n",Cores[c].Voices[v].WetR?"Yes":"No");
|
||||
fprintf(dump," - Loop Start Adress: %x\n",Cores[c].Voices[v].LoopStartA);
|
||||
fprintf(dump," - Sound Start Adress: %x\n",Cores[c].Voices[v].StartA);
|
||||
fprintf(dump," - Next Data Adress: %x\n",Cores[c].Voices[v].NextA);
|
||||
fprintf(dump," - Play Start Cycle: %d\n",Cores[c].Voices[v].PlayCycle);
|
||||
fprintf(dump," - Play Status: %s\n",(Cores[c].Voices[v].ADSR.Phase>0)?"Playing":"Not Playing");
|
||||
fprintf(dump," - Block Sample: %d\n",Cores[c].Voices[v].SCurrent);
|
||||
}
|
||||
fprintf(dump,"#### END OF DUMP.\n\n");
|
||||
}
|
||||
}
|
||||
fclose(dump);
|
||||
dump = fopen( "logs/effects.txt", "wt" );
|
||||
if (dump)
|
||||
{
|
||||
for(c=0;c<2;c++)
|
||||
{
|
||||
fprintf(dump,"#### CORE %d EFFECTS PROCESSOR DUMP.\n",c);
|
||||
|
||||
fprintf(dump," - IN_COEF_L: %x\n",Cores[c].Revb.IN_COEF_R);
|
||||
fprintf(dump," - IN_COEF_R: %x\n",Cores[c].Revb.IN_COEF_L);
|
||||
|
||||
fprintf(dump," - FB_ALPHA: %x\n",Cores[c].Revb.FB_ALPHA);
|
||||
fprintf(dump," - FB_X: %x\n",Cores[c].Revb.FB_X);
|
||||
fprintf(dump," - FB_SRC_A: %x\n",Cores[c].Revb.FB_SRC_A);
|
||||
fprintf(dump," - FB_SRC_B: %x\n",Cores[c].Revb.FB_SRC_B);
|
||||
|
||||
fprintf(dump," - IIR_ALPHA: %x\n",Cores[c].Revb.IIR_ALPHA);
|
||||
fprintf(dump," - IIR_COEF: %x\n",Cores[c].Revb.IIR_COEF);
|
||||
fprintf(dump," - IIR_SRC_A0: %x\n",Cores[c].Revb.IIR_SRC_A0);
|
||||
fprintf(dump," - IIR_SRC_A1: %x\n",Cores[c].Revb.IIR_SRC_A1);
|
||||
fprintf(dump," - IIR_SRC_B1: %x\n",Cores[c].Revb.IIR_SRC_B0);
|
||||
fprintf(dump," - IIR_SRC_B0: %x\n",Cores[c].Revb.IIR_SRC_B1);
|
||||
fprintf(dump," - IIR_DEST_A0: %x\n",Cores[c].Revb.IIR_DEST_A0);
|
||||
fprintf(dump," - IIR_DEST_A1: %x\n",Cores[c].Revb.IIR_DEST_A1);
|
||||
fprintf(dump," - IIR_DEST_B0: %x\n",Cores[c].Revb.IIR_DEST_B0);
|
||||
fprintf(dump," - IIR_DEST_B1: %x\n",Cores[c].Revb.IIR_DEST_B1);
|
||||
|
||||
fprintf(dump," - ACC_COEF_A: %x\n",Cores[c].Revb.ACC_COEF_A);
|
||||
fprintf(dump," - ACC_COEF_B: %x\n",Cores[c].Revb.ACC_COEF_B);
|
||||
fprintf(dump," - ACC_COEF_C: %x\n",Cores[c].Revb.ACC_COEF_C);
|
||||
fprintf(dump," - ACC_COEF_D: %x\n",Cores[c].Revb.ACC_COEF_D);
|
||||
fprintf(dump," - ACC_SRC_A0: %x\n",Cores[c].Revb.ACC_SRC_A0);
|
||||
fprintf(dump," - ACC_SRC_A1: %x\n",Cores[c].Revb.ACC_SRC_A1);
|
||||
fprintf(dump," - ACC_SRC_B0: %x\n",Cores[c].Revb.ACC_SRC_B0);
|
||||
fprintf(dump," - ACC_SRC_B1: %x\n",Cores[c].Revb.ACC_SRC_B1);
|
||||
fprintf(dump," - ACC_SRC_C0: %x\n",Cores[c].Revb.ACC_SRC_C0);
|
||||
fprintf(dump," - ACC_SRC_C1: %x\n",Cores[c].Revb.ACC_SRC_C1);
|
||||
fprintf(dump," - ACC_SRC_D0: %x\n",Cores[c].Revb.ACC_SRC_D0);
|
||||
fprintf(dump," - ACC_SRC_D1: %x\n",Cores[c].Revb.ACC_SRC_D1);
|
||||
|
||||
fprintf(dump," - MIX_DEST_A0: %x\n",Cores[c].Revb.MIX_DEST_A0);
|
||||
fprintf(dump," - MIX_DEST_A1: %x\n",Cores[c].Revb.MIX_DEST_A1);
|
||||
fprintf(dump," - MIX_DEST_B0: %x\n",Cores[c].Revb.MIX_DEST_B0);
|
||||
fprintf(dump," - MIX_DEST_B1: %x\n",Cores[c].Revb.MIX_DEST_B1);
|
||||
fprintf(dump,"#### END OF DUMP.\n\n");
|
||||
}
|
||||
fclose(dump);
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
//GiGaHeRz's SPU2 Driver
|
||||
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
|
||||
//
|
||||
//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
|
||||
//
|
||||
|
||||
#ifndef DEBUG_H_INCLUDED
|
||||
#define DEBUG_H_INCLUDED
|
||||
|
||||
extern FILE *spu2Log;
|
||||
|
||||
void FileLog(const char *fmt, ...);
|
||||
void ConLog(const char *fmt, ...);
|
||||
|
||||
void DoFullDump();
|
||||
|
||||
namespace WaveDump
|
||||
{
|
||||
enum CoreSourceType
|
||||
{
|
||||
// Core's input stream, usually pulled from ADMA streams.
|
||||
CoreSrc_Input = 0
|
||||
|
||||
// Output of the actual 24 input voices which have dry output enabled.
|
||||
, CoreSrc_DryVoiceMix
|
||||
|
||||
// Output of the actual 24 input voices that have wet output enabled.
|
||||
, CoreSrc_WetVoiceMix
|
||||
|
||||
// Wet mix including inputs and externals, prior to the application of reverb.
|
||||
, CoreSrc_PreReverb
|
||||
|
||||
// Wet mix after reverb has turned it into a pile of garbly gook.
|
||||
, CoreSrc_PostReverb
|
||||
|
||||
// Final output of the core. For core 0, it's the feed into Core1.
|
||||
// For Core1, it's the feed into SndOut.
|
||||
, CoreSrc_External
|
||||
|
||||
, CoreSrc_Count
|
||||
};
|
||||
|
||||
void Open();
|
||||
void Close();
|
||||
void WriteCore( uint coreidx, CoreSourceType src, s16 left, s16 right );
|
||||
}
|
||||
|
||||
using WaveDump::CoreSrc_Input;
|
||||
using WaveDump::CoreSrc_DryVoiceMix;
|
||||
using WaveDump::CoreSrc_WetVoiceMix;
|
||||
using WaveDump::CoreSrc_PreReverb;
|
||||
using WaveDump::CoreSrc_PostReverb;
|
||||
using WaveDump::CoreSrc_External;
|
||||
|
||||
#endif // DEBUG_H_INCLUDED //
|
|
@ -0,0 +1,269 @@
|
|||
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
* Developed and maintained by the Pcsx2 Development Team.
|
||||
*
|
||||
* Originally based on SPU2ghz v1.9 beta, by David Quintana.
|
||||
*
|
||||
* 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 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
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "spu2.h"
|
||||
#include "DPLII.h"
|
||||
#include <string.h>
|
||||
|
||||
static const u8 sLogTable[256] = {
|
||||
0x00,0x3C,0x60,0x78,0x8C,0x9C,0xA8,0xB4,0xBE,0xC8,0xD0,0xD8,0xDE,0xE4,0xEA,0xF0,
|
||||
0xF6,0xFA,0xFE,0x04,0x08,0x0C,0x10,0x14,0x16,0x1A,0x1E,0x20,0x24,0x26,0x2A,0x2C,
|
||||
0x2E,0x32,0x34,0x36,0x38,0x3A,0x3E,0x40,0x42,0x44,0x46,0x48,0x4A,0x4C,0x4E,0x50,
|
||||
0x50,0x52,0x54,0x56,0x58,0x5A,0x5A,0x5C,0x5E,0x60,0x60,0x62,0x64,0x66,0x66,0x68,
|
||||
0x6A,0x6A,0x6C,0x6E,0x6E,0x70,0x70,0x72,0x74,0x74,0x76,0x76,0x78,0x7A,0x7A,0x7C,
|
||||
0x7C,0x7E,0x7E,0x80,0x80,0x82,0x82,0x84,0x84,0x86,0x86,0x88,0x88,0x8A,0x8A,0x8C,
|
||||
0x8C,0x8C,0x8E,0x8E,0x90,0x90,0x92,0x92,0x92,0x94,0x94,0x96,0x96,0x96,0x98,0x98,
|
||||
0x9A,0x9A,0x9A,0x9C,0x9C,0x9C,0x9E,0x9E,0xA0,0xA0,0xA0,0xA2,0xA2,0xA2,0xA4,0xA4,
|
||||
0xA4,0xA6,0xA6,0xA6,0xA8,0xA8,0xA8,0xAA,0xAA,0xAA,0xAC,0xAC,0xAC,0xAC,0xAE,0xAE,
|
||||
0xAE,0xB0,0xB0,0xB0,0xB2,0xB2,0xB2,0xB2,0xB4,0xB4,0xB4,0xB6,0xB6,0xB6,0xB6,0xB8,
|
||||
0xB8,0xB8,0xB8,0xBA,0xBA,0xBA,0xBC,0xBC,0xBC,0xBC,0xBE,0xBE,0xBE,0xBE,0xC0,0xC0,
|
||||
0xC0,0xC0,0xC2,0xC2,0xC2,0xC2,0xC2,0xC4,0xC4,0xC4,0xC4,0xC6,0xC6,0xC6,0xC6,0xC8,
|
||||
0xC8,0xC8,0xC8,0xC8,0xCA,0xCA,0xCA,0xCA,0xCC,0xCC,0xCC,0xCC,0xCC,0xCE,0xCE,0xCE,
|
||||
0xCE,0xCE,0xD0,0xD0,0xD0,0xD0,0xD0,0xD2,0xD2,0xD2,0xD2,0xD2,0xD4,0xD4,0xD4,0xD4,
|
||||
0xD4,0xD6,0xD6,0xD6,0xD6,0xD6,0xD8,0xD8,0xD8,0xD8,0xD8,0xD8,0xDA,0xDA,0xDA,0xDA,
|
||||
0xDA,0xDC,0xDC,0xDC,0xDC,0xDC,0xDC,0xDE,0xDE,0xDE,0xDE,0xDE,0xDE,0xE0,0xE0,0xE0,
|
||||
};
|
||||
|
||||
DPLII::DPLII( s32 lowpass_freq, s32 samplerate ) :
|
||||
LAccum( 0 ),
|
||||
RAccum( 0 ),
|
||||
ANum( 0 ),
|
||||
lpf_l( lowpass_freq, samplerate ),
|
||||
lpf_r( lowpass_freq, samplerate ),
|
||||
bufdone( 1 ),
|
||||
Gfl( 0 ),
|
||||
Gfr( 0 ),
|
||||
LMax( 0 ),
|
||||
RMax( 0 )
|
||||
{
|
||||
memset( LBuff, 0, sizeof( LBuff ) );
|
||||
memset( RBuff, 0, sizeof( RBuff ) );
|
||||
memset( spdif_data, 0, sizeof( spdif_data ) );
|
||||
}
|
||||
|
||||
// Takes a single stereo input sample and translates it into six output samples
|
||||
// for 5.1 audio support on non-DPL2 hardware.
|
||||
void DPLII::Convert( s16 *obuffer, s32 ValL, s32 ValR )
|
||||
{
|
||||
ValL >>= 2;
|
||||
ValR >>= 2;
|
||||
if(PlayMode&4)
|
||||
{
|
||||
spdif_get_samples(spdif_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
spdif_data[0]=0;
|
||||
spdif_data[1]=0;
|
||||
spdif_data[2]=0;
|
||||
spdif_data[3]=0;
|
||||
spdif_data[4]=0;
|
||||
spdif_data[5]=0;
|
||||
}
|
||||
|
||||
//const u8 shift = SndOutVolumeShift;
|
||||
|
||||
s32 XL = abs(ValL>>8);
|
||||
s32 XR = abs(ValR>>8);
|
||||
|
||||
if(XL>LMax) LMax = XL;
|
||||
if(XR>RMax) RMax = XR;
|
||||
|
||||
ANum++;
|
||||
if(ANum>=128)
|
||||
{
|
||||
ANum=0;
|
||||
LAccum = 1+((LAccum * 224 + LMax * 31)>>8);
|
||||
RAccum = 1+((RAccum * 224 + RMax * 31)>>8);
|
||||
|
||||
LMax = 0;
|
||||
RMax = 0;
|
||||
|
||||
s32 Tfl=(RAccum)*255/(LAccum);
|
||||
s32 Tfr=(LAccum)*255/(RAccum);
|
||||
|
||||
int gMax = max(Tfl,Tfr);
|
||||
Tfl=Tfl*255/gMax;
|
||||
Tfr=Tfr*255/gMax;
|
||||
|
||||
if(Tfl>255) Tfl=255;
|
||||
if(Tfr>255) Tfr=255;
|
||||
if(Tfl<1) Tfl=1;
|
||||
if(Tfr<1) Tfr=1;
|
||||
|
||||
Gfl = (Gfl * 200 + Tfl * 56)>>8;
|
||||
Gfr = (Gfr * 200 + Tfr * 56)>>8;
|
||||
|
||||
}
|
||||
|
||||
s32 L,R,C,LFE,SL,SR,LL,LR;
|
||||
|
||||
extern double pow_2_31;
|
||||
LL = (s32)(lpf_l.sample((ValL>>4)/pow_2_31)*pow_2_31);
|
||||
LR = (s32)(lpf_r.sample((ValR>>4)/pow_2_31)*pow_2_31);
|
||||
LFE = (LL + LR)>>4;
|
||||
|
||||
C=(ValL+ValR)>>1; //16.8
|
||||
|
||||
ValL-=C;//16.8
|
||||
ValR-=C;//16.8
|
||||
|
||||
L=ValL>>8; //16.0
|
||||
R=ValR>>8; //16.0
|
||||
C=C>>8; //16.0
|
||||
|
||||
const s32 Cfl = 1 + sLogTable[Gfl];
|
||||
const s32 Cfr = 1 + sLogTable[Gfr];
|
||||
|
||||
const s32 VL = (ValL>>4) * Cfl; //16.12
|
||||
const s32 VR = (ValR>>4) * Cfr;
|
||||
|
||||
const s32 SC = (VL-VR)>>15;
|
||||
|
||||
SL = (((VR/148 - VL/209)>>4)*Cfr)>>8;
|
||||
SR = (((VR/209 - VL/148)>>4)*Cfl)>>8;
|
||||
|
||||
int AddCX = (C * Config_DSound51.AddCLR)>>8;
|
||||
|
||||
obuffer[0]=spdif_data[0] + (((L * Config_DSound51.GainL ))>>8) + AddCX;
|
||||
obuffer[1]=spdif_data[1] + (((R * Config_DSound51.GainR ))>>8) + AddCX;
|
||||
obuffer[2]=spdif_data[2] + (((C * Config_DSound51.GainC ))>>8); // - AddCX;
|
||||
obuffer[3]=spdif_data[3] + (((LFE * Config_DSound51.GainLFE))>>8);
|
||||
obuffer[4]=spdif_data[4] + (((SL * Config_DSound51.GainSL ))>>8);
|
||||
obuffer[5]=spdif_data[5] + (((SR * Config_DSound51.GainSR ))>>8);
|
||||
|
||||
#if 0
|
||||
if( UseAveraging )
|
||||
{
|
||||
LAccum+=abs(ValL);
|
||||
RAccum+=abs(ValR);
|
||||
ANum++;
|
||||
|
||||
if(ANum>=512)
|
||||
{
|
||||
LMax=0;RMax=0;
|
||||
|
||||
LAccum/=ANum;
|
||||
RAccum/=ANum;
|
||||
ANum=0;
|
||||
|
||||
for(int i=0;i<127;i++)
|
||||
{
|
||||
LMax+=LBuff[i];
|
||||
RMax+=RBuff[i];
|
||||
LBuff[i]=LBuff[i+1];
|
||||
RBuff[i]=RBuff[i+1];
|
||||
}
|
||||
LBuff[127]=LAccum;
|
||||
RBuff[127]=RAccum;
|
||||
LMax+=LAccum;
|
||||
RMax+=RAccum;
|
||||
|
||||
s32 TL = (LMax>>15)+1;
|
||||
s32 TR = (RMax>>15)+1;
|
||||
|
||||
Gfl=(RMax)/(TL);
|
||||
Gfr=(LMax)/(TR);
|
||||
|
||||
if(Gfl>255) Gfl=255;
|
||||
if(Gfr>255) Gfr=255;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(ValL>LMax) LMax = ValL;
|
||||
if(-ValL>LMax) LMax = -ValL;
|
||||
if(ValR>RMax) RMax = ValR;
|
||||
if(-ValR>RMax) RMax = -ValR;
|
||||
|
||||
ANum++;
|
||||
if(ANum>=128)
|
||||
{
|
||||
// shift into a 21 bit value
|
||||
const u8 shift = SndOutVolumeShift-5;
|
||||
|
||||
ANum=0;
|
||||
LAccum = ((LAccum * 224) + (LMax>>shift))>>8;
|
||||
RAccum = ((RAccum * 224) + (RMax>>shift))>>8;
|
||||
|
||||
LMax=0;
|
||||
RMax=0;
|
||||
|
||||
if(LAccum<1) LAccum=1;
|
||||
if(RAccum<1) RAccum=1;
|
||||
|
||||
Gfl=(RAccum*256)/LAccum;
|
||||
Gfr=(LAccum*256)/RAccum;
|
||||
|
||||
int gMax = max(Gfl,Gfr);
|
||||
|
||||
Gfl=(Gfl*256)/gMax;
|
||||
Gfr=(Gfr*256)/gMax;
|
||||
|
||||
if(Gfl>255) Gfl=255;
|
||||
if(Gfr>255) Gfr=255;
|
||||
if(Gfl<1) Gfl=1;
|
||||
if(Gfr<1) Gfr=1;
|
||||
}
|
||||
}
|
||||
|
||||
Gfr = 1; Gfl = 1;
|
||||
|
||||
extern double pow_2_31;
|
||||
|
||||
// shift Values into 12 bits:
|
||||
u8 shift2 = SndOutVolumeShift + 4;
|
||||
|
||||
const s32 LL = (s32)(lpf_l.sample((ValL>>shift2)/pow_2_31)*pow_2_31);
|
||||
const s32 LR = (s32)(lpf_r.sample((ValR>>shift2)/pow_2_31)*pow_2_31);
|
||||
const s32 LFE = (LL + LR)>>4;
|
||||
|
||||
s32 C = (ValL+ValR)>>1; //16.8
|
||||
|
||||
ValL -= C;//16.8
|
||||
ValR -= C;//16.8
|
||||
|
||||
const s32 L = ValL>>SndOutVolumeShift; //16.0
|
||||
const s32 R = ValR>>SndOutVolumeShift; //16.0
|
||||
C >>= SndOutVolumeShift; //16.0
|
||||
|
||||
const s32 VL = (ValL>>4) * Gfl; //16.12
|
||||
const s32 VR = (ValR>>4) * Gfr;
|
||||
|
||||
s32 SL = (VL/209 - VR/148)>>4; //16.0 (?)
|
||||
s32 SR = (VL/148 - VR/209)>>4; //16.0 (?)
|
||||
|
||||
// increase surround stereo separation
|
||||
const int SC = (SL+SR)>>1; //16.0
|
||||
const int SLd = SL - SC; //16.0
|
||||
const int SRd = SL - SC; //16.0
|
||||
|
||||
SL = (SLd * 209 + SC * 148)>>8; //16.0
|
||||
SR = (SRd * 209 + SC * 148)>>8; //16.0
|
||||
|
||||
obuffer[0]=spdif_data[0] + (((L * Config_DSound51.GainL ) + (C * Config_DSound51.AddCLR))>>8);
|
||||
obuffer[1]=spdif_data[1] + (((R * Config_DSound51.GainR ) + (C * Config_DSound51.AddCLR))>>8);
|
||||
obuffer[2]=spdif_data[2] + (((C * Config_DSound51.GainC ))>>8);
|
||||
obuffer[3]=spdif_data[3] + (((LFE * Config_DSound51.GainLFE))>>8);
|
||||
obuffer[4]=spdif_data[4] + (((SL * Config_DSound51.GainSL ))>>8);
|
||||
obuffer[5]=spdif_data[5] + (((SR * Config_DSound51.GainSR ))>>8);
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,385 @@
|
|||
//GiGaHeRz's SPU2 Driver
|
||||
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
|
||||
//
|
||||
//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
|
||||
//
|
||||
#include "spu2.h"
|
||||
|
||||
extern "C" {
|
||||
#include "liba52/inttypes.h"
|
||||
#include "liba52/a52.h"
|
||||
#include "liba52/mm_accel.h"
|
||||
}
|
||||
|
||||
extern u32 spdif_read_data(u8 *buff, u32 max_data);
|
||||
|
||||
#define DATA_SIZE 0x100000
|
||||
|
||||
u32 use51=0;
|
||||
|
||||
u8 databuffer[DATA_SIZE];
|
||||
u32 data_in_buffer;
|
||||
|
||||
s32 output_buffer[0x10000][6];
|
||||
s32 output_write_cursor=0;
|
||||
s32 output_read_cursor=0;
|
||||
s32 output_buffer_data=0;
|
||||
|
||||
int flags,srate,bitrate;
|
||||
|
||||
#define CHANNEL_CENTER 1
|
||||
#define CHANNEL_STEREO 2
|
||||
#define CHANNEL_SURROUND 4
|
||||
#define CHANNEL_LFE 8
|
||||
|
||||
int sample_flags = 0;
|
||||
|
||||
u32 frame_size;
|
||||
|
||||
a52_state_t* ac3dec;
|
||||
sample_t *decode_buffer = NULL;
|
||||
|
||||
s32 data_rate=4;
|
||||
|
||||
int state=0;
|
||||
|
||||
FILE *fSpdifDump;
|
||||
|
||||
extern u32 core;
|
||||
void __fastcall ReadInput(V_Core& thiscore, s32& PDataL,s32& PDataR);
|
||||
|
||||
union spdif_frame { // total size: 32bits
|
||||
struct {
|
||||
u32 preamble:4; //4
|
||||
u32 databits:24; //28
|
||||
u32 valid:1; //29
|
||||
u32 subcode:1; //30
|
||||
u32 chanstat:1; //31
|
||||
u32 parity:1; //32 // parity not including preamble
|
||||
} bits;
|
||||
u32 whole;
|
||||
};
|
||||
|
||||
union spdif_block {
|
||||
spdif_frame frames[192];
|
||||
u8 bytes [192*4];
|
||||
};
|
||||
|
||||
/*
|
||||
spdif_block bbuffer[2];
|
||||
u8 *bbuff = bbuffer[0].bytes;
|
||||
u32 bbuff_bytes = 0;
|
||||
*/
|
||||
|
||||
bool check_frame(spdif_frame f)
|
||||
{
|
||||
u32 w = f.whole>>4;
|
||||
u32 t = 0;
|
||||
|
||||
for(int i=0;i>28;i++)
|
||||
{
|
||||
t=t^(w&1);
|
||||
w>>=1;
|
||||
}
|
||||
|
||||
return (t==0)&&(f.bits.valid);
|
||||
}
|
||||
|
||||
void spdif_Write(s32 data)
|
||||
{
|
||||
spdif_frame f;
|
||||
|
||||
f.whole=data;
|
||||
|
||||
if(check_frame(f))
|
||||
{
|
||||
int dec = f.bits.databits;
|
||||
databuffer[data_in_buffer++]=(dec )&0xFF;
|
||||
databuffer[data_in_buffer++]=(dec>> 8)&0xFF;
|
||||
databuffer[data_in_buffer++]=(dec>>16)&0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
void spdif_remove_data(unsigned int bytes)
|
||||
{
|
||||
if(bytes<data_in_buffer)
|
||||
{
|
||||
memcpy(databuffer,databuffer+bytes,data_in_buffer-bytes);
|
||||
}
|
||||
data_in_buffer-=bytes;
|
||||
}
|
||||
|
||||
s32 stoi(sample_t n) //input: [-1..1]
|
||||
{
|
||||
s32 sign=(n<0) + (n>0)*-1; //1 if positive, -1 if negative, 0 otherwise
|
||||
n=abs(n)+1; //make it [1..2]
|
||||
s32 k=*(s32*)&n;
|
||||
k=k&0x7FFFFF;
|
||||
return k*sign;
|
||||
}
|
||||
|
||||
void spdif_update()
|
||||
{
|
||||
s32 Data,Zero;
|
||||
|
||||
core=0;
|
||||
V_Core& thiscore( Cores[core] );
|
||||
for(int i=0;i<data_rate;i++)
|
||||
{
|
||||
ReadInput(thiscore, Data,Zero);
|
||||
|
||||
if(fSpdifDump)
|
||||
{
|
||||
fwrite(&Data,4,1,fSpdifDump);
|
||||
fwrite(&Zero,4,1,fSpdifDump);
|
||||
}
|
||||
|
||||
if(ac3dec)
|
||||
spdif_Write(Data);
|
||||
}
|
||||
|
||||
if(!ac3dec) return;
|
||||
|
||||
if(state==0)
|
||||
{
|
||||
if(data_in_buffer<7) return;
|
||||
|
||||
if(databuffer[0]!=0)
|
||||
{
|
||||
flags=0;
|
||||
}
|
||||
frame_size = a52_syncinfo (databuffer, &flags, &srate, &bitrate);
|
||||
if(frame_size==0)
|
||||
{
|
||||
spdif_remove_data(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
state=1;
|
||||
}
|
||||
}
|
||||
if(state==1)
|
||||
{
|
||||
if(data_in_buffer<frame_size) return;
|
||||
|
||||
flags = A52_ADJUST_LEVEL;
|
||||
|
||||
if(use51) flags|=A52_3F2R|A52_LFE;
|
||||
else flags|=A52_STEREO;
|
||||
|
||||
sample_t level=1;
|
||||
a52_frame(ac3dec,databuffer,&flags,&level,0);
|
||||
|
||||
//decode
|
||||
for(int i=0;i<6;i++)
|
||||
{
|
||||
a52_block(ac3dec);
|
||||
|
||||
// processing
|
||||
for(int j=0;j<256;j++)
|
||||
{
|
||||
s32* samples = output_buffer[output_write_cursor];
|
||||
sample_flags=0;
|
||||
|
||||
int output_cursor=j;
|
||||
|
||||
int n=0;
|
||||
if(flags&A52_LFE)
|
||||
{
|
||||
sample_flags|=CHANNEL_LFE;
|
||||
|
||||
samples[3] = stoi(decode_buffer[(0<<8)+output_cursor]); //lfe
|
||||
n=1;
|
||||
}
|
||||
|
||||
switch(flags&(~A52_LFE))
|
||||
{
|
||||
case A52_STEREO:
|
||||
sample_flags |= CHANNEL_STEREO;
|
||||
samples[0] = stoi(decode_buffer[((n+0)<<8)+output_cursor]); //l
|
||||
samples[1] = stoi(decode_buffer[((n+1)<<8)+output_cursor]); //r
|
||||
samples[2] = 0; //c
|
||||
samples[4] = 0; //sl
|
||||
samples[5] = 0; //sr
|
||||
break;
|
||||
case A52_2F1R:
|
||||
sample_flags |= CHANNEL_STEREO|CHANNEL_SURROUND;
|
||||
samples[0] = stoi(decode_buffer[((n+0)<<8)+output_cursor]); //l
|
||||
samples[1] = stoi(decode_buffer[((n+1)<<8)+output_cursor]); //r
|
||||
samples[2] = 0; //c
|
||||
samples[4] = stoi(decode_buffer[((n+2)<<8)+output_cursor]); //sl
|
||||
samples[5] = stoi(decode_buffer[((n+2)<<8)+output_cursor]); //sr
|
||||
break;
|
||||
case A52_2F2R:
|
||||
sample_flags |= CHANNEL_STEREO|CHANNEL_SURROUND;
|
||||
samples[0] = stoi(decode_buffer[((n+0)<<8)+output_cursor]); //l
|
||||
samples[1] = stoi(decode_buffer[((n+1)<<8)+output_cursor]); //r
|
||||
samples[2] = 0; //c
|
||||
samples[4] = stoi(decode_buffer[((n+2)<<8)+output_cursor]); //sl
|
||||
samples[5] = stoi(decode_buffer[((n+3)<<8)+output_cursor]); //sr
|
||||
break;
|
||||
case A52_3F:
|
||||
sample_flags |= CHANNEL_STEREO|CHANNEL_CENTER;
|
||||
samples[0] = stoi(decode_buffer[((n+0)<<8)+output_cursor]); //l
|
||||
samples[1] = stoi(decode_buffer[((n+2)<<8)+output_cursor]); //r
|
||||
samples[2] = stoi(decode_buffer[((n+1)<<8)+output_cursor]); //c
|
||||
samples[4] = 0; //sl
|
||||
samples[5] = 0; //sr
|
||||
break;
|
||||
case A52_3F1R:
|
||||
sample_flags |= CHANNEL_STEREO|CHANNEL_SURROUND|CHANNEL_CENTER;
|
||||
samples[0] = stoi(decode_buffer[((n+0)<<8)+output_cursor]); //l
|
||||
samples[1] = stoi(decode_buffer[((n+2)<<8)+output_cursor]); //r
|
||||
samples[2] = stoi(decode_buffer[((n+1)<<8)+output_cursor]); //c
|
||||
samples[4] = stoi(decode_buffer[((n+2)<<8)+output_cursor]); //sl
|
||||
samples[5] = stoi(decode_buffer[((n+2)<<8)+output_cursor]); //sr
|
||||
break;
|
||||
case A52_3F2R:
|
||||
sample_flags |= CHANNEL_STEREO|CHANNEL_SURROUND|CHANNEL_CENTER;
|
||||
samples[0] = stoi(decode_buffer[((n+0)<<8)+output_cursor]); //l
|
||||
samples[1] = stoi(decode_buffer[((n+2)<<8)+output_cursor]); //r
|
||||
samples[2] = stoi(decode_buffer[((n+1)<<8)+output_cursor]); //c
|
||||
samples[4] = stoi(decode_buffer[((n+2)<<8)+output_cursor]); //sl
|
||||
samples[5] = stoi(decode_buffer[((n+2)<<8)+output_cursor]); //sr
|
||||
break;
|
||||
default:
|
||||
samples[0] = stoi(decode_buffer[((n+0)<<8)+output_cursor]); //l
|
||||
samples[1] = stoi(decode_buffer[((n+1)<<8)+output_cursor]); //r
|
||||
samples[2] = 0; //c
|
||||
samples[4] = 0; //sl
|
||||
samples[5] = 0; //sr
|
||||
break;
|
||||
}
|
||||
|
||||
output_write_cursor=(output_write_cursor+1)&0xFFFF;
|
||||
output_buffer_data++;
|
||||
}
|
||||
}
|
||||
spdif_remove_data(frame_size);
|
||||
}
|
||||
}
|
||||
|
||||
u32 spdif_init()
|
||||
{
|
||||
data_rate=1; // words/tick
|
||||
|
||||
ac3dec = a52_init(0);
|
||||
if(!ac3dec) return 1;
|
||||
|
||||
decode_buffer = a52_samples(ac3dec);
|
||||
a52_dynrng (ac3dec, NULL, NULL);
|
||||
data_in_buffer=0;
|
||||
|
||||
fSpdifDump = fopen("logs/spdif.dat","wb");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spdif_set51(u32 is_5_1_out)
|
||||
{
|
||||
use51 = is_5_1_out;
|
||||
}
|
||||
|
||||
void spdif_shutdown()
|
||||
{
|
||||
if(ac3dec) a52_free(ac3dec);
|
||||
ac3dec=NULL;
|
||||
if(fSpdifDump)
|
||||
fclose(fSpdifDump);
|
||||
}
|
||||
|
||||
int spdif_decode_one(s32 *channels)
|
||||
{
|
||||
channels[0]=0;
|
||||
channels[1]=0;
|
||||
channels[2]=0;
|
||||
channels[3]=0;
|
||||
channels[4]=0;
|
||||
channels[5]=0;
|
||||
|
||||
if(output_buffer_data==0) return 0;
|
||||
|
||||
memcpy(channels,output_buffer[output_read_cursor],4*6);
|
||||
output_read_cursor=(output_read_cursor+1)&0xFFFF;
|
||||
output_buffer_data--;
|
||||
|
||||
return sample_flags;
|
||||
}
|
||||
|
||||
void spdif_get_samples(s32*samples)
|
||||
{
|
||||
s32 channels[6];
|
||||
s32 flags = spdif_decode_one(channels);
|
||||
|
||||
if(use51)
|
||||
{
|
||||
samples[0]=0;
|
||||
samples[1]=0;
|
||||
samples[2]=0;
|
||||
samples[3]=0;
|
||||
samples[4]=0;
|
||||
samples[5]=0;
|
||||
|
||||
if(flags&CHANNEL_STEREO)
|
||||
{
|
||||
samples[0]=channels[0];
|
||||
samples[1]=channels[1];
|
||||
}
|
||||
|
||||
if(flags&CHANNEL_CENTER)
|
||||
{
|
||||
samples[2]=channels[2];
|
||||
}
|
||||
|
||||
if(flags&CHANNEL_LFE)
|
||||
{
|
||||
samples[3]=channels[3];
|
||||
}
|
||||
|
||||
if(flags&CHANNEL_SURROUND)
|
||||
{
|
||||
samples[4]=channels[4];
|
||||
samples[5]=channels[5];
|
||||
}
|
||||
}
|
||||
else // downmix to stereo (no DPL2 encoding... yet ;)
|
||||
{
|
||||
samples[0]=0;
|
||||
samples[1]=0;
|
||||
|
||||
if(flags&CHANNEL_STEREO)
|
||||
{
|
||||
samples[0]=channels[0];
|
||||
samples[1]=channels[1];
|
||||
}
|
||||
|
||||
if(flags&CHANNEL_CENTER)
|
||||
{
|
||||
samples[0]+=channels[2];
|
||||
samples[1]+=channels[2];
|
||||
}
|
||||
|
||||
if(flags&CHANNEL_LFE)
|
||||
{
|
||||
samples[0]+=channels[3];
|
||||
samples[1]+=channels[3];
|
||||
}
|
||||
|
||||
if(flags&CHANNEL_SURROUND)
|
||||
{
|
||||
samples[0]+=channels[4];
|
||||
samples[1]+=channels[5];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,442 @@
|
|||
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
* Developed and maintained by the Pcsx2 Development Team.
|
||||
*
|
||||
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
||||
*
|
||||
* 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 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
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "spu2.h"
|
||||
#include "regtable.h"
|
||||
#include "dialogs.h"
|
||||
|
||||
#include "svnrev.h"
|
||||
|
||||
// [Air]: Adding the spu2init boolean wasn't necessary except to help me in
|
||||
// debugging the spu2 suspend/resume behavior (when user hits escape).
|
||||
static bool spu2open=false; // has spu2open plugin interface been called?
|
||||
static bool spu2init=false; // has spu2init plugin interface been called?
|
||||
|
||||
//static s32 logvolume[16384];
|
||||
static u32 pClocks=0;
|
||||
|
||||
// Pcsx2 expects ASNI, not unicode, so this MUST always be char...
|
||||
static char libraryName[256];
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD dwReason,LPVOID lpvReserved)
|
||||
{
|
||||
if( dwReason == DLL_PROCESS_ATTACH )
|
||||
hInstance = hinstDLL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void InitLibraryName()
|
||||
{
|
||||
#ifdef PUBLIC
|
||||
|
||||
// Public Release!
|
||||
// Output a simplified string that's just our name:
|
||||
|
||||
strcpy( libraryName, "SPU2-X" );
|
||||
|
||||
#elif defined( SVN_REV_UNKNOWN )
|
||||
|
||||
// Unknown revision.
|
||||
// Output a name that includes devbuild status but not
|
||||
// subversion revision tags:
|
||||
|
||||
strcpy( libraryName, "SPU2-X"
|
||||
# ifdef _DEBUG_FAST
|
||||
"-Debug"
|
||||
# elif defined( DEBUG )
|
||||
"-Debug/Strict" // strict debugging is slow!
|
||||
# else
|
||||
"-Dev"
|
||||
# endif
|
||||
);
|
||||
#else
|
||||
|
||||
// Use TortoiseSVN's SubWCRev utility's output
|
||||
// to label the specific revision:
|
||||
|
||||
sprintf_s( libraryName, "SPU2-X r%d%s"
|
||||
# ifdef _DEBUG_FAST
|
||||
"-Debug"
|
||||
# elif defined( _DEBUG )
|
||||
"-Debug/Strict" // strict debugging is slow!
|
||||
# else
|
||||
"-Dev"
|
||||
# endif
|
||||
,SVN_REV,
|
||||
SVN_MODS ? "m" : ""
|
||||
);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
EXPORT_C_(u32) PS2EgetLibType()
|
||||
{
|
||||
return PS2E_LT_SPU2;
|
||||
}
|
||||
|
||||
EXPORT_C_(char*) PS2EgetLibName()
|
||||
{
|
||||
InitLibraryName();
|
||||
return libraryName;
|
||||
}
|
||||
|
||||
EXPORT_C_(u32) PS2EgetLibVersion2(u32 type)
|
||||
{
|
||||
return (VersionInfo::PluginApi<<16) | (VersionInfo::Release<<8) | VersionInfo::Revision;
|
||||
}
|
||||
|
||||
EXPORT_C_(void) SPU2configure()
|
||||
{
|
||||
configure();
|
||||
}
|
||||
|
||||
EXPORT_C_(void) SPU2about()
|
||||
{
|
||||
//InitLibraryName();
|
||||
//SysMessage( libraryName );
|
||||
AboutBox();
|
||||
}
|
||||
|
||||
EXPORT_C_(s32) SPU2test()
|
||||
{
|
||||
return SndTest();
|
||||
}
|
||||
|
||||
EXPORT_C_(s32) SPU2init()
|
||||
{
|
||||
#define MAKESURE(a,b) \
|
||||
/*fprintf(stderr,"%08p: %08p == %08p\n",&(regtable[a>>1]),regtable[a>>1],U16P(b));*/ \
|
||||
assert(regtable[(a)>>1]==U16P(b))
|
||||
|
||||
MAKESURE(0x800,zero);
|
||||
|
||||
s32 c=0,v=0;
|
||||
ReadSettings();
|
||||
|
||||
#ifdef SPU2_LOG
|
||||
if(AccessLog())
|
||||
{
|
||||
spu2Log = _wfopen( AccessLogFileName, _T("w") );
|
||||
setvbuf(spu2Log, NULL, _IONBF, 0);
|
||||
FileLog("SPU2init\n");
|
||||
}
|
||||
#endif
|
||||
srand((unsigned)time(NULL));
|
||||
|
||||
disableFreezes=false;
|
||||
|
||||
if (spu2init)
|
||||
{
|
||||
ConLog( " * SPU2: Already initialized - Ignoring SPU2init signal." );
|
||||
return 0;
|
||||
}
|
||||
|
||||
spu2init=true;
|
||||
|
||||
spu2regs = (short*)malloc(0x010000);
|
||||
_spu2mem = (short*)malloc(0x200000);
|
||||
|
||||
// adpcm decoder cache:
|
||||
// the cache data size is determined by taking the number of adpcm blocks
|
||||
// (2MB / 16) and multiplying it by the decoded block size (28 samples).
|
||||
// Thus: pcm_cache_data = 7,340,032 bytes (ouch!)
|
||||
// Expanded: 16 bytes expands to 56 bytes [3.5:1 ratio]
|
||||
// Resulting in 2MB * 3.5.
|
||||
|
||||
pcm_cache_data = (PcmCacheEntry*)calloc( pcm_BlockCount, sizeof(PcmCacheEntry) );
|
||||
|
||||
if( (spu2regs == NULL) || (_spu2mem == NULL) ||
|
||||
(pcm_cache_data == NULL) )
|
||||
{
|
||||
SysMessage("SPU2: Error allocating Memory\n"); return -1;
|
||||
}
|
||||
|
||||
for(int mem=0;mem<0x800;mem++)
|
||||
{
|
||||
u16 *ptr=regtable[mem>>1];
|
||||
if(!ptr) {
|
||||
regtable[mem>>1] = &(spu2Ru16(mem));
|
||||
}
|
||||
}
|
||||
|
||||
memset(spu2regs,0,0x010000);
|
||||
memset(_spu2mem,0,0x200000);
|
||||
Cores[0].Reset();
|
||||
Cores[1].Reset();
|
||||
|
||||
DMALogOpen();
|
||||
|
||||
/*for(v=0;v<16384;v++)
|
||||
{
|
||||
logvolume[v]=(s32)(s32)floor(log((double)(v+1))*3376.7);
|
||||
}*/
|
||||
|
||||
// Initializes lowpass filter for reverb in mixer.cpp
|
||||
//LowPassFilterInit();
|
||||
InitADSR();
|
||||
|
||||
#ifdef STREAM_DUMP
|
||||
il0=fopen("logs/spu2input0.pcm","wb");
|
||||
il1=fopen("logs/spu2input1.pcm","wb");
|
||||
#endif
|
||||
|
||||
#ifdef EFFECTS_DUMP
|
||||
el0=fopen("logs/spu2fx0.pcm","wb");
|
||||
el1=fopen("logs/spu2fx1.pcm","wb");
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef S2R_ENABLE
|
||||
if(!replay_mode)
|
||||
s2r_open("replay_dump.s2r");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_C_(s32) SPU2open(void *pDsp)
|
||||
{
|
||||
if( spu2open ) return 0;
|
||||
|
||||
FileLog("[%10d] SPU2 Open\n",Cycles);
|
||||
|
||||
/*
|
||||
if(debugDialogOpen==0)
|
||||
{
|
||||
hDebugDialog = CreateDialogParam(hInstance,MAKEINTRESOURCE(IDD_DEBUG),0,DebugProc,0);
|
||||
ShowWindow(hDebugDialog,SW_SHOWNORMAL);
|
||||
debugDialogOpen=1;
|
||||
}*/
|
||||
|
||||
spu2open=true;
|
||||
if (!SndInit())
|
||||
{
|
||||
spdif_init();
|
||||
|
||||
DspLoadLibrary(dspPlugin,dspPluginModule);
|
||||
|
||||
WaveDump::Open();
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
SPU2close();
|
||||
return -1;
|
||||
};
|
||||
}
|
||||
|
||||
EXPORT_C_(void) SPU2close()
|
||||
{
|
||||
if( !spu2open ) return;
|
||||
FileLog("[%10d] SPU2 Close\n",Cycles);
|
||||
|
||||
DspCloseLibrary();
|
||||
spdif_shutdown();
|
||||
SndClose();
|
||||
|
||||
spu2open = false;
|
||||
}
|
||||
|
||||
EXPORT_C_(void) SPU2shutdown()
|
||||
{
|
||||
if(!spu2init) return;
|
||||
|
||||
ConLog( " * SPU2: Shutting down.\n" );
|
||||
|
||||
SPU2close();
|
||||
|
||||
#ifdef S2R_ENABLE
|
||||
if(!replay_mode)
|
||||
s2r_close();
|
||||
#endif
|
||||
|
||||
DoFullDump();
|
||||
#ifdef STREAM_DUMP
|
||||
fclose(il0);
|
||||
fclose(il1);
|
||||
#endif
|
||||
#ifdef EFFECTS_DUMP
|
||||
fclose(el0);
|
||||
fclose(el1);
|
||||
#endif
|
||||
WaveDump::Close();
|
||||
|
||||
DMALogClose();
|
||||
|
||||
spu2init = false;
|
||||
|
||||
SAFE_FREE(spu2regs);
|
||||
SAFE_FREE(_spu2mem);
|
||||
|
||||
SAFE_FREE( pcm_cache_data );
|
||||
|
||||
spu2regs = NULL;
|
||||
_spu2mem = NULL;
|
||||
pcm_cache_data = NULL;
|
||||
|
||||
#ifdef SPU2_LOG
|
||||
if(!AccessLog()) return;
|
||||
FileLog("[%10d] SPU2shutdown\n",Cycles);
|
||||
if(spu2Log) fclose(spu2Log);
|
||||
#endif
|
||||
}
|
||||
|
||||
EXPORT_C_(void) SPU2setClockPtr(u32 *ptr)
|
||||
{
|
||||
cPtr=ptr;
|
||||
hasPtr=(cPtr!=NULL);
|
||||
}
|
||||
|
||||
bool numpad_plus = false, numpad_plus_old = false;
|
||||
|
||||
EXPORT_C_(void) SPU2async(u32 cycles)
|
||||
{
|
||||
#ifndef PUBLIC
|
||||
u32 oldClocks = lClocks;
|
||||
static u32 timer=0,time1=0,time2=0;
|
||||
timer++;
|
||||
if (timer == 1){
|
||||
time1=timeGetTime();
|
||||
}
|
||||
if (timer == 3000){
|
||||
time2 = timeGetTime()-time1 ;
|
||||
timer=0;
|
||||
}
|
||||
#endif
|
||||
|
||||
DspUpdate();
|
||||
|
||||
#ifndef PUBLIC
|
||||
/*numpad_plus = (GetAsyncKeyState(VK_ADD)&0x8000)!=0;
|
||||
if(numpad_plus && !numpad_plus_old)
|
||||
{
|
||||
DoFullDump();
|
||||
}
|
||||
numpad_plus_old = numpad_plus;*/
|
||||
#endif
|
||||
|
||||
if(hasPtr)
|
||||
{
|
||||
TimeUpdate(*cPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
pClocks+=cycles;
|
||||
TimeUpdate(pClocks);
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT_C_(void) SPU2irqCallback(void (*SPU2callback)(),void (*DMA4callback)(),void (*DMA7callback)())
|
||||
{
|
||||
_irqcallback=SPU2callback;
|
||||
dma4callback=DMA4callback;
|
||||
dma7callback=DMA7callback;
|
||||
}
|
||||
|
||||
EXPORT_C_(u16) SPU2read(u32 rmem)
|
||||
{
|
||||
// if(!replay_mode)
|
||||
// s2r_readreg(Cycles,rmem);
|
||||
|
||||
if(hasPtr) TimeUpdate(*cPtr);
|
||||
|
||||
u16 ret=0xDEAD; u32 core=0, mem=rmem&0xFFFF, omem=mem;
|
||||
if (mem & 0x400) { omem^=0x400; core=1; }
|
||||
|
||||
if(rmem==0x1f9001AC)
|
||||
{
|
||||
ret = DmaRead(core);
|
||||
}
|
||||
else if (rmem>>16 == 0x1f80)
|
||||
{
|
||||
ret = SPU_ps1_read(rmem);
|
||||
}
|
||||
else if ((mem&0xFFFF)>=0x800)
|
||||
{
|
||||
ret=spu2Ru16(mem);
|
||||
ConLog(" * SPU2: Read from reg>=0x800: %x value %x\n",mem,ret);
|
||||
FileLog(" * SPU2: Read from reg>=0x800: %x value %x\n",mem,ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = *(regtable[(mem>>1)]);
|
||||
|
||||
FileLog("[%10d] SPU2 read mem %x (core %d, register %x): %x\n",Cycles, mem, core, (omem & 0x7ff), ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_C_(void) SPU2write(u32 rmem, u16 value)
|
||||
{
|
||||
#ifdef S2R_ENABLE
|
||||
if(!replay_mode)
|
||||
s2r_writereg(Cycles,rmem,value);
|
||||
#endif
|
||||
|
||||
if(rmem==0x1f9001ac)
|
||||
{
|
||||
//RegWriteLog(0,value);
|
||||
if((Cores[0].IRQEnable)&&(Cores[0].TSA==Cores[0].IRQA))
|
||||
{
|
||||
Spdif.Info=4;
|
||||
SetIrqCall();
|
||||
}
|
||||
spu2M_Write( Cores[0].TSA++, value );
|
||||
Cores[0].TSA&=0xfffff;
|
||||
}
|
||||
else if(rmem==0x1f9005ac)
|
||||
{
|
||||
//RegWriteLog(1,value);
|
||||
if((Cores[0].IRQEnable)&&(Cores[0].TSA==Cores[0].IRQA))
|
||||
{
|
||||
Spdif.Info=4;
|
||||
SetIrqCall();
|
||||
}
|
||||
spu2M_Write( Cores[1].TSA++, value );
|
||||
Cores[1].TSA&=0xfffff;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(hasPtr) TimeUpdate(*cPtr);
|
||||
|
||||
if (rmem>>16 == 0x1f80)
|
||||
SPU_ps1_write(rmem,value);
|
||||
else
|
||||
SPU2_FastWrite( rmem, value );
|
||||
}
|
||||
}
|
||||
|
||||
// if start is 1, starts recording spu2 data, else stops
|
||||
// returns a non zero value if successful
|
||||
// for now, pData is not used
|
||||
EXPORT_C_(int) SPU2setupRecording(int start, void* pData)
|
||||
{
|
||||
// Don't record if we have a bogus state.
|
||||
if( disableFreezes ) return 0;
|
||||
|
||||
if(start==0)
|
||||
RecordStop();
|
||||
else if(start==1)
|
||||
RecordStart();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,515 @@
|
|||
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
* Developed and maintained by the Pcsx2 Development Team.
|
||||
*
|
||||
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
||||
*
|
||||
* 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 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
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "spu2.h"
|
||||
|
||||
extern u8 callirq;
|
||||
|
||||
FILE *DMA4LogFile=0;
|
||||
FILE *DMA7LogFile=0;
|
||||
FILE *ADMA4LogFile=0;
|
||||
FILE *ADMA7LogFile=0;
|
||||
FILE *ADMAOutLogFile=0;
|
||||
|
||||
FILE *REGWRTLogFile[2]={0,0};
|
||||
|
||||
int packcount=0;
|
||||
|
||||
u16* MBASE[2] = {0,0};
|
||||
|
||||
u16* DMABaseAddr;
|
||||
|
||||
void DMALogOpen()
|
||||
{
|
||||
if(!DMALog()) return;
|
||||
DMA4LogFile = _wfopen( DMA4LogFileName, _T("wb") );
|
||||
DMA7LogFile = _wfopen( DMA7LogFileName, _T("wb") );
|
||||
ADMA4LogFile = fopen( "logs/adma4.raw", "wb" );
|
||||
ADMA7LogFile = fopen( "logs/adma7.raw", "wb" );
|
||||
ADMAOutLogFile = fopen( "logs/admaOut.raw", "wb" );
|
||||
//REGWRTLogFile[0]=fopen("logs/RegWrite0.raw","wb");
|
||||
//REGWRTLogFile[1]=fopen("logs/RegWrite1.raw","wb");
|
||||
}
|
||||
void DMA4LogWrite(void *lpData, u32 ulSize) {
|
||||
if(!DMALog()) return;
|
||||
if (!DMA4LogFile) return;
|
||||
fwrite(lpData,ulSize,1,DMA4LogFile);
|
||||
}
|
||||
|
||||
void DMA7LogWrite(void *lpData, u32 ulSize) {
|
||||
if(!DMALog()) return;
|
||||
if (!DMA7LogFile) return;
|
||||
fwrite(lpData,ulSize,1,DMA7LogFile);
|
||||
}
|
||||
|
||||
void ADMA4LogWrite(void *lpData, u32 ulSize) {
|
||||
if(!DMALog()) return;
|
||||
if (!ADMA4LogFile) return;
|
||||
fwrite(lpData,ulSize,1,ADMA4LogFile);
|
||||
}
|
||||
void ADMA7LogWrite(void *lpData, u32 ulSize) {
|
||||
if(!DMALog()) return;
|
||||
if (!ADMA7LogFile) return;
|
||||
fwrite(lpData,ulSize,1,ADMA7LogFile);
|
||||
}
|
||||
void ADMAOutLogWrite(void *lpData, u32 ulSize) {
|
||||
if(!DMALog()) return;
|
||||
if (!ADMAOutLogFile) return;
|
||||
fwrite(lpData,ulSize,1,ADMAOutLogFile);
|
||||
}
|
||||
|
||||
void RegWriteLog(u32 core,u16 value)
|
||||
{
|
||||
if(!DMALog()) return;
|
||||
if (!REGWRTLogFile[core]) return;
|
||||
fwrite(&value,2,1,REGWRTLogFile[core]);
|
||||
}
|
||||
|
||||
void DMALogClose() {
|
||||
if(!DMALog()) return;
|
||||
if (DMA4LogFile) fclose(DMA4LogFile);
|
||||
if (DMA7LogFile) fclose(DMA7LogFile);
|
||||
if (REGWRTLogFile[0]) fclose(REGWRTLogFile[0]);
|
||||
if (REGWRTLogFile[1]) fclose(REGWRTLogFile[1]);
|
||||
if (ADMA4LogFile) fclose(ADMA4LogFile);
|
||||
if (ADMA7LogFile) fclose(ADMA7LogFile);
|
||||
if (ADMAOutLogFile) fclose(ADMAOutLogFile);
|
||||
}
|
||||
|
||||
|
||||
__forceinline u16 DmaRead(u32 core)
|
||||
{
|
||||
const u16 ret = (u16)spu2M_Read(Cores[core].TDA);
|
||||
Cores[core].TDA++;
|
||||
Cores[core].TDA&=0xfffff;
|
||||
return ret;
|
||||
}
|
||||
|
||||
__forceinline void DmaWrite(u32 core, u16 value)
|
||||
{
|
||||
spu2M_Write( Cores[core].TSA, value );
|
||||
Cores[core].TSA++;
|
||||
Cores[core].TSA&=0xfffff;
|
||||
}
|
||||
|
||||
void AutoDMAReadBuffer(int core, int mode) //mode: 0= split stereo; 1 = do not split stereo
|
||||
{
|
||||
int spos=((Cores[core].InputPos+0xff)&0x100); //starting position of the free buffer
|
||||
|
||||
if(core==0)
|
||||
ADMA4LogWrite(Cores[core].DMAPtr+Cores[core].InputDataProgress,0x400);
|
||||
else
|
||||
ADMA7LogWrite(Cores[core].DMAPtr+Cores[core].InputDataProgress,0x400);
|
||||
|
||||
if(mode)
|
||||
{
|
||||
//hacky :p
|
||||
|
||||
memcpy((Cores[core].ADMATempBuffer+(spos<<1)),Cores[core].DMAPtr+Cores[core].InputDataProgress,0x400);
|
||||
Cores[core].MADR+=0x400;
|
||||
Cores[core].InputDataLeft-=0x200;
|
||||
Cores[core].InputDataProgress+=0x200;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy((Cores[core].ADMATempBuffer+spos),Cores[core].DMAPtr+Cores[core].InputDataProgress,0x200);
|
||||
//memcpy((spu2mem+0x2000+(core<<10)+spos),Cores[core].DMAPtr+Cores[core].InputDataProgress,0x200);
|
||||
Cores[core].MADR+=0x200;
|
||||
Cores[core].InputDataLeft-=0x100;
|
||||
Cores[core].InputDataProgress+=0x100;
|
||||
|
||||
memcpy((Cores[core].ADMATempBuffer+spos+0x200),Cores[core].DMAPtr+Cores[core].InputDataProgress,0x200);
|
||||
//memcpy((spu2mem+0x2200+(core<<10)+spos),Cores[core].DMAPtr+Cores[core].InputDataProgress,0x200);
|
||||
Cores[core].MADR+=0x200;
|
||||
Cores[core].InputDataLeft-=0x100;
|
||||
Cores[core].InputDataProgress+=0x100;
|
||||
}
|
||||
// See ReadInput at mixer.cpp for explanation on the commented out lines
|
||||
//
|
||||
}
|
||||
|
||||
void StartADMAWrite(int core,u16 *pMem, u32 sz)
|
||||
{
|
||||
int size=(sz)&(~511);
|
||||
|
||||
if(MsgAutoDMA()) ConLog(" * SPU2: DMA%c AutoDMA Transfer of %d bytes to %x (%02x %x %04x).\n",
|
||||
(core==0)?'4':'7',size<<1,Cores[core].TSA,Cores[core].DMABits,Cores[core].AutoDMACtrl,(~Cores[core].Regs.ATTR)&0x7fff);
|
||||
|
||||
Cores[core].InputDataProgress=0;
|
||||
if((Cores[core].AutoDMACtrl&(core+1))==0)
|
||||
{
|
||||
Cores[core].TSA=0x2000+(core<<10);
|
||||
Cores[core].DMAICounter=size;
|
||||
}
|
||||
else if(size>=512)
|
||||
{
|
||||
Cores[core].InputDataLeft=size;
|
||||
if(Cores[core].AdmaInProgress==0)
|
||||
{
|
||||
#ifdef PCM24_S1_INTERLEAVE
|
||||
if((core==1)&&((PlayMode&8)==8))
|
||||
{
|
||||
AutoDMAReadBuffer(core,1);
|
||||
}
|
||||
else
|
||||
{
|
||||
AutoDMAReadBuffer(core,0);
|
||||
}
|
||||
#else
|
||||
if(((PlayMode&4)==4)&&(core==0))
|
||||
Cores[0].InputPos=0;
|
||||
|
||||
AutoDMAReadBuffer(core,0);
|
||||
#endif
|
||||
|
||||
if(size==512)
|
||||
Cores[core].DMAICounter=size;
|
||||
}
|
||||
|
||||
Cores[core].AdmaInProgress=1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Cores[core].InputDataLeft=0;
|
||||
Cores[core].DMAICounter=1;
|
||||
}
|
||||
Cores[core].TADR=Cores[core].MADR+(size<<1);
|
||||
}
|
||||
|
||||
void DoDMAWrite(int core,u16 *pMem,u32 size)
|
||||
{
|
||||
// Perform an alignment check.
|
||||
// Not really important. Everything should work regardless,
|
||||
// but it could be indicative of an emulation foopah elsewhere.
|
||||
|
||||
#if 0
|
||||
uptr pa = ((uptr)pMem)&7;
|
||||
uptr pm = Cores[core].TSA&0x7;
|
||||
|
||||
if( pa )
|
||||
{
|
||||
fprintf(stderr, "* SPU2 DMA Write > Missaligned SOURCE! Core: %d TSA: 0x%x TDA: 0x%x Size: 0x%x\n", core, Cores[core].TSA, Cores[core].TDA, size);
|
||||
}
|
||||
|
||||
if( pm )
|
||||
{
|
||||
fprintf(stderr, "* SPU2 DMA Write > Missaligned TARGET! Core: %d TSA: 0x%x TDA: 0x%x Size: 0x%x\n", core, Cores[core].TSA, Cores[core].TDA, size );
|
||||
}
|
||||
#endif
|
||||
|
||||
if(core==0)
|
||||
DMA4LogWrite(pMem,size<<1);
|
||||
else
|
||||
DMA7LogWrite(pMem,size<<1);
|
||||
|
||||
if(MsgDMA()) ConLog(" * SPU2: DMA%c Transfer of %d bytes to %x (%02x %x %04x).\n",(core==0)?'4':'7',size<<1,Cores[core].TSA,Cores[core].DMABits,Cores[core].AutoDMACtrl,(~Cores[core].Regs.ATTR)&0x7fff);
|
||||
|
||||
Cores[core].TSA &= 0xfffff;
|
||||
|
||||
u32 buff1end = Cores[core].TSA + size;
|
||||
u32 buff2end=0;
|
||||
if( buff1end > 0x100000 )
|
||||
{
|
||||
buff2end = buff1end - 0x100000;
|
||||
buff1end = 0x100000;
|
||||
}
|
||||
|
||||
const int cacheIdxStart = Cores[core].TSA / pcm_WordsPerBlock;
|
||||
const int cacheIdxEnd = (buff1end+pcm_WordsPerBlock-1) / pcm_WordsPerBlock;
|
||||
PcmCacheEntry* cacheLine = &pcm_cache_data[cacheIdxStart];
|
||||
PcmCacheEntry& cacheEnd = pcm_cache_data[cacheIdxEnd];
|
||||
|
||||
do
|
||||
{
|
||||
cacheLine->Validated = false;
|
||||
cacheLine++;
|
||||
} while ( cacheLine != &cacheEnd );
|
||||
|
||||
#if 0
|
||||
// Pcm Cache Invalidation!
|
||||
// It's a requirement that we mask bits for the blocks that are written to *only*,
|
||||
// because doing anything else can cause the cache to fail, thanks to the progressive
|
||||
// nature of the SPU2's ADPCM encoding. (the same thing that makes it impossible
|
||||
// to use SSE optimizations on it).
|
||||
|
||||
u8* cache = (u8*)pcm_cache_flags;
|
||||
|
||||
// Step 1: Clear bits in the front remainder.
|
||||
|
||||
const int pcmTSA = Cores[core].TSA / pcm_WordsPerBlock;
|
||||
const int pcmTDA = buff1end / pcm_WordsPerBlock;
|
||||
const int remFront = pcmTSA & 31;
|
||||
const int remBack = ((buff1end+pcm_WordsPerBlock-1)/pcm_WordsPerBlock) & 31; // round up to get the end remainder
|
||||
|
||||
int flagTSA = pcmTSA / 32;
|
||||
|
||||
if( remFront )
|
||||
{
|
||||
// need to clear some upper bits of this u32
|
||||
uint mask = (1ul<<remFront)-1;
|
||||
cache[flagTSA++] &= mask;
|
||||
}
|
||||
|
||||
// Step 2: Clear the middle run
|
||||
const int flagClearLen = pcmTDA-pcmTSA;
|
||||
memset( &cache[flagTSA], 0, flagClearLen );
|
||||
|
||||
// Step 3: Clear bits in the end remainder.
|
||||
|
||||
if( remBack )
|
||||
{
|
||||
// need to clear some lower bits in this u32
|
||||
uint mask = ~(1ul<<remBack)-1;
|
||||
cache[flagTSA + flagClearLen] &= mask;
|
||||
}
|
||||
#endif
|
||||
|
||||
//ConLog( " * SPU2 : Cache Clear Range! TSA=0x%x, TDA=0x%x (low8=0x%x, high8=0x%x, len=0x%x)\n",
|
||||
// Cores[core].TSA, buff1end, flagTSA, flagTDA, clearLen );
|
||||
|
||||
|
||||
// First Branch needs cleared:
|
||||
// It starts at TSA and goes to buff1end.
|
||||
|
||||
const u32 buff1size = (buff1end-Cores[core].TSA);
|
||||
memcpy( GetMemPtr( Cores[core].TSA ), pMem, buff1size*2 );
|
||||
|
||||
if( buff2end > 0 )
|
||||
{
|
||||
// second branch needs copied:
|
||||
// It starts at the beginning of memory and moves forward to buff2end
|
||||
|
||||
// endpoint cache should be irrelevant, since it's almost certainly dynamic
|
||||
// memory below 0x2800 (registers and such)
|
||||
//const u32 endpt2 = (buff2end + roundUp) / indexer_scalar;
|
||||
//memset( pcm_cache_flags, 0, endpt2 );
|
||||
|
||||
memcpy( GetMemPtr( 0 ), &pMem[buff1size], buff2end*2 );
|
||||
|
||||
Cores[core].TDA = (buff2end+1) & 0xfffff;
|
||||
|
||||
if(Cores[core].IRQEnable)
|
||||
{
|
||||
// Flag interrupt?
|
||||
// If IRQA occurs between start and dest, flag it.
|
||||
// Since the buffer wraps, the conditional might seem odd, but it works.
|
||||
|
||||
if( ( Cores[core].IRQA >= Cores[core].TSA ) ||
|
||||
( Cores[core].IRQA <= Cores[core].TDA ) )
|
||||
{
|
||||
Spdif.Info=4<<core;
|
||||
SetIrqCall();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Buffer doesn't wrap/overflow!
|
||||
// Just set the TDA and check for an IRQ...
|
||||
|
||||
Cores[core].TDA = buff1end;
|
||||
|
||||
if(Cores[core].IRQEnable)
|
||||
{
|
||||
// Flag interrupt?
|
||||
// If IRQA occurs between start and dest, flag it:
|
||||
|
||||
if( ( Cores[core].IRQA >= Cores[core].TSA ) &&
|
||||
( Cores[core].IRQA <= Cores[core].TDA ) )
|
||||
{
|
||||
Spdif.Info=4<<core;
|
||||
SetIrqCall();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Cores[core].TSA=Cores[core].TDA&0xFFFF0;
|
||||
Cores[core].DMAICounter=size;
|
||||
Cores[core].TADR=Cores[core].MADR+(size<<1);
|
||||
}
|
||||
|
||||
void SPU2readDMA(int core, u16* pMem, u32 size)
|
||||
{
|
||||
if(hasPtr) TimeUpdate(*cPtr);
|
||||
|
||||
Cores[core].TSA &= 0xffff8;
|
||||
|
||||
u32 buff1end = Cores[core].TSA + size;
|
||||
u32 buff2end = 0;
|
||||
if( buff1end > 0x100000 )
|
||||
{
|
||||
buff2end = buff1end - 0x100000;
|
||||
buff1end = 0x100000;
|
||||
}
|
||||
|
||||
const u32 buff1size = (buff1end-Cores[core].TSA);
|
||||
memcpy( pMem, GetMemPtr( Cores[core].TSA ), buff1size*2 );
|
||||
|
||||
if( buff2end > 0 )
|
||||
{
|
||||
// second branch needs cleared:
|
||||
// It starts at the beginning of memory and moves forward to buff2end
|
||||
|
||||
memcpy( &pMem[buff1size], GetMemPtr( 0 ), buff2end*2 );
|
||||
|
||||
Cores[core].TDA = (buff2end+0x20) & 0xfffff;
|
||||
|
||||
for( int i=0; i<2; i++ )
|
||||
{
|
||||
if(Cores[i].IRQEnable)
|
||||
{
|
||||
// Flag interrupt?
|
||||
// If IRQA occurs between start and dest, flag it.
|
||||
// Since the buffer wraps, the conditional might seem odd, but it works.
|
||||
|
||||
if( ( Cores[i].IRQA >= Cores[core].TSA ) ||
|
||||
( Cores[i].IRQA <= Cores[core].TDA ) )
|
||||
{
|
||||
Spdif.Info=4<<i;
|
||||
SetIrqCall();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Buffer doesn't wrap/overflow!
|
||||
// Just set the TDA and check for an IRQ...
|
||||
|
||||
Cores[core].TDA = buff1end;
|
||||
|
||||
for( int i=0; i<2; i++ )
|
||||
{
|
||||
if(Cores[i].IRQEnable)
|
||||
{
|
||||
// Flag interrupt?
|
||||
// If IRQA occurs between start and dest, flag it:
|
||||
|
||||
if( ( Cores[i].IRQA >= Cores[i].TSA ) &&
|
||||
( Cores[i].IRQA <= Cores[i].TDA+0x1f ) )
|
||||
{
|
||||
Spdif.Info=4<<i;
|
||||
SetIrqCall();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Cores[core].TSA=Cores[core].TDA & 0xFFFFF;
|
||||
|
||||
Cores[core].DMAICounter=size;
|
||||
Cores[core].Regs.STATX &= ~0x80;
|
||||
//Cores[core].Regs.ATTR |= 0x30;
|
||||
Cores[core].TADR=Cores[core].MADR+(size<<1);
|
||||
|
||||
}
|
||||
|
||||
void SPU2writeDMA(int core, u16* pMem, u32 size)
|
||||
{
|
||||
if(hasPtr) TimeUpdate(*cPtr);
|
||||
|
||||
Cores[core].DMAPtr=pMem;
|
||||
|
||||
if(size<2) {
|
||||
//if(dma7callback) dma7callback();
|
||||
Cores[core].Regs.STATX &= ~0x80;
|
||||
//Cores[core].Regs.ATTR |= 0x30;
|
||||
Cores[core].DMAICounter=1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef PUBLIC
|
||||
DebugCores[core].lastsize=size;
|
||||
#endif
|
||||
Cores[core].TSA&=~7;
|
||||
|
||||
bool adma_enable = ((Cores[core].AutoDMACtrl&(core+1))==(core+1));
|
||||
|
||||
if(adma_enable)
|
||||
{
|
||||
Cores[core].TSA&=0x1fff;
|
||||
StartADMAWrite(core,pMem,size);
|
||||
}
|
||||
else
|
||||
{
|
||||
DoDMAWrite(core,pMem,size);
|
||||
}
|
||||
Cores[core].Regs.STATX &= ~0x80;
|
||||
//Cores[core].Regs.ATTR |= 0x30;
|
||||
}
|
||||
|
||||
u32 CALLBACK SPU2ReadMemAddr(int core)
|
||||
{
|
||||
return Cores[core].MADR;
|
||||
}
|
||||
void CALLBACK SPU2WriteMemAddr(int core,u32 value)
|
||||
{
|
||||
Cores[core].MADR=value;
|
||||
}
|
||||
|
||||
void CALLBACK SPU2setDMABaseAddr(uptr baseaddr)
|
||||
{
|
||||
DMABaseAddr = (u16*)baseaddr;
|
||||
}
|
||||
|
||||
void CALLBACK SPU2readDMA4Mem(u16 *pMem, u32 size) { //size now in 16bit units
|
||||
FileLog("[%10d] SPU2 readDMA4Mem size %x\n",Cycles, size<<1);
|
||||
SPU2readDMA(0,pMem,size);
|
||||
}
|
||||
|
||||
void CALLBACK SPU2writeDMA4Mem(u16* pMem, u32 size) { //size now in 16bit units
|
||||
FileLog("[%10d] SPU2 writeDMA4Mem size %x at address %x\n",Cycles, size<<1, Cores[0].TSA);
|
||||
#ifdef S2R_ENABLE
|
||||
if(!replay_mode)
|
||||
s2r_writedma4(Cycles,pMem,size);
|
||||
#endif
|
||||
SPU2writeDMA(0,pMem,size);
|
||||
}
|
||||
|
||||
void CALLBACK SPU2interruptDMA4() {
|
||||
FileLog("[%10d] SPU2 interruptDMA4\n",Cycles);
|
||||
Cores[0].Regs.STATX |= 0x80;
|
||||
//Cores[0].Regs.ATTR &= ~0x30;
|
||||
}
|
||||
|
||||
void CALLBACK SPU2readDMA7Mem(u16* pMem, u32 size) {
|
||||
FileLog("[%10d] SPU2 readDMA7Mem size %x\n",Cycles, size<<1);
|
||||
|
||||
SPU2readDMA(1,pMem,size);
|
||||
}
|
||||
|
||||
void CALLBACK SPU2writeDMA7Mem(u16* pMem, u32 size) {
|
||||
FileLog("[%10d] SPU2 writeDMA7Mem size %x at address %x\n",Cycles, size<<1, Cores[1].TSA);
|
||||
#ifdef S2R_ENABLE
|
||||
if(!replay_mode)
|
||||
s2r_writedma7(Cycles,pMem,size);
|
||||
#endif
|
||||
SPU2writeDMA(1,pMem,size);
|
||||
}
|
||||
|
||||
void CALLBACK SPU2interruptDMA7() {
|
||||
FileLog("[%10d] SPU2 interruptDMA7\n",Cycles);
|
||||
Cores[1].Regs.STATX |= 0x80;
|
||||
//Cores[1].Regs.ATTR &= ~0x30;
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
//GiGaHeRz's SPU2 Driver
|
||||
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
|
||||
//
|
||||
//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
|
||||
//
|
||||
#ifndef DMA_H_INCLUDED
|
||||
#define DMA_H_INCLUDED
|
||||
|
||||
void DMALogOpen();
|
||||
void DMA4LogWrite(void *lpData, u32 ulSize);
|
||||
void DMA7LogWrite(void *lpData, u32 ulSize);
|
||||
void DMALogClose();
|
||||
|
||||
extern void DmaWrite(u32 core, u16 data);
|
||||
extern u16 DmaRead(u32 core);
|
||||
|
||||
extern void AutoDMAReadBuffer(int core, int mode);
|
||||
|
||||
#endif // DMA_H_INCLUDED //
|
|
@ -0,0 +1,454 @@
|
|||
//GiGaHeRz's SPU2 Driver
|
||||
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
|
||||
//
|
||||
//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
|
||||
//
|
||||
|
||||
#include "spu2.h"
|
||||
#include "regtable.h"
|
||||
|
||||
#include "svnrev.h"
|
||||
|
||||
// [Air]: Adding the spu2init boolean wasn't necessary except to help me in
|
||||
// debugging the spu2 suspend/resume behavior (when user hits escape).
|
||||
static bool spu2open=false; // has spu2open plugin interface been called?
|
||||
static bool spu2init=false; // has spu2init plugin interface been called?
|
||||
|
||||
static s32 logvolume[16384];
|
||||
static u32 pClocks=0;
|
||||
|
||||
|
||||
static const u8 version = PS2E_SPU2_VERSION;
|
||||
static const u8 revision = 1;
|
||||
static const u8 build = 9; // increase that with each version
|
||||
|
||||
static char libraryName[256];
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD dwReason,LPVOID lpvReserved)
|
||||
{
|
||||
if(dwReason==DLL_PROCESS_ATTACH) hInstance=hinstDLL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void InitLibraryName()
|
||||
{
|
||||
#ifdef PUBLIC
|
||||
|
||||
// Public Release!
|
||||
// Output a simplified string that's just our name:
|
||||
|
||||
strcpy( libraryName, "SPU2ghz" );
|
||||
|
||||
#elif defined( SVN_REV_UNKNOWN )
|
||||
|
||||
// Unknown revision.
|
||||
// Output a name that includes devbuild status but not
|
||||
// subversion revision tags:
|
||||
|
||||
strcpy( libraryName, "SPU2ghz"
|
||||
# ifdef _DEBUG_FAST
|
||||
"-Debug"
|
||||
# elif defined( DEBUG )
|
||||
"-Debug/Strict" // strict debugging is slow!
|
||||
# else
|
||||
"-Dev"
|
||||
# endif
|
||||
);
|
||||
#else
|
||||
|
||||
// Use TortoiseSVN's SubWCRev utility's output
|
||||
// to label the specific revision:
|
||||
|
||||
sprintf_s( libraryName, "SPU2ghz r%d%s"
|
||||
# ifdef _DEBUG_FAST
|
||||
"-Debug"
|
||||
# elif defined( _DEBUG )
|
||||
"-Debug/Strict" // strict debugging is slow!
|
||||
# else
|
||||
"-Dev"
|
||||
# endif
|
||||
,SVN_REV,
|
||||
SVN_MODS ? "m" : ""
|
||||
);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
EXPORT_C_(u32) PS2EgetLibType()
|
||||
{
|
||||
return PS2E_LT_SPU2;
|
||||
}
|
||||
|
||||
EXPORT_C_(char*) PS2EgetLibName()
|
||||
{
|
||||
InitLibraryName();
|
||||
return libraryName;
|
||||
}
|
||||
|
||||
EXPORT_C_(u32) PS2EgetLibVersion2(u32 type)
|
||||
{
|
||||
return (version<<16)|(revision<<8)|build;
|
||||
}
|
||||
|
||||
EXPORT_C_(void) SPU2configure() {
|
||||
configure();
|
||||
}
|
||||
|
||||
EXPORT_C_(void) SPU2about() {
|
||||
InitLibraryName();
|
||||
SysMessage( libraryName );
|
||||
}
|
||||
|
||||
EXPORT_C_(s32) SPU2test() {
|
||||
return SndTest();
|
||||
}
|
||||
|
||||
EXPORT_C_(s32) SPU2init()
|
||||
{
|
||||
#define MAKESURE(a,b) \
|
||||
/*fprintf(stderr,"%08p: %08p == %08p\n",&(regtable[a>>1]),regtable[a>>1],U16P(b));*/ \
|
||||
assert(regtable[(a)>>1]==U16P(b))
|
||||
|
||||
MAKESURE(0x800,zero);
|
||||
|
||||
s32 c=0,v=0;
|
||||
ReadSettings();
|
||||
|
||||
#ifdef SPU2_LOG
|
||||
if(AccessLog())
|
||||
{
|
||||
spu2Log = fopen(AccessLogFileName, "w");
|
||||
setvbuf(spu2Log, NULL, _IONBF, 0);
|
||||
FileLog("SPU2init\n");
|
||||
}
|
||||
#endif
|
||||
srand((unsigned)time(NULL));
|
||||
|
||||
disableFreezes=false;
|
||||
|
||||
if (spu2init)
|
||||
{
|
||||
ConLog( " * SPU2: Already initialized - Ignoring SPU2init signal." );
|
||||
return 0;
|
||||
}
|
||||
|
||||
spu2init=true;
|
||||
|
||||
spu2regs = (short*)malloc(0x010000);
|
||||
_spu2mem = (short*)malloc(0x200000);
|
||||
|
||||
// adpcm decoder cache:
|
||||
// the cache data size is determined by taking the number of adpcm blocks
|
||||
// (2MB / 16) and multiplying it by the decoded block size (28 samples).
|
||||
// Thus: pcm_cache_data = 7,340,032 bytes (ouch!)
|
||||
// Expanded: 16 bytes expands to 56 bytes [3.5:1 ratio]
|
||||
// Resulting in 2MB * 3.5.
|
||||
|
||||
pcm_cache_data = (PcmCacheEntry*)calloc( pcm_BlockCount, sizeof(PcmCacheEntry) );
|
||||
|
||||
if( (spu2regs == NULL) || (_spu2mem == NULL) ||
|
||||
(pcm_cache_data == NULL) )
|
||||
{
|
||||
SysMessage("SPU2: Error allocating Memory\n"); return -1;
|
||||
}
|
||||
|
||||
for(int mem=0;mem<0x800;mem++)
|
||||
{
|
||||
u16 *ptr=regtable[mem>>1];
|
||||
if(!ptr) {
|
||||
regtable[mem>>1] = &(spu2Ru16(mem));
|
||||
}
|
||||
}
|
||||
|
||||
memset(spu2regs,0,0x010000);
|
||||
memset(_spu2mem,0,0x200000);
|
||||
memset(&Cores,0,(sizeof(V_Core) * 2));
|
||||
CoreReset(0);
|
||||
CoreReset(1);
|
||||
|
||||
DMALogOpen();
|
||||
|
||||
for(v=0;v<16384;v++)
|
||||
{
|
||||
logvolume[v]=(s32)(s32)floor(log((double)(v+1))*3376.7);
|
||||
}
|
||||
|
||||
LowPassFilterInit();
|
||||
InitADSR();
|
||||
|
||||
#ifdef STREAM_DUMP
|
||||
il0=fopen("logs/spu2input0.pcm","wb");
|
||||
il1=fopen("logs/spu2input1.pcm","wb");
|
||||
#endif
|
||||
|
||||
#ifdef EFFECTS_DUMP
|
||||
el0=fopen("logs/spu2fx0.pcm","wb");
|
||||
el1=fopen("logs/spu2fx1.pcm","wb");
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef S2R_ENABLE
|
||||
if(!replay_mode)
|
||||
s2r_open("replay_dump.s2r");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_C_(s32) SPU2open(void *pDsp)
|
||||
{
|
||||
if( spu2open ) return 0;
|
||||
|
||||
FileLog("[%10d] SPU2 Open\n",Cycles);
|
||||
|
||||
/*
|
||||
if(debugDialogOpen==0)
|
||||
{
|
||||
hDebugDialog = CreateDialogParam(hInstance,MAKEINTRESOURCE(IDD_DEBUG),0,DebugProc,0);
|
||||
ShowWindow(hDebugDialog,SW_SHOWNORMAL);
|
||||
debugDialogOpen=1;
|
||||
}*/
|
||||
|
||||
spu2open=true;
|
||||
if (!SndInit())
|
||||
{
|
||||
srate_pv=(double)SampleRate/48000.0;
|
||||
|
||||
spdif_init();
|
||||
|
||||
DspLoadLibrary(dspPlugin,dspPluginModule);
|
||||
|
||||
WaveDump::Open();
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
SPU2close();
|
||||
return -1;
|
||||
};
|
||||
}
|
||||
|
||||
EXPORT_C_(void) SPU2close()
|
||||
{
|
||||
if( !spu2open ) return;
|
||||
FileLog("[%10d] SPU2 Close\n",Cycles);
|
||||
|
||||
DspCloseLibrary();
|
||||
spdif_shutdown();
|
||||
SndClose();
|
||||
|
||||
spu2open = false;
|
||||
}
|
||||
|
||||
EXPORT_C_(void) SPU2shutdown()
|
||||
{
|
||||
if(!spu2init) return;
|
||||
|
||||
ConLog( " * SPU2: Shutting down.\n" );
|
||||
|
||||
SPU2close();
|
||||
|
||||
#ifdef S2R_ENABLE
|
||||
if(!replay_mode)
|
||||
s2r_close();
|
||||
#endif
|
||||
|
||||
DoFullDump();
|
||||
#ifdef STREAM_DUMP
|
||||
fclose(il0);
|
||||
fclose(il1);
|
||||
#endif
|
||||
#ifdef EFFECTS_DUMP
|
||||
fclose(el0);
|
||||
fclose(el1);
|
||||
#endif
|
||||
WaveDump::Close();
|
||||
|
||||
DMALogClose();
|
||||
|
||||
spu2init = false;
|
||||
|
||||
SAFE_FREE(spu2regs);
|
||||
SAFE_FREE(_spu2mem);
|
||||
|
||||
SAFE_FREE( pcm_cache_data );
|
||||
|
||||
spu2regs = NULL;
|
||||
_spu2mem = NULL;
|
||||
pcm_cache_data = NULL;
|
||||
|
||||
#ifdef SPU2_LOG
|
||||
if(!AccessLog()) return;
|
||||
FileLog("[%10d] SPU2shutdown\n",Cycles);
|
||||
if(spu2Log) fclose(spu2Log);
|
||||
#endif
|
||||
}
|
||||
|
||||
EXPORT_C_(void) SPU2setClockPtr(u32 *ptr)
|
||||
{
|
||||
cPtr=ptr;
|
||||
hasPtr=(cPtr!=NULL);
|
||||
}
|
||||
|
||||
bool numpad_minus_old=false;
|
||||
bool numpad_minus = false;
|
||||
bool numpad_plus = false, numpad_plus_old = false;
|
||||
|
||||
EXPORT_C_(void) SPU2async(u32 cycles)
|
||||
{
|
||||
#ifndef PUBLIC
|
||||
u32 oldClocks = lClocks;
|
||||
static u32 timer=0,time1=0,time2=0;
|
||||
timer++;
|
||||
if (timer == 1){
|
||||
time1=timeGetTime();
|
||||
}
|
||||
if (timer == 3000){
|
||||
time2 = timeGetTime()-time1 ;
|
||||
timer=0;
|
||||
}
|
||||
#endif
|
||||
|
||||
DspUpdate();
|
||||
|
||||
if(LimiterToggleEnabled)
|
||||
{
|
||||
numpad_minus = (GetAsyncKeyState(VK_SUBTRACT)&0x8000)!=0;
|
||||
|
||||
if(numpad_minus && !numpad_minus_old)
|
||||
{
|
||||
if(LimitMode) LimitMode=0;
|
||||
else LimitMode=1;
|
||||
SndUpdateLimitMode();
|
||||
}
|
||||
numpad_minus_old = numpad_minus;
|
||||
}
|
||||
|
||||
#ifndef PUBLIC
|
||||
/*numpad_plus = (GetAsyncKeyState(VK_ADD)&0x8000)!=0;
|
||||
if(numpad_plus && !numpad_plus_old)
|
||||
{
|
||||
DoFullDump();
|
||||
}
|
||||
numpad_plus_old = numpad_plus;*/
|
||||
#endif
|
||||
|
||||
if(hasPtr)
|
||||
{
|
||||
TimeUpdate(*cPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
pClocks+=cycles;
|
||||
TimeUpdate(pClocks);
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT_C_(void) SPU2irqCallback(void (*SPU2callback)(),void (*DMA4callback)(),void (*DMA7callback)())
|
||||
{
|
||||
_irqcallback=SPU2callback;
|
||||
dma4callback=DMA4callback;
|
||||
dma7callback=DMA7callback;
|
||||
}
|
||||
|
||||
EXPORT_C_(u16) SPU2read(u32 rmem)
|
||||
{
|
||||
// if(!replay_mode)
|
||||
// s2r_readreg(Cycles,rmem);
|
||||
|
||||
if(hasPtr) TimeUpdate(*cPtr);
|
||||
|
||||
u16 ret=0xDEAD; u32 core=0, mem=rmem&0xFFFF, omem=mem;
|
||||
if (mem & 0x400) { omem^=0x400; core=1; }
|
||||
|
||||
if(rmem==0x1f9001AC)
|
||||
{
|
||||
ret = DmaRead(core);
|
||||
}
|
||||
else if (rmem>>16 == 0x1f80)
|
||||
{
|
||||
ret = SPU_ps1_read(rmem);
|
||||
}
|
||||
else if ((mem&0xFFFF)>=0x800)
|
||||
{
|
||||
ret=spu2Ru16(mem);
|
||||
ConLog(" * SPU2: Read from reg>=0x800: %x value %x\n",mem,ret);
|
||||
FileLog(" * SPU2: Read from reg>=0x800: %x value %x\n",mem,ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = *(regtable[(mem>>1)]);
|
||||
|
||||
FileLog("[%10d] SPU2 read mem %x (core %d, register %x): %x\n",Cycles, mem, core, (omem & 0x7ff), ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_C_(void) SPU2write(u32 rmem, u16 value)
|
||||
{
|
||||
#ifdef S2R_ENABLE
|
||||
if(!replay_mode)
|
||||
s2r_writereg(Cycles,rmem,value);
|
||||
#endif
|
||||
|
||||
if(rmem==0x1f9001ac)
|
||||
{
|
||||
//RegWriteLog(0,value);
|
||||
if((Cores[0].IRQEnable)&&(Cores[0].TSA==Cores[0].IRQA))
|
||||
{
|
||||
Spdif.Info=4;
|
||||
SetIrqCall();
|
||||
}
|
||||
spu2M_Write( Cores[0].TSA++, value );
|
||||
Cores[0].TSA&=0xfffff;
|
||||
}
|
||||
else if(rmem==0x1f9005ac)
|
||||
{
|
||||
//RegWriteLog(1,value);
|
||||
if((Cores[0].IRQEnable)&&(Cores[0].TSA==Cores[0].IRQA))
|
||||
{
|
||||
Spdif.Info=4;
|
||||
SetIrqCall();
|
||||
}
|
||||
spu2M_Write( Cores[1].TSA++, value );
|
||||
Cores[1].TSA&=0xfffff;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(hasPtr) TimeUpdate(*cPtr);
|
||||
|
||||
if (rmem>>16 == 0x1f80)
|
||||
SPU_ps1_write(rmem,value);
|
||||
else
|
||||
SPU2_FastWrite( rmem, value );
|
||||
}
|
||||
}
|
||||
|
||||
// if start is 1, starts recording spu2 data, else stops
|
||||
// returns a non zero value if successful
|
||||
// for now, pData is not used
|
||||
EXPORT_C_(int) SPU2setupRecording(int start, void* pData)
|
||||
{
|
||||
// Don't record if we have a bogus state.
|
||||
if( disableFreezes ) return 0;
|
||||
|
||||
if(start==0)
|
||||
RecordStop();
|
||||
else if(start==1)
|
||||
RecordStart();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
* Developed and maintained by the Pcsx2 Development Team.
|
||||
*
|
||||
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
||||
*
|
||||
* 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 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
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lowpass.h"
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
LPF_data::LPF_data( double freq, double srate )
|
||||
{
|
||||
double omega = 2.0 * freq / srate;
|
||||
static const double g = 1.0;
|
||||
|
||||
// calculating coefficients:
|
||||
|
||||
double k,p,q,a;
|
||||
double a0,a1,a2,a3,a4;
|
||||
|
||||
k=(4.0*g-3.0)/(g+1.0);
|
||||
p=1.0-0.25*k;p*=p;
|
||||
|
||||
// LP:
|
||||
a=1.0/(tan(0.5*omega)*(1.0+p));
|
||||
p=1.0+a;
|
||||
q=1.0-a;
|
||||
|
||||
a0=1.0/(k+p*p*p*p);
|
||||
a1=4.0*(k+p*p*p*q);
|
||||
a2=6.0*(k+p*p*q*q);
|
||||
a3=4.0*(k+p*q*q*q);
|
||||
a4= (k+q*q*q*q);
|
||||
p=a0*(k+1.0);
|
||||
|
||||
coef[0] = p;
|
||||
coef[1] = 4.0*p;
|
||||
coef[2] = 6.0*p;
|
||||
coef[3] = 4.0*p;
|
||||
coef[4] = p;
|
||||
coef[5] = -a1*a0;
|
||||
coef[6] = -a2*a0;
|
||||
coef[7] = -a3*a0;
|
||||
coef[8] = -a4*a0;
|
||||
}
|
||||
|
||||
// Processes a single sample into the LPF.
|
||||
double LPF_data::sample( double inval )
|
||||
{
|
||||
const double out = (coef[0]*inval) + d[0];
|
||||
d[0] = (coef[1]*inval) + (coef[5]*out) + d[1];
|
||||
d[1] = (coef[2]*inval) + (coef[6]*out) + d[2];
|
||||
d[2] = (coef[3]*inval) + (coef[7]*out) + d[3];
|
||||
d[3] = (coef[4]*inval) + (coef[8]*out);
|
||||
|
||||
return out;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
//GiGaHeRz's SPU2 Driver
|
||||
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
|
||||
//
|
||||
//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
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#ifndef _LOWPASS_H_
|
||||
#define _LOWPASS_H_
|
||||
|
||||
#include "BaseTypes.h"
|
||||
|
||||
struct LPF_data
|
||||
{
|
||||
double coef[9];
|
||||
double d[4];
|
||||
|
||||
LPF_data( double freq, double srate );
|
||||
double sample( double inval );
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,252 @@
|
|||
//GiGaHeRz's SPU2 Driver
|
||||
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
|
||||
//
|
||||
//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
|
||||
//
|
||||
|
||||
#include "SPU2.h"
|
||||
#include "regtable.h"
|
||||
|
||||
const char *ParamNames[8]={"VOLL","VOLR","PITCH","ADSR1","ADSR2","ENVX","VOLXL","VOLXR"};
|
||||
const char *AddressNames[6]={"SSAH","SSAL","LSAH","LSAL","NAXH","NAXL"};
|
||||
|
||||
__forceinline void RegLog(int level, char *RName,u32 mem,u32 core,u16 value)
|
||||
{
|
||||
if( level > 1 )
|
||||
FileLog("[%10d] SPU2 write mem %08x (core %d, register %s) value %04x\n",Cycles,mem,core,RName,value);
|
||||
}
|
||||
|
||||
void SPU2writeLog(u32 rmem, u16 value)
|
||||
{
|
||||
#ifndef PUBLIC
|
||||
u32 vx=0, vc=0, core=0, omem, mem;
|
||||
omem=mem=rmem & 0x7FF; //FFFF;
|
||||
if (mem & 0x400) { omem^=0x400; core=1; }
|
||||
|
||||
if( omem < 0x0180 ) // Voice Params (VP)
|
||||
{
|
||||
const u32 voice = (omem & 0x1F0) >> 4;
|
||||
const u32 param = (omem & 0xF) >> 1;
|
||||
char dest[192];
|
||||
sprintf( dest, "Voice %d %s", voice,ParamNames[param] );
|
||||
RegLog( 2, dest, rmem, core, value );
|
||||
}
|
||||
else if ((omem >= 0x01C0) && (omem < 0x02DE)) // Voice Addressing Params (VA)
|
||||
{
|
||||
const u32 voice = ((omem-0x01C0) / 12);
|
||||
const u32 address = ((omem-0x01C0) % 12)>>1;
|
||||
|
||||
char dest[192];
|
||||
sprintf( dest, "Voice %d %s", voice, AddressNames[address] );
|
||||
RegLog( 2, dest, rmem, core, value );
|
||||
}
|
||||
else if ((mem >= 0x0760) && (mem < 0x07b0))
|
||||
{
|
||||
omem=mem; core=0;
|
||||
if (mem >= 0x0788) {omem-=0x28; core=1;}
|
||||
switch(omem)
|
||||
{
|
||||
case REG_P_EVOLL: RegLog(2,"EVOLL",rmem,core,value); break;
|
||||
case REG_P_EVOLR: RegLog(2,"EVOLR",rmem,core,value); break;
|
||||
case REG_P_AVOLL: if (core) { RegLog(2,"AVOLL",rmem,core,value); } break;
|
||||
case REG_P_AVOLR: if (core) { RegLog(2,"AVOLR",rmem,core,value); } break;
|
||||
case REG_P_BVOLL: RegLog(2,"BVOLL",rmem,core,value); break;
|
||||
case REG_P_BVOLR: RegLog(2,"BVOLR",rmem,core,value); break;
|
||||
case REG_P_MVOLXL: RegLog(2,"MVOLXL",rmem,core,value); break;
|
||||
case REG_P_MVOLXR: RegLog(2,"MVOLXR",rmem,core,value); break;
|
||||
case R_IIR_ALPHA: RegLog(2,"IIR_ALPHA",rmem,core,value); break;
|
||||
case R_ACC_COEF_A: RegLog(2,"ACC_COEF_A",rmem,core,value); break;
|
||||
case R_ACC_COEF_B: RegLog(2,"ACC_COEF_B",rmem,core,value); break;
|
||||
case R_ACC_COEF_C: RegLog(2,"ACC_COEF_C",rmem,core,value); break;
|
||||
case R_ACC_COEF_D: RegLog(2,"ACC_COEF_D",rmem,core,value); break;
|
||||
case R_IIR_COEF: RegLog(2,"IIR_COEF",rmem,core,value); break;
|
||||
case R_FB_ALPHA: RegLog(2,"FB_ALPHA",rmem,core,value); break;
|
||||
case R_FB_X: RegLog(2,"FB_X",rmem,core,value); break;
|
||||
case R_IN_COEF_L: RegLog(2,"IN_COEF_L",rmem,core,value); break;
|
||||
case R_IN_COEF_R: RegLog(2,"IN_COEF_R",rmem,core,value); break;
|
||||
|
||||
}
|
||||
}
|
||||
else if ((mem>=0x07C0) && (mem<0x07CE))
|
||||
{
|
||||
switch(mem)
|
||||
{
|
||||
case SPDIF_OUT:
|
||||
RegLog(2,"SPDIF_OUT",rmem,-1,value);
|
||||
break;
|
||||
case IRQINFO:
|
||||
RegLog(2,"IRQINFO",rmem,-1,value);
|
||||
break;
|
||||
case 0x7c4:
|
||||
if(Spdif.Unknown1 != value) ConLog(" * SPU2: SPDIF Unknown Register 1 set to %04x\n",value);
|
||||
RegLog(2,"SPDIF_UNKNOWN1",rmem,-1,value);
|
||||
break;
|
||||
case SPDIF_MODE:
|
||||
if(Spdif.Mode != value) ConLog(" * SPU2: SPDIF Mode set to %04x\n",value);
|
||||
RegLog(2,"SPDIF_MODE",rmem,-1,value);
|
||||
break;
|
||||
case SPDIF_MEDIA:
|
||||
if(Spdif.Media != value) ConLog(" * SPU2: SPDIF Media set to %04x\n",value);
|
||||
RegLog(2,"SPDIF_MEDIA",rmem,-1,value);
|
||||
break;
|
||||
case 0x7ca:
|
||||
if(Spdif.Unknown2 != value) ConLog(" * SPU2: SPDIF Unknown Register 2 set to %04x\n",value);
|
||||
RegLog(2,"SPDIF_UNKNOWN2",rmem,-1,value);
|
||||
break;
|
||||
case SPDIF_COPY:
|
||||
if(Spdif.Protection != value) ConLog(" * SPU2: SPDIF Copy set to %04x\n",value);
|
||||
RegLog(2,"SPDIF_COPY",rmem,-1,value);
|
||||
break;
|
||||
}
|
||||
UpdateSpdifMode();
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(omem)
|
||||
{
|
||||
case REG_C_ATTR:
|
||||
RegLog(4,"ATTR",rmem,core,value);
|
||||
break;
|
||||
case REG_S_PMON:
|
||||
RegLog(1,"PMON0",rmem,core,value);
|
||||
break;
|
||||
case (REG_S_PMON + 2):
|
||||
RegLog(1,"PMON1",rmem,core,value);
|
||||
break;
|
||||
case REG_S_NON:
|
||||
RegLog(1,"NON0",rmem,core,value);
|
||||
break;
|
||||
case (REG_S_NON + 2):
|
||||
RegLog(1,"NON1",rmem,core,value);
|
||||
break;
|
||||
case REG_S_VMIXL:
|
||||
RegLog(1,"VMIXL0",rmem,core,value);
|
||||
case (REG_S_VMIXL + 2):
|
||||
RegLog(1,"VMIXL1",rmem,core,value);
|
||||
break;
|
||||
case REG_S_VMIXEL:
|
||||
RegLog(1,"VMIXEL0",rmem,core,value);
|
||||
break;
|
||||
case (REG_S_VMIXEL + 2):
|
||||
RegLog(1,"VMIXEL1",rmem,core,value);
|
||||
break;
|
||||
case REG_S_VMIXR:
|
||||
RegLog(1,"VMIXR0",rmem,core,value);
|
||||
break;
|
||||
case (REG_S_VMIXR + 2):
|
||||
RegLog(1,"VMIXR1",rmem,core,value);
|
||||
break;
|
||||
case REG_S_VMIXER:
|
||||
RegLog(1,"VMIXER0",rmem,core,value);
|
||||
break;
|
||||
case (REG_S_VMIXER + 2):
|
||||
RegLog(1,"VMIXER1",rmem,core,value);
|
||||
break;
|
||||
case REG_P_MMIX:
|
||||
RegLog(1,"MMIX",rmem,core,value);
|
||||
break;
|
||||
case REG_A_IRQA:
|
||||
RegLog(2,"IRQAH",rmem,core,value);
|
||||
break;
|
||||
case (REG_A_IRQA + 2):
|
||||
RegLog(2,"IRQAL",rmem,core,value);
|
||||
break;
|
||||
case (REG_S_KON + 2):
|
||||
RegLog(1,"KON1",rmem,core,value);
|
||||
break;
|
||||
case REG_S_KON:
|
||||
RegLog(1,"KON0",rmem,core,value);
|
||||
break;
|
||||
case (REG_S_KOFF + 2):
|
||||
RegLog(1,"KOFF1",rmem,core,value);
|
||||
break;
|
||||
case REG_S_KOFF:
|
||||
RegLog(1,"KOFF0",rmem,core,value);
|
||||
break;
|
||||
case REG_A_TSA:
|
||||
RegLog(2,"TSAH",rmem,core,value);
|
||||
break;
|
||||
case (REG_A_TSA + 2):
|
||||
RegLog(2,"TSAL",rmem,core,value);
|
||||
break;
|
||||
case REG_S_ENDX:
|
||||
//ConLog(" * SPU2: Core %d ENDX cleared!\n",core);
|
||||
RegLog(2,"ENDX0",rmem,core,value);
|
||||
break;
|
||||
case (REG_S_ENDX + 2):
|
||||
//ConLog(" * SPU2: Core %d ENDX cleared!\n",core);
|
||||
RegLog(2,"ENDX1",rmem,core,value);
|
||||
break;
|
||||
case REG_P_MVOLL:
|
||||
RegLog(1,"MVOLL",rmem,core,value);
|
||||
break;
|
||||
case REG_P_MVOLR:
|
||||
RegLog(1,"MVOLR",rmem,core,value);
|
||||
break;
|
||||
case REG_S_ADMAS:
|
||||
RegLog(3,"ADMAS",rmem,core,value);
|
||||
ConLog(" * SPU2: Core %d AutoDMAControl set to %d\n",core,value);
|
||||
break;
|
||||
case REG_P_STATX:
|
||||
RegLog(3,"STATX",rmem,core,value);
|
||||
break;
|
||||
case REG_A_ESA:
|
||||
RegLog(1,"ESAH",rmem,core,value);
|
||||
break;
|
||||
case (REG_A_ESA + 2):
|
||||
RegLog(1,"ESAL",rmem,core,value);
|
||||
break;
|
||||
case REG_A_EEA:
|
||||
RegLog(1,"EEAH",rmem,core,value);
|
||||
break;
|
||||
|
||||
#define LOG_REVB_REG(n,t) \
|
||||
case R_##n: \
|
||||
RegLog(2,t "H",mem,core,value); \
|
||||
break; \
|
||||
case (R_##n + 2): \
|
||||
RegLog(2,t "L",mem,core,value); \
|
||||
break;
|
||||
|
||||
LOG_REVB_REG(FB_SRC_A,"FB_SRC_A")
|
||||
LOG_REVB_REG(FB_SRC_B,"FB_SRC_B")
|
||||
LOG_REVB_REG(IIR_SRC_A0,"IIR_SRC_A0")
|
||||
LOG_REVB_REG(IIR_SRC_A1,"IIR_SRC_A1")
|
||||
LOG_REVB_REG(IIR_SRC_B1,"IIR_SRC_B1")
|
||||
LOG_REVB_REG(IIR_SRC_B0,"IIR_SRC_B0")
|
||||
LOG_REVB_REG(IIR_DEST_A0,"IIR_DEST_A0")
|
||||
LOG_REVB_REG(IIR_DEST_A1,"IIR_DEST_A1")
|
||||
LOG_REVB_REG(IIR_DEST_B0,"IIR_DEST_B0")
|
||||
LOG_REVB_REG(IIR_DEST_B1,"IIR_DEST_B1")
|
||||
LOG_REVB_REG(ACC_SRC_A0,"ACC_SRC_A0")
|
||||
LOG_REVB_REG(ACC_SRC_A1,"ACC_SRC_A1")
|
||||
LOG_REVB_REG(ACC_SRC_B0,"ACC_SRC_B0")
|
||||
LOG_REVB_REG(ACC_SRC_B1,"ACC_SRC_B1")
|
||||
LOG_REVB_REG(ACC_SRC_C0,"ACC_SRC_C0")
|
||||
LOG_REVB_REG(ACC_SRC_C1,"ACC_SRC_C1")
|
||||
LOG_REVB_REG(ACC_SRC_D0,"ACC_SRC_D0")
|
||||
LOG_REVB_REG(ACC_SRC_D1,"ACC_SRC_D1")
|
||||
LOG_REVB_REG(MIX_DEST_A0,"MIX_DEST_A0")
|
||||
LOG_REVB_REG(MIX_DEST_A1,"MIX_DEST_A1")
|
||||
LOG_REVB_REG(MIX_DEST_B0,"MIX_DEST_B0")
|
||||
LOG_REVB_REG(MIX_DEST_B1,"MIX_DEST_B1")
|
||||
|
||||
default:
|
||||
RegLog(2,"UNKNOWN",rmem,core,value); spu2Ru16(mem) = value;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,314 @@
|
|||
//GiGaHeRz's SPU2 Driver
|
||||
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
|
||||
//
|
||||
//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
|
||||
//
|
||||
|
||||
#include "spu2.h"
|
||||
#include "regtable.h"
|
||||
|
||||
// This var is used to confirm that our lookup table is "correct"
|
||||
// If the assertion in DllMain fails, it means the table has too too few entries.
|
||||
// (it can't have too many because that would generate a compiler error).
|
||||
const u16 zero=0;
|
||||
|
||||
#define PCORE(c,p) \
|
||||
U16P(Cores[c].##p)
|
||||
|
||||
#define PVCP(c,v,p) \
|
||||
PCORE(c,Voices[v].##p)
|
||||
|
||||
#define PVC(c,v) \
|
||||
PVCP(c,v,VolumeL.Reg_VOL), \
|
||||
PVCP(c,v,VolumeR.Reg_VOL), \
|
||||
PVCP(c,v,Pitch), \
|
||||
PVCP(c,v,ADSR.Reg_ADSR1), \
|
||||
PVCP(c,v,ADSR.Reg_ADSR2), \
|
||||
PVCP(c,v,ADSR.Value)+1, \
|
||||
PVCP(c,v,VolumeL.Value)+1, \
|
||||
PVCP(c,v,VolumeR.Value)+1
|
||||
|
||||
#define PVCA(c,v) \
|
||||
PVCP(c,v,StartA)+1, \
|
||||
PVCP(c,v,StartA), \
|
||||
PVCP(c,v,LoopStartA)+1, \
|
||||
PVCP(c,v,LoopStartA), \
|
||||
PVCP(c,v,NextA)+1, \
|
||||
PVCP(c,v,NextA)
|
||||
|
||||
#define PRAW(a) \
|
||||
((u16*)NULL)
|
||||
|
||||
#define PREVB_REG(c,n) \
|
||||
PCORE(c,Revb.##n)+1, \
|
||||
PCORE(c,Revb.##n)
|
||||
|
||||
#pragma pack(1)
|
||||
u16* regtable[0x800] =
|
||||
{
|
||||
// Voice Params: 8 params, 24 voices = 0x180 bytes
|
||||
PVC(0, 0),PVC(0, 1),PVC(0, 2),PVC(0, 3),PVC(0, 4),PVC(0, 5),
|
||||
PVC(0, 6),PVC(0, 7),PVC(0, 8),PVC(0, 9),PVC(0,10),PVC(0,11),
|
||||
PVC(0,12),PVC(0,13),PVC(0,14),PVC(0,15),PVC(0,16),PVC(0,17),
|
||||
PVC(0,18),PVC(0,19),PVC(0,20),PVC(0,21),PVC(0,22),PVC(0,23),
|
||||
|
||||
PCORE(0,Regs.PMON),
|
||||
PCORE(0,Regs.PMON)+1,
|
||||
PCORE(0,Regs.NON),
|
||||
PCORE(0,Regs.NON)+1,
|
||||
PCORE(0,Regs.VMIXL),
|
||||
PCORE(0,Regs.VMIXL)+1,
|
||||
PCORE(0,Regs.VMIXEL),
|
||||
PCORE(0,Regs.VMIXEL)+1,
|
||||
PCORE(0,Regs.VMIXR),
|
||||
PCORE(0,Regs.VMIXR)+1,
|
||||
PCORE(0,Regs.VMIXER),
|
||||
PCORE(0,Regs.VMIXER)+1,
|
||||
PCORE(0,Regs.MMIX),
|
||||
|
||||
PCORE(0,Regs.ATTR),
|
||||
|
||||
PCORE(0,IRQA)+1,
|
||||
PCORE(0,IRQA),
|
||||
|
||||
U16P(zero),
|
||||
U16P(zero),
|
||||
U16P(zero),
|
||||
U16P(zero),
|
||||
|
||||
PCORE(0,TSA)+1,
|
||||
PCORE(0,TSA),
|
||||
|
||||
PRAW(0x1ac), PRAW(0x1ae),
|
||||
|
||||
PCORE(0,AutoDMACtrl),
|
||||
|
||||
PRAW(0x1b2), PRAW(0x1b4), PRAW(0x1b6), PRAW(0x1b8), PRAW(0x1ba), PRAW(0x1bc), PRAW(0x1be), // unknown
|
||||
|
||||
// Voice Addresses
|
||||
PVCA(0, 0),PVCA(0, 1),PVCA(0, 2),PVCA(0, 3),PVCA(0, 4),PVCA(0, 5),
|
||||
PVCA(0, 6),PVCA(0, 7),PVCA(0, 8),PVCA(0, 9),PVCA(0,10),PVCA(0,11),
|
||||
PVCA(0,12),PVCA(0,13),PVCA(0,14),PVCA(0,15),PVCA(0,16),PVCA(0,17),
|
||||
PVCA(0,18),PVCA(0,19),PVCA(0,20),PVCA(0,21),PVCA(0,22),PVCA(0,23),
|
||||
|
||||
PCORE(0,EffectsStartA)+1,
|
||||
PCORE(0,EffectsStartA),
|
||||
|
||||
PREVB_REG(0,FB_SRC_A),
|
||||
PREVB_REG(0,FB_SRC_B),
|
||||
PREVB_REG(0,IIR_SRC_A0),
|
||||
PREVB_REG(0,IIR_SRC_A1),
|
||||
PREVB_REG(0,IIR_SRC_B1),
|
||||
PREVB_REG(0,IIR_SRC_B0),
|
||||
PREVB_REG(0,IIR_DEST_A0),
|
||||
PREVB_REG(0,IIR_DEST_A1),
|
||||
PREVB_REG(0,IIR_DEST_B0),
|
||||
PREVB_REG(0,IIR_DEST_B1),
|
||||
PREVB_REG(0,ACC_SRC_A0),
|
||||
PREVB_REG(0,ACC_SRC_A1),
|
||||
PREVB_REG(0,ACC_SRC_B0),
|
||||
PREVB_REG(0,ACC_SRC_B1),
|
||||
PREVB_REG(0,ACC_SRC_C0),
|
||||
PREVB_REG(0,ACC_SRC_C1),
|
||||
PREVB_REG(0,ACC_SRC_D0),
|
||||
PREVB_REG(0,ACC_SRC_D1),
|
||||
PREVB_REG(0,MIX_DEST_A0),
|
||||
PREVB_REG(0,MIX_DEST_A1),
|
||||
PREVB_REG(0,MIX_DEST_B0),
|
||||
PREVB_REG(0,MIX_DEST_B1),
|
||||
|
||||
PCORE(0,EffectsEndA)+1,
|
||||
U16P(zero),
|
||||
|
||||
PCORE(0,Regs.ENDX),
|
||||
PCORE(0,Regs.ENDX)+1,
|
||||
PCORE(0,Regs.STATX),
|
||||
|
||||
//0x346 here
|
||||
PRAW(0x346),
|
||||
PRAW(0x348),PRAW(0x34A),PRAW(0x34C),PRAW(0x34E),
|
||||
PRAW(0x350),PRAW(0x352),PRAW(0x354),PRAW(0x356),
|
||||
PRAW(0x358),PRAW(0x35A),PRAW(0x35C),PRAW(0x35E),
|
||||
PRAW(0x360),PRAW(0x362),PRAW(0x364),PRAW(0x366),
|
||||
PRAW(0x368),PRAW(0x36A),PRAW(0x36C),PRAW(0x36E),
|
||||
PRAW(0x370),PRAW(0x372),PRAW(0x374),PRAW(0x376),
|
||||
PRAW(0x378),PRAW(0x37A),PRAW(0x37C),PRAW(0x37E),
|
||||
PRAW(0x380),PRAW(0x382),PRAW(0x384),PRAW(0x386),
|
||||
PRAW(0x388),PRAW(0x38A),PRAW(0x38C),PRAW(0x38E),
|
||||
PRAW(0x390),PRAW(0x392),PRAW(0x394),PRAW(0x396),
|
||||
PRAW(0x398),PRAW(0x39A),PRAW(0x39C),PRAW(0x39E),
|
||||
PRAW(0x3A0),PRAW(0x3A2),PRAW(0x3A4),PRAW(0x3A6),
|
||||
PRAW(0x3A8),PRAW(0x3AA),PRAW(0x3AC),PRAW(0x3AE),
|
||||
PRAW(0x3B0),PRAW(0x3B2),PRAW(0x3B4),PRAW(0x3B6),
|
||||
PRAW(0x3B8),PRAW(0x3BA),PRAW(0x3BC),PRAW(0x3BE),
|
||||
PRAW(0x3C0),PRAW(0x3C2),PRAW(0x3C4),PRAW(0x3C6),
|
||||
PRAW(0x3C8),PRAW(0x3CA),PRAW(0x3CC),PRAW(0x3CE),
|
||||
PRAW(0x3D0),PRAW(0x3D2),PRAW(0x3D4),PRAW(0x3D6),
|
||||
PRAW(0x3D8),PRAW(0x3DA),PRAW(0x3DC),PRAW(0x3DE),
|
||||
PRAW(0x3E0),PRAW(0x3E2),PRAW(0x3E4),PRAW(0x3E6),
|
||||
PRAW(0x3E8),PRAW(0x3EA),PRAW(0x3EC),PRAW(0x3EE),
|
||||
PRAW(0x3F0),PRAW(0x3F2),PRAW(0x3F4),PRAW(0x3F6),
|
||||
PRAW(0x3F8),PRAW(0x3FA),PRAW(0x3FC),PRAW(0x3FE),
|
||||
|
||||
//AND... we reached 0x400!
|
||||
// Voice Params: 8 params, 24 voices = 0x180 bytes
|
||||
PVC(1, 0),PVC(1, 1),PVC(1, 2),PVC(1, 3),PVC(1, 4),PVC(1, 5),
|
||||
PVC(1, 6),PVC(1, 7),PVC(1, 8),PVC(1, 9),PVC(1,10),PVC(1,11),
|
||||
PVC(1,12),PVC(1,13),PVC(1,14),PVC(1,15),PVC(1,16),PVC(1,17),
|
||||
PVC(1,18),PVC(1,19),PVC(1,20),PVC(1,21),PVC(1,22),PVC(1,23),
|
||||
|
||||
PCORE(1,Regs.PMON),
|
||||
PCORE(1,Regs.PMON)+1,
|
||||
PCORE(1,Regs.NON),
|
||||
PCORE(1,Regs.NON)+1,
|
||||
PCORE(1,Regs.VMIXL),
|
||||
PCORE(1,Regs.VMIXL)+1,
|
||||
PCORE(1,Regs.VMIXEL),
|
||||
PCORE(1,Regs.VMIXEL)+1,
|
||||
PCORE(1,Regs.VMIXR),
|
||||
PCORE(1,Regs.VMIXR)+1,
|
||||
PCORE(1,Regs.VMIXER),
|
||||
PCORE(1,Regs.VMIXER)+1,
|
||||
PCORE(1,Regs.MMIX),
|
||||
|
||||
PCORE(1,Regs.ATTR),
|
||||
|
||||
PCORE(1,IRQA)+1,
|
||||
PCORE(1,IRQA),
|
||||
|
||||
U16P(zero),
|
||||
U16P(zero),
|
||||
U16P(zero),
|
||||
U16P(zero),
|
||||
|
||||
PCORE(1,TSA)+1,
|
||||
PCORE(1,TSA),
|
||||
|
||||
PRAW(0x5ac), PRAW(0x5ae),
|
||||
|
||||
PCORE(1,AutoDMACtrl),
|
||||
|
||||
PRAW(0x5b2), PRAW(0x5b4), PRAW(0x5b6), PRAW(0x5b8), PRAW(0x5ba), PRAW(0x5bc), PRAW(0x5be), // unknown
|
||||
|
||||
// Voice Addresses
|
||||
PVCA(1, 0),PVCA(1, 1),PVCA(1, 2),PVCA(1, 3),PVCA(1, 4),PVCA(1, 5),
|
||||
PVCA(1, 6),PVCA(1, 7),PVCA(1, 8),PVCA(1, 9),PVCA(1,10),PVCA(1,11),
|
||||
PVCA(1,12),PVCA(1,13),PVCA(1,14),PVCA(1,15),PVCA(1,16),PVCA(1,17),
|
||||
PVCA(1,18),PVCA(1,19),PVCA(1,20),PVCA(1,21),PVCA(1,22),PVCA(1,23),
|
||||
|
||||
PCORE(1,EffectsStartA)+1,
|
||||
PCORE(1,EffectsStartA),
|
||||
|
||||
PREVB_REG(1,FB_SRC_A),
|
||||
PREVB_REG(1,FB_SRC_B),
|
||||
PREVB_REG(1,IIR_SRC_A0),
|
||||
PREVB_REG(1,IIR_SRC_A1),
|
||||
PREVB_REG(1,IIR_SRC_B1),
|
||||
PREVB_REG(1,IIR_SRC_B0),
|
||||
PREVB_REG(1,IIR_DEST_A0),
|
||||
PREVB_REG(1,IIR_DEST_A1),
|
||||
PREVB_REG(1,IIR_DEST_B0),
|
||||
PREVB_REG(1,IIR_DEST_B1),
|
||||
PREVB_REG(1,ACC_SRC_A0),
|
||||
PREVB_REG(1,ACC_SRC_A1),
|
||||
PREVB_REG(1,ACC_SRC_B0),
|
||||
PREVB_REG(1,ACC_SRC_B1),
|
||||
PREVB_REG(1,ACC_SRC_C0),
|
||||
PREVB_REG(1,ACC_SRC_C1),
|
||||
PREVB_REG(1,ACC_SRC_D0),
|
||||
PREVB_REG(1,ACC_SRC_D1),
|
||||
PREVB_REG(1,MIX_DEST_A0),
|
||||
PREVB_REG(1,MIX_DEST_A1),
|
||||
PREVB_REG(1,MIX_DEST_B0),
|
||||
PREVB_REG(1,MIX_DEST_B1),
|
||||
|
||||
PCORE(1,EffectsEndA)+1,
|
||||
U16P(zero),
|
||||
|
||||
PCORE(1,Regs.ENDX),
|
||||
PCORE(1,Regs.ENDX)+1,
|
||||
PCORE(1,Regs.STATX),
|
||||
|
||||
PRAW(0x746),
|
||||
PRAW(0x748),PRAW(0x74A),PRAW(0x74C),PRAW(0x74E),
|
||||
PRAW(0x750),PRAW(0x752),PRAW(0x754),PRAW(0x756),
|
||||
PRAW(0x758),PRAW(0x75A),PRAW(0x75C),PRAW(0x75E),
|
||||
|
||||
//0x760: weird area
|
||||
PCORE(0,MasterL.Reg_VOL),
|
||||
PCORE(0,MasterR.Reg_VOL),
|
||||
PCORE(0,FxL)+1,
|
||||
PCORE(0,FxR)+1,
|
||||
PCORE(0,ExtL)+1,
|
||||
PCORE(0,ExtR)+1,
|
||||
PCORE(0,InpL)+1,
|
||||
PCORE(0,InpR)+1,
|
||||
PCORE(0,MasterL.Value)+1,
|
||||
PCORE(0,MasterR.Value)+1,
|
||||
PCORE(0,Revb.IIR_ALPHA),
|
||||
PCORE(0,Revb.ACC_COEF_A),
|
||||
PCORE(0,Revb.ACC_COEF_B),
|
||||
PCORE(0,Revb.ACC_COEF_C),
|
||||
PCORE(0,Revb.ACC_COEF_D),
|
||||
PCORE(0,Revb.IIR_COEF),
|
||||
PCORE(0,Revb.FB_ALPHA),
|
||||
PCORE(0,Revb.FB_X),
|
||||
PCORE(0,Revb.IN_COEF_L),
|
||||
PCORE(0,Revb.IN_COEF_R),
|
||||
|
||||
PCORE(1,MasterL.Reg_VOL),
|
||||
PCORE(1,MasterR.Reg_VOL),
|
||||
PCORE(1,FxL)+1,
|
||||
PCORE(1,FxR)+1,
|
||||
PCORE(1,ExtL)+1,
|
||||
PCORE(1,ExtR)+1,
|
||||
PCORE(1,InpL)+1,
|
||||
PCORE(1,InpR)+1,
|
||||
PCORE(1,MasterL.Value)+1,
|
||||
PCORE(1,MasterR.Value)+1,
|
||||
PCORE(1,Revb.IIR_ALPHA),
|
||||
PCORE(1,Revb.ACC_COEF_A),
|
||||
PCORE(1,Revb.ACC_COEF_B),
|
||||
PCORE(1,Revb.ACC_COEF_C),
|
||||
PCORE(1,Revb.ACC_COEF_D),
|
||||
PCORE(1,Revb.IIR_COEF),
|
||||
PCORE(1,Revb.FB_ALPHA),
|
||||
PCORE(1,Revb.FB_X),
|
||||
PCORE(1,Revb.IN_COEF_L),
|
||||
PCORE(1,Revb.IN_COEF_R),
|
||||
|
||||
PRAW(0x7B0),PRAW(0x7B2),PRAW(0x7B4),PRAW(0x7B6),
|
||||
PRAW(0x7B8),PRAW(0x7BA),PRAW(0x7BC),PRAW(0x7BE),
|
||||
|
||||
// SPDIF interface
|
||||
U16P(Spdif.Out),
|
||||
U16P(Spdif.Info),
|
||||
U16P(Spdif.Unknown1),
|
||||
U16P(Spdif.Mode),
|
||||
U16P(Spdif.Media),
|
||||
U16P(Spdif.Unknown2),
|
||||
U16P(Spdif.Protection),
|
||||
|
||||
PRAW(0x7CE),
|
||||
PRAW(0x7D0),PRAW(0x7D2),PRAW(0x7D4),PRAW(0x7D6),
|
||||
PRAW(0x7D8),PRAW(0x7DA),PRAW(0x7DC),PRAW(0x7DE),
|
||||
PRAW(0x7E0),PRAW(0x7E2),PRAW(0x7E4),PRAW(0x7E6),
|
||||
PRAW(0x7E8),PRAW(0x7EA),PRAW(0x7EC),PRAW(0x7EE),
|
||||
PRAW(0x7F0),PRAW(0x7F2),PRAW(0x7F4),PRAW(0x7F6),
|
||||
PRAW(0x7F8),PRAW(0x7FA),PRAW(0x7FC),PRAW(0x7FE),
|
||||
|
||||
U16P(zero)
|
||||
};
|
||||
#pragma pack()
|
|
@ -0,0 +1,32 @@
|
|||
//GiGaHeRz's SPU2 Driver
|
||||
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
|
||||
//
|
||||
//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
|
||||
//
|
||||
|
||||
#ifndef _REGTABLE_H_
|
||||
#define _REGTABLE_H_
|
||||
|
||||
#define U16P(x) ( (u16*)&(x) )
|
||||
|
||||
// Returns the hiword of a 32 bit integer.
|
||||
#define U16P_HI(x) ( ((u16*)&(x))+1 )
|
||||
|
||||
// Yay! Global namespace pollution 101!
|
||||
extern const u16 zero;
|
||||
|
||||
extern u16* regtable[0x800];
|
||||
|
||||
#endif
|
|
@ -0,0 +1,182 @@
|
|||
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
* Developed and maintained by the Pcsx2 Development Team.
|
||||
*
|
||||
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
||||
*
|
||||
* 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 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
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "spu2.h"
|
||||
|
||||
//static LPF_data lowpass_left( 11000, SampleRate );
|
||||
//static LPF_data lowpass_right( 11000, SampleRate );
|
||||
|
||||
static s32 EffectsBufferIndexer( V_Core& thiscore, s32 offset )
|
||||
{
|
||||
u32 pos = thiscore.EffectsStartA + thiscore.ReverbX + offset;
|
||||
|
||||
// Need to use modulus here, because games can and will drop the buffer size
|
||||
// without notice, and it leads to offsets several times past the end of the buffer.
|
||||
|
||||
if( pos > thiscore.EffectsEndA )
|
||||
{
|
||||
pos = thiscore.EffectsStartA + ((thiscore.ReverbX + offset) % (u32)thiscore.EffectsBufferSize);
|
||||
//pos -= thiscore.EffectsEndA+1;
|
||||
//pos += thiscore.EffectsStartA;
|
||||
}
|
||||
else if( pos < thiscore.EffectsStartA )
|
||||
{
|
||||
pos = thiscore.EffectsEndA+1 - ((thiscore.ReverbX + offset) % (u32)thiscore.EffectsBufferSize );
|
||||
//pos -= thiscore.EffectsStartA;
|
||||
//pos += thiscore.EffectsEndA+1;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
/*void LowPass(s32& VL, s32& VR)
|
||||
{
|
||||
VL = (s32)( lowpass_left.sample(VL/65536.0) * 65536.0 );
|
||||
VR = (s32)( lowpass_right.sample(VR/65536.0) * 65536.0 );
|
||||
}*/
|
||||
|
||||
void Reverb_AdvanceBuffer( V_Core& thiscore )
|
||||
{
|
||||
if( (Cycles & 1) && (thiscore.EffectsBufferSize > 0) )
|
||||
{
|
||||
thiscore.ReverbX += 1;
|
||||
if(thiscore.ReverbX >= (u32)thiscore.EffectsBufferSize )
|
||||
thiscore.ReverbX %= (u32)thiscore.EffectsBufferSize;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void DoReverb( V_Core& thiscore, s32& OutL, s32& OutR, s32 InL, s32 InR)
|
||||
{
|
||||
// Reverb processing occurs at 24khz, so we skip processing every other sample,
|
||||
// and use the previous calculation for this core instead.
|
||||
|
||||
if( thiscore.EffectsBufferSize <= 0 )
|
||||
{
|
||||
// StartA is past EndA, so effects are disabled.
|
||||
OutL = InL;
|
||||
OutR = InR;
|
||||
//ConLog( " * SPU2: Effects disabled due to leapfrogged EffectsStart." );
|
||||
return;
|
||||
}
|
||||
|
||||
if((Cycles&1)==0)
|
||||
{
|
||||
OutL = thiscore.LastEffectL;
|
||||
OutR = thiscore.LastEffectR;
|
||||
|
||||
thiscore.LastEffectL = InL;
|
||||
thiscore.LastEffectR = InR;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Advance the current reverb buffer pointer, and cache the read/write addresses we'll be
|
||||
// needing for this session of reverb.
|
||||
|
||||
const u32 src_a0 = EffectsBufferIndexer( thiscore, thiscore.Revb.IIR_SRC_A0 );
|
||||
const u32 src_a1 = EffectsBufferIndexer( thiscore, thiscore.Revb.IIR_SRC_A1 );
|
||||
const u32 src_b0 = EffectsBufferIndexer( thiscore, thiscore.Revb.IIR_SRC_B0 );
|
||||
const u32 src_b1 = EffectsBufferIndexer( thiscore, thiscore.Revb.IIR_SRC_B1 );
|
||||
|
||||
const u32 dest_a0 = EffectsBufferIndexer( thiscore, thiscore.Revb.IIR_DEST_A0 );
|
||||
const u32 dest_a1 = EffectsBufferIndexer( thiscore, thiscore.Revb.IIR_DEST_A1 );
|
||||
const u32 dest_b0 = EffectsBufferIndexer( thiscore, thiscore.Revb.IIR_DEST_B0 );
|
||||
const u32 dest_b1 = EffectsBufferIndexer( thiscore, thiscore.Revb.IIR_DEST_B1 );
|
||||
|
||||
const u32 dest2_a0 = EffectsBufferIndexer( thiscore, thiscore.Revb.IIR_DEST_A0 + 1 );
|
||||
const u32 dest2_a1 = EffectsBufferIndexer( thiscore, thiscore.Revb.IIR_DEST_A1 + 1 );
|
||||
const u32 dest2_b0 = EffectsBufferIndexer( thiscore, thiscore.Revb.IIR_DEST_B0 + 1 );
|
||||
const u32 dest2_b1 = EffectsBufferIndexer( thiscore, thiscore.Revb.IIR_DEST_B1 + 1 );
|
||||
|
||||
const u32 acc_src_a0 = EffectsBufferIndexer( thiscore, thiscore.Revb.ACC_SRC_A0 );
|
||||
const u32 acc_src_b0 = EffectsBufferIndexer( thiscore, thiscore.Revb.ACC_SRC_B0 );
|
||||
const u32 acc_src_c0 = EffectsBufferIndexer( thiscore, thiscore.Revb.ACC_SRC_C0 );
|
||||
const u32 acc_src_d0 = EffectsBufferIndexer( thiscore, thiscore.Revb.ACC_SRC_D0 );
|
||||
|
||||
const u32 acc_src_a1 = EffectsBufferIndexer( thiscore, thiscore.Revb.ACC_SRC_A1 );
|
||||
const u32 acc_src_b1 = EffectsBufferIndexer( thiscore, thiscore.Revb.ACC_SRC_B1 );
|
||||
const u32 acc_src_c1 = EffectsBufferIndexer( thiscore, thiscore.Revb.ACC_SRC_C1 );
|
||||
const u32 acc_src_d1 = EffectsBufferIndexer( thiscore, thiscore.Revb.ACC_SRC_D1 );
|
||||
|
||||
const u32 fb_src_a0 = EffectsBufferIndexer( thiscore, thiscore.Revb.MIX_DEST_A0 - thiscore.Revb.FB_SRC_A );
|
||||
const u32 fb_src_a1 = EffectsBufferIndexer( thiscore, thiscore.Revb.MIX_DEST_A1 - thiscore.Revb.FB_SRC_A );
|
||||
const u32 fb_src_b0 = EffectsBufferIndexer( thiscore, thiscore.Revb.MIX_DEST_B0 - thiscore.Revb.FB_SRC_B );
|
||||
const u32 fb_src_b1 = EffectsBufferIndexer( thiscore, thiscore.Revb.MIX_DEST_B1 - thiscore.Revb.FB_SRC_B );
|
||||
|
||||
const u32 mix_dest_a0 = EffectsBufferIndexer( thiscore, thiscore.Revb.MIX_DEST_A0 );
|
||||
const u32 mix_dest_a1 = EffectsBufferIndexer( thiscore, thiscore.Revb.MIX_DEST_A1 );
|
||||
const u32 mix_dest_b0 = EffectsBufferIndexer( thiscore, thiscore.Revb.MIX_DEST_B0 );
|
||||
const u32 mix_dest_b1 = EffectsBufferIndexer( thiscore, thiscore.Revb.MIX_DEST_B1 );
|
||||
|
||||
// -----------------------------------------
|
||||
// End Buffer Pointers, Begin Reverb!
|
||||
// -----------------------------------------
|
||||
|
||||
const s32 INPUT_SAMPLE_L = (thiscore.LastEffectL+InL);
|
||||
const s32 INPUT_SAMPLE_R = (thiscore.LastEffectR+InR);
|
||||
|
||||
//const s32 INPUT_SAMPLE_L = (s32)( lowpass_left.sample( (thiscore.LastEffectL+InL)/65536.0 ) * 65536.0 );
|
||||
//const s32 INPUT_SAMPLE_R = (s32)( lowpass_right.sample( (thiscore.LastEffectR+InR)/65536.0 ) * 65536.0 );
|
||||
|
||||
const s32 IIR_INPUT_A0 = ((_spu2mem[src_a0] * thiscore.Revb.IIR_COEF) + (INPUT_SAMPLE_L * thiscore.Revb.IN_COEF_L))>>16;
|
||||
const s32 IIR_INPUT_A1 = ((_spu2mem[src_a1] * thiscore.Revb.IIR_COEF) + (INPUT_SAMPLE_R * thiscore.Revb.IN_COEF_R))>>16;
|
||||
const s32 IIR_INPUT_B0 = ((_spu2mem[src_b0] * thiscore.Revb.IIR_COEF) + (INPUT_SAMPLE_L * thiscore.Revb.IN_COEF_L))>>16;
|
||||
const s32 IIR_INPUT_B1 = ((_spu2mem[src_b1] * thiscore.Revb.IIR_COEF) + (INPUT_SAMPLE_R * thiscore.Revb.IN_COEF_R))>>16;
|
||||
|
||||
const s32 IIR_A0 = (IIR_INPUT_A0 * thiscore.Revb.IIR_ALPHA) + (_spu2mem[dest_a0] * (0x7fff - thiscore.Revb.IIR_ALPHA));
|
||||
const s32 IIR_A1 = (IIR_INPUT_A1 * thiscore.Revb.IIR_ALPHA) + (_spu2mem[dest_a1] * (0x7fff - thiscore.Revb.IIR_ALPHA));
|
||||
const s32 IIR_B0 = (IIR_INPUT_B0 * thiscore.Revb.IIR_ALPHA) + (_spu2mem[dest_b0] * (0x7fff - thiscore.Revb.IIR_ALPHA));
|
||||
const s32 IIR_B1 = (IIR_INPUT_B1 * thiscore.Revb.IIR_ALPHA) + (_spu2mem[dest_b1] * (0x7fff - thiscore.Revb.IIR_ALPHA));
|
||||
|
||||
_spu2mem[dest2_a0] = clamp_mix( IIR_A0 >> 16 );
|
||||
_spu2mem[dest2_a1] = clamp_mix( IIR_A1 >> 16 );
|
||||
_spu2mem[dest2_b0] = clamp_mix( IIR_B0 >> 16 );
|
||||
_spu2mem[dest2_b1] = clamp_mix( IIR_B1 >> 16 );
|
||||
|
||||
const s32 ACC0 =
|
||||
((_spu2mem[acc_src_a0] * thiscore.Revb.ACC_COEF_A)) +
|
||||
((_spu2mem[acc_src_b0] * thiscore.Revb.ACC_COEF_B)) +
|
||||
((_spu2mem[acc_src_c0] * thiscore.Revb.ACC_COEF_C)) +
|
||||
((_spu2mem[acc_src_d0] * thiscore.Revb.ACC_COEF_D));
|
||||
|
||||
const s32 ACC1 =
|
||||
((_spu2mem[acc_src_a1] * thiscore.Revb.ACC_COEF_A)) +
|
||||
((_spu2mem[acc_src_b1] * thiscore.Revb.ACC_COEF_B)) +
|
||||
((_spu2mem[acc_src_c1] * thiscore.Revb.ACC_COEF_C)) +
|
||||
((_spu2mem[acc_src_d1] * thiscore.Revb.ACC_COEF_D));
|
||||
|
||||
const s32 FB_A0 = (_spu2mem[fb_src_a0] * thiscore.Revb.FB_ALPHA);
|
||||
const s32 FB_A1 = (_spu2mem[fb_src_a1] * thiscore.Revb.FB_ALPHA);
|
||||
const s32 FB_B0 = (_spu2mem[fb_src_b0] * (0x7fff - thiscore.Revb.FB_ALPHA)); //>>16;
|
||||
const s32 FB_B1 = (_spu2mem[fb_src_b1] * (0x7fff - thiscore.Revb.FB_ALPHA)); //>>16;
|
||||
|
||||
const s32 fb_xor_a0 = (_spu2mem[fb_src_a0] * ( thiscore.Revb.FB_ALPHA ^ 0x8000 ))>>2;
|
||||
const s32 fb_xor_a1 = (_spu2mem[fb_src_a1] * ( thiscore.Revb.FB_ALPHA ^ 0x8000 ))>>2;
|
||||
|
||||
_spu2mem[mix_dest_a0] = clamp_mix( (ACC0 - FB_A0) >> 16 );
|
||||
_spu2mem[mix_dest_a1] = clamp_mix( (ACC1 - FB_A1) >> 16 );
|
||||
_spu2mem[mix_dest_b0] = clamp_mix( (MulShr32(thiscore.Revb.FB_ALPHA<<14, ACC0) - fb_xor_a0 - ((_spu2mem[fb_src_b0] * thiscore.Revb.FB_X)>>2)) >> 14 );
|
||||
_spu2mem[mix_dest_b1] = clamp_mix( (MulShr32(thiscore.Revb.FB_ALPHA<<14, ACC1) - fb_xor_a1 - ((_spu2mem[fb_src_b1] * thiscore.Revb.FB_X)>>2)) >> 14 );
|
||||
|
||||
OutL = thiscore.LastEffectL = clamp_mix(_spu2mem[mix_dest_a0] + _spu2mem[mix_dest_b0]);
|
||||
OutR = thiscore.LastEffectR = clamp_mix(_spu2mem[mix_dest_a1] + _spu2mem[mix_dest_b1]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,231 @@
|
|||
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
* Developed and maintained by the Pcsx2 Development Team.
|
||||
*
|
||||
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
||||
*
|
||||
* 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 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
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "SPU2.h"
|
||||
|
||||
namespace Savestate {
|
||||
|
||||
struct SPU2freezeData
|
||||
{
|
||||
u32 version;
|
||||
u8 unkregs[0x10000];
|
||||
u8 mem[0x200000];
|
||||
|
||||
u32 id;
|
||||
V_Core Cores[2];
|
||||
V_SPDIF Spdif;
|
||||
s16 OutPos;
|
||||
s16 InputPos;
|
||||
u32 Cycles;
|
||||
s32 uTicks;
|
||||
int PlayMode;
|
||||
|
||||
PcmCacheEntry cacheData;
|
||||
};
|
||||
|
||||
// Arbitrary ID to identify SPU2-X saves.
|
||||
static const u32 SAVE_ID = 0x1227521;
|
||||
|
||||
// versioning for saves.
|
||||
// Increment this when changes to the savestate system are made.
|
||||
|
||||
static const u32 SAVE_VERSION = 0x0001;
|
||||
|
||||
static void wipe_the_cache()
|
||||
{
|
||||
memset( pcm_cache_data, 0, pcm_BlockCount * sizeof(PcmCacheEntry) );
|
||||
}
|
||||
|
||||
|
||||
static s16 old_state_sBuffer[pcm_DecodedSamplesPerBlock] = {0};
|
||||
|
||||
typedef s32 __fastcall FreezeHandlerFunction( SPU2freezeData& data );
|
||||
|
||||
s32 __fastcall FreezeIt( SPU2freezeData& spud )
|
||||
{
|
||||
if( disableFreezes )
|
||||
{
|
||||
// No point in making a save state since the SPU2
|
||||
// state is completely bogus anyway... Let's just
|
||||
// give this some random ID that no one will recognize.
|
||||
|
||||
strcpy( (char*)&spud, "invalid" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
spud.id = SAVE_ID;
|
||||
spud.version = SAVE_VERSION;
|
||||
|
||||
memcpy(spud.unkregs, spu2regs, 0x010000);
|
||||
memcpy(spud.mem, _spu2mem, 0x200000);
|
||||
memcpy(spud.Cores, Cores, sizeof(Cores));
|
||||
memcpy(&spud.Spdif, &Spdif, sizeof(Spdif));
|
||||
spud.OutPos = OutPos;
|
||||
spud.InputPos = InputPos;
|
||||
spud.Cycles = Cycles;
|
||||
spud.uTicks = uTicks;
|
||||
spud.PlayMode = PlayMode;
|
||||
|
||||
// Save our cache:
|
||||
// We could just force the user to rebuild the cache when loading
|
||||
// from stavestates, but for most games the cache is pretty
|
||||
// small and compresses well.
|
||||
//
|
||||
// Potential Alternative:
|
||||
// If the cache is not saved then it is necessary to save the
|
||||
// decoded blocks currently in use by active voices. This allows
|
||||
// voices to resume seamlessly on load.
|
||||
|
||||
PcmCacheEntry* pcmDst = &spud.cacheData;
|
||||
int blksSaved=0;
|
||||
|
||||
for( int bidx=0; bidx<pcm_BlockCount; bidx++ )
|
||||
{
|
||||
if( pcm_cache_data[bidx].Validated )
|
||||
{
|
||||
// save a cache block!
|
||||
memcpy( pcmDst, &pcm_cache_data[bidx], sizeof(PcmCacheEntry) );
|
||||
pcmDst++;
|
||||
blksSaved++;
|
||||
}
|
||||
}
|
||||
|
||||
//printf( " * SPU2 > FreezeSave > Saved %d cache blocks.\n", blksSaved++ );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 __fastcall ThawIt( SPU2freezeData& spud )
|
||||
{
|
||||
if( spud.id != SAVE_ID || spud.version < SAVE_VERSION )
|
||||
{
|
||||
printf("\n*** SPU2-X Warning:\n");
|
||||
if( spud.id == SAVE_ID )
|
||||
printf("\tSavestate version is from an older version of this plugin.\n");
|
||||
else
|
||||
printf("\tThe savestate you are trying to load was not made with this plugin.\n");
|
||||
|
||||
printf("\tAudio may not recover correctly. Save your game to memorycard, reset,\n\n");
|
||||
printf(" and then continue from there.\n\n");
|
||||
|
||||
disableFreezes=true;
|
||||
resetClock = true;
|
||||
|
||||
// Do *not* reset the cores.
|
||||
// We'll need some "hints" as to how the cores should be initialized,
|
||||
// and the only way to get that is to use the game's existing core settings
|
||||
// and hope they kinda match the settings for the savestate (IRQ enables and such).
|
||||
//
|
||||
|
||||
//CoreReset( 0 );
|
||||
//CoreReset( 1 );
|
||||
|
||||
// adpcm cache : Clear all the cache flags and buffers.
|
||||
|
||||
wipe_the_cache();
|
||||
}
|
||||
else
|
||||
{
|
||||
disableFreezes=false;
|
||||
|
||||
// base stuff
|
||||
memcpy(spu2regs, spud.unkregs, 0x010000);
|
||||
memcpy(_spu2mem, spud.mem, 0x200000);
|
||||
|
||||
memcpy(Cores, spud.Cores, sizeof(Cores));
|
||||
memcpy(&Spdif, &spud.Spdif, sizeof(Spdif));
|
||||
OutPos = spud.OutPos;
|
||||
InputPos = spud.InputPos;
|
||||
Cycles = spud.Cycles;
|
||||
uTicks = spud.uTicks;
|
||||
PlayMode = spud.PlayMode;
|
||||
|
||||
// Load the ADPCM cache:
|
||||
|
||||
wipe_the_cache();
|
||||
|
||||
const PcmCacheEntry* pcmSrc = &spud.cacheData;
|
||||
int blksLoaded=0;
|
||||
|
||||
for( int bidx=0; bidx<pcm_BlockCount; bidx++ )
|
||||
{
|
||||
if( pcm_cache_data[bidx].Validated )
|
||||
{
|
||||
// load a cache block!
|
||||
memcpy( &pcm_cache_data[bidx], pcmSrc, sizeof(PcmCacheEntry) );
|
||||
pcmSrc++;
|
||||
blksLoaded++;
|
||||
}
|
||||
}
|
||||
|
||||
// Go through the V_Voice structs and recalculate SBuffer pointer from
|
||||
// the NextA setting.
|
||||
|
||||
for( int c=0; c<2; c++ )
|
||||
{
|
||||
for( int v=0; v<24; v++ )
|
||||
{
|
||||
const int cacheIdx = Cores[c].Voices[v].NextA / pcm_WordsPerBlock;
|
||||
Cores[c].Voices[v].SBuffer = pcm_cache_data[cacheIdx].Sampledata;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 __fastcall SizeIt()
|
||||
{
|
||||
if( disableFreezes ) return 8; // length of the string id "invalid" (plus a zero!)
|
||||
|
||||
int size = sizeof(SPU2freezeData);
|
||||
|
||||
// calculate the amount of memory consumed by our cache:
|
||||
|
||||
for( int bidx=0; bidx<pcm_BlockCount; bidx++ )
|
||||
{
|
||||
if( pcm_cache_data[bidx].Validated )
|
||||
size += pcm_DecodedSamplesPerBlock*sizeof(PcmCacheEntry);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
using namespace Savestate;
|
||||
|
||||
EXPORT_C_(s32) SPU2freeze(int mode, freezeData *data)
|
||||
{
|
||||
jASSUME( mode > 0 && mode < 3 );
|
||||
jASSUME( data != NULL );
|
||||
|
||||
if( data->data == NULL ) return -1;
|
||||
SPU2freezeData& spud = (SPU2freezeData&)*(data->data);
|
||||
|
||||
switch( mode )
|
||||
{
|
||||
case FREEZE_LOAD: return ThawIt( spud );
|
||||
case FREEZE_SAVE: return FreezeIt( spud );
|
||||
case FREEZE_SIZE: return SizeIt();
|
||||
|
||||
jNO_DEFAULT;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,722 @@
|
|||
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
* Developed and maintained by the Pcsx2 Development Team.
|
||||
*
|
||||
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
||||
*
|
||||
* 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 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
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
// [TODO] : The layout of this code file is now a complete hackish mess after
|
||||
// numerous timestretch-related additions. The whole thing should really be
|
||||
// rethought and redone at this point.
|
||||
|
||||
#include "spu2.h"
|
||||
#include "SoundTouch/SoundTouch.h"
|
||||
#include "SoundTouch/WavFile.h"
|
||||
|
||||
#include <new>
|
||||
|
||||
static int ts_stats_stretchblocks = 0;
|
||||
static int ts_stats_normalblocks = 0;
|
||||
static int ts_stats_logcounter = 0;
|
||||
|
||||
class NullOutModule: public SndOutModule
|
||||
{
|
||||
public:
|
||||
s32 Init(SndBuffer *) { return 0; }
|
||||
void Close() { }
|
||||
s32 Test() const { return 0; }
|
||||
void Configure(HWND parent) { }
|
||||
bool Is51Out() const { return false; }
|
||||
int GetEmptySampleCount() const { return 0; }
|
||||
|
||||
const wchar_t* GetIdent() const
|
||||
{
|
||||
return _T("nullout");
|
||||
}
|
||||
|
||||
const wchar_t* GetLongName() const
|
||||
{
|
||||
return _T("No Sound (Emulate SPU2 only)");
|
||||
}
|
||||
|
||||
} NullOut;
|
||||
|
||||
SndOutModule* mods[]=
|
||||
{
|
||||
&NullOut,
|
||||
XAudio2Out,
|
||||
DSoundOut,
|
||||
WaveOut,
|
||||
//ASIOOut,
|
||||
NULL // signals the end of our list
|
||||
};
|
||||
|
||||
int FindOutputModuleById( const wchar_t* omodid )
|
||||
{
|
||||
int modcnt = 0;
|
||||
while( mods[modcnt] != NULL )
|
||||
{
|
||||
if( wcscmp( mods[modcnt]->GetIdent(), omodid ) == 0 )
|
||||
break;
|
||||
++modcnt;
|
||||
}
|
||||
return modcnt;
|
||||
}
|
||||
|
||||
|
||||
__forceinline s16 SndScaleVol( s32 inval )
|
||||
{
|
||||
return inval >> SndOutVolumeShift;
|
||||
}
|
||||
|
||||
|
||||
// records last buffer status (fill %, range -100 to 100, with 0 being 50% full)
|
||||
float lastPct;
|
||||
float lastEmergencyAdj;
|
||||
|
||||
float cTempo=1;
|
||||
float eTempo = 1;
|
||||
int freezeTempo = 0;
|
||||
|
||||
soundtouch::SoundTouch* pSoundTouch=NULL;
|
||||
|
||||
|
||||
//usefull when timestretch isn't available
|
||||
|
||||
class SndBufferImpl: public SndBuffer
|
||||
{
|
||||
private:
|
||||
s32 *buffer;
|
||||
s32 size;
|
||||
s32 rpos;
|
||||
s32 wpos;
|
||||
s32 data;
|
||||
|
||||
// data prediction amount, used to "commit" data that hasn't
|
||||
// finished timestretch processing.
|
||||
s32 predictData;
|
||||
|
||||
bool pw;
|
||||
bool underrun_freeze;
|
||||
|
||||
protected:
|
||||
int GetAlignedBufferSize( int comp )
|
||||
{
|
||||
return (comp + SndOutPacketSize-1) & ~(SndOutPacketSize-1);
|
||||
}
|
||||
|
||||
public:
|
||||
SndBufferImpl( float latencyMS )
|
||||
{
|
||||
rpos=0;
|
||||
wpos=0;
|
||||
data=0;
|
||||
size=GetAlignedBufferSize( (int)(latencyMS * SampleRate / 500.0f ) );
|
||||
buffer = new s32[size];
|
||||
pw=false;
|
||||
underrun_freeze = false;
|
||||
predictData = 0;
|
||||
}
|
||||
|
||||
virtual ~SndBufferImpl()
|
||||
{
|
||||
delete buffer;
|
||||
}
|
||||
|
||||
virtual void WriteSamples(s32 *bData, int nSamples)
|
||||
{
|
||||
int free = size-data;
|
||||
predictData = 0;
|
||||
|
||||
jASSUME( data <= size );
|
||||
|
||||
// Problem:
|
||||
// If the SPU2 gets out of sync with the SndOut device, the writepos of the
|
||||
// circular buffer will overtake the readpos, leading to a prolonged period
|
||||
// of hopscotching read/write accesses (ie, lots of staticy crap sound for
|
||||
// several seconds).
|
||||
//
|
||||
// Compromise:
|
||||
// When an overrun occurs, we adapt by discarding a portion of the buffer.
|
||||
// The older portion of the buffer is discarded rather than incoming data,
|
||||
// so that the overall audio synchronization is better.
|
||||
|
||||
if( free < nSamples )
|
||||
{
|
||||
// Buffer overrun!
|
||||
// Dump samples from the read portion of the buffer instead of dropping
|
||||
// the newly written stuff.
|
||||
|
||||
s32 comp;
|
||||
|
||||
if( !timeStretchDisabled )
|
||||
{
|
||||
// If we overran it means the timestretcher failed. We need to speed
|
||||
// up audio playback.
|
||||
cTempo += cTempo * 0.12f;
|
||||
eTempo += eTempo * 0.40f;
|
||||
if( eTempo > 7.5f ) eTempo = 7.5f;
|
||||
pSoundTouch->setTempo( eTempo );
|
||||
|
||||
// Throw out just a little bit (two packets worth) to help
|
||||
// give the TS some room to work:
|
||||
|
||||
comp = SndOutPacketSize*2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Toss half the buffer plus whatever's being written anew:
|
||||
comp = GetAlignedBufferSize( (size + nSamples ) / 2 );
|
||||
if( comp > (size-SndOutPacketSize) ) comp = size-SndOutPacketSize;
|
||||
}
|
||||
|
||||
data -= comp;
|
||||
rpos = (rpos+comp)%size;
|
||||
if( MsgOverruns() )
|
||||
ConLog(" * SPU2 > Overrun Compensation (%d packets tossed)\n", comp / SndOutPacketSize );
|
||||
lastPct = 0.0; // normalize the timestretcher
|
||||
}
|
||||
|
||||
// copy in two phases, since there's a chance the packet
|
||||
// wraps around the buffer (it'd be nice to deal in packets only, but
|
||||
// the timestretcher and DSP options require flexibility).
|
||||
|
||||
const int endPos = wpos + nSamples;
|
||||
const int secondCopyLen = endPos - size;
|
||||
s32* wposbuffer = &buffer[wpos];
|
||||
|
||||
data += nSamples;
|
||||
if( secondCopyLen > 0 )
|
||||
{
|
||||
nSamples -= secondCopyLen;
|
||||
memcpy( buffer, &bData[nSamples], secondCopyLen * sizeof( *bData ) );
|
||||
wpos = secondCopyLen;
|
||||
}
|
||||
else
|
||||
wpos += nSamples;
|
||||
|
||||
memcpy( wposbuffer, bData, nSamples * sizeof( *bData ) );
|
||||
}
|
||||
|
||||
protected:
|
||||
// Returns TRUE if there is data to be output, or false if no data
|
||||
// is available to be copied.
|
||||
bool CheckUnderrunStatus( int& nSamples, int& quietSampleCount )
|
||||
{
|
||||
quietSampleCount = 0;
|
||||
if( underrun_freeze )
|
||||
{
|
||||
int toFill = (int)(size * ( timeStretchDisabled ? 0.50f : 0.1f ) );
|
||||
toFill = GetAlignedBufferSize( toFill );
|
||||
|
||||
// toFill is now aligned to a SndOutPacket
|
||||
|
||||
if( data < toFill )
|
||||
{
|
||||
quietSampleCount = nSamples;
|
||||
return false;
|
||||
}
|
||||
|
||||
underrun_freeze = false;
|
||||
if( MsgOverruns() )
|
||||
ConLog(" * SPU2 > Underrun compensation (%d packets buffered)\n", toFill / SndOutPacketSize );
|
||||
lastPct = 0.0; // normalize timestretcher
|
||||
}
|
||||
else if( data < nSamples )
|
||||
{
|
||||
nSamples = data;
|
||||
quietSampleCount = SndOutPacketSize - data;
|
||||
underrun_freeze = true;
|
||||
|
||||
if( !timeStretchDisabled )
|
||||
{
|
||||
// timeStretcher failed it's job. We need to slow down the audio some.
|
||||
|
||||
cTempo -= (cTempo * 0.12f);
|
||||
eTempo -= (eTempo * 0.30f);
|
||||
if( eTempo < 0.1f ) eTempo = 0.1f;
|
||||
pSoundTouch->setTempo( eTempo );
|
||||
}
|
||||
|
||||
return nSamples != 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
void ReadSamples( s16* bData )
|
||||
{
|
||||
int nSamples = SndOutPacketSize;
|
||||
|
||||
// Problem:
|
||||
// If the SPU2 gets even the least bit out of sync with the SndOut device,
|
||||
// the readpos of the circular buffer will overtake the writepos,
|
||||
// leading to a prolonged period of hopscotching read/write accesses (ie,
|
||||
// lots of staticy crap sound for several seconds).
|
||||
//
|
||||
// Fix:
|
||||
// If the read position overtakes the write position, abort the
|
||||
// transfer immediately and force the SndOut driver to wait until
|
||||
// the read buffer has filled up again before proceeding.
|
||||
// This will cause one brief hiccup that can never exceed the user's
|
||||
// set buffer length in duration.
|
||||
|
||||
int quietSamples;
|
||||
if( CheckUnderrunStatus( nSamples, quietSamples ) )
|
||||
{
|
||||
jASSUME( nSamples <= SndOutPacketSize );
|
||||
|
||||
// [Air] [TODO]: This loop is probably a candidiate for SSE2 optimization.
|
||||
|
||||
const int endPos = rpos + nSamples;
|
||||
const int secondCopyLen = endPos - size;
|
||||
const s32* rposbuffer = &buffer[rpos];
|
||||
|
||||
data -= nSamples;
|
||||
|
||||
if( secondCopyLen > 0 )
|
||||
{
|
||||
nSamples -= secondCopyLen;
|
||||
for( int i=0; i<secondCopyLen; i++ )
|
||||
bData[nSamples+i] = SndScaleVol( buffer[i] );
|
||||
rpos = secondCopyLen;
|
||||
}
|
||||
else
|
||||
rpos += nSamples;
|
||||
|
||||
for( int i=0; i<nSamples; i++ )
|
||||
bData[i] = SndScaleVol( rposbuffer[i] );
|
||||
}
|
||||
|
||||
// If quietSamples != 0 it means we have an underrun...
|
||||
// Let's just dull out some silence, because that's usually the least
|
||||
// painful way of dealing with underruns:
|
||||
memset( bData, 0, quietSamples * sizeof(*bData) );
|
||||
}
|
||||
|
||||
void ReadSamples( s32* bData )
|
||||
{
|
||||
int nSamples = SndOutPacketSize;
|
||||
|
||||
// Problem:
|
||||
// If the SPU2 gets even the least bit out of sync with the SndOut device,
|
||||
// the readpos of the circular buffer will overtake the writepos,
|
||||
// leading to a prolonged period of hopscotching read/write accesses (ie,
|
||||
// lots of staticy crap sound for several seconds).
|
||||
//
|
||||
// Fix:
|
||||
// If the read position overtakes the write position, abort the
|
||||
// transfer immediately and force the SndOut driver to wait until
|
||||
// the read buffer has filled up again before proceeding.
|
||||
// This will cause one brief hiccup that can never exceed the user's
|
||||
// set buffer length in duration.
|
||||
|
||||
int quietSamples;
|
||||
if( CheckUnderrunStatus( nSamples, quietSamples ) )
|
||||
{
|
||||
// nSamples is garaunteed non-zero if CheckUnderrunStatus
|
||||
// returned true.
|
||||
|
||||
const int endPos = rpos + nSamples;
|
||||
const int secondCopyLen = endPos - size;
|
||||
const int oldrpos = rpos;
|
||||
|
||||
data -= nSamples;
|
||||
|
||||
if( secondCopyLen > 0 )
|
||||
{
|
||||
nSamples -= secondCopyLen;
|
||||
memcpy( &bData[nSamples], buffer, secondCopyLen * sizeof( *bData ) );
|
||||
rpos = secondCopyLen;
|
||||
}
|
||||
else
|
||||
rpos += nSamples;
|
||||
|
||||
memcpy( bData, &buffer[oldrpos], nSamples * sizeof( *bData ) );
|
||||
}
|
||||
|
||||
// If quietSamples != 0 it means we have an underrun...
|
||||
// Let's just dull out some silence, because that's usually the least
|
||||
// painful way of dealing with underruns:
|
||||
memset( bData, 0, quietSamples * sizeof(*bData) );
|
||||
}
|
||||
|
||||
void PredictDataWrite( int samples )
|
||||
{
|
||||
predictData += samples;
|
||||
}
|
||||
|
||||
virtual void PauseOnWrite(bool doPause) { pw = doPause; }
|
||||
|
||||
// Calculate the buffer status percentage.
|
||||
// Returns range from -1.0 to 1.0
|
||||
// 1.0 = buffer overflow!
|
||||
// 0.0 = buffer nominal (50% full)
|
||||
// -1.0 = buffer underflow!
|
||||
float GetStatusPct()
|
||||
{
|
||||
// Get the buffer status of the output driver too, so that we can
|
||||
// obtain a more accurate overall buffer status.
|
||||
|
||||
int drvempty = mods[OutputModule]->GetEmptySampleCount(); // / 2;
|
||||
|
||||
//ConLog( "Data %d >>> driver: %d predict: %d\n", data, drvempty, predictData );
|
||||
|
||||
float result = (float)(data + predictData - drvempty) - (size/2);
|
||||
result /= (size/2);
|
||||
return result;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
SndBufferImpl *sndBuffer=NULL;
|
||||
|
||||
s32* sndTempBuffer=NULL;
|
||||
s32 sndTempProgress=NULL;
|
||||
s16* sndTempBuffer16=NULL;
|
||||
|
||||
void UpdateTempoChange()
|
||||
{
|
||||
if( --freezeTempo > 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float statusPct = sndBuffer->GetStatusPct();
|
||||
float pctChange = statusPct - lastPct;
|
||||
|
||||
float tempoChange;
|
||||
float emergencyAdj = 0;
|
||||
float newcee = cTempo; // workspace var. for cTempo
|
||||
|
||||
// IMPORTANT!
|
||||
// If you plan to tweak these values, make sure you're using a release build
|
||||
// OUTSIDE THE DEBUGGER to test it! The Visual Studio debugger can really cause
|
||||
// erratic behavior in the audio buffers, and makes the timestretcher seem a
|
||||
// lot more inconsistent than it really is.
|
||||
|
||||
// We have two factors.
|
||||
// * Distance from nominal buffer status (50% full)
|
||||
// * The change from previous update to this update.
|
||||
|
||||
// Prediction based on the buffer change:
|
||||
// (linear seems to work better here)
|
||||
|
||||
tempoChange = pctChange * 0.75f;
|
||||
|
||||
if( statusPct * tempoChange < 0.0f )
|
||||
{
|
||||
// only apply tempo change if it is in synch with the buffer status.
|
||||
// In other words, if the buffer is high (over 0%), and is decreasing,
|
||||
// ignore it. It'll just muck things up.
|
||||
|
||||
tempoChange = 0;
|
||||
}
|
||||
|
||||
// Sudden spikes in framerate can cause the nominal buffer status
|
||||
// to go critical, in which case we have to enact an emergency
|
||||
// stretch. The following cubic formulas do that. Values near
|
||||
// the extremeites give much larger results than those near 0.
|
||||
// And the value is added only this time, and does not accumulate.
|
||||
// (otherwise a large value like this would cause problems down the road)
|
||||
|
||||
// Constants:
|
||||
// Weight - weights the statusPct's "emergency" consideration.
|
||||
// higher values here will make the buffer perform more drastic
|
||||
// compensations at the outer edges of the buffer (at -75 or +75%
|
||||
// or beyond, for example).
|
||||
|
||||
// Range - scales the adjustment to the given range (more or less).
|
||||
// The actual range is dependent on the weight used, so if you increase
|
||||
// Weight you'll usually want to decrease Range somewhat to compensate.
|
||||
|
||||
// Prediction based on the buffer fill status:
|
||||
|
||||
const float statusWeight = 2.99f;
|
||||
const float statusRange = 0.068f;
|
||||
|
||||
// "non-emergency" deadzone: In this area stretching will be strongly discouraged.
|
||||
// Note: due tot he nature of timestretch latency, it's always a wee bit harder to
|
||||
// cope with low fps (underruns) tha it is high fps (overruns). So to help out a
|
||||
// little, the low-end portions of this check are less forgiving than the high-sides.
|
||||
|
||||
if( cTempo < 0.965f || cTempo > 1.060f ||
|
||||
pctChange < -0.38f || pctChange > 0.54f ||
|
||||
statusPct < -0.32f || statusPct > 0.39f ||
|
||||
eTempo < 0.89f || eTempo > 1.19f )
|
||||
{
|
||||
emergencyAdj = ( pow( statusPct*statusWeight, 3.0f ) * statusRange);
|
||||
}
|
||||
|
||||
// Smooth things out by factoring our previous adjustment into this one.
|
||||
// It helps make the system 'feel' a little smarter by giving it at least
|
||||
// one packet worth of history to help work off of:
|
||||
|
||||
emergencyAdj = (emergencyAdj * 0.75f) + (lastEmergencyAdj * 0.25f );
|
||||
|
||||
lastEmergencyAdj = emergencyAdj;
|
||||
lastPct = statusPct;
|
||||
|
||||
// Accumulate a fraction of the tempo change into the tempo itself.
|
||||
// This helps the system run "smarter" to games that run consistently
|
||||
// fast or slow by altering the base tempo to something closer to the
|
||||
// game's active speed. In tests most games normalize within 2 seconds
|
||||
// at 100ms latency, which is pretty good (larger buffers normalize even
|
||||
// quicker).
|
||||
|
||||
newcee += newcee * (tempoChange+emergencyAdj) * 0.03f;
|
||||
|
||||
// Apply tempoChange as a scale of cTempo. That way the effect is proportional
|
||||
// to the current tempo. (otherwise tempos rate of change at the extremes would
|
||||
// be too drastic)
|
||||
|
||||
float newTempo = newcee + ( emergencyAdj * cTempo );
|
||||
|
||||
// ... and as a final optimization, only stretch if the new tempo is outside
|
||||
// a nominal threshold. Keep this threshold check small, because it could
|
||||
// cause some serious side effects otherwise. (enlarging the cTempo check above
|
||||
// is usually better/safer)
|
||||
if( newTempo < 0.970f || newTempo > 1.045f )
|
||||
{
|
||||
cTempo = (float)newcee;
|
||||
|
||||
if( newTempo < 0.10f ) newTempo = 0.10f;
|
||||
else if( newTempo > 10.0f ) newTempo = 10.0f;
|
||||
|
||||
if( cTempo < 0.15f ) cTempo = 0.15f;
|
||||
else if( cTempo > 7.5f ) cTempo = 7.5f;
|
||||
|
||||
pSoundTouch->setTempo( eTempo = (float)newTempo );
|
||||
ts_stats_stretchblocks++;
|
||||
|
||||
/*ConLog(" * SPU2: [Nominal %d%%] [Emergency: %d%%] (baseTempo: %d%% ) (newTempo: %d%%) (buffer: %d%%)\n",
|
||||
//(relation < 0.0) ? "Normalize" : "",
|
||||
(int)(tempoChange * 100.0 * 0.03),
|
||||
(int)(emergencyAdj * 100.0),
|
||||
(int)(cTempo * 100.0),
|
||||
(int)(newTempo * 100.0),
|
||||
(int)(statusPct * 100.0)
|
||||
);*/
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nominal operation -- turn off stretching.
|
||||
// note: eTempo 'slides' toward 1.0 for smoother audio and better
|
||||
// protection against spikes.
|
||||
if( cTempo != 1.0f )
|
||||
{
|
||||
cTempo = 1.0f;
|
||||
eTempo = ( 1.0f + eTempo ) * 0.5f;
|
||||
pSoundTouch->setTempo( eTempo );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( eTempo != cTempo )
|
||||
pSoundTouch->setTempo( eTempo=cTempo );
|
||||
ts_stats_normalblocks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void soundtouchInit()
|
||||
{
|
||||
pSoundTouch = new soundtouch::SoundTouch();
|
||||
pSoundTouch->setSampleRate(SampleRate);
|
||||
pSoundTouch->setChannels(2);
|
||||
|
||||
pSoundTouch->setSetting( SETTING_USE_QUICKSEEK, 0 );
|
||||
pSoundTouch->setSetting( SETTING_USE_AA_FILTER, 0 );
|
||||
|
||||
pSoundTouch->setSetting( SETTING_SEQUENCE_MS, SoundtouchCfg::SequenceLenMS );
|
||||
pSoundTouch->setSetting( SETTING_SEEKWINDOW_MS, SoundtouchCfg::SeekWindowMS );
|
||||
pSoundTouch->setSetting( SETTING_OVERLAP_MS, SoundtouchCfg::OverlapMS );
|
||||
|
||||
pSoundTouch->setTempo(1);
|
||||
|
||||
// some timestretch management vars:
|
||||
|
||||
cTempo = 1.0;
|
||||
eTempo = 1.0;
|
||||
lastPct = 0;
|
||||
lastEmergencyAdj = 0;
|
||||
|
||||
// just freeze tempo changes for a while at startup.
|
||||
// the driver buffers are bogus anyway.
|
||||
freezeTempo = 8;
|
||||
}
|
||||
|
||||
static void _sndInitFail()
|
||||
{
|
||||
// If a failure occurs, just initialize the NoSound driver. This'll allow
|
||||
// the game to emulate properly (hopefully), albeit without sound.
|
||||
OutputModule = FindOutputModuleById( NullOut.GetIdent() );
|
||||
mods[OutputModule]->Init( sndBuffer );
|
||||
}
|
||||
|
||||
s32 SndInit()
|
||||
{
|
||||
if( mods[OutputModule] == NULL )
|
||||
{
|
||||
_sndInitFail();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// initialize sound buffer
|
||||
// Buffer actually attempts to run ~50%, so allocate near double what
|
||||
// the requested latency is:
|
||||
|
||||
try
|
||||
{
|
||||
sndBuffer = new SndBufferImpl( SndOutLatencyMS * (timeStretchDisabled ? 1.5f : 2.0f ) );
|
||||
sndTempBuffer = new s32[SndOutPacketSize];
|
||||
sndTempBuffer16 = new s16[SndOutPacketSize];
|
||||
}
|
||||
catch( std::bad_alloc& )
|
||||
{
|
||||
// out of memory exception (most likely)
|
||||
|
||||
SysMessage( "Out of memory error occured while initializing SPU2." );
|
||||
_sndInitFail();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// clear buffers!
|
||||
// Fixes loopy sounds on emu resets.
|
||||
memset( sndTempBuffer, 0, sizeof(s32) * SndOutPacketSize );
|
||||
memset( sndTempBuffer16, 0, sizeof(s16) * SndOutPacketSize );
|
||||
|
||||
sndTempProgress = 0;
|
||||
|
||||
soundtouchInit(); // initializes the timestretching
|
||||
|
||||
// some crap
|
||||
spdif_set51(mods[OutputModule]->Is51Out());
|
||||
|
||||
// initialize module
|
||||
if( mods[OutputModule]->Init(sndBuffer) == -1 )
|
||||
{
|
||||
_sndInitFail();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SndClose()
|
||||
{
|
||||
mods[OutputModule]->Close();
|
||||
|
||||
SAFE_DELETE_OBJ( sndBuffer );
|
||||
SAFE_DELETE_ARRAY( sndTempBuffer );
|
||||
SAFE_DELETE_ARRAY( sndTempBuffer16 );
|
||||
SAFE_DELETE_OBJ( pSoundTouch );
|
||||
}
|
||||
|
||||
s32 SndWrite(s32 ValL, s32 ValR)
|
||||
{
|
||||
// Log final output to wavefile.
|
||||
WaveDump::WriteCore( 1, CoreSrc_External, SndScaleVol(ValL), SndScaleVol(ValR) );
|
||||
|
||||
RecordWrite(SndScaleVol(ValL),SndScaleVol(ValR));
|
||||
|
||||
if(mods[OutputModule] == &NullOut) // null output doesn't need buffering or stretching! :p
|
||||
return 0;
|
||||
|
||||
sndTempBuffer[sndTempProgress++] = ValL;
|
||||
sndTempBuffer[sndTempProgress++] = ValR;
|
||||
|
||||
// If we haven't accumulated a full packet yet, do nothing more:
|
||||
if(sndTempProgress < SndOutPacketSize) return 1;
|
||||
|
||||
if(dspPluginEnabled)
|
||||
{
|
||||
for(int i=0;i<SndOutPacketSize;i++) { sndTempBuffer16[i] = SndScaleVol( sndTempBuffer[i] ); }
|
||||
|
||||
// send to winamp DSP
|
||||
sndTempProgress = DspProcess(sndTempBuffer16,sndTempProgress>>1)<<1;
|
||||
|
||||
for(int i=0;i<sndTempProgress;i++) { sndTempBuffer[i] = sndTempBuffer16[i]<<SndOutVolumeShift; }
|
||||
}
|
||||
|
||||
static int equalized = 0;
|
||||
if( !timeStretchDisabled )
|
||||
{
|
||||
bool progress = false;
|
||||
|
||||
// data prediction helps keep the tempo adjustments more accurate.
|
||||
// The timestretcher returns packets in belated "clump" form.
|
||||
// Meaning that most of the time we'll get nothing back, and then
|
||||
// suddenly we'll get several chunks back at once. Thus we use
|
||||
// data prediction to make the timestretcher more responsive.
|
||||
|
||||
sndBuffer->PredictDataWrite( (int)( sndTempProgress / eTempo ) );
|
||||
for(int i=0;i<sndTempProgress;i++) { ((float*)sndTempBuffer)[i] = sndTempBuffer[i]/2147483648.0f; }
|
||||
|
||||
pSoundTouch->putSamples((float*)sndTempBuffer, sndTempProgress>>1);
|
||||
|
||||
while( ( sndTempProgress = pSoundTouch->receiveSamples((float*)sndTempBuffer, sndTempProgress>>1)<<1 ) != 0 )
|
||||
{
|
||||
// [Air] [TODO] : Implement an SSE downsampler to int.
|
||||
for(int i=0;i<sndTempProgress;i++)
|
||||
{
|
||||
sndTempBuffer[i] = (s32)(((float*)sndTempBuffer)[i]*2147483648.0f);
|
||||
}
|
||||
sndBuffer->WriteSamples(sndTempBuffer, sndTempProgress);
|
||||
progress = true;
|
||||
}
|
||||
|
||||
UpdateTempoChange();
|
||||
|
||||
if( MsgOverruns() )
|
||||
{
|
||||
if( progress )
|
||||
{
|
||||
if( ++ts_stats_logcounter > 300 )
|
||||
{
|
||||
ts_stats_logcounter = 0;
|
||||
ConLog( " * SPU2 > Timestretch Stats > %d%% of packets stretched.\n",
|
||||
( ts_stats_stretchblocks * 100 ) / ( ts_stats_normalblocks + ts_stats_stretchblocks ) );
|
||||
ts_stats_normalblocks = 0;
|
||||
ts_stats_stretchblocks = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sndBuffer->WriteSamples(sndTempBuffer, sndTempProgress);
|
||||
sndTempProgress=0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
s32 SndTest()
|
||||
{
|
||||
if( mods[OutputModule] == NULL )
|
||||
return -1;
|
||||
|
||||
return mods[OutputModule]->Test();
|
||||
}
|
||||
|
||||
void SndConfigure(HWND parent, u32 module )
|
||||
{
|
||||
if( mods[module] == NULL )
|
||||
return;
|
||||
|
||||
mods[module]->Configure(parent);
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
//GiGaHeRz's SPU2 Driver
|
||||
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
|
||||
//
|
||||
//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
|
||||
//
|
||||
#ifndef SNDOUT_H_INCLUDE
|
||||
#define SNDOUT_H_INCLUDE
|
||||
|
||||
#include "BaseTypes.h"
|
||||
#include "lowpass.h"
|
||||
|
||||
// Number of stereo samples per SndOut block.
|
||||
// All drivers must work in units of this size when communicating with
|
||||
// SndOut.
|
||||
static const int SndOutPacketSize = 1024;
|
||||
|
||||
// Overall master volume shift.
|
||||
// Converts the mixer's 32 bit value into a 16 bit value.
|
||||
static const int SndOutVolumeShift = 10;
|
||||
|
||||
// Samplerate of the SPU2. For accurate playback we need to match this
|
||||
// exactly. Trying to scale samplerates and maintain SPU2's Ts timing accuracy
|
||||
// is too problematic. :)
|
||||
static const int SampleRate = 48000;
|
||||
|
||||
extern s32 SndInit();
|
||||
extern void SndClose();
|
||||
extern s32 SndWrite(s32 ValL, s32 ValR);
|
||||
extern s32 SndTest();
|
||||
extern void SndConfigure(HWND parent, u32 outmodidx );
|
||||
extern bool SndGetStats(u32 *written, u32 *played);
|
||||
extern s16 SndScaleVol( s32 inval );
|
||||
|
||||
int FindOutputModuleById( const wchar_t* omodid );
|
||||
|
||||
class SndBuffer
|
||||
{
|
||||
public:
|
||||
virtual ~SndBuffer() {}
|
||||
|
||||
virtual void WriteSamples(s32 *buffer, int nSamples)=0;
|
||||
virtual void PauseOnWrite(bool doPause)=0;
|
||||
|
||||
virtual void ReadSamples( s16* bData )=0;
|
||||
virtual void ReadSamples( s32* bData )=0;
|
||||
|
||||
//virtual s32 GetBufferUsage()=0;
|
||||
//virtual s32 GetBufferSize()=0;
|
||||
};
|
||||
|
||||
class SndOutModule
|
||||
{
|
||||
public:
|
||||
// Virtual destructor, because it helps fight C+++ funny-business.
|
||||
virtual ~SndOutModule(){};
|
||||
|
||||
// Returns a unique identification string for this driver.
|
||||
// (usually just matches the driver's cpp filename)
|
||||
virtual const wchar_t* GetIdent() const=0;
|
||||
|
||||
// Returns the long name / description for this driver.
|
||||
// (for use in configuration screen)
|
||||
virtual const wchar_t* GetLongName() const=0;
|
||||
|
||||
virtual s32 Init(SndBuffer *buffer)=0;
|
||||
virtual void Close()=0;
|
||||
virtual s32 Test() const=0;
|
||||
virtual void Configure(HWND parent)=0;
|
||||
virtual bool Is51Out() const=0;
|
||||
|
||||
// Returns the number of empty samples in the output buffer.
|
||||
// (which is effectively the amount of data played since the last update)
|
||||
virtual int GetEmptySampleCount() const=0;
|
||||
};
|
||||
|
||||
|
||||
//internal
|
||||
extern SndOutModule *WaveOut;
|
||||
extern SndOutModule *DSoundOut;
|
||||
extern SndOutModule *FModOut;
|
||||
extern SndOutModule *ASIOOut;
|
||||
extern SndOutModule *XAudio2Out;
|
||||
extern SndOutModule *DSound51Out;
|
||||
|
||||
extern SndOutModule* mods[];
|
||||
|
||||
#endif // SNDOUT_H_INCLUDE
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,209 @@
|
|||
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
* Developed and maintained by the Pcsx2 Development Team.
|
||||
*
|
||||
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
||||
*
|
||||
* 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 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
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SPU2_H_INCLUDED
|
||||
#define SPU2_H_INCLUDED
|
||||
|
||||
#include "BaseTypes.h"
|
||||
#include "PS2Edefs.h"
|
||||
|
||||
namespace VersionInfo
|
||||
{
|
||||
static const u8 PluginApi = PS2E_SPU2_VERSION;
|
||||
static const u8 Release = 1;
|
||||
static const u8 Revision = 0; // increase that with each version
|
||||
}
|
||||
|
||||
|
||||
#define EXPORT_C_(type) extern "C" __declspec(dllexport) type __stdcall
|
||||
|
||||
// We have our own versions that have the DLLExport attribute configured:
|
||||
|
||||
EXPORT_C_(s32) SPU2init();
|
||||
EXPORT_C_(s32) SPU2open(void *pDsp);
|
||||
EXPORT_C_(void) SPU2close();
|
||||
EXPORT_C_(void) SPU2shutdown();
|
||||
EXPORT_C_(void) SPU2write(u32 mem, u16 value);
|
||||
EXPORT_C_(u16) SPU2read(u32 mem);
|
||||
EXPORT_C_(void) SPU2readDMA4Mem(u16 *pMem, u32 size);
|
||||
EXPORT_C_(void) SPU2writeDMA4Mem(u16 *pMem, u32 size);
|
||||
EXPORT_C_(void) SPU2interruptDMA4();
|
||||
EXPORT_C_(void) SPU2readDMA7Mem(u16* pMem, u32 size);
|
||||
EXPORT_C_(void) SPU2writeDMA7Mem(u16 *pMem, u32 size);
|
||||
|
||||
// all addresses passed by dma will be pointers to the array starting at baseaddr
|
||||
// This function is necessary to successfully save and reload the spu2 state
|
||||
EXPORT_C_(void) SPU2setDMABaseAddr(uptr baseaddr);
|
||||
|
||||
EXPORT_C_(void) SPU2interruptDMA7();
|
||||
EXPORT_C_(u32) SPU2ReadMemAddr(int core);
|
||||
EXPORT_C_(void) SPU2WriteMemAddr(int core,u32 value);
|
||||
EXPORT_C_(void) SPU2irqCallback(void (*SPU2callback)(),void (*DMA4callback)(),void (*DMA7callback)());
|
||||
|
||||
// extended funcs
|
||||
// if start is 1, starts recording spu2 data, else stops
|
||||
// returns a non zero value if successful
|
||||
// for now, pData is not used
|
||||
EXPORT_C_(int) SPU2setupRecording(int start, void* pData);
|
||||
|
||||
EXPORT_C_(void) SPU2setClockPtr(u32* ptr);
|
||||
|
||||
EXPORT_C_(void) SPU2async(u32 cycles);
|
||||
EXPORT_C_(s32) SPU2freeze(int mode, freezeData *data);
|
||||
EXPORT_C_(void) SPU2configure();
|
||||
EXPORT_C_(void) SPU2about();
|
||||
EXPORT_C_(s32) SPU2test();
|
||||
|
||||
|
||||
//#define EFFECTS_DUMP
|
||||
|
||||
//Plugin parts
|
||||
#include "config.h"
|
||||
#include "defs.h"
|
||||
#include "regs.h"
|
||||
#include "dma.h"
|
||||
#include "sndout.h"
|
||||
|
||||
#include "spu2replay.h"
|
||||
|
||||
#define SPU2_LOG
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Helper macros
|
||||
//--------------------------------------------------------------------------------------
|
||||
#ifndef SAFE_FREE
|
||||
# define SAFE_FREE(p) { if(p) { free(p); (p)=NULL; } }
|
||||
#endif
|
||||
#ifndef SAFE_DELETE_ARRAY
|
||||
# define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p); (p)=NULL; } }
|
||||
#endif
|
||||
#ifndef SAFE_DELETE_OBJ
|
||||
# define SAFE_DELETE_OBJ(p) { if(p) { delete (p); (p)=NULL; } }
|
||||
#endif
|
||||
#ifndef SAFE_RELEASE
|
||||
# define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
|
||||
#endif
|
||||
|
||||
// The SPU2 has a dynamic memory range which is used for several internal operations, such as
|
||||
// registers, CORE 1/2 mixing, AutoDMAs, and some other fancy stuff. We exclude this range
|
||||
// from the cache here:
|
||||
static const s32 SPU2_DYN_MEMLINE = 0x2800;
|
||||
|
||||
// 8 short words per encoded PCM block. (as stored in SPU2 ram)
|
||||
static const int pcm_WordsPerBlock = 8;
|
||||
|
||||
// number of cachable ADPCM blocks (any blocks above the SPU2_DYN_MEMLINE)
|
||||
static const int pcm_BlockCount = 0x100000 / pcm_WordsPerBlock;
|
||||
|
||||
// 28 samples per decoded PCM block (as stored in our cache)
|
||||
static const int pcm_DecodedSamplesPerBlock = 28;
|
||||
|
||||
struct PcmCacheEntry
|
||||
{
|
||||
bool Validated;
|
||||
s16 Sampledata[pcm_DecodedSamplesPerBlock];
|
||||
};
|
||||
|
||||
extern void spdif_set51(u32 is_5_1_out);
|
||||
extern u32 spdif_init();
|
||||
extern void spdif_shutdown();
|
||||
extern void spdif_get_samples(s32 *samples); // fills the buffer with [l,r,c,lfe,sl,sr] if using 5.1 output, or [l,r] if using stereo
|
||||
|
||||
extern short *spu2regs;
|
||||
extern short *_spu2mem;
|
||||
|
||||
extern PcmCacheEntry* pcm_cache_data;
|
||||
|
||||
extern s16 __forceinline * __fastcall GetMemPtr(u32 addr);
|
||||
extern s16 __forceinline __fastcall spu2M_Read( u32 addr );
|
||||
extern void __inline __fastcall spu2M_Write( u32 addr, s16 value );
|
||||
extern void __inline __fastcall spu2M_Write( u32 addr, u16 value );
|
||||
|
||||
#define spu2Rs16(mmem) (*(s16 *)((s8 *)spu2regs + ((mmem) & 0x1fff)))
|
||||
#define spu2Ru16(mmem) (*(u16 *)((s8 *)spu2regs + ((mmem) & 0x1fff)))
|
||||
|
||||
extern void VoiceStart(int core,int vc);
|
||||
extern void VoiceStop(int core,int vc);
|
||||
|
||||
extern u8 callirq;
|
||||
|
||||
extern void (* _irqcallback)();
|
||||
extern void (* dma4callback)();
|
||||
extern void (* dma7callback)();
|
||||
|
||||
extern void SetIrqCall();
|
||||
|
||||
extern double srate_pv;
|
||||
|
||||
extern s16 *input_data;
|
||||
extern u32 input_data_ptr;
|
||||
|
||||
extern int PlayMode;
|
||||
extern int recording;
|
||||
extern bool disableFreezes;
|
||||
|
||||
|
||||
extern s32 uTicks;
|
||||
extern u32 lClocks;
|
||||
extern u32* cPtr;
|
||||
extern bool hasPtr;
|
||||
extern bool resetClock;
|
||||
|
||||
extern void RegLog(int level, char *RName,u32 mem,u32 core,u16 value);
|
||||
extern void SPU2writeLog(u32 rmem, u16 value);
|
||||
|
||||
extern void __fastcall TimeUpdate(u32 cClocks);
|
||||
extern u16 SPU_ps1_read(u32 mem);
|
||||
extern void SPU_ps1_write(u32 mem, u16 value);
|
||||
extern void SPU2_FastWrite( u32 rmem, u16 value );
|
||||
|
||||
extern void StartVoices(int core, u32 value);
|
||||
extern void StopVoices(int core, u32 value);
|
||||
|
||||
extern s32 DspLoadLibrary(wchar_t *fileName, int modNum);
|
||||
extern void DspCloseLibrary();
|
||||
extern int DspProcess(s16 *buffer, int samples);
|
||||
extern void DspUpdate(); // to let the Dsp process window messages
|
||||
|
||||
extern void RecordStart();
|
||||
extern void RecordStop();
|
||||
extern void RecordWrite(s16 left, s16 right);
|
||||
|
||||
extern void UpdateSpdifMode();
|
||||
extern void LowPassFilterInit();
|
||||
extern void InitADSR();
|
||||
extern void SndUpdateLimitMode();
|
||||
|
||||
//////////////////////////////
|
||||
// The Mixer Section //
|
||||
//////////////////////////////
|
||||
|
||||
extern void Mix();
|
||||
extern s32 clamp_mix(s32 x, u8 bitshift=0);
|
||||
extern void Reverb_AdvanceBuffer( V_Core& thiscore );
|
||||
extern void DoReverb( V_Core& thiscore, s32& OutL, s32& OutR, s32 InL, s32 InR);
|
||||
extern s32 MulShr32( s32 srcval, s32 mulval );
|
||||
|
||||
//#define PCM24_S1_INTERLEAVE
|
||||
|
||||
#endif // SPU2_H_INCLUDED //
|
|
@ -0,0 +1,192 @@
|
|||
//GiGaHeRz's SPU2 Driver
|
||||
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
|
||||
//
|
||||
//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
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "spu2.h"
|
||||
|
||||
FILE* s2rfile;
|
||||
|
||||
void s2r_write16(s16 data)
|
||||
{
|
||||
fwrite(&data,2,1,s2rfile);
|
||||
}
|
||||
|
||||
void s2r_write32(u32 data)
|
||||
{
|
||||
fwrite(&data,4,1,s2rfile);
|
||||
}
|
||||
|
||||
#define EMITC(i,a) s2r_write32(((u32)(i&0xFF)<<24)|(a&0xFFFFFF))
|
||||
|
||||
int s2r_open(char *filename)
|
||||
{
|
||||
s2rfile=fopen(filename,"wb");
|
||||
return s2rfile?0:-1;
|
||||
}
|
||||
|
||||
void s2r_readreg(u32 ticks,u32 addr)
|
||||
{
|
||||
if(!s2rfile) return;
|
||||
s2r_write32(ticks);
|
||||
EMITC(0,addr);
|
||||
}
|
||||
|
||||
void s2r_writereg(u32 ticks,u32 addr,s16 value)
|
||||
{
|
||||
if(!s2rfile) return;
|
||||
s2r_write32(ticks);
|
||||
EMITC(1,addr);
|
||||
s2r_write16(value);
|
||||
}
|
||||
|
||||
void s2r_writedma4(u32 ticks,u16*data,u32 len)
|
||||
{
|
||||
u32 i;
|
||||
if(!s2rfile) return;
|
||||
s2r_write32(ticks);
|
||||
EMITC(2,len);
|
||||
for(i=0;i<len;i++,data++)
|
||||
s2r_write16(*data);
|
||||
}
|
||||
|
||||
void s2r_writedma7(u32 ticks,u16*data,u32 len)
|
||||
{
|
||||
u32 i;
|
||||
if(!s2rfile) return;
|
||||
s2r_write32(ticks);
|
||||
EMITC(3,len);
|
||||
for(i=0;i<len;i++,data++)
|
||||
s2r_write16(*data);
|
||||
}
|
||||
|
||||
void s2r_close()
|
||||
{
|
||||
if(!s2rfile) return;
|
||||
fclose(s2rfile);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////
|
||||
// replay code
|
||||
|
||||
bool replay_mode=false;
|
||||
|
||||
u16 dmabuffer[0xFFFFF];
|
||||
|
||||
u32 dsp=0;
|
||||
|
||||
u32 lasync;
|
||||
u32 pcycles;
|
||||
u32 pclocks;
|
||||
|
||||
u32 oldlimit;
|
||||
|
||||
void dummy1()
|
||||
{
|
||||
}
|
||||
|
||||
void dummy4()
|
||||
{
|
||||
SPU2interruptDMA4();
|
||||
}
|
||||
|
||||
void dummy7()
|
||||
{
|
||||
SPU2interruptDMA7();
|
||||
}
|
||||
|
||||
#define Cread(a,b,c,d) if(fread(a,b,c,d)<b) break;
|
||||
|
||||
void CALLBACK s2r_replay(HWND hwnd, HINSTANCE hinst, LPSTR filename, int nCmdShow)
|
||||
{
|
||||
// load file
|
||||
FILE *file=fopen(filename,"rb");
|
||||
|
||||
if(!file) return;
|
||||
// if successful, init the plugin
|
||||
|
||||
replay_mode=true;
|
||||
|
||||
SPU2init();
|
||||
SPU2irqCallback(dummy1,dummy4,dummy7);
|
||||
SPU2setClockPtr(&pclocks);
|
||||
SPU2open(&dsp);
|
||||
|
||||
pclocks=0;
|
||||
pcycles=0;
|
||||
|
||||
SPU2async(0);
|
||||
|
||||
while(!feof(file))
|
||||
{
|
||||
u32 ccycle=0;
|
||||
u32 evid=0;
|
||||
u32 sval=0;
|
||||
u32 tval=0;
|
||||
|
||||
Cread(&ccycle,4,1,file);
|
||||
Cread(&sval,4,1,file);
|
||||
|
||||
evid=sval>>24;
|
||||
sval&=0xFFFFFF;
|
||||
|
||||
while((ccycle-lasync)>64)
|
||||
{
|
||||
lasync+=64;
|
||||
pcycles=lasync;
|
||||
pclocks=pcycles*768;
|
||||
|
||||
SPU2async(pclocks);
|
||||
}
|
||||
pcycles=ccycle;
|
||||
pclocks=pcycles*768;
|
||||
|
||||
|
||||
switch(evid)
|
||||
{
|
||||
case 0:
|
||||
SPU2read(sval);
|
||||
break;
|
||||
case 1:
|
||||
Cread(&tval,2,1,file);
|
||||
SPU2write(sval,tval);
|
||||
break;
|
||||
case 2:
|
||||
Cread(dmabuffer,sval,2,file);
|
||||
SPU2writeDMA4Mem(dmabuffer,sval);
|
||||
break;
|
||||
case 3:
|
||||
Cread(dmabuffer,sval,2,file);
|
||||
SPU2writeDMA7Mem(dmabuffer,sval);
|
||||
break;
|
||||
default:
|
||||
// not implemented
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//shutdown
|
||||
SPU2close();
|
||||
SPU2shutdown();
|
||||
fclose(file);
|
||||
|
||||
replay_mode=false;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
//GiGaHeRz's SPU2 Driver
|
||||
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
|
||||
//
|
||||
//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
|
||||
//
|
||||
|
||||
#ifndef SPU2REPLAY_H_INCLUDED
|
||||
#define SPU2REPLAY_H_INCLUDED
|
||||
|
||||
// s2r dumping
|
||||
int s2r_open(char *filename);
|
||||
void s2r_readreg(u32 ticks,u32 addr);
|
||||
void s2r_writereg(u32 ticks,u32 addr,s16 value);
|
||||
void s2r_writedma4(u32 ticks,u16*data,u32 len);
|
||||
void s2r_writedma7(u32 ticks,u16*data,u32 len);
|
||||
void s2r_close();
|
||||
|
||||
// s2r playing
|
||||
void CALLBACK s2r_replay(HWND hwnd, HINSTANCE hinst, LPSTR filename, int nCmdShow);
|
||||
|
||||
extern bool replay_mode;
|
||||
|
||||
#endif//SPU2REPLAY_H_INCLUDED
|
|
@ -0,0 +1,128 @@
|
|||
//GiGaHeRz's SPU2 Driver
|
||||
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
|
||||
//
|
||||
//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
|
||||
//
|
||||
|
||||
#include <stdexcept>
|
||||
#include <new>
|
||||
|
||||
#include "spu2.h"
|
||||
|
||||
#include "SoundTouch/WavFile.h"
|
||||
|
||||
static WavOutFile* _new_WavOutFile( const char* destfile )
|
||||
{
|
||||
return new WavOutFile( destfile, 48000, 16, 2 );
|
||||
}
|
||||
|
||||
namespace WaveDump
|
||||
{
|
||||
static WavOutFile* m_CoreWav[2][CoreSrc_Count] = { NULL };
|
||||
|
||||
static const char* m_tbl_CoreOutputTypeNames[CoreSrc_Count] =
|
||||
{
|
||||
"Input",
|
||||
"DryVoiceMix",
|
||||
"WetVoiceMix",
|
||||
"PreReverb",
|
||||
"PostReverb",
|
||||
"External"
|
||||
};
|
||||
|
||||
void Open()
|
||||
{
|
||||
#ifndef PUBLIC
|
||||
if( !WaveLog() ) return;
|
||||
|
||||
char wavfilename[256];
|
||||
|
||||
for( uint cidx=0; cidx<2; cidx++ )
|
||||
{
|
||||
for( int srcidx=0; srcidx<CoreSrc_Count; srcidx++ )
|
||||
{
|
||||
SAFE_DELETE_OBJ( m_CoreWav[cidx][srcidx] );
|
||||
|
||||
sprintf( wavfilename, "logs\\spu2x-Core%d-%s.wav",
|
||||
cidx, m_tbl_CoreOutputTypeNames[ srcidx ] );
|
||||
|
||||
try
|
||||
{
|
||||
m_CoreWav[cidx][srcidx] = _new_WavOutFile( wavfilename );
|
||||
}
|
||||
catch( std::runtime_error& ex )
|
||||
{
|
||||
printf( "SPU2-X > %s.\n\tWave Log for this core source disabled.", ex.what() );
|
||||
m_CoreWav[cidx][srcidx] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Close()
|
||||
{
|
||||
#ifndef PUBLIC
|
||||
for( uint cidx=0; cidx<2; cidx++ )
|
||||
{
|
||||
for( int srcidx=0; srcidx<CoreSrc_Count; srcidx++ )
|
||||
{
|
||||
SAFE_DELETE_OBJ( m_CoreWav[cidx][srcidx] );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void WriteCore( uint coreidx, CoreSourceType src, s16 left, s16 right )
|
||||
{
|
||||
#ifndef PUBLIC
|
||||
if( m_CoreWav[coreidx][src] != NULL )
|
||||
{
|
||||
s16 buffer[2] = { left, right };
|
||||
m_CoreWav[coreidx][src]->write( buffer, 2 );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
WavOutFile* m_wavrecord = NULL;
|
||||
|
||||
void RecordStart()
|
||||
{
|
||||
SAFE_DELETE_OBJ( m_wavrecord );
|
||||
|
||||
try
|
||||
{
|
||||
m_wavrecord = new WavOutFile( "recording.wav", 48000, 16, 2 );
|
||||
}
|
||||
catch( std::runtime_error& )
|
||||
{
|
||||
SysMessage("SPU2-X couldn't open file for recording: %s.\nRecording to wavfile disabled.", "recording.wav");
|
||||
m_wavrecord = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void RecordStop()
|
||||
{
|
||||
SAFE_DELETE_OBJ( m_wavrecord );
|
||||
}
|
||||
|
||||
void RecordWrite(s16 left, s16 right)
|
||||
{
|
||||
if( m_wavrecord == NULL ) return;
|
||||
|
||||
s16 buffer[2] = { left, right };
|
||||
m_wavrecord->write( buffer, 2 );
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
* Developed and maintained by the Pcsx2 Development Team.
|
||||
*
|
||||
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
||||
*
|
||||
* 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 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
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "spu2.h"
|
||||
#include "dialogs.h"
|
||||
#include <CommCtrl.h>
|
||||
|
||||
#include "svnrev.h"
|
||||
#include "Hyperlinks.h"
|
||||
|
||||
static LRESULT WINAPI AboutProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch(uMsg)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
{
|
||||
ConvertStaticToHyperlink( hDlg, IDC_LINK_GOOGLECODE );
|
||||
ConvertStaticToHyperlink( hDlg, IDC_LINK_WEBSITE );
|
||||
|
||||
wchar_t outstr[256];
|
||||
#ifdef PUBLIC
|
||||
swprintf_s( outstr, _T("Release v%d.%d -- Compiled on ") _T(__DATE__),
|
||||
VersionInfo::Release, VersionInfo::Revision );
|
||||
#else
|
||||
swprintf_s( outstr, _T("Build r%d -- Compiled on ") _T(__DATE__), SVN_REV );
|
||||
#endif
|
||||
SetWindowText( GetDlgItem(hDlg, IDC_LABEL_VERSION_INFO), outstr );
|
||||
ShowWindow( hDlg, true );
|
||||
}
|
||||
return TRUE;
|
||||
|
||||
case WM_COMMAND:
|
||||
switch( LOWORD(wParam) )
|
||||
{
|
||||
case IDOK:
|
||||
EndDialog(hDlg, TRUE );
|
||||
return TRUE;
|
||||
|
||||
case IDC_LINK_WEBSITE:
|
||||
ShellExecute(hDlg, _T("open"), _T("http://www.pcsx2.net/"),
|
||||
NULL, NULL, SW_SHOWNORMAL);
|
||||
return TRUE;
|
||||
|
||||
case IDC_LINK_GOOGLECODE:
|
||||
ShellExecute(hDlg, _T("open"), _T("http://code.google.com/p/pcsx2"),
|
||||
NULL, NULL, SW_SHOWNORMAL);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void AboutBox()
|
||||
{
|
||||
DialogBox( hInstance, MAKEINTRESOURCE(IDD_ABOUT), GetActiveWindow(), (DLGPROC)AboutProc );
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
* Developed and maintained by the Pcsx2 Development Team.
|
||||
*
|
||||
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
||||
*
|
||||
* 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 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
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "dialogs.h"
|
||||
|
||||
//////
|
||||
|
||||
const TCHAR CfgFile[] = _T("inis\\SPU2-X.ini");
|
||||
|
||||
|
||||
/*| Config File Format: |¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*\
|
||||
+--+---------------------+------------------------+
|
||||
| |
|
||||
| Option=Value |
|
||||
| |
|
||||
| |
|
||||
| Boolean Values: TRUE,YES,1,T,Y mean 'true', |
|
||||
| everything else means 'false'. |
|
||||
| |
|
||||
| All Values are limited to 255 chars. |
|
||||
| |
|
||||
+-------------------------------------------------+
|
||||
\*_____________________________________________*/
|
||||
|
||||
|
||||
void CfgWriteBool(const TCHAR* Section, const TCHAR* Name, bool Value)
|
||||
{
|
||||
const TCHAR *Data = Value ? _T("TRUE") : _T("FALSE");
|
||||
WritePrivateProfileString( Section, Name, Data, CfgFile );
|
||||
}
|
||||
|
||||
void CfgWriteInt(const TCHAR* Section, const TCHAR* Name, int Value)
|
||||
{
|
||||
TCHAR Data[32];
|
||||
_itow( Value, Data, 10 );
|
||||
WritePrivateProfileString(Section,Name,Data,CfgFile);
|
||||
}
|
||||
|
||||
/*void CfgWriteStr(const TCHAR* Section, const TCHAR* Name, const TCHAR *Data)
|
||||
{
|
||||
WritePrivateProfileString( Section, Name, Data, CfgFile );
|
||||
}*/
|
||||
|
||||
void CfgWriteStr(const TCHAR* Section, const TCHAR* Name, const wstring& Data)
|
||||
{
|
||||
WritePrivateProfileString( Section, Name, Data.c_str(), CfgFile );
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
bool CfgReadBool(const TCHAR *Section,const TCHAR* Name, bool Default)
|
||||
{
|
||||
TCHAR Data[255] = {0};
|
||||
|
||||
GetPrivateProfileString( Section, Name, _T(""), Data, 255, CfgFile );
|
||||
Data[254]=0;
|
||||
if(wcslen(Data)==0) {
|
||||
CfgWriteBool(Section,Name,Default);
|
||||
return Default;
|
||||
}
|
||||
|
||||
if(wcscmp(Data,_T("1"))==0) return true;
|
||||
if(wcscmp(Data,_T("Y"))==0) return true;
|
||||
if(wcscmp(Data,_T("T"))==0) return true;
|
||||
if(wcscmp(Data,_T("YES"))==0) return true;
|
||||
if(wcscmp(Data,_T("TRUE"))==0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int CfgReadInt(const TCHAR* Section, const TCHAR* Name,int Default)
|
||||
{
|
||||
TCHAR Data[255]={0};
|
||||
GetPrivateProfileString(Section,Name,_T(""),Data,255,CfgFile);
|
||||
Data[254]=0;
|
||||
|
||||
if(wcslen(Data)==0) {
|
||||
CfgWriteInt(Section,Name,Default);
|
||||
return Default;
|
||||
}
|
||||
|
||||
return _wtoi(Data);
|
||||
}
|
||||
|
||||
void CfgReadStr(const TCHAR* Section, const TCHAR* Name, TCHAR* Data, int DataSize, const TCHAR* Default)
|
||||
{
|
||||
GetPrivateProfileString(Section,Name,_T(""),Data,DataSize,CfgFile);
|
||||
|
||||
if(wcslen(Data)==0) {
|
||||
swprintf_s( Data, DataSize, _T("%s"), Default );
|
||||
CfgWriteStr( Section, Name, Data );
|
||||
}
|
||||
}
|
||||
|
||||
void CfgReadStr(const TCHAR* Section, const TCHAR* Name, wstring Data, int DataSize, const TCHAR* Default)
|
||||
{
|
||||
wchar_t workspace[512];
|
||||
GetPrivateProfileString(Section,Name,_T(""),workspace,DataSize,CfgFile);
|
||||
|
||||
Data = workspace;
|
||||
|
||||
if(Data.empty())
|
||||
{
|
||||
Data = Default;
|
||||
CfgWriteStr( Section, Name, Default );
|
||||
}
|
||||
}
|
||||
|
||||
// Tries to read the requested value.
|
||||
// Returns FALSE if the value isn't found.
|
||||
bool CfgFindName( const TCHAR *Section, const TCHAR* Name)
|
||||
{
|
||||
// Only load 24 characters. No need to load more.
|
||||
TCHAR Data[24]={0};
|
||||
GetPrivateProfileString(Section,Name,_T(""),Data,24,CfgFile);
|
||||
Data[23]=0;
|
||||
|
||||
if(wcslen(Data)==0) return false;
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,306 @@
|
|||
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
* Developed and maintained by the Pcsx2 Development Team.
|
||||
*
|
||||
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
||||
*
|
||||
* 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 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
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "spu2.h"
|
||||
#include "dialogs.h"
|
||||
|
||||
#ifndef PUBLIC
|
||||
static const int LATENCY_MAX = 3000;
|
||||
#else
|
||||
static const int LATENCY_MAX = 750;
|
||||
#endif
|
||||
|
||||
static const int LATENCY_MIN = 40;
|
||||
|
||||
int AutoDMAPlayRate[2] = {0,0};
|
||||
|
||||
// MIXING
|
||||
int Interpolation=1;
|
||||
/* values:
|
||||
0: no interpolation (use nearest)
|
||||
1. linear interpolation
|
||||
2. cubic interpolation
|
||||
*/
|
||||
|
||||
bool EffectsDisabled=false;
|
||||
|
||||
// OUTPUT
|
||||
int SndOutLatencyMS=160;
|
||||
bool timeStretchDisabled=false;
|
||||
|
||||
u32 OutputModule=0; //OUTPUT_DSOUND;
|
||||
|
||||
CONFIG_DSOUNDOUT Config_DSoundOut;
|
||||
CONFIG_WAVEOUT Config_WaveOut;
|
||||
CONFIG_XAUDIO2 Config_XAudio2;
|
||||
|
||||
// DSP
|
||||
bool dspPluginEnabled=false;
|
||||
int dspPluginModule=0;
|
||||
wchar_t dspPlugin[256];
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void ReadSettings()
|
||||
{
|
||||
AutoDMAPlayRate[0] = CfgReadInt(_T("MIXING"),_T("AutoDMA_Play_Rate_0"),0);
|
||||
AutoDMAPlayRate[1] = CfgReadInt(_T("MIXING"),_T("AutoDMA_Play_Rate_1"),0);
|
||||
|
||||
Interpolation = CfgReadInt(_T("MIXING"),_T("Interpolation"),1);
|
||||
|
||||
timeStretchDisabled = CfgReadBool( _T("OUTPUT"), _T("Disable_Timestretch"), false );
|
||||
EffectsDisabled = CfgReadBool( _T("MIXING"), _T("Disable_Effects"), false );
|
||||
|
||||
SndOutLatencyMS=CfgReadInt(_T("OUTPUT"),_T("Latency"), 160);
|
||||
|
||||
wchar_t omodid[128];
|
||||
CfgReadStr( _T("OUTPUT"), _T("Output_Module"), omodid, 127, XAudio2Out->GetIdent() );
|
||||
|
||||
// find the driver index of this module:
|
||||
OutputModule = FindOutputModuleById( omodid );
|
||||
|
||||
CfgReadStr( _T("DSP PLUGIN"),_T("Filename"),dspPlugin,255,_T(""));
|
||||
dspPluginModule = CfgReadInt(_T("DSP PLUGIN"),_T("ModuleNum"),0);
|
||||
dspPluginEnabled= CfgReadBool(_T("DSP PLUGIN"),_T("Enabled"),false);
|
||||
|
||||
// Read DSOUNDOUT and WAVEOUT configs:
|
||||
CfgReadStr( _T("DSOUNDOUT"), _T("Device"), Config_DSoundOut.Device, 254, _T("default") );
|
||||
CfgReadStr( _T("WAVEOUT"), _T("Device"), Config_WaveOut.Device, 254, _T("default") );
|
||||
Config_DSoundOut.NumBuffers = CfgReadInt( _T("DSOUNDOUT"), _T("Buffer_Count"), 5 );
|
||||
Config_WaveOut.NumBuffers = CfgReadInt( _T("WAVEOUT"), _T("Buffer_Count"), 4 );
|
||||
|
||||
SoundtouchCfg::ReadSettings();
|
||||
DebugConfig::ReadSettings();
|
||||
|
||||
// Sanity Checks
|
||||
// -------------
|
||||
|
||||
Clampify( SndOutLatencyMS, LATENCY_MIN, LATENCY_MAX );
|
||||
|
||||
Clampify( Config_DSoundOut.NumBuffers, (s8)2, (s8)8 );
|
||||
Clampify( Config_DSoundOut.NumBuffers, (s8)3, (s8)8 );
|
||||
|
||||
if( mods[OutputModule] == NULL )
|
||||
{
|
||||
// Unsupported or legacy module.
|
||||
fprintf( stderr, " * SPU2: Unknown output module '%s' specified in configuration file.\n", omodid );
|
||||
fprintf( stderr, " * SPU2: Defaulting to DirectSound (%S).\n", DSoundOut->GetIdent() );
|
||||
OutputModule = FindOutputModuleById( DSoundOut->GetIdent() );
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void WriteSettings()
|
||||
{
|
||||
CfgWriteInt(_T("MIXING"),_T("Interpolation"),Interpolation);
|
||||
|
||||
CfgWriteInt(_T("MIXING"),_T("AutoDMA_Play_Rate_0"),AutoDMAPlayRate[0]);
|
||||
CfgWriteInt(_T("MIXING"),_T("AutoDMA_Play_Rate_1"),AutoDMAPlayRate[1]);
|
||||
|
||||
CfgWriteBool(_T("MIXING"),_T("Disable_Effects"),EffectsDisabled);
|
||||
|
||||
CfgWriteStr(_T("OUTPUT"),_T("Output_Module"),mods[OutputModule]->GetIdent() );
|
||||
CfgWriteInt(_T("OUTPUT"),_T("Latency"),SndOutLatencyMS);
|
||||
CfgWriteBool(_T("OUTPUT"),_T("Disable_Timestretch"),timeStretchDisabled);
|
||||
|
||||
if( Config_DSoundOut.Device.empty() ) Config_DSoundOut.Device = _T("default");
|
||||
if( Config_WaveOut.Device.empty() ) Config_WaveOut.Device = _T("default");
|
||||
|
||||
CfgWriteStr(_T("DSOUNDOUT"),_T("Device"),Config_DSoundOut.Device);
|
||||
CfgWriteInt(_T("DSOUNDOUT"),_T("Buffer_Count"),Config_DSoundOut.NumBuffers);
|
||||
|
||||
CfgWriteStr(_T("WAVEOUT"),_T("Device"),Config_WaveOut.Device);
|
||||
CfgWriteInt(_T("WAVEOUT"),_T("Buffer_Count"),Config_WaveOut.NumBuffers);
|
||||
|
||||
CfgWriteStr(_T("DSP PLUGIN"),_T("Filename"),dspPlugin);
|
||||
CfgWriteInt(_T("DSP PLUGIN"),_T("ModuleNum"),dspPluginModule);
|
||||
CfgWriteBool(_T("DSP PLUGIN"),_T("Enabled"),dspPluginEnabled);
|
||||
|
||||
SoundtouchCfg::WriteSettings();
|
||||
DebugConfig::WriteSettings();
|
||||
|
||||
}
|
||||
|
||||
BOOL CALLBACK ConfigProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
|
||||
{
|
||||
int wmId,wmEvent;
|
||||
wchar_t temp[384]={0};
|
||||
|
||||
switch(uMsg)
|
||||
{
|
||||
case WM_PAINT:
|
||||
return FALSE;
|
||||
|
||||
case WM_INITDIALOG:
|
||||
{
|
||||
SendDialogMsg( hWnd, IDC_INTERPOLATE, CB_RESETCONTENT,0,0 );
|
||||
SendDialogMsg( hWnd, IDC_INTERPOLATE, CB_ADDSTRING,0,(LPARAM) _T("0 - Nearest (none/fast)") );
|
||||
SendDialogMsg( hWnd, IDC_INTERPOLATE, CB_ADDSTRING,0,(LPARAM) _T("1 - Linear (recommended)") );
|
||||
SendDialogMsg( hWnd, IDC_INTERPOLATE, CB_ADDSTRING,0,(LPARAM) _T("2 - Cubic (better/slower)") );
|
||||
SendDialogMsg( hWnd, IDC_INTERPOLATE, CB_SETCURSEL,Interpolation,0 );
|
||||
|
||||
SendDialogMsg( hWnd, IDC_OUTPUT, CB_RESETCONTENT,0,0 );
|
||||
|
||||
int modidx = 0;
|
||||
while( mods[modidx] != NULL )
|
||||
{
|
||||
swprintf_s( temp, 72, _T("%d - %s"), modidx, mods[modidx]->GetLongName() );
|
||||
SendDialogMsg( hWnd, IDC_OUTPUT, CB_ADDSTRING,0,(LPARAM)temp );
|
||||
++modidx;
|
||||
}
|
||||
SendDialogMsg( hWnd, IDC_OUTPUT, CB_SETCURSEL, OutputModule, 0 );
|
||||
|
||||
int minexp = (int)(pow( (double)LATENCY_MIN+1, 1.0/3.0 ) * 128.0);
|
||||
int maxexp = (int)(pow( (double)LATENCY_MAX+2, 1.0/3.0 ) * 128.0);
|
||||
INIT_SLIDER( IDC_LATENCY_SLIDER, minexp, maxexp, 200, 42, 12 );
|
||||
|
||||
SendDialogMsg( hWnd, IDC_LATENCY_SLIDER, TBM_SETPOS, TRUE, (int)(pow( (double)SndOutLatencyMS, 1.0/3.0 ) * 128.0) );
|
||||
swprintf_s(temp,_T("%d ms (avg)"),SndOutLatencyMS);
|
||||
SetWindowText(GetDlgItem(hWnd,IDC_LATENCY_LABEL),temp);
|
||||
|
||||
EnableWindow( GetDlgItem( hWnd, IDC_OPEN_CONFIG_SOUNDTOUCH ), !timeStretchDisabled );
|
||||
EnableWindow( GetDlgItem( hWnd, IDC_OPEN_CONFIG_DEBUG ), DebugEnabled );
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
wmId = LOWORD(wParam);
|
||||
wmEvent = HIWORD(wParam);
|
||||
// Parse the menu selections:
|
||||
switch (wmId)
|
||||
{
|
||||
case IDOK:
|
||||
{
|
||||
double res = ((int)SendDialogMsg( hWnd, IDC_LATENCY_SLIDER, TBM_GETPOS, 0, 0 )) / 128.0;
|
||||
SndOutLatencyMS = (int)pow( res, 3.0 );
|
||||
Clampify( SndOutLatencyMS, LATENCY_MIN, LATENCY_MAX );
|
||||
|
||||
Interpolation = (int)SendDialogMsg( hWnd, IDC_INTERPOLATE, CB_GETCURSEL,0,0 );
|
||||
OutputModule = (int)SendDialogMsg( hWnd, IDC_OUTPUT, CB_GETCURSEL,0,0 );
|
||||
|
||||
WriteSettings();
|
||||
EndDialog(hWnd,0);
|
||||
}
|
||||
break;
|
||||
|
||||
case IDCANCEL:
|
||||
EndDialog(hWnd,0);
|
||||
break;
|
||||
|
||||
case IDC_OUTCONF:
|
||||
SndConfigure( hWnd,
|
||||
(int)SendMessage(GetDlgItem(hWnd,IDC_OUTPUT),CB_GETCURSEL,0,0)
|
||||
);
|
||||
break;
|
||||
|
||||
case IDC_OPEN_CONFIG_DEBUG:
|
||||
{
|
||||
// Quick Hack -- DebugEnabled is re-loaded with the DebugConfig's API,
|
||||
// so we need to override it here:
|
||||
|
||||
bool dbgtmp = DebugEnabled;
|
||||
DebugConfig::OpenDialog();
|
||||
DebugEnabled = dbgtmp;
|
||||
}
|
||||
break;
|
||||
|
||||
case IDC_OPEN_CONFIG_SOUNDTOUCH:
|
||||
SoundtouchCfg::OpenDialog( hWnd );
|
||||
break;
|
||||
|
||||
HANDLE_CHECK(IDC_EFFECTS,EffectsDisabled);
|
||||
HANDLE_CHECK(IDC_DSP_ENABLE,dspPluginEnabled);
|
||||
HANDLE_CHECKNB(IDC_TS_ENABLE,timeStretchDisabled);
|
||||
EnableWindow( GetDlgItem( hWnd, IDC_OPEN_CONFIG_SOUNDTOUCH ), !timeStretchDisabled );
|
||||
break;
|
||||
|
||||
HANDLE_CHECKNB(IDC_DEBUG,DebugEnabled);
|
||||
DebugConfig::EnableControls( hWnd );
|
||||
EnableWindow( GetDlgItem( hWnd, IDC_OPEN_CONFIG_DEBUG ), DebugEnabled );
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_HSCROLL:
|
||||
{
|
||||
wmEvent = LOWORD(wParam);
|
||||
HWND hwndDlg = (HWND)lParam;
|
||||
|
||||
// TB_THUMBTRACK passes the curpos in wParam. Other messages
|
||||
// have to use TBM_GETPOS, so they will override this assignment (see below)
|
||||
|
||||
int curpos = HIWORD( wParam );
|
||||
|
||||
switch( wmEvent )
|
||||
{
|
||||
//case TB_ENDTRACK:
|
||||
//case TB_THUMBPOSITION:
|
||||
case TB_LINEUP:
|
||||
case TB_LINEDOWN:
|
||||
case TB_PAGEUP:
|
||||
case TB_PAGEDOWN:
|
||||
curpos = (int)SendMessage(hwndDlg,TBM_GETPOS,0,0);
|
||||
|
||||
case TB_THUMBTRACK:
|
||||
Clampify( curpos,
|
||||
(int)SendMessage(hwndDlg,TBM_GETRANGEMIN,0,0),
|
||||
(int)SendMessage(hwndDlg,TBM_GETRANGEMAX,0,0)
|
||||
);
|
||||
|
||||
SendMessage((HWND)lParam,TBM_SETPOS,TRUE,curpos);
|
||||
|
||||
if( hwndDlg == GetDlgItem( hWnd, IDC_LATENCY_SLIDER ) )
|
||||
{
|
||||
double res = pow( curpos / 128.0, 3.0 );
|
||||
curpos = (int)res;
|
||||
swprintf_s(temp,_T("%d ms (avg)"),curpos);
|
||||
SetDlgItemText(hWnd,IDC_LATENCY_LABEL,temp);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void configure()
|
||||
{
|
||||
INT_PTR ret;
|
||||
ReadSettings();
|
||||
ret = DialogBoxParam(hInstance,MAKEINTRESOURCE(IDD_CONFIG),GetActiveWindow(),(DLGPROC)ConfigProc,1);
|
||||
if(ret==-1)
|
||||
{
|
||||
MessageBoxEx(GetActiveWindow(),_T("Error Opening the config dialog."),_T("OMG ERROR!"),MB_OK,0);
|
||||
return;
|
||||
}
|
||||
ReadSettings();
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
* Developed and maintained by the Pcsx2 Development Team.
|
||||
*
|
||||
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
||||
*
|
||||
* 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 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
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_H_INCLUDED
|
||||
#define CONFIG_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
extern bool DebugEnabled;
|
||||
|
||||
extern bool _MsgToConsole;
|
||||
extern bool _MsgKeyOnOff;
|
||||
extern bool _MsgVoiceOff;
|
||||
extern bool _MsgDMA;
|
||||
extern bool _MsgAutoDMA;
|
||||
extern bool _MsgOverruns;
|
||||
extern bool _MsgCache;
|
||||
|
||||
extern bool _AccessLog;
|
||||
extern bool _DMALog;
|
||||
extern bool _WaveLog;
|
||||
|
||||
extern bool _CoresDump;
|
||||
extern bool _MemDump;
|
||||
extern bool _RegDump;
|
||||
|
||||
static __forceinline bool MsgToConsole() { return _MsgToConsole & DebugEnabled; }
|
||||
|
||||
static __forceinline bool MsgKeyOnOff() { return _MsgKeyOnOff & MsgToConsole(); }
|
||||
static __forceinline bool MsgVoiceOff() { return _MsgVoiceOff & MsgToConsole(); }
|
||||
static __forceinline bool MsgDMA() { return _MsgDMA & MsgToConsole(); }
|
||||
static __forceinline bool MsgAutoDMA() { return _MsgAutoDMA & MsgDMA(); }
|
||||
static __forceinline bool MsgOverruns() { return _MsgOverruns & MsgToConsole(); }
|
||||
static __forceinline bool MsgCache() { return _MsgCache & MsgToConsole(); }
|
||||
|
||||
static __forceinline bool AccessLog() { return _AccessLog & DebugEnabled; }
|
||||
static __forceinline bool DMALog() { return _DMALog & DebugEnabled; }
|
||||
static __forceinline bool WaveLog() { return _WaveLog & DebugEnabled; }
|
||||
|
||||
static __forceinline bool CoresDump() { return _CoresDump & DebugEnabled; }
|
||||
static __forceinline bool MemDump() { return _MemDump & DebugEnabled; }
|
||||
static __forceinline bool RegDump() { return _RegDump & DebugEnabled; }
|
||||
|
||||
|
||||
extern wchar_t AccessLogFileName[255];
|
||||
extern wchar_t WaveLogFileName[255];
|
||||
extern wchar_t DMA4LogFileName[255];
|
||||
extern wchar_t DMA7LogFileName[255];
|
||||
extern wchar_t CoresDumpFileName[255];
|
||||
extern wchar_t MemDumpFileName[255];
|
||||
extern wchar_t RegDumpFileName[255];
|
||||
|
||||
extern int Interpolation;
|
||||
|
||||
extern bool EffectsDisabled;
|
||||
|
||||
extern int AutoDMAPlayRate[2];
|
||||
|
||||
extern u32 OutputModule;
|
||||
extern int SndOutLatencyMS;
|
||||
|
||||
extern wchar_t dspPlugin[];
|
||||
extern int dspPluginModule;
|
||||
|
||||
extern bool dspPluginEnabled;
|
||||
extern bool timeStretchDisabled;
|
||||
|
||||
class SoundtouchCfg
|
||||
{
|
||||
// Timestretch Slider Bounds, Min/Max
|
||||
static const int SequenceLen_Min = 30;
|
||||
static const int SequenceLen_Max = 90;
|
||||
|
||||
static const int SeekWindow_Min = 10;
|
||||
static const int SeekWindow_Max = 32;
|
||||
|
||||
static const int Overlap_Min = 3;
|
||||
static const int Overlap_Max = 15;
|
||||
|
||||
public:
|
||||
static int SequenceLenMS;
|
||||
static int SeekWindowMS;
|
||||
static int OverlapMS;
|
||||
|
||||
static void ReadSettings();
|
||||
static void WriteSettings();
|
||||
static void OpenDialog( HWND hWnd );
|
||||
|
||||
protected:
|
||||
static void ClampValues();
|
||||
static BOOL CALLBACK DialogProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
|
||||
|
||||
};
|
||||
|
||||
|
||||
// *** BEGIN DRIVER-SPECIFIC CONFIGURATION ***
|
||||
// -------------------------------------------
|
||||
|
||||
// DSOUND
|
||||
struct CONFIG_XAUDIO2
|
||||
{
|
||||
std::wstring Device;
|
||||
s8 NumBuffers;
|
||||
|
||||
bool ExpandTo51;
|
||||
|
||||
CONFIG_XAUDIO2() :
|
||||
Device(),
|
||||
NumBuffers( 2 ),
|
||||
ExpandTo51( true )
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// DSOUND
|
||||
struct CONFIG_DSOUNDOUT
|
||||
{
|
||||
std::wstring Device;
|
||||
s8 NumBuffers;
|
||||
|
||||
CONFIG_DSOUNDOUT() :
|
||||
Device(),
|
||||
NumBuffers( 3 )
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// WAVEOUT
|
||||
struct CONFIG_WAVEOUT
|
||||
{
|
||||
std::wstring Device;
|
||||
s8 NumBuffers;
|
||||
|
||||
CONFIG_WAVEOUT() :
|
||||
Device(),
|
||||
NumBuffers( 4 )
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
extern CONFIG_DSOUNDOUT Config_DSoundOut;
|
||||
extern CONFIG_WAVEOUT Config_WaveOut;
|
||||
extern CONFIG_XAUDIO2 Config_XAudio2;
|
||||
|
||||
//////
|
||||
|
||||
void ReadSettings();
|
||||
void WriteSettings();
|
||||
void configure();
|
||||
void AboutBox();
|
||||
|
||||
#endif // CONFIG_H_INCLUDED
|
|
@ -0,0 +1,243 @@
|
|||
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
* Developed and maintained by the Pcsx2 Development Team.
|
||||
*
|
||||
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
||||
*
|
||||
* 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 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
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "spu2.h"
|
||||
#include "dialogs.h"
|
||||
|
||||
|
||||
bool DebugEnabled=false;
|
||||
bool _MsgToConsole=false;
|
||||
bool _MsgKeyOnOff=false;
|
||||
bool _MsgVoiceOff=false;
|
||||
bool _MsgDMA=false;
|
||||
bool _MsgAutoDMA=false;
|
||||
bool _MsgOverruns=false;
|
||||
bool _MsgCache=false;
|
||||
|
||||
bool _AccessLog=false;
|
||||
bool _DMALog=false;
|
||||
bool _WaveLog=false;
|
||||
|
||||
bool _CoresDump=false;
|
||||
bool _MemDump=false;
|
||||
bool _RegDump=false;
|
||||
|
||||
|
||||
|
||||
wchar_t AccessLogFileName[255];
|
||||
wchar_t WaveLogFileName[255];
|
||||
|
||||
wchar_t DMA4LogFileName[255];
|
||||
wchar_t DMA7LogFileName[255];
|
||||
|
||||
wchar_t CoresDumpFileName[255];
|
||||
wchar_t MemDumpFileName[255];
|
||||
wchar_t RegDumpFileName[255];
|
||||
|
||||
namespace DebugConfig {
|
||||
|
||||
static const TCHAR* Section = _T("DEBUG");
|
||||
|
||||
void ReadSettings()
|
||||
{
|
||||
DebugEnabled = CfgReadBool(Section, _T("Global_Enable"),0);
|
||||
_MsgToConsole= CfgReadBool(Section, _T("Show_Messages"),0);
|
||||
_MsgKeyOnOff = CfgReadBool(Section, _T("Show_Messages_Key_On_Off"),0);
|
||||
_MsgVoiceOff = CfgReadBool(Section, _T("Show_Messages_Voice_Off"),0);
|
||||
_MsgDMA = CfgReadBool(Section, _T("Show_Messages_DMA_Transfer"),0);
|
||||
_MsgAutoDMA = CfgReadBool(Section, _T("Show_Messages_AutoDMA"),0);
|
||||
_MsgOverruns = CfgReadBool(Section, _T("Show_Messages_Overruns"),0);
|
||||
_MsgCache = CfgReadBool(Section, _T("Show_Messages_CacheStats"),0);
|
||||
|
||||
_AccessLog = CfgReadBool(Section, _T("Log_Register_Access"),0);
|
||||
_DMALog = CfgReadBool(Section, _T("Log_DMA_Transfers"),0);
|
||||
_WaveLog = CfgReadBool(Section, _T("Log_WAVE_Output"),0);
|
||||
|
||||
_CoresDump = CfgReadBool(Section, _T("Dump_Info"),0);
|
||||
_MemDump = CfgReadBool(Section, _T("Dump_Memory"),0);
|
||||
_RegDump = CfgReadBool(Section, _T("Dump_Regs"),0);
|
||||
|
||||
CfgReadStr(Section,_T("Access_Log_Filename"),AccessLogFileName,255,_T("logs\\SPU2Log.txt"));
|
||||
CfgReadStr(Section,_T("WaveLog_Filename"), WaveLogFileName, 255,_T("logs\\SPU2log.wav"));
|
||||
CfgReadStr(Section,_T("DMA4Log_Filename"), DMA4LogFileName, 255,_T("logs\\SPU2dma4.dat"));
|
||||
CfgReadStr(Section,_T("DMA7Log_Filename"), DMA7LogFileName, 255,_T("logs\\SPU2dma7.dat"));
|
||||
|
||||
CfgReadStr(Section,_T("Info_Dump_Filename"),CoresDumpFileName,255,_T("logs\\SPU2Cores.txt"));
|
||||
CfgReadStr(Section,_T("Mem_Dump_Filename"), MemDumpFileName, 255,_T("logs\\SPU2mem.dat"));
|
||||
CfgReadStr(Section,_T("Reg_Dump_Filename"), RegDumpFileName, 255,_T("logs\\SPU2regs.dat"));
|
||||
}
|
||||
|
||||
|
||||
void WriteSettings()
|
||||
{
|
||||
CfgWriteBool(Section,_T("Global_Debug_Enabled"),DebugEnabled);
|
||||
|
||||
CfgWriteBool(Section,_T("Show_Messages"), _MsgToConsole);
|
||||
CfgWriteBool(Section,_T("Show_Messages_Key_On_Off"), _MsgKeyOnOff);
|
||||
CfgWriteBool(Section,_T("Show_Messages_Voice_Off"), _MsgVoiceOff);
|
||||
CfgWriteBool(Section,_T("Show_Messages_DMA_Transfer"),_MsgDMA);
|
||||
CfgWriteBool(Section,_T("Show_Messages_AutoDMA"), _MsgAutoDMA);
|
||||
CfgWriteBool(Section,_T("Show_Messages_Overruns"), _MsgOverruns);
|
||||
CfgWriteBool(Section,_T("Show_Messages_CacheStats"), _MsgCache);
|
||||
|
||||
CfgWriteBool(Section,_T("Log_Register_Access"),_AccessLog);
|
||||
CfgWriteBool(Section,_T("Log_DMA_Transfers"), _DMALog);
|
||||
CfgWriteBool(Section,_T("Log_WAVE_Output"), _WaveLog);
|
||||
|
||||
CfgWriteBool(Section,_T("Dump_Info"), _CoresDump);
|
||||
CfgWriteBool(Section,_T("Dump_Memory"),_MemDump);
|
||||
CfgWriteBool(Section,_T("Dump_Regs"), _RegDump);
|
||||
|
||||
CfgWriteStr(Section,_T("Access_Log_Filename"),AccessLogFileName);
|
||||
CfgWriteStr(Section,_T("WaveLog_Filename"), WaveLogFileName);
|
||||
CfgWriteStr(Section,_T("DMA4Log_Filename"), DMA4LogFileName);
|
||||
CfgWriteStr(Section,_T("DMA7Log_Filename"), DMA7LogFileName);
|
||||
|
||||
CfgWriteStr(Section,_T("Info_Dump_Filename"),CoresDumpFileName);
|
||||
CfgWriteStr(Section,_T("Mem_Dump_Filename"), MemDumpFileName);
|
||||
CfgWriteStr(Section,_T("Reg_Dump_Filename"), RegDumpFileName);
|
||||
|
||||
}
|
||||
|
||||
static void EnableMessages( HWND hWnd )
|
||||
{
|
||||
ENABLE_CONTROL(IDC_MSGSHOW, DebugEnabled);
|
||||
ENABLE_CONTROL(IDC_MSGKEY, MsgToConsole());
|
||||
ENABLE_CONTROL(IDC_MSGVOICE,MsgToConsole());
|
||||
ENABLE_CONTROL(IDC_MSGDMA, MsgToConsole());
|
||||
ENABLE_CONTROL(IDC_MSGADMA, MsgDMA());
|
||||
ENABLE_CONTROL(IDC_DBG_OVERRUNS, MsgToConsole());
|
||||
ENABLE_CONTROL(IDC_DBG_CACHE, MsgToConsole());
|
||||
}
|
||||
|
||||
void EnableControls( HWND hWnd )
|
||||
{
|
||||
EnableMessages( hWnd );
|
||||
ENABLE_CONTROL(IDC_LOGDMA, DebugEnabled);
|
||||
#ifdef PUBLIC
|
||||
ENABLE_CONTROL(IDC_LOGREGS, false);
|
||||
ENABLE_CONTROL(IDC_LOGWAVE, false);
|
||||
#else
|
||||
ENABLE_CONTROL(IDC_LOGREGS, DebugEnabled);
|
||||
ENABLE_CONTROL(IDC_LOGWAVE, DebugEnabled);
|
||||
#endif
|
||||
ENABLE_CONTROL(IDC_DUMPCORE,DebugEnabled);
|
||||
ENABLE_CONTROL(IDC_DUMPMEM, DebugEnabled);
|
||||
ENABLE_CONTROL(IDC_DUMPREGS,DebugEnabled);
|
||||
}
|
||||
|
||||
static BOOL CALLBACK DialogProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
|
||||
{
|
||||
int wmId,wmEvent;
|
||||
wchar_t temp[384]={0};
|
||||
|
||||
switch(uMsg)
|
||||
{
|
||||
case WM_PAINT:
|
||||
return FALSE;
|
||||
|
||||
case WM_INITDIALOG:
|
||||
{
|
||||
EnableControls( hWnd );
|
||||
|
||||
// Debugging / Logging Flags:
|
||||
SET_CHECK(IDC_DEBUG, DebugEnabled);
|
||||
SET_CHECK(IDC_MSGSHOW, _MsgToConsole);
|
||||
SET_CHECK(IDC_MSGKEY, _MsgKeyOnOff);
|
||||
SET_CHECK(IDC_MSGVOICE,_MsgVoiceOff);
|
||||
SET_CHECK(IDC_MSGDMA, _MsgDMA);
|
||||
SET_CHECK(IDC_MSGADMA, _MsgAutoDMA);
|
||||
SET_CHECK(IDC_DBG_OVERRUNS, _MsgOverruns );
|
||||
SET_CHECK(IDC_DBG_CACHE, _MsgCache );
|
||||
SET_CHECK(IDC_LOGREGS, _AccessLog);
|
||||
SET_CHECK(IDC_LOGDMA, _DMALog);
|
||||
SET_CHECK(IDC_LOGWAVE, _WaveLog);
|
||||
SET_CHECK(IDC_DUMPCORE,_CoresDump);
|
||||
SET_CHECK(IDC_DUMPMEM, _MemDump);
|
||||
SET_CHECK(IDC_DUMPREGS,_RegDump);
|
||||
|
||||
#ifdef PUBLIC
|
||||
ShowWindow( GetDlgItem( hWnd, IDC_MSG_PUBLIC_BUILD ), true );
|
||||
#else
|
||||
ShowWindow( GetDlgItem( hWnd, IDC_MSG_PUBLIC_BUILD ), false );
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
wmId = LOWORD(wParam);
|
||||
wmEvent = HIWORD(wParam);
|
||||
// Parse the menu selections:
|
||||
switch (wmId)
|
||||
{
|
||||
case IDOK:
|
||||
WriteSettings();
|
||||
EndDialog(hWnd,0);
|
||||
break;
|
||||
|
||||
case IDCANCEL:
|
||||
EndDialog(hWnd,0);
|
||||
break;
|
||||
|
||||
HANDLE_CHECKNB(IDC_MSGSHOW,_MsgToConsole);
|
||||
EnableMessages( hWnd );
|
||||
break;
|
||||
|
||||
HANDLE_CHECK(IDC_MSGKEY,_MsgKeyOnOff);
|
||||
HANDLE_CHECK(IDC_MSGVOICE,_MsgVoiceOff);
|
||||
HANDLE_CHECKNB(IDC_MSGDMA,_MsgDMA);
|
||||
ENABLE_CONTROL(IDC_MSGADMA, MsgDMA());
|
||||
break;
|
||||
|
||||
HANDLE_CHECK(IDC_MSGADMA,_MsgAutoDMA);
|
||||
HANDLE_CHECK(IDC_DBG_OVERRUNS,_MsgOverruns);
|
||||
HANDLE_CHECK(IDC_DBG_CACHE,_MsgCache);
|
||||
HANDLE_CHECK(IDC_LOGREGS,_AccessLog);
|
||||
HANDLE_CHECK(IDC_LOGDMA, _DMALog);
|
||||
HANDLE_CHECK(IDC_LOGWAVE,_WaveLog);
|
||||
HANDLE_CHECK(IDC_DUMPCORE,_CoresDump);
|
||||
HANDLE_CHECK(IDC_DUMPMEM, _MemDump);
|
||||
HANDLE_CHECK(IDC_DUMPREGS,_RegDump);
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
void OpenDialog()
|
||||
{
|
||||
INT_PTR ret;
|
||||
ret = DialogBoxParam(hInstance,MAKEINTRESOURCE(IDD_CONFIG_DEBUG),GetActiveWindow(),(DLGPROC)DialogProc,1);
|
||||
if(ret==-1)
|
||||
{
|
||||
MessageBoxEx(GetActiveWindow(),_T("Error Opening the debug configuration dialog."),_T("OMG ERROR!"),MB_OK,0);
|
||||
return;
|
||||
}
|
||||
ReadSettings();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
* Developed and maintained by the Pcsx2 Development Team.
|
||||
*
|
||||
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
||||
*
|
||||
* 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 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
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "spu2.h"
|
||||
#include "dialogs.h"
|
||||
|
||||
int SoundtouchCfg::SequenceLenMS = 63;
|
||||
int SoundtouchCfg::SeekWindowMS = 16;
|
||||
int SoundtouchCfg::OverlapMS = 7;
|
||||
|
||||
void SoundtouchCfg::ClampValues()
|
||||
{
|
||||
Clampify( SequenceLenMS, SequenceLen_Min, SequenceLen_Max );
|
||||
Clampify( SeekWindowMS, SeekWindow_Min, SeekWindow_Max );
|
||||
Clampify( OverlapMS, Overlap_Min, Overlap_Max );
|
||||
}
|
||||
|
||||
void SoundtouchCfg::ReadSettings()
|
||||
{
|
||||
SequenceLenMS = CfgReadInt( _T("SOUNDTOUCH"), _T("SequenceLengthMS"), 63 );
|
||||
SeekWindowMS = CfgReadInt( _T("SOUNDTOUCH"), _T("SeekWindowMS"), 16 );
|
||||
OverlapMS = CfgReadInt( _T("SOUNDTOUCH"), _T("OverlapMS"), 7 );
|
||||
|
||||
ClampValues();
|
||||
}
|
||||
|
||||
void SoundtouchCfg::WriteSettings()
|
||||
{
|
||||
CfgWriteInt( _T("SOUNDTOUCH"), _T("SequenceLengthMS"), SequenceLenMS );
|
||||
CfgWriteInt( _T("SOUNDTOUCH"), _T("SeekWindowMS"), SeekWindowMS );
|
||||
CfgWriteInt( _T("SOUNDTOUCH"), _T("OverlapMS"), OverlapMS );
|
||||
}
|
||||
|
||||
BOOL CALLBACK SoundtouchCfg::DialogProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
|
||||
{
|
||||
int wmId,wmEvent;
|
||||
wchar_t temp[384]={0};
|
||||
|
||||
switch(uMsg)
|
||||
{
|
||||
case WM_PAINT:
|
||||
return FALSE;
|
||||
|
||||
case WM_INITDIALOG:
|
||||
{
|
||||
INIT_SLIDER( IDC_SEQLEN_SLIDER, SequenceLen_Min, SequenceLen_Max, 20, 5, 1 );
|
||||
INIT_SLIDER( IDC_SEEKWIN_SLIDER, SeekWindow_Min, SeekWindow_Max, 5, 2, 1 );
|
||||
INIT_SLIDER( IDC_OVERLAP_SLIDER, Overlap_Min, Overlap_Max, 3, 2, 1 );
|
||||
|
||||
SendDialogMsg( hWnd, IDC_SEQLEN_SLIDER, TBM_SETPOS, TRUE, SequenceLenMS );
|
||||
SendDialogMsg( hWnd, IDC_SEEKWIN_SLIDER, TBM_SETPOS, TRUE, SeekWindowMS );
|
||||
SendDialogMsg( hWnd, IDC_OVERLAP_SLIDER, TBM_SETPOS, TRUE, OverlapMS );
|
||||
}
|
||||
|
||||
case WM_COMMAND:
|
||||
wmId = LOWORD(wParam);
|
||||
wmEvent = HIWORD(wParam);
|
||||
// Parse the menu selections:
|
||||
if( wmId == IDOK )
|
||||
{
|
||||
SequenceLenMS = (int)SendDialogMsg( hWnd, IDC_SEQLEN_SLIDER, TBM_GETPOS, 0, 0 );
|
||||
SeekWindowMS = (int)SendDialogMsg( hWnd, IDC_SEEKWIN_SLIDER, TBM_GETPOS, 0, 0 );
|
||||
OverlapMS = (int)SendDialogMsg( hWnd, IDC_OVERLAP_SLIDER, TBM_GETPOS, 0, 0 );
|
||||
|
||||
ClampValues();
|
||||
WriteSettings();
|
||||
EndDialog(hWnd,0);
|
||||
}
|
||||
else if( wmId == IDCANCEL )
|
||||
{
|
||||
EndDialog(hWnd,0);
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_HSCROLL:
|
||||
DoHandleScrollMessage( hWnd, wParam, lParam );
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void SoundtouchCfg::OpenDialog( HWND hWnd )
|
||||
{
|
||||
INT_PTR ret;
|
||||
ret = DialogBox( hInstance, MAKEINTRESOURCE(IDD_CONFIG_SOUNDTOUCH), hWnd, (DLGPROC)DialogProc );
|
||||
if(ret==-1)
|
||||
{
|
||||
MessageBoxEx(GetActiveWindow(),_T("Error Opening the Soundtouch advanced dialog."),_T("OMG ERROR!"),MB_OK,0);
|
||||
return;
|
||||
}
|
||||
ReadSettings();
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
* Developed and maintained by the Pcsx2 Development Team.
|
||||
*
|
||||
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
||||
*
|
||||
* 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 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
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef _DIALOGS_H_
|
||||
#define _DIALOGS_H_
|
||||
|
||||
#include "BaseTypes.h"
|
||||
|
||||
#include <commctrl.h>
|
||||
#include <initguid.h>
|
||||
|
||||
extern HINSTANCE hInstance;
|
||||
|
||||
#define SET_CHECK(idc,value) SendMessage(GetDlgItem(hWnd,idc),BM_SETCHECK,((value)==0)?BST_UNCHECKED:BST_CHECKED,0)
|
||||
#define HANDLE_CHECK(idc,hvar) case idc: (hvar) = !(hvar); SendMessage(GetDlgItem(hWnd,idc),BM_SETCHECK,(hvar)?BST_CHECKED:BST_UNCHECKED,0); break
|
||||
#define HANDLE_CHECKNB(idc,hvar)case idc: (hvar) = !(hvar); SendMessage(GetDlgItem(hWnd,idc),BM_SETCHECK,(hvar)?BST_CHECKED:BST_UNCHECKED,0)
|
||||
#define ENABLE_CONTROL(idc,value) EnableWindow(GetDlgItem(hWnd,idc),value)
|
||||
|
||||
#define INIT_SLIDER(idc,minrange,maxrange,tickfreq,pagesize,linesize) \
|
||||
SendMessage(GetDlgItem(hWnd,idc),TBM_SETRANGEMIN,FALSE,minrange); \
|
||||
SendMessage(GetDlgItem(hWnd,idc),TBM_SETRANGEMAX,FALSE,maxrange); \
|
||||
SendMessage(GetDlgItem(hWnd,idc),TBM_SETTICFREQ,tickfreq,0); \
|
||||
SendMessage(GetDlgItem(hWnd,idc),TBM_SETPAGESIZE,0,pagesize); \
|
||||
SendMessage(GetDlgItem(hWnd,idc),TBM_SETLINESIZE,0,linesize)
|
||||
|
||||
#define HANDLE_SCROLL_MESSAGE(idc,idcDisplay) \
|
||||
if((HWND)lParam == GetDlgItem(hWnd,idc)) return DoHandleScrollMessage( GetDlgItem(hWnd,idcDisplay), wParam, lParam )
|
||||
|
||||
namespace DebugConfig
|
||||
{
|
||||
extern void ReadSettings();
|
||||
extern void WriteSettings();
|
||||
extern void OpenDialog();
|
||||
extern void EnableControls( HWND hWnd );
|
||||
}
|
||||
|
||||
extern int SendDialogMsg( HWND hwnd, int dlgId, UINT code, WPARAM wParam, LPARAM lParam);
|
||||
extern HRESULT GUIDFromString( const char *str, LPGUID guid );
|
||||
|
||||
extern void AssignSliderValue( HWND idcwnd, HWND hwndDisplay, int value );
|
||||
extern void AssignSliderValue( HWND hWnd, int idc, int editbox, int value );
|
||||
extern int GetSliderValue( HWND hWnd, int idc );
|
||||
extern BOOL DoHandleScrollMessage( HWND hwndDisplay, WPARAM wParam, LPARAM lParam );
|
||||
|
||||
bool CfgFindName( const TCHAR *Section, const TCHAR* Name);
|
||||
|
||||
void CfgWriteBool(const TCHAR* Section, const TCHAR* Name, bool Value);
|
||||
void CfgWriteInt(const TCHAR* Section, const TCHAR* Name, int Value);
|
||||
void CfgWriteStr(const TCHAR* Section, const TCHAR* Name, const wstring& Data);
|
||||
|
||||
bool CfgReadBool(const TCHAR *Section,const TCHAR* Name, bool Default);
|
||||
void CfgReadStr(const TCHAR* Section, const TCHAR* Name, wstring Data, int DataSize, const TCHAR* Default);
|
||||
void CfgReadStr(const TCHAR* Section, const TCHAR* Name, TCHAR* Data, int DataSize, const TCHAR* Default);
|
||||
int CfgReadInt(const TCHAR* Section, const TCHAR* Name,int Default);
|
||||
|
||||
|
||||
// Items Specific to DirectSound
|
||||
#define STRFY(x) #x
|
||||
#define verifyc(x) Verifyc(x,STRFY(x))
|
||||
|
||||
extern void Verifyc(HRESULT hr, const char* fn);
|
||||
|
||||
struct ds_device_data
|
||||
{
|
||||
std::wstring name;
|
||||
GUID guid;
|
||||
bool hasGuid;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,501 @@
|
|||
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
* Developed and maintained by the Pcsx2 Development Team.
|
||||
*
|
||||
* Originally based on SPU2ghz v1.9 beta, by David Quintana.
|
||||
*
|
||||
* 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 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
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#define _WIN32_DCOM
|
||||
#include "spu2.h"
|
||||
#include "DPLII.h"
|
||||
#include "dialogs.h"
|
||||
|
||||
#include <dsound.h>
|
||||
#include <mmsystem.h>
|
||||
#include <audiodefs.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
static ds_device_data devices[32];
|
||||
static int ndevs;
|
||||
static GUID DevGuid; // currently employed GUID.
|
||||
static bool haveGuid;
|
||||
|
||||
class DSound51_Driver;
|
||||
|
||||
class DSound51: public SndOutModule
|
||||
{
|
||||
friend class DSound51_Driver;
|
||||
private:
|
||||
static const int MAX_BUFFER_COUNT = 8;
|
||||
|
||||
static const int PacketsPerBuffer = 1;
|
||||
static const int BufferSize = SndOutPacketSize * PacketsPerBuffer * 3;
|
||||
static const int BufferSizeBytes = BufferSize << 1;
|
||||
|
||||
DSound51_Driver* m_driver;
|
||||
|
||||
public:
|
||||
s32 Init(SndBuffer* sb);
|
||||
void Close();
|
||||
const char* GetIdent() const;
|
||||
const char* GetLongName() const;
|
||||
int GetEmptySampleCount() const;
|
||||
bool Is51Out() const;
|
||||
s32 Test() const;
|
||||
virtual void Configure(HWND parent);
|
||||
|
||||
protected:
|
||||
static BOOL CALLBACK ConfigProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
|
||||
|
||||
} DS51;
|
||||
|
||||
// Internal DSound 5.1 control structures.
|
||||
class DSound51_Driver
|
||||
{
|
||||
SndBuffer *buff;
|
||||
u32 numBuffers; // cached copy of our configuration setting.
|
||||
DPLII dpl2;
|
||||
|
||||
int channel;
|
||||
int myLastWrite;
|
||||
|
||||
bool dsound_running;
|
||||
HANDLE thread;
|
||||
DWORD tid;
|
||||
|
||||
IDirectSound8* dsound;
|
||||
IDirectSoundBuffer8* buffer;
|
||||
IDirectSoundNotify8* buffer_notify;
|
||||
HANDLE buffer_events[DSound51::MAX_BUFFER_COUNT];
|
||||
|
||||
WAVEFORMATEXTENSIBLE wfx;
|
||||
|
||||
|
||||
public:
|
||||
DSound51_Driver( SndBuffer* sb );
|
||||
~DSound51_Driver();
|
||||
|
||||
DWORD WorkerThread();
|
||||
int GetEmptySampleCount() const;
|
||||
};
|
||||
|
||||
static DWORD CALLBACK RThread(DSound51_Driver* obj)
|
||||
{
|
||||
return obj->WorkerThread();
|
||||
}
|
||||
|
||||
static BOOL CALLBACK DSEnumCallback( LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext )
|
||||
{
|
||||
//strcpy(devices[ndevs].name,lpcstrDescription);
|
||||
_snprintf_s(devices[ndevs].name,255,"%s",lpcstrDescription);
|
||||
|
||||
if(lpGuid)
|
||||
{
|
||||
devices[ndevs].guid=*lpGuid;
|
||||
devices[ndevs].hasGuid = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
devices[ndevs].hasGuid = false;
|
||||
}
|
||||
ndevs++;
|
||||
|
||||
if(ndevs<32) return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DSound51_Driver::DSound51_Driver( SndBuffer *sb ) :
|
||||
buff( sb ),
|
||||
numBuffers( Config_DSound51.NumBuffers ),
|
||||
dpl2( Config_DSound51.LowpassLFE, SampleRate )
|
||||
{
|
||||
//
|
||||
// Initialize DSound
|
||||
//
|
||||
GUID cGuid;
|
||||
bool success = false;
|
||||
|
||||
if( (strlen(Config_DSound51.Device)>0)&&(!FAILED(GUIDFromString(Config_DSound51.Device,&cGuid))))
|
||||
{
|
||||
if( !FAILED( DirectSoundCreate8(&cGuid,&dsound,NULL) ) )
|
||||
success = true;
|
||||
}
|
||||
|
||||
// if the GUID failed, just open up the default dsound driver:
|
||||
if( !success )
|
||||
{
|
||||
verifyc(DirectSoundCreate8(NULL,&dsound,NULL));
|
||||
}
|
||||
|
||||
verifyc(dsound->SetCooperativeLevel(GetDesktopWindow(),DSSCL_PRIORITY));
|
||||
IDirectSoundBuffer* buffer_;
|
||||
DSBUFFERDESC desc;
|
||||
|
||||
// Set up WAV format structure.
|
||||
|
||||
memset(&wfx, 0, sizeof(WAVEFORMATEX));
|
||||
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
wfx.Format.nSamplesPerSec = SampleRate;
|
||||
wfx.Format.nChannels=6;
|
||||
wfx.Format.wBitsPerSample = 16;
|
||||
wfx.Format.nBlockAlign = wfx.Format.nChannels*wfx.Format.wBitsPerSample/8;
|
||||
wfx.Format.nAvgBytesPerSec = SampleRate * wfx.Format.nBlockAlign;
|
||||
wfx.Format.cbSize=22;
|
||||
wfx.Samples.wValidBitsPerSample=0;
|
||||
wfx.dwChannelMask=SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT;
|
||||
wfx.SubFormat=KSDATAFORMAT_SUBTYPE_PCM;
|
||||
|
||||
verifyc(dsound->SetSpeakerConfig(DSSPEAKER_5POINT1));
|
||||
|
||||
// Set up DSBUFFERDESC structure.
|
||||
|
||||
memset(&desc, 0, sizeof(DSBUFFERDESC));
|
||||
desc.dwSize = sizeof(DSBUFFERDESC);
|
||||
desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY;// _CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY;
|
||||
desc.dwBufferBytes = DSound51::BufferSizeBytes * numBuffers;
|
||||
desc.lpwfxFormat = &wfx.Format;
|
||||
|
||||
desc.dwFlags |=DSBCAPS_LOCSOFTWARE;
|
||||
desc.dwFlags|=DSBCAPS_GLOBALFOCUS;
|
||||
|
||||
verifyc(dsound->CreateSoundBuffer(&desc,&buffer_,0));
|
||||
verifyc(buffer_->QueryInterface(IID_IDirectSoundBuffer8,(void**)&buffer));
|
||||
buffer_->Release();
|
||||
|
||||
verifyc(buffer->QueryInterface(IID_IDirectSoundNotify8,(void**)&buffer_notify));
|
||||
|
||||
DSBPOSITIONNOTIFY not[DSound51::MAX_BUFFER_COUNT];
|
||||
|
||||
for(u32 i=0;i<numBuffers;i++)
|
||||
{
|
||||
buffer_events[i]=CreateEvent(NULL,FALSE,FALSE,NULL);
|
||||
not[i].dwOffset=(wfx.Format.nBlockAlign*2 + DSound51::BufferSizeBytes*(i+1))%desc.dwBufferBytes;
|
||||
not[i].hEventNotify=buffer_events[i];
|
||||
}
|
||||
|
||||
buffer_notify->SetNotificationPositions(numBuffers,not);
|
||||
|
||||
LPVOID p1=0,p2=0;
|
||||
DWORD s1=0,s2=0;
|
||||
|
||||
verifyc(buffer->Lock(0,desc.dwBufferBytes,&p1,&s1,&p2,&s2,0));
|
||||
assert(p2==0);
|
||||
memset(p1,0,s1);
|
||||
verifyc(buffer->Unlock(p1,s1,p2,s2));
|
||||
|
||||
//Play the buffer !
|
||||
verifyc(buffer->Play(0,0,DSBPLAY_LOOPING));
|
||||
|
||||
// Start Thread
|
||||
myLastWrite = 0;
|
||||
dsound_running=true;
|
||||
thread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)RThread,this,0,&tid);
|
||||
SetThreadPriority(thread,THREAD_PRIORITY_TIME_CRITICAL);
|
||||
}
|
||||
|
||||
|
||||
DSound51_Driver::~DSound51_Driver()
|
||||
{
|
||||
// Stop Thread
|
||||
fprintf(stderr," * SPU2: Waiting for DSound thread to finish...");
|
||||
dsound_running = false;
|
||||
|
||||
WaitForSingleObject(thread,INFINITE);
|
||||
CloseHandle(thread);
|
||||
|
||||
fprintf(stderr," Done.\n");
|
||||
|
||||
//
|
||||
// Clean up
|
||||
//
|
||||
if( buffer != NULL )
|
||||
{
|
||||
buffer->Stop();
|
||||
|
||||
for(u32 i=0;i<numBuffers;i++)
|
||||
{
|
||||
if( buffer_events[i] == NULL ) continue;
|
||||
CloseHandle( buffer_events[i] );
|
||||
buffer_events[i] = NULL;
|
||||
}
|
||||
|
||||
SAFE_RELEASE( buffer_notify );
|
||||
SAFE_RELEASE( buffer );
|
||||
}
|
||||
SAFE_RELEASE( dsound );
|
||||
|
||||
}
|
||||
|
||||
DWORD DSound51_Driver::WorkerThread()
|
||||
{
|
||||
while( dsound_running )
|
||||
{
|
||||
u32 rv = WaitForMultipleObjects(numBuffers,buffer_events,FALSE,200);
|
||||
|
||||
s16* p1, *oldp1;
|
||||
LPVOID p2;
|
||||
DWORD s1,s2;
|
||||
|
||||
u32 poffset = DSound51::BufferSizeBytes * rv;
|
||||
|
||||
#ifdef _DEBUG
|
||||
verifyc(buffer->Lock(poffset,DSound51::BufferSizeBytes,(LPVOID*)&p1,&s1,&p2,&s2,0));
|
||||
#else
|
||||
if( FAILED(buffer->Lock(poffset,DSound51::BufferSizeBytes,(LPVOID*)&p1,&s1,&p2,&s2,0) ) )
|
||||
{
|
||||
fputs( " * SPU2 : Directsound Warning > Buffer lock failure. You may need to increase the DSound buffer count.\n", stderr );
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
oldp1 = p1;
|
||||
|
||||
for(int p=0; p<DSound51::PacketsPerBuffer; p++, p1+=SndOutPacketSize )
|
||||
{
|
||||
s32 temp[SndOutPacketSize];
|
||||
s32* s = temp;
|
||||
s16* t = p1;
|
||||
|
||||
buff->ReadSamples( temp );
|
||||
for(int j=0;j<SndOutPacketSize/2;j++)
|
||||
{
|
||||
// DPL2 code here: inputs s[0] and s[1]. outputs t[0] to t[5]
|
||||
dpl2.Convert( t, s[0], s[1] );
|
||||
|
||||
t+=6;
|
||||
s+=2;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef PUBLIC
|
||||
verifyc(buffer->Unlock(oldp1,s1,p2,s2));
|
||||
#else
|
||||
buffer->Unlock(oldp1,s1,p2,s2);
|
||||
#endif
|
||||
|
||||
// Set the write pointer to the beginning of the next block.
|
||||
myLastWrite = (poffset + DSound51::BufferSizeBytes) % (DSound51::BufferSizeBytes*numBuffers);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DSound51_Driver::GetEmptySampleCount() const
|
||||
{
|
||||
DWORD play, write;
|
||||
buffer->GetCurrentPosition( &play, &write );
|
||||
|
||||
// Note: Dsound's write cursor is bogus. Use our own instead:
|
||||
|
||||
int empty = play - myLastWrite;
|
||||
if( empty < 0 )
|
||||
empty = -empty;
|
||||
|
||||
return empty / 6;
|
||||
}
|
||||
|
||||
s32 DSound51::Init(SndBuffer* sb)
|
||||
{
|
||||
if( m_driver == NULL )
|
||||
m_driver = new DSound51_Driver( sb );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DSound51::Close()
|
||||
{
|
||||
SAFE_DELETE_OBJ( m_driver );
|
||||
}
|
||||
|
||||
BOOL CALLBACK DSound51::ConfigProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
|
||||
{
|
||||
int wmId,wmEvent;
|
||||
int tSel=0;
|
||||
|
||||
switch(uMsg)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
|
||||
haveGuid = ! FAILED(GUIDFromString(Config_DSound51.Device,&DevGuid));
|
||||
SendMessage(GetDlgItem(hWnd,IDC_DS_DEVICE),CB_RESETCONTENT,0,0);
|
||||
|
||||
ndevs=0;
|
||||
DirectSoundEnumerate(DSEnumCallback,NULL);
|
||||
|
||||
tSel=-1;
|
||||
for(int i=0;i<ndevs;i++)
|
||||
{
|
||||
SendMessage(GetDlgItem(hWnd,IDC_DS_DEVICE),CB_ADDSTRING,0,(LPARAM)devices[i].name);
|
||||
if(haveGuid && IsEqualGUID(devices[i].guid,DevGuid))
|
||||
{
|
||||
tSel=i;
|
||||
}
|
||||
}
|
||||
|
||||
if(tSel>=0)
|
||||
{
|
||||
SendMessage(GetDlgItem(hWnd,IDC_DS_DEVICE),CB_SETCURSEL,tSel,0);
|
||||
}
|
||||
|
||||
INIT_SLIDER(IDC_LEFT_GAIN_SLIDER,0,512,64,16,8);
|
||||
INIT_SLIDER(IDC_RIGHT_GAIN_SLIDER,0,512,64,16,8);
|
||||
INIT_SLIDER(IDC_RLEFT_GAIN_SLIDER,0,512,64,16,8);
|
||||
INIT_SLIDER(IDC_RRIGHT_GAIN_SLIDER,0,512,64,16,8);
|
||||
INIT_SLIDER(IDC_CENTER_GAIN_SLIDER,0,512,64,16,8);
|
||||
INIT_SLIDER(IDC_LFE_SLIDER,0,512,64,16,8);
|
||||
INIT_SLIDER(IDC_LR_CENTER_SLIDER,0,512,64,16,8);
|
||||
|
||||
AssignSliderValue( hWnd, IDC_LEFT_GAIN_SLIDER, IDC_LEFT_GAIN_EDIT, Config_DSound51.GainL );
|
||||
AssignSliderValue( hWnd, IDC_RIGHT_GAIN_SLIDER, IDC_RIGHT_GAIN_EDIT, Config_DSound51.GainR );
|
||||
AssignSliderValue( hWnd, IDC_RLEFT_GAIN_SLIDER, IDC_RLEFT_GAIN_EDIT, Config_DSound51.GainSL );
|
||||
AssignSliderValue( hWnd, IDC_RRIGHT_GAIN_SLIDER, IDC_RRIGHT_GAIN_EDIT, Config_DSound51.GainSR );
|
||||
AssignSliderValue( hWnd, IDC_CENTER_GAIN_SLIDER, IDC_CENTER_GAIN_EDIT, Config_DSound51.GainC);
|
||||
AssignSliderValue( hWnd, IDC_LFE_SLIDER, IDC_LFE_EDIT, Config_DSound51.GainLFE);
|
||||
AssignSliderValue( hWnd, IDC_LR_CENTER_SLIDER, IDC_LR_CENTER_EDIT, Config_DSound51.AddCLR );
|
||||
|
||||
char temp[128];
|
||||
INIT_SLIDER( IDC_BUFFERS_SLIDER, 2, MAX_BUFFER_COUNT, 2, 1, 1 );
|
||||
SendMessage(GetDlgItem(hWnd,IDC_BUFFERS_SLIDER),TBM_SETPOS,TRUE,Config_DSound51.NumBuffers);
|
||||
sprintf_s(temp, 128, "%d (%d ms latency)",Config_DSound51.NumBuffers, 1000 / (96000 / (Config_DSound51.NumBuffers * BufferSize)));
|
||||
SetWindowText(GetDlgItem(hWnd,IDC_LATENCY_LABEL2),temp);
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
wmId = LOWORD(wParam);
|
||||
wmEvent = HIWORD(wParam);
|
||||
// Parse the menu selections:
|
||||
switch (wmId)
|
||||
{
|
||||
case IDOK:
|
||||
{
|
||||
int i = (int)SendMessage(GetDlgItem(hWnd,IDC_DS_DEVICE),CB_GETCURSEL,0,0);
|
||||
|
||||
if(!devices[i].hasGuid)
|
||||
{
|
||||
Config_DSound51.Device[0]=0; // clear device name to ""
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf_s(Config_DSound51.Device,256,"{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
|
||||
devices[i].guid.Data1,
|
||||
devices[i].guid.Data2,
|
||||
devices[i].guid.Data3,
|
||||
devices[i].guid.Data4[0],
|
||||
devices[i].guid.Data4[1],
|
||||
devices[i].guid.Data4[2],
|
||||
devices[i].guid.Data4[3],
|
||||
devices[i].guid.Data4[4],
|
||||
devices[i].guid.Data4[5],
|
||||
devices[i].guid.Data4[6],
|
||||
devices[i].guid.Data4[7]
|
||||
);
|
||||
|
||||
Config_DSound51.NumBuffers = GetSliderValue( hWnd, IDC_BUFFERS_SLIDER );
|
||||
Config_DSound51.GainL = GetSliderValue( hWnd, IDC_LEFT_GAIN_SLIDER );
|
||||
Config_DSound51.GainR = GetSliderValue( hWnd, IDC_RIGHT_GAIN_SLIDER );
|
||||
Config_DSound51.GainSL = GetSliderValue( hWnd, IDC_RLEFT_GAIN_SLIDER );
|
||||
Config_DSound51.GainSR = GetSliderValue( hWnd, IDC_RRIGHT_GAIN_SLIDER );
|
||||
Config_DSound51.GainLFE = GetSliderValue( hWnd, IDC_LFE_SLIDER );
|
||||
Config_DSound51.GainC = GetSliderValue( hWnd, IDC_CENTER_GAIN_SLIDER );
|
||||
Config_DSound51.AddCLR = GetSliderValue( hWnd, IDC_LR_CENTER_SLIDER );
|
||||
|
||||
if( Config_DSound51.NumBuffers < 2 ) Config_DSound51.NumBuffers = 2;
|
||||
if( Config_DSound51.NumBuffers > MAX_BUFFER_COUNT ) Config_DSound51.NumBuffers = MAX_BUFFER_COUNT;
|
||||
}
|
||||
}
|
||||
EndDialog(hWnd,0);
|
||||
break;
|
||||
case IDCANCEL:
|
||||
EndDialog(hWnd,0);
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_HSCROLL:
|
||||
wmId = LOWORD(wParam);
|
||||
wmEvent = HIWORD(wParam);
|
||||
switch(wmId) {
|
||||
//case TB_ENDTRACK:
|
||||
//case TB_THUMBPOSITION:
|
||||
case TB_LINEUP:
|
||||
case TB_LINEDOWN:
|
||||
case TB_PAGEUP:
|
||||
case TB_PAGEDOWN:
|
||||
wmEvent=(int)SendMessage((HWND)lParam,TBM_GETPOS,0,0);
|
||||
case TB_THUMBTRACK:
|
||||
if( wmEvent < 2 ) wmEvent = 2;
|
||||
if( wmEvent > MAX_BUFFER_COUNT ) wmEvent = MAX_BUFFER_COUNT;
|
||||
SendMessage((HWND)lParam,TBM_SETPOS,TRUE,wmEvent);
|
||||
sprintf_s(temp,128,"%d (%d ms latency)",wmEvent, 1000 / (96000 / (wmEvent * BufferSize)));
|
||||
SetWindowText(GetDlgItem(hWnd,IDC_LATENCY_LABEL2),temp);
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_VSCROLL:
|
||||
HANDLE_SCROLL_MESSAGE(IDC_LEFT_GAIN_SLIDER,IDC_LEFT_GAIN_EDIT);
|
||||
HANDLE_SCROLL_MESSAGE(IDC_RIGHT_GAIN_SLIDER,IDC_RIGHT_GAIN_EDIT);
|
||||
HANDLE_SCROLL_MESSAGE(IDC_RLEFT_GAIN_SLIDER,IDC_RLEFT_GAIN_EDIT);
|
||||
HANDLE_SCROLL_MESSAGE(IDC_RRIGHT_GAIN_SLIDER,IDC_RRIGHT_GAIN_EDIT);
|
||||
HANDLE_SCROLL_MESSAGE(IDC_CENTER_GAIN_SLIDER,IDC_CENTER_GAIN_EDIT);
|
||||
HANDLE_SCROLL_MESSAGE(IDC_LFE_SLIDER,IDC_LFE_EDIT);
|
||||
HANDLE_SCROLL_MESSAGE(IDC_LR_CENTER_SLIDER,IDC_LR_CENTER_EDIT);
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void DSound51::Configure(HWND parent)
|
||||
{
|
||||
INT_PTR ret;
|
||||
ret=DialogBoxParam(hInstance,MAKEINTRESOURCE(IDD_DSOUND51),GetActiveWindow(),(DLGPROC)ConfigProc,1);
|
||||
if(ret==-1)
|
||||
{
|
||||
MessageBoxEx(GetActiveWindow(),"Error Opening the config dialog.","OMG ERROR!",MB_OK,0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
s32 DSound51::Test() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool DSound51::Is51Out() const { return true; }
|
||||
|
||||
int DSound51::GetEmptySampleCount() const
|
||||
{
|
||||
if( m_driver == NULL ) return 0;
|
||||
return m_driver->GetEmptySampleCount();
|
||||
}
|
||||
|
||||
const char* DSound51::GetIdent() const
|
||||
{
|
||||
return "dsound51";
|
||||
}
|
||||
|
||||
const char* DSound51::GetLongName() const
|
||||
{
|
||||
return "DSound 5.1 (Experimental)";
|
||||
}
|
||||
|
||||
SndOutModule *DSound51Out=&DS51;
|
|
@ -0,0 +1,168 @@
|
|||
// Hyperlinks.cpp
|
||||
//
|
||||
// Copyright 2002 Neal Stublen
|
||||
// All rights reserved.
|
||||
//
|
||||
// http://www.awesoftware.com
|
||||
//
|
||||
|
||||
// This taken as found on Codeguru: http://www.codeguru.com/cpp/controls/staticctrl/article.php/c5803
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "Hyperlinks.h"
|
||||
|
||||
|
||||
#define PROP_ORIGINAL_FONT TEXT("_Hyperlink_Original_Font_")
|
||||
#define PROP_ORIGINAL_PROC TEXT("_Hyperlink_Original_Proc_")
|
||||
#define PROP_STATIC_HYPERLINK TEXT("_Hyperlink_From_Static_")
|
||||
#define PROP_UNDERLINE_FONT TEXT("_Hyperlink_Underline_Font_")
|
||||
|
||||
|
||||
LRESULT CALLBACK _HyperlinkParentProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
WNDPROC pfnOrigProc = (WNDPROC) GetProp(hwnd, PROP_ORIGINAL_PROC);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case WM_CTLCOLORSTATIC:
|
||||
{
|
||||
HDC hdc = (HDC) wParam;
|
||||
HWND hwndCtl = (HWND) lParam;
|
||||
|
||||
BOOL fHyperlink = (NULL != GetProp(hwndCtl, PROP_STATIC_HYPERLINK));
|
||||
if (fHyperlink)
|
||||
{
|
||||
LRESULT lr = CallWindowProc(pfnOrigProc, hwnd, message, wParam, lParam);
|
||||
SetTextColor(hdc, RGB(0, 0, 192));
|
||||
return lr;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case WM_DESTROY:
|
||||
{
|
||||
SetWindowLong(hwnd, GWL_WNDPROC, (LONG) pfnOrigProc);
|
||||
RemoveProp(hwnd, PROP_ORIGINAL_PROC);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return CallWindowProc(pfnOrigProc, hwnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
LRESULT CALLBACK _HyperlinkProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
WNDPROC pfnOrigProc = (WNDPROC) GetProp(hwnd, PROP_ORIGINAL_PROC);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case WM_DESTROY:
|
||||
{
|
||||
SetWindowLong(hwnd, GWL_WNDPROC, (LONG) pfnOrigProc);
|
||||
RemoveProp(hwnd, PROP_ORIGINAL_PROC);
|
||||
|
||||
HFONT hOrigFont = (HFONT) GetProp(hwnd, PROP_ORIGINAL_FONT);
|
||||
SendMessage(hwnd, WM_SETFONT, (WPARAM) hOrigFont, 0);
|
||||
RemoveProp(hwnd, PROP_ORIGINAL_FONT);
|
||||
|
||||
HFONT hFont = (HFONT) GetProp(hwnd, PROP_UNDERLINE_FONT);
|
||||
DeleteObject(hFont);
|
||||
RemoveProp(hwnd, PROP_UNDERLINE_FONT);
|
||||
|
||||
RemoveProp(hwnd, PROP_STATIC_HYPERLINK);
|
||||
|
||||
break;
|
||||
}
|
||||
case WM_MOUSEMOVE:
|
||||
{
|
||||
if (GetCapture() != hwnd)
|
||||
{
|
||||
HFONT hFont = (HFONT) GetProp(hwnd, PROP_UNDERLINE_FONT);
|
||||
SendMessage(hwnd, WM_SETFONT, (WPARAM) hFont, FALSE);
|
||||
InvalidateRect(hwnd, NULL, FALSE);
|
||||
SetCapture(hwnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
RECT rect;
|
||||
GetWindowRect(hwnd, &rect);
|
||||
|
||||
POINT pt = { LOWORD(lParam), HIWORD(lParam) };
|
||||
ClientToScreen(hwnd, &pt);
|
||||
|
||||
if (!PtInRect(&rect, pt))
|
||||
{
|
||||
HFONT hFont = (HFONT) GetProp(hwnd, PROP_ORIGINAL_FONT);
|
||||
SendMessage(hwnd, WM_SETFONT, (WPARAM) hFont, FALSE);
|
||||
InvalidateRect(hwnd, NULL, FALSE);
|
||||
ReleaseCapture();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_SETCURSOR:
|
||||
{
|
||||
// Since IDC_HAND is not available on all operating systems,
|
||||
// we will load the arrow cursor if IDC_HAND is not present.
|
||||
HCURSOR hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_HAND));
|
||||
if (NULL == hCursor)
|
||||
{
|
||||
hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
|
||||
}
|
||||
SetCursor(hCursor);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return CallWindowProc(pfnOrigProc, hwnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
BOOL ConvertStaticToHyperlink(HWND hwndCtl)
|
||||
{
|
||||
// Subclass the parent so we can color the controls as we desire.
|
||||
|
||||
HWND hwndParent = GetParent(hwndCtl);
|
||||
if (NULL != hwndParent)
|
||||
{
|
||||
WNDPROC pfnOrigProc = (WNDPROC) GetWindowLong(hwndParent, GWL_WNDPROC);
|
||||
if (pfnOrigProc != _HyperlinkParentProc)
|
||||
{
|
||||
SetProp(hwndParent, PROP_ORIGINAL_PROC, (HANDLE) pfnOrigProc);
|
||||
SetWindowLong(hwndParent, GWL_WNDPROC, (LONG) (WNDPROC) _HyperlinkParentProc);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the control will send notifications.
|
||||
|
||||
DWORD dwStyle = GetWindowLong(hwndCtl, GWL_STYLE);
|
||||
SetWindowLong(hwndCtl, GWL_STYLE, dwStyle | SS_NOTIFY);
|
||||
|
||||
// Subclass the existing control.
|
||||
|
||||
WNDPROC pfnOrigProc = (WNDPROC) GetWindowLong(hwndCtl, GWL_WNDPROC);
|
||||
SetProp(hwndCtl, PROP_ORIGINAL_PROC, (HANDLE) pfnOrigProc);
|
||||
SetWindowLong(hwndCtl, GWL_WNDPROC, (LONG) (WNDPROC) _HyperlinkProc);
|
||||
|
||||
// Create an updated font by adding an underline.
|
||||
|
||||
HFONT hOrigFont = (HFONT) SendMessage(hwndCtl, WM_GETFONT, 0, 0);
|
||||
SetProp(hwndCtl, PROP_ORIGINAL_FONT, (HANDLE) hOrigFont);
|
||||
|
||||
LOGFONT lf;
|
||||
GetObject(hOrigFont, sizeof(lf), &lf);
|
||||
lf.lfUnderline = TRUE;
|
||||
|
||||
HFONT hFont = CreateFontIndirect(&lf);
|
||||
SetProp(hwndCtl, PROP_UNDERLINE_FONT, (HANDLE) hFont);
|
||||
|
||||
// Set a flag on the control so we know what color it should be.
|
||||
|
||||
SetProp(hwndCtl, PROP_STATIC_HYPERLINK, (HANDLE) 1);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ConvertStaticToHyperlink(HWND hwndParent, UINT uiCtlId)
|
||||
{
|
||||
return ConvertStaticToHyperlink(GetDlgItem(hwndParent, uiCtlId));
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
// Hyperlinks.h
|
||||
//
|
||||
// Copyright 2002 Neal Stublen
|
||||
// All rights reserved.
|
||||
//
|
||||
// http://www.awesoftware.com
|
||||
//
|
||||
|
||||
BOOL ConvertStaticToHyperlink(HWND hwndCtl);
|
||||
BOOL ConvertStaticToHyperlink(HWND hwndParent, UINT uiCtlId);
|
|
@ -0,0 +1,196 @@
|
|||
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
* Developed and maintained by the Pcsx2 Development Team.
|
||||
*
|
||||
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
||||
*
|
||||
* 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 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
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "SPU2.h"
|
||||
#include "Dialogs.h"
|
||||
#include "RegTable.h"
|
||||
|
||||
|
||||
static bool debugDialogOpen=false;
|
||||
static HWND hDebugDialog=NULL;
|
||||
|
||||
static BOOL CALLBACK DebugProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
|
||||
{
|
||||
int wmId,wmEvent;
|
||||
|
||||
switch(uMsg)
|
||||
{
|
||||
case WM_PAINT:
|
||||
return FALSE;
|
||||
case WM_INITDIALOG:
|
||||
{
|
||||
debugDialogOpen=true;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
wmId = LOWORD(wParam);
|
||||
wmEvent = HIWORD(wParam);
|
||||
// Parse the menu selections:
|
||||
switch (wmId)
|
||||
{
|
||||
case IDOK:
|
||||
case IDCANCEL:
|
||||
debugDialogOpen=false;
|
||||
EndDialog(hWnd,0);
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#ifndef PUBLIC
|
||||
|
||||
int FillRectangle(HDC dc, int left, int top, int width, int height)
|
||||
{
|
||||
RECT r = { left, top, left+width, top+height };
|
||||
|
||||
return FillRect(dc, &r, (HBRUSH)GetStockObject(DC_BRUSH));
|
||||
}
|
||||
|
||||
BOOL DrawRectangle(HDC dc, int left, int top, int width, int height)
|
||||
{
|
||||
RECT r = { left, top, left+width, top+height };
|
||||
|
||||
POINT p[5] = {
|
||||
{ r.left, r.top },
|
||||
{ r.right, r.top },
|
||||
{ r.right, r.bottom },
|
||||
{ r.left, r.bottom },
|
||||
{ r.left, r.top },
|
||||
};
|
||||
|
||||
return Polyline(dc, p, 5);
|
||||
}
|
||||
|
||||
|
||||
HFONT hf = NULL;
|
||||
int lCount=0;
|
||||
void UpdateDebugDialog()
|
||||
{
|
||||
if(!debugDialogOpen) return;
|
||||
|
||||
lCount++;
|
||||
if(lCount>=(SampleRate/10))
|
||||
{
|
||||
HDC hdc = GetDC(hDebugDialog);
|
||||
|
||||
if(!hf)
|
||||
{
|
||||
hf = CreateFont( 8, 0, 0, 0, 0, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
|
||||
DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, _T("Lucida Console") );
|
||||
}
|
||||
|
||||
SelectObject(hdc,hf);
|
||||
SelectObject(hdc,GetStockObject(DC_BRUSH));
|
||||
SelectObject(hdc,GetStockObject(DC_PEN));
|
||||
|
||||
for(int c=0;c<2;c++)
|
||||
{
|
||||
for(int v=0;v<24;v++)
|
||||
{
|
||||
int IX = 8+256*c;
|
||||
int IY = 8+ 32*v;
|
||||
V_Voice& vc(Cores[c].Voices[v]);
|
||||
V_VoiceDebug& vcd( DebugCores[c].Voices[v] );
|
||||
|
||||
SetDCBrushColor(hdc,RGB( 0, 0, 0));
|
||||
if((vc.ADSR.Phase>0)&&(vc.ADSR.Phase<6))
|
||||
{
|
||||
SetDCBrushColor(hdc,RGB( 0, 0,128));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(vcd.lastStopReason==1)
|
||||
{
|
||||
SetDCBrushColor(hdc,RGB(128, 0, 0));
|
||||
}
|
||||
if(vcd.lastStopReason==2)
|
||||
{
|
||||
SetDCBrushColor(hdc,RGB( 0,128, 0));
|
||||
}
|
||||
}
|
||||
|
||||
FillRectangle(hdc,IX,IY,252,30);
|
||||
|
||||
SetDCPenColor(hdc,RGB( 255, 128, 32));
|
||||
|
||||
DrawRectangle(hdc,IX,IY,252,30);
|
||||
|
||||
SetDCBrushColor (hdc,RGB( 0,255, 0));
|
||||
|
||||
int vl = abs(((vc.VolumeL.Value >> 16) * 24) >> 15);
|
||||
int vr = abs(((vc.VolumeR.Value >> 16) * 24) >> 15);
|
||||
|
||||
FillRectangle(hdc,IX+38,IY+26 - vl, 4, vl);
|
||||
FillRectangle(hdc,IX+42,IY+26 - vr, 4, vr);
|
||||
|
||||
int adsr = (vc.ADSR.Value>>16) * 24 / 32768;
|
||||
|
||||
FillRectangle(hdc,IX+48,IY+26 - adsr, 4, adsr);
|
||||
|
||||
int peak = vcd.displayPeak * 24 / 32768;
|
||||
|
||||
FillRectangle(hdc,IX+56,IY+26 - peak, 4, peak);
|
||||
|
||||
SetTextColor(hdc,RGB( 0,255, 0));
|
||||
SetBkColor (hdc,RGB( 0, 0, 0));
|
||||
|
||||
static wchar_t t[1024];
|
||||
|
||||
swprintf_s(t,_T("%06x"),vc.StartA);
|
||||
TextOut(hdc,IX+4,IY+3,t,6);
|
||||
|
||||
swprintf_s(t,_T("%06x"),vc.NextA);
|
||||
TextOut(hdc,IX+4,IY+12,t,6);
|
||||
|
||||
swprintf_s(t,_T("%06x"),vc.LoopStartA);
|
||||
TextOut(hdc,IX+4,IY+21,t,6);
|
||||
|
||||
vcd.displayPeak = 0;
|
||||
|
||||
if(vcd.lastSetStartA != vc.StartA)
|
||||
{
|
||||
printf(" *** Warning! Core %d Voice %d: StartA should be %06x, and is %06x.\n",
|
||||
c,v,vcd.lastSetStartA,vc.StartA);
|
||||
vcd.lastSetStartA = vc.StartA;
|
||||
}
|
||||
}
|
||||
}
|
||||
ReleaseDC(hDebugDialog,hdc);
|
||||
lCount=0;
|
||||
}
|
||||
|
||||
MSG msg;
|
||||
while(PeekMessage(&msg,hDebugDialog,0,0,PM_REMOVE))
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,425 @@
|
|||
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
* Developed and maintained by the Pcsx2 Development Team.
|
||||
*
|
||||
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
||||
*
|
||||
* 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 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
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#define _WIN32_DCOM
|
||||
#include "spu2.h"
|
||||
#include "dialogs.h"
|
||||
|
||||
#include <dsound.h>
|
||||
|
||||
static ds_device_data devices[32];
|
||||
static int ndevs;
|
||||
static GUID DevGuid; // currently employed GUID.
|
||||
static bool haveGuid;
|
||||
|
||||
class DSound: public SndOutModule
|
||||
{
|
||||
private:
|
||||
static const uint MAX_BUFFER_COUNT = 8;
|
||||
|
||||
static const int PacketsPerBuffer = 1;
|
||||
static const int BufferSize = SndOutPacketSize * PacketsPerBuffer;
|
||||
static const int BufferSizeBytes = BufferSize << 1;
|
||||
|
||||
|
||||
u32 numBuffers; // cached copy of our configuration setting.
|
||||
int channel;
|
||||
int myLastWrite; // last write position, in bytes
|
||||
|
||||
bool dsound_running;
|
||||
HANDLE thread;
|
||||
DWORD tid;
|
||||
|
||||
IDirectSound8* dsound;
|
||||
IDirectSoundBuffer8* buffer;
|
||||
IDirectSoundNotify8* buffer_notify;
|
||||
HANDLE buffer_events[MAX_BUFFER_COUNT];
|
||||
|
||||
WAVEFORMATEX wfx;
|
||||
|
||||
HANDLE waitEvent;
|
||||
|
||||
SndBuffer *buff;
|
||||
|
||||
static DWORD CALLBACK RThread(DSound*obj)
|
||||
{
|
||||
return obj->Thread();
|
||||
}
|
||||
|
||||
DWORD CALLBACK Thread()
|
||||
{
|
||||
|
||||
while( dsound_running )
|
||||
{
|
||||
u32 rv = WaitForMultipleObjects(numBuffers,buffer_events,FALSE,200);
|
||||
|
||||
s16* p1, *oldp1;
|
||||
LPVOID p2;
|
||||
DWORD s1,s2;
|
||||
|
||||
u32 poffset=BufferSizeBytes * rv;
|
||||
|
||||
if( FAILED(buffer->Lock(poffset,BufferSizeBytes,(LPVOID*)&p1,&s1,&p2,&s2,0) ) )
|
||||
{
|
||||
assert( 0 );
|
||||
fputs( " * SPU2 : Directsound Warning > Buffer lock failure. You may need to increase\n\tyour configured DSound buffer count.\n", stderr );
|
||||
continue;
|
||||
}
|
||||
oldp1 = p1;
|
||||
|
||||
for(int p=0; p<PacketsPerBuffer; p++, p1+=SndOutPacketSize )
|
||||
buff->ReadSamples( p1 );
|
||||
|
||||
buffer->Unlock(oldp1,s1,p2,s2);
|
||||
|
||||
// Set the write pointer to the beginning of the next block.
|
||||
myLastWrite = (poffset + BufferSizeBytes) & ~BufferSizeBytes;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public:
|
||||
s32 Init(SndBuffer *sb)
|
||||
{
|
||||
buff = sb;
|
||||
numBuffers = Config_DSoundOut.NumBuffers;
|
||||
|
||||
//
|
||||
// Initialize DSound
|
||||
//
|
||||
GUID cGuid;
|
||||
|
||||
try
|
||||
{
|
||||
if( Config_DSoundOut.Device.empty() )
|
||||
throw std::runtime_error( "screw it" );
|
||||
|
||||
// Convert from unicode to ANSI:
|
||||
char guid[256];
|
||||
sprintf_s( guid, "%S", Config_DSoundOut.Device.c_str() );
|
||||
|
||||
if( (FAILED(GUIDFromString( guid, &cGuid ))) ||
|
||||
FAILED( DirectSoundCreate8(&cGuid,&dsound,NULL) ) )
|
||||
throw std::runtime_error( "try again?" );
|
||||
}
|
||||
catch( std::runtime_error& )
|
||||
{
|
||||
// if the GUID failed, just open up the default dsound driver:
|
||||
if( FAILED(DirectSoundCreate8(NULL,&dsound,NULL) ) )
|
||||
throw std::runtime_error( "DirectSound failed to initialize!" );
|
||||
}
|
||||
|
||||
if( FAILED(dsound->SetCooperativeLevel(GetDesktopWindow(),DSSCL_PRIORITY)) )
|
||||
throw std::runtime_error( "DirectSound Error: Cooperative level could not be set." );
|
||||
|
||||
IDirectSoundBuffer* buffer_;
|
||||
DSBUFFERDESC desc;
|
||||
|
||||
// Set up WAV format structure.
|
||||
|
||||
memset(&wfx, 0, sizeof(WAVEFORMATEX));
|
||||
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wfx.nSamplesPerSec = SampleRate;
|
||||
wfx.nChannels=2;
|
||||
wfx.wBitsPerSample = 16;
|
||||
wfx.nBlockAlign = 2*2;
|
||||
wfx.nAvgBytesPerSec = SampleRate * wfx.nBlockAlign;
|
||||
wfx.cbSize=0;
|
||||
|
||||
// Set up DSBUFFERDESC structure.
|
||||
|
||||
memset(&desc, 0, sizeof(DSBUFFERDESC));
|
||||
desc.dwSize = sizeof(DSBUFFERDESC);
|
||||
desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY;// _CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY;
|
||||
desc.dwBufferBytes = BufferSizeBytes * numBuffers;
|
||||
desc.lpwfxFormat = &wfx;
|
||||
|
||||
desc.dwFlags |= DSBCAPS_LOCSOFTWARE;
|
||||
desc.dwFlags |= DSBCAPS_GLOBALFOCUS;
|
||||
|
||||
if( FAILED(dsound->CreateSoundBuffer(&desc,&buffer_,0) ) ||
|
||||
FAILED(buffer_->QueryInterface(IID_IDirectSoundBuffer8,(void**)&buffer)) )
|
||||
throw std::runtime_error( "DirectSound Error: Interface could not be queried." );
|
||||
|
||||
buffer_->Release();
|
||||
|
||||
verifyc( buffer->QueryInterface(IID_IDirectSoundNotify8,(void**)&buffer_notify) );
|
||||
|
||||
DSBPOSITIONNOTIFY not[MAX_BUFFER_COUNT];
|
||||
|
||||
for(u32 i=0;i<numBuffers;i++)
|
||||
{
|
||||
// [Air] note: wfx.nBlockAlign modifier was *10 -- seems excessive to me but maybe
|
||||
// it was needed for some quirky driver? Theoretically we want the notification as soon
|
||||
// as possible after the buffer has finished playing.
|
||||
|
||||
buffer_events[i]=CreateEvent(NULL,FALSE,FALSE,NULL);
|
||||
not[i].dwOffset=(wfx.nBlockAlign*2 + BufferSizeBytes*(i+1))%desc.dwBufferBytes;
|
||||
not[i].hEventNotify=buffer_events[i];
|
||||
}
|
||||
|
||||
buffer_notify->SetNotificationPositions(numBuffers,not);
|
||||
|
||||
LPVOID p1=0,p2=0;
|
||||
DWORD s1=0,s2=0;
|
||||
|
||||
verifyc(buffer->Lock(0,desc.dwBufferBytes,&p1,&s1,&p2,&s2,0));
|
||||
assert(p2==0);
|
||||
memset(p1,0,s1);
|
||||
verifyc(buffer->Unlock(p1,s1,p2,s2));
|
||||
|
||||
//Play the buffer !
|
||||
verifyc(buffer->Play(0,0,DSBPLAY_LOOPING));
|
||||
|
||||
// Start Thread
|
||||
myLastWrite = 0;
|
||||
dsound_running=true;
|
||||
thread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)RThread,this,0,&tid);
|
||||
SetThreadPriority(thread,THREAD_PRIORITY_TIME_CRITICAL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Close()
|
||||
{
|
||||
// Stop Thread
|
||||
fprintf(stderr," * SPU2: Waiting for DSound thread to finish...");
|
||||
dsound_running=false;
|
||||
|
||||
WaitForSingleObject(thread,INFINITE);
|
||||
CloseHandle(thread);
|
||||
|
||||
fprintf(stderr," Done.\n");
|
||||
|
||||
//
|
||||
// Clean up
|
||||
//
|
||||
if( buffer != NULL )
|
||||
{
|
||||
buffer->Stop();
|
||||
|
||||
for(u32 i=0;i<numBuffers;i++)
|
||||
{
|
||||
if( buffer_events[i] != NULL )
|
||||
CloseHandle(buffer_events[i]);
|
||||
buffer_events[i] = NULL;
|
||||
}
|
||||
|
||||
SAFE_RELEASE( buffer_notify );
|
||||
SAFE_RELEASE( buffer );
|
||||
}
|
||||
|
||||
SAFE_RELEASE( dsound );
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static BOOL CALLBACK DSEnumCallback( LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext )
|
||||
{
|
||||
devices[ndevs].name = lpcstrDescription;
|
||||
|
||||
if(lpGuid)
|
||||
{
|
||||
devices[ndevs].guid = *lpGuid;
|
||||
devices[ndevs].hasGuid = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
devices[ndevs].hasGuid = false;
|
||||
}
|
||||
ndevs++;
|
||||
|
||||
if(ndevs<32) return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOL CALLBACK ConfigProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
|
||||
{
|
||||
int wmId,wmEvent;
|
||||
int tSel=0;
|
||||
|
||||
switch(uMsg)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
{
|
||||
wchar_t temp[128];
|
||||
|
||||
char temp2[192];
|
||||
sprintf_s( temp2, "%S", Config_DSoundOut.Device.c_str() );
|
||||
haveGuid = ! FAILED(GUIDFromString(temp2,&DevGuid));
|
||||
SendMessage(GetDlgItem(hWnd,IDC_DS_DEVICE),CB_RESETCONTENT,0,0);
|
||||
|
||||
ndevs=0;
|
||||
DirectSoundEnumerate( DSEnumCallback, NULL );
|
||||
|
||||
tSel=-1;
|
||||
for(int i=0;i<ndevs;i++)
|
||||
{
|
||||
SendMessage(GetDlgItem(hWnd,IDC_DS_DEVICE),CB_ADDSTRING,0,(LPARAM)devices[i].name.c_str());
|
||||
if(haveGuid && IsEqualGUID(devices[i].guid,DevGuid))
|
||||
{
|
||||
tSel=i;
|
||||
}
|
||||
}
|
||||
|
||||
if(tSel>=0)
|
||||
{
|
||||
SendMessage(GetDlgItem(hWnd,IDC_DS_DEVICE),CB_SETCURSEL,tSel,0);
|
||||
}
|
||||
|
||||
INIT_SLIDER( IDC_BUFFERS_SLIDER, 2, MAX_BUFFER_COUNT, 2, 1, 1 );
|
||||
SendMessage(GetDlgItem(hWnd,IDC_BUFFERS_SLIDER),TBM_SETPOS,TRUE,Config_DSoundOut.NumBuffers);
|
||||
swprintf_s(temp, _T("%d (%d ms latency)"),Config_DSoundOut.NumBuffers, 1000 / (96000 / (Config_DSoundOut.NumBuffers * BufferSize)));
|
||||
SetWindowText(GetDlgItem(hWnd,IDC_LATENCY_LABEL),temp);
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
{
|
||||
wchar_t temp[128];
|
||||
|
||||
wmId = LOWORD(wParam);
|
||||
wmEvent = HIWORD(wParam);
|
||||
// Parse the menu selections:
|
||||
switch (wmId)
|
||||
{
|
||||
case IDOK:
|
||||
{
|
||||
int i = (int)SendMessage(GetDlgItem(hWnd,IDC_DS_DEVICE),CB_GETCURSEL,0,0);
|
||||
|
||||
if(!devices[i].hasGuid)
|
||||
{
|
||||
Config_DSoundOut.Device[0] = 0; // clear device name to ""
|
||||
}
|
||||
else
|
||||
{
|
||||
swprintf_s(temp, _T("{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}"),
|
||||
devices[i].guid.Data1,
|
||||
devices[i].guid.Data2,
|
||||
devices[i].guid.Data3,
|
||||
devices[i].guid.Data4[0],
|
||||
devices[i].guid.Data4[1],
|
||||
devices[i].guid.Data4[2],
|
||||
devices[i].guid.Data4[3],
|
||||
devices[i].guid.Data4[4],
|
||||
devices[i].guid.Data4[5],
|
||||
devices[i].guid.Data4[6],
|
||||
devices[i].guid.Data4[7]
|
||||
);
|
||||
Config_DSoundOut.Device = temp;
|
||||
}
|
||||
|
||||
Config_DSoundOut.NumBuffers = (int)SendMessage( GetDlgItem( hWnd, IDC_BUFFERS_SLIDER ), TBM_GETPOS, 0, 0 );
|
||||
|
||||
if( Config_DSoundOut.NumBuffers < 2 ) Config_DSoundOut.NumBuffers = 2;
|
||||
if( Config_DSoundOut.NumBuffers > MAX_BUFFER_COUNT ) Config_DSoundOut.NumBuffers = MAX_BUFFER_COUNT;
|
||||
}
|
||||
EndDialog(hWnd,0);
|
||||
break;
|
||||
case IDCANCEL:
|
||||
EndDialog(hWnd,0);
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_HSCROLL:
|
||||
{
|
||||
wmId = LOWORD(wParam);
|
||||
wmEvent = HIWORD(wParam);
|
||||
switch(wmId) {
|
||||
//case TB_ENDTRACK:
|
||||
//case TB_THUMBPOSITION:
|
||||
case TB_LINEUP:
|
||||
case TB_LINEDOWN:
|
||||
case TB_PAGEUP:
|
||||
case TB_PAGEDOWN:
|
||||
wmEvent=(int)SendMessage((HWND)lParam,TBM_GETPOS,0,0);
|
||||
case TB_THUMBTRACK:
|
||||
{
|
||||
wchar_t temp[128];
|
||||
if( wmEvent < 2 ) wmEvent = 2;
|
||||
if( wmEvent > MAX_BUFFER_COUNT ) wmEvent = MAX_BUFFER_COUNT;
|
||||
SendMessage((HWND)lParam,TBM_SETPOS,TRUE,wmEvent);
|
||||
swprintf_s(temp,_T("%d (%d ms latency)"),wmEvent, 1000 / (96000 / (wmEvent * BufferSize)));
|
||||
SetWindowText(GetDlgItem(hWnd,IDC_LATENCY_LABEL),temp);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void Configure(HWND parent)
|
||||
{
|
||||
INT_PTR ret;
|
||||
ret=DialogBoxParam(hInstance,MAKEINTRESOURCE(IDD_DSOUND),GetActiveWindow(),(DLGPROC)ConfigProc,1);
|
||||
if(ret==-1)
|
||||
{
|
||||
MessageBoxEx(GetActiveWindow(),_T("Error Opening the config dialog."),_T("OMG ERROR!"),MB_OK,0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool Is51Out() const { return false; }
|
||||
|
||||
s32 Test() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GetEmptySampleCount() const
|
||||
{
|
||||
DWORD play, write;
|
||||
buffer->GetCurrentPosition( &play, &write );
|
||||
|
||||
// Note: Dsound's write cursor is bogus. Use our own instead:
|
||||
|
||||
int empty = play - myLastWrite;
|
||||
if( empty < 0 )
|
||||
empty = -empty;
|
||||
|
||||
return empty / 2;
|
||||
}
|
||||
|
||||
const wchar_t* GetIdent() const
|
||||
{
|
||||
return _T("dsound");
|
||||
}
|
||||
|
||||
const wchar_t* GetLongName() const
|
||||
{
|
||||
return _T("DirectSound (nice)");
|
||||
}
|
||||
|
||||
} DS;
|
||||
|
||||
SndOutModule *DSoundOut=&DS;
|
|
@ -0,0 +1,370 @@
|
|||
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
* Developed and maintained by the Pcsx2 Development Team.
|
||||
*
|
||||
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
||||
*
|
||||
* 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 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
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#define _WIN32_DCOM
|
||||
|
||||
#include "spu2.h"
|
||||
#include "dialogs.h"
|
||||
#include <MMReg.h>
|
||||
#include <xaudio2.h>
|
||||
|
||||
|
||||
static const double SndOutNormalizer = (double)(1UL<<(SndOutVolumeShift+16));
|
||||
|
||||
class XAudio2Mod: public SndOutModule
|
||||
{
|
||||
private:
|
||||
static const int PacketsPerBuffer = 1;
|
||||
static const int MAX_BUFFER_COUNT = 3;
|
||||
|
||||
class BaseStreamingVoice : public IXAudio2VoiceCallback
|
||||
{
|
||||
protected:
|
||||
SndBuffer* m_sndout;
|
||||
IXAudio2SourceVoice* pSourceVoice;
|
||||
s16* qbuffer;
|
||||
|
||||
const uint m_nBuffers;
|
||||
const uint m_nChannels;
|
||||
const uint m_BufferSize;
|
||||
const uint m_BufferSizeBytes;
|
||||
|
||||
CRITICAL_SECTION cs;
|
||||
|
||||
public:
|
||||
int GetEmptySampleCount() const
|
||||
{
|
||||
XAUDIO2_VOICE_STATE state;
|
||||
pSourceVoice->GetState( &state );
|
||||
return state.SamplesPlayed & (m_BufferSize-1);
|
||||
}
|
||||
|
||||
virtual ~BaseStreamingVoice()
|
||||
{
|
||||
IXAudio2SourceVoice* killMe = pSourceVoice;
|
||||
pSourceVoice = NULL;
|
||||
killMe->FlushSourceBuffers();
|
||||
EnterCriticalSection( &cs );
|
||||
killMe->DestroyVoice();
|
||||
SAFE_DELETE_ARRAY( qbuffer );
|
||||
LeaveCriticalSection( &cs );
|
||||
DeleteCriticalSection( &cs );
|
||||
}
|
||||
|
||||
BaseStreamingVoice( SndBuffer* sb, uint numChannels ) :
|
||||
m_sndout( sb ),
|
||||
m_nBuffers( Config_XAudio2.NumBuffers ),
|
||||
m_nChannels( numChannels ),
|
||||
m_BufferSize( SndOutPacketSize/2 * m_nChannels * PacketsPerBuffer ),
|
||||
m_BufferSizeBytes( m_BufferSize * sizeof(s16) )
|
||||
{
|
||||
}
|
||||
|
||||
virtual void Init( IXAudio2* pXAudio2 ) = 0;
|
||||
|
||||
protected:
|
||||
// Several things must be initialized separate of the constructor, due to the fact that
|
||||
// virtual calls can't be made from the constructor's context.
|
||||
void _init( IXAudio2* pXAudio2, uint chanConfig )
|
||||
{
|
||||
WAVEFORMATEX wfx;
|
||||
|
||||
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wfx.nSamplesPerSec = SampleRate;
|
||||
wfx.nChannels = m_nChannels;
|
||||
wfx.wBitsPerSample = 16;
|
||||
wfx.nBlockAlign = 2*m_nChannels;
|
||||
wfx.nAvgBytesPerSec = SampleRate * wfx.nBlockAlign;
|
||||
wfx.cbSize = 0;
|
||||
|
||||
//wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
//wfx.dwChannelMask = chanConfig;
|
||||
|
||||
//
|
||||
// Create an XAudio2 voice to stream this wave
|
||||
//
|
||||
HRESULT hr;
|
||||
if( FAILED(hr = pXAudio2->CreateSourceVoice( &pSourceVoice, &wfx,
|
||||
XAUDIO2_VOICE_NOSRC, 1.0f, this ) ) )
|
||||
{
|
||||
SysMessage( "Error %#X creating source voice\n", hr );
|
||||
SAFE_RELEASE( pXAudio2 );
|
||||
return;
|
||||
}
|
||||
|
||||
InitializeCriticalSection( &cs );
|
||||
EnterCriticalSection( &cs );
|
||||
|
||||
pSourceVoice->FlushSourceBuffers();
|
||||
pSourceVoice->Start( 0, 0 );
|
||||
|
||||
qbuffer = new s16[m_nBuffers * m_BufferSize];
|
||||
ZeroMemory( qbuffer, m_BufferSizeBytes * m_nBuffers );
|
||||
|
||||
// Start some buffers.
|
||||
|
||||
for( uint i=0; i<m_nBuffers; i++ )
|
||||
{
|
||||
XAUDIO2_BUFFER buf = {0};
|
||||
buf.AudioBytes = m_BufferSizeBytes;
|
||||
buf.pContext = &qbuffer[i*m_BufferSize];
|
||||
buf.pAudioData = (BYTE*)buf.pContext;
|
||||
pSourceVoice->SubmitSourceBuffer( &buf );
|
||||
}
|
||||
|
||||
LeaveCriticalSection( &cs );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
class StreamingVoice_Stereo : public BaseStreamingVoice
|
||||
{
|
||||
public:
|
||||
StreamingVoice_Stereo( SndBuffer* sb, IXAudio2* pXAudio2 ) :
|
||||
BaseStreamingVoice( sb, 2 )
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~StreamingVoice_Stereo() {}
|
||||
|
||||
void Init( IXAudio2* pXAudio2 )
|
||||
{
|
||||
_init( pXAudio2, SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT );
|
||||
}
|
||||
|
||||
protected:
|
||||
STDMETHOD_(void, OnVoiceProcessingPassStart) () {}
|
||||
STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) { };
|
||||
STDMETHOD_(void, OnVoiceProcessingPassEnd) () {}
|
||||
STDMETHOD_(void, OnStreamEnd) () {}
|
||||
STDMETHOD_(void, OnBufferStart) ( void* ) {}
|
||||
STDMETHOD_(void, OnBufferEnd) ( void* context )
|
||||
{
|
||||
EnterCriticalSection( &cs );
|
||||
|
||||
// All of these checks are necessary because XAudio2 is wonky shizat.
|
||||
if( pSourceVoice == NULL || context == NULL ) return;
|
||||
|
||||
s16* qb = (s16*)context;
|
||||
|
||||
for(int p=0; p<PacketsPerBuffer; p++, qb+=SndOutPacketSize )
|
||||
m_sndout->ReadSamples( qb );
|
||||
|
||||
XAUDIO2_BUFFER buf = {0};
|
||||
buf.AudioBytes = m_BufferSizeBytes;
|
||||
buf.pAudioData = (BYTE*)context;
|
||||
buf.pContext = context;
|
||||
|
||||
pSourceVoice->SubmitSourceBuffer( &buf );
|
||||
LeaveCriticalSection( &cs );
|
||||
}
|
||||
STDMETHOD_(void, OnLoopEnd) ( void* ) {}
|
||||
STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error) { };
|
||||
|
||||
};
|
||||
|
||||
class StreamingVoice_Surround51 : public BaseStreamingVoice
|
||||
{
|
||||
public:
|
||||
//LPF_data m_lpf_left;
|
||||
//LPF_data m_lpf_right;
|
||||
|
||||
s32 buffer[2 * SndOutPacketSize * PacketsPerBuffer];
|
||||
|
||||
StreamingVoice_Surround51( SndBuffer* sb, IXAudio2* pXAudio2 ) :
|
||||
BaseStreamingVoice( sb, 6 )
|
||||
//m_lpf_left( Config_XAudio2.LowpassLFE, SampleRate ),
|
||||
//m_lpf_right( Config_XAudio2.LowpassLFE, SampleRate )
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~StreamingVoice_Surround51() {}
|
||||
|
||||
void Init( IXAudio2* pXAudio2 )
|
||||
{
|
||||
_init( pXAudio2, SPEAKER_5POINT1 );
|
||||
}
|
||||
|
||||
protected:
|
||||
STDMETHOD_(void, OnVoiceProcessingPassStart) () {}
|
||||
STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) { };
|
||||
STDMETHOD_(void, OnVoiceProcessingPassEnd) () {}
|
||||
STDMETHOD_(void, OnStreamEnd) () {}
|
||||
STDMETHOD_(void, OnBufferStart) ( void* ) {}
|
||||
STDMETHOD_(void, OnBufferEnd) ( void* context )
|
||||
{
|
||||
EnterCriticalSection( &cs );
|
||||
|
||||
// All of these checks are necessary because XAudio2 is wonky shizat.
|
||||
if( pSourceVoice == NULL || context == NULL ) return;
|
||||
|
||||
s16* qb = (s16*)context;
|
||||
|
||||
for(int p=0; p<PacketsPerBuffer; p++ )
|
||||
{
|
||||
m_sndout->ReadSamples( buffer );
|
||||
const s32* src = buffer;
|
||||
|
||||
for( int i=0; i<SndOutPacketSize/2; i++, qb+=6, src+=2 )
|
||||
{
|
||||
// Left and right Front!
|
||||
qb[0] = SndScaleVol( src[0] );
|
||||
qb[1] = SndScaleVol( src[1] );
|
||||
|
||||
// Center and Subwoofer/LFE -->
|
||||
// This method is simple and sounds nice. It relies on the speaker/soundcard
|
||||
// systems do to their own low pass / crossover. Manual lowpass is wasted effort
|
||||
// and can't match solid state results anyway.
|
||||
|
||||
qb[2] = qb[3] = (src[0] + src[1]) >> (SndOutVolumeShift+1);
|
||||
|
||||
// Left and right rear!
|
||||
qb[4] = SndScaleVol( src[0] );
|
||||
qb[5] = SndScaleVol( src[1] );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
XAUDIO2_BUFFER buf = { 0 };
|
||||
buf.AudioBytes = m_BufferSizeBytes;
|
||||
buf.pAudioData = (BYTE*)context;
|
||||
buf.pContext = context;
|
||||
|
||||
pSourceVoice->SubmitSourceBuffer( &buf );
|
||||
LeaveCriticalSection( &cs );
|
||||
}
|
||||
STDMETHOD_(void, OnLoopEnd) ( void* ) {}
|
||||
STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error) { };
|
||||
|
||||
};
|
||||
|
||||
IXAudio2* pXAudio2;
|
||||
IXAudio2MasteringVoice* pMasteringVoice;
|
||||
BaseStreamingVoice* voiceContext;
|
||||
|
||||
public:
|
||||
|
||||
s32 Init( SndBuffer *sb )
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
//
|
||||
// Initialize XAudio2
|
||||
//
|
||||
CoInitializeEx( NULL, COINIT_MULTITHREADED );
|
||||
|
||||
UINT32 flags = 0;
|
||||
#ifdef _DEBUG
|
||||
flags |= XAUDIO2_DEBUG_ENGINE;
|
||||
#endif
|
||||
|
||||
if ( FAILED(hr = XAudio2Create( &pXAudio2, flags ) ) )
|
||||
{
|
||||
SysMessage( "Failed to init XAudio2 engine: %#X\n", hr );
|
||||
CoUninitialize();
|
||||
return -1;
|
||||
}
|
||||
|
||||
XAUDIO2_DEVICE_DETAILS deviceDetails;
|
||||
pXAudio2->GetDeviceDetails( 0, &deviceDetails );
|
||||
|
||||
//
|
||||
// Create a mastering voice
|
||||
//
|
||||
if ( FAILED(hr = pXAudio2->CreateMasteringVoice( &pMasteringVoice, 0, SampleRate ) ) )
|
||||
{
|
||||
SysMessage( "Failed creating mastering voice: %#X\n", hr );
|
||||
SAFE_RELEASE( pXAudio2 );
|
||||
CoUninitialize();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( Config_XAudio2.ExpandTo51 && deviceDetails.OutputFormat.Format.nChannels >= 6 )
|
||||
{
|
||||
ConLog( "* SPU2 > 5.1 speaker expansion enabled." );
|
||||
voiceContext = new StreamingVoice_Surround51( sb, pXAudio2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
voiceContext = new StreamingVoice_Stereo( sb, pXAudio2 );
|
||||
}
|
||||
|
||||
voiceContext->Init( pXAudio2 );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Close()
|
||||
{
|
||||
// Clean up?
|
||||
// All XAudio2 interfaces are released when the engine is destroyed,
|
||||
// but being tidy never hurt.
|
||||
|
||||
// Actually it can hurt. As of DXSDK Aug 2008, doing a full cleanup causes
|
||||
// XA2 on Vista to crash. Even if you copy/paste code directly from Microsoft.
|
||||
// But doing no cleanup at all causes XA2 under XP to crash. So after much trial
|
||||
// and error we found a happy compromise as follows:
|
||||
|
||||
SAFE_DELETE_OBJ( voiceContext );
|
||||
|
||||
voiceContext = NULL;
|
||||
|
||||
if( pMasteringVoice != NULL )
|
||||
pMasteringVoice->DestroyVoice();
|
||||
|
||||
pMasteringVoice = NULL;
|
||||
|
||||
SAFE_RELEASE( pXAudio2 );
|
||||
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
virtual void Configure(HWND parent)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool Is51Out() const { return false; }
|
||||
|
||||
s32 Test() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GetEmptySampleCount() const
|
||||
{
|
||||
if( voiceContext == NULL ) return 0;
|
||||
return voiceContext->GetEmptySampleCount();
|
||||
}
|
||||
|
||||
const wchar_t* GetIdent() const
|
||||
{
|
||||
return _T("xaudio2");
|
||||
}
|
||||
|
||||
const wchar_t* GetLongName() const
|
||||
{
|
||||
return _T("XAudio 2 (Recommended)");
|
||||
}
|
||||
|
||||
} XA2;
|
||||
|
||||
SndOutModule *XAudio2Out = &XA2;
|
|
@ -0,0 +1,279 @@
|
|||
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
* Developed and maintained by the Pcsx2 Development Team.
|
||||
*
|
||||
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
||||
*
|
||||
* 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 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
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "spu2.h"
|
||||
#include "dialogs.h"
|
||||
#include <windows.h>
|
||||
|
||||
|
||||
class WaveOutModule: public SndOutModule
|
||||
{
|
||||
private:
|
||||
static const uint MAX_BUFFER_COUNT = 8;
|
||||
|
||||
static const int PacketsPerBuffer = (1024 / SndOutPacketSize);
|
||||
static const int BufferSize = SndOutPacketSize*PacketsPerBuffer;
|
||||
static const int BufferSizeBytes = BufferSize << 1;
|
||||
|
||||
u32 numBuffers;
|
||||
HWAVEOUT hwodevice;
|
||||
WAVEFORMATEX wformat;
|
||||
WAVEHDR whbuffer[MAX_BUFFER_COUNT];
|
||||
|
||||
s16* qbuffer;
|
||||
|
||||
#define QBUFFER(x) (qbuffer + BufferSize * (x))
|
||||
|
||||
bool waveout_running;
|
||||
HANDLE thread;
|
||||
DWORD tid;
|
||||
|
||||
SndBuffer *buff;
|
||||
|
||||
wchar_t ErrText[256];
|
||||
|
||||
static DWORD CALLBACK RThread(WaveOutModule*obj)
|
||||
{
|
||||
return obj->Thread();
|
||||
}
|
||||
|
||||
DWORD CALLBACK Thread()
|
||||
{
|
||||
while( waveout_running )
|
||||
{
|
||||
bool didsomething = false;
|
||||
for(u32 i=0;i<numBuffers;i++)
|
||||
{
|
||||
if(!(whbuffer[i].dwFlags & WHDR_DONE) ) continue;
|
||||
|
||||
WAVEHDR *buf=whbuffer+i;
|
||||
|
||||
buf->dwBytesRecorded = buf->dwBufferLength;
|
||||
|
||||
s16 *t = (s16*)buf->lpData;
|
||||
for(int p=0; p<PacketsPerBuffer; p++, t+=SndOutPacketSize )
|
||||
buff->ReadSamples( t );
|
||||
|
||||
whbuffer[i].dwFlags&=~WHDR_DONE;
|
||||
waveOutWrite(hwodevice,buf,sizeof(WAVEHDR));
|
||||
didsomething = true;
|
||||
}
|
||||
|
||||
if( didsomething )
|
||||
Sleep(1);
|
||||
else
|
||||
Sleep(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public:
|
||||
s32 Init(SndBuffer *sb)
|
||||
{
|
||||
buff = sb;
|
||||
numBuffers = Config_WaveOut.NumBuffers;
|
||||
|
||||
MMRESULT woores;
|
||||
|
||||
if (Test()) return -1;
|
||||
|
||||
wformat.wFormatTag=WAVE_FORMAT_PCM;
|
||||
wformat.nSamplesPerSec=SampleRate;
|
||||
wformat.wBitsPerSample=16;
|
||||
wformat.nChannels=2;
|
||||
wformat.nBlockAlign=((wformat.wBitsPerSample * wformat.nChannels) / 8);
|
||||
wformat.nAvgBytesPerSec=(wformat.nSamplesPerSec * wformat.nBlockAlign);
|
||||
wformat.cbSize=0;
|
||||
|
||||
qbuffer=new s16[BufferSize*numBuffers];
|
||||
|
||||
woores = waveOutOpen(&hwodevice,WAVE_MAPPER,&wformat,0,0,0);
|
||||
if (woores != MMSYSERR_NOERROR)
|
||||
{
|
||||
waveOutGetErrorText(woores,(wchar_t *)&ErrText,255);
|
||||
SysMessage("WaveOut Error: %s",ErrText);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for(u32 i=0;i<numBuffers;i++)
|
||||
{
|
||||
whbuffer[i].dwBufferLength=BufferSizeBytes;
|
||||
whbuffer[i].dwBytesRecorded=BufferSizeBytes;
|
||||
whbuffer[i].dwFlags=0;
|
||||
whbuffer[i].dwLoops=0;
|
||||
whbuffer[i].dwUser=0;
|
||||
whbuffer[i].lpData=(LPSTR)QBUFFER(i);
|
||||
whbuffer[i].lpNext=0;
|
||||
whbuffer[i].reserved=0;
|
||||
waveOutPrepareHeader(hwodevice,whbuffer+i,sizeof(WAVEHDR));
|
||||
whbuffer[i].dwFlags|=WHDR_DONE; //avoid deadlock
|
||||
}
|
||||
|
||||
// Start Thread
|
||||
// [Air]: The waveout code does not use wait objects, so setting a time critical
|
||||
// priority level is a bad idea. Standard priority will do fine. The buffer will get the
|
||||
// love it needs and won't suck resources idling pointlessly. Just don't try to
|
||||
// run it in uber-low-latency mode.
|
||||
waveout_running = true;
|
||||
thread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)RThread,this,0,&tid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Close()
|
||||
{
|
||||
// Stop Thread
|
||||
fprintf(stderr," * SPU2: Waiting for waveOut thread to finish...");
|
||||
waveout_running=false;
|
||||
|
||||
WaitForSingleObject(thread,INFINITE);
|
||||
CloseHandle(thread);
|
||||
|
||||
fprintf(stderr," Done.\n");
|
||||
|
||||
//
|
||||
// Clean up
|
||||
//
|
||||
waveOutReset(hwodevice);
|
||||
for(u32 i=0;i<numBuffers;i++)
|
||||
{
|
||||
waveOutUnprepareHeader(hwodevice,&whbuffer[i],sizeof(WAVEHDR));
|
||||
}
|
||||
waveOutClose(hwodevice);
|
||||
|
||||
SAFE_DELETE_ARRAY( qbuffer );
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static BOOL CALLBACK ConfigProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
|
||||
{
|
||||
int wmId,wmEvent;
|
||||
int tSel=0;
|
||||
|
||||
switch(uMsg)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
|
||||
wchar_t temp[128];
|
||||
INIT_SLIDER( IDC_BUFFERS_SLIDER, 3, MAX_BUFFER_COUNT, 2, 1, 1 );
|
||||
SendMessage(GetDlgItem(hWnd,IDC_BUFFERS_SLIDER),TBM_SETPOS,TRUE,Config_WaveOut.NumBuffers);
|
||||
swprintf_s(temp, 128, _T("%d (%d ms latency)"),Config_WaveOut.NumBuffers, 1000 / (96000 / (Config_WaveOut.NumBuffers * BufferSize)));
|
||||
SetWindowText(GetDlgItem(hWnd,IDC_LATENCY_LABEL),temp);
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
wmId = LOWORD(wParam);
|
||||
wmEvent = HIWORD(wParam);
|
||||
// Parse the menu selections:
|
||||
switch (wmId)
|
||||
{
|
||||
case IDOK:
|
||||
{
|
||||
Config_WaveOut.NumBuffers = (int)SendMessage( GetDlgItem( hWnd, IDC_BUFFERS_SLIDER ), TBM_GETPOS, 0, 0 );
|
||||
|
||||
if( Config_WaveOut.NumBuffers < 3 ) Config_WaveOut.NumBuffers = 3;
|
||||
if( Config_WaveOut.NumBuffers > MAX_BUFFER_COUNT ) Config_WaveOut.NumBuffers = MAX_BUFFER_COUNT;
|
||||
}
|
||||
EndDialog(hWnd,0);
|
||||
break;
|
||||
case IDCANCEL:
|
||||
EndDialog(hWnd,0);
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_HSCROLL:
|
||||
wmId = LOWORD(wParam);
|
||||
wmEvent = HIWORD(wParam);
|
||||
switch(wmId) {
|
||||
//case TB_ENDTRACK:
|
||||
//case TB_THUMBPOSITION:
|
||||
case TB_LINEUP:
|
||||
case TB_LINEDOWN:
|
||||
case TB_PAGEUP:
|
||||
case TB_PAGEDOWN:
|
||||
wmEvent=(int)SendMessage((HWND)lParam,TBM_GETPOS,0,0);
|
||||
case TB_THUMBTRACK:
|
||||
if( wmEvent < 3 ) wmEvent = 3;
|
||||
if( wmEvent > MAX_BUFFER_COUNT ) wmEvent = MAX_BUFFER_COUNT;
|
||||
SendMessage((HWND)lParam,TBM_SETPOS,TRUE,wmEvent);
|
||||
swprintf_s(temp, _T("%d (%d ms latency)"),wmEvent, 1000 / (96000 / (wmEvent * BufferSize)));
|
||||
SetWindowText(GetDlgItem(hWnd,IDC_LATENCY_LABEL),temp);
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void Configure(HWND parent)
|
||||
{
|
||||
INT_PTR ret;
|
||||
ret=DialogBoxParam(hInstance,MAKEINTRESOURCE(IDD_WAVEOUT),GetActiveWindow(),(DLGPROC)ConfigProc,1);
|
||||
if(ret==-1)
|
||||
{
|
||||
MessageBoxEx(GetActiveWindow(), _T("Error Opening the config dialog."), _T("OMG ERROR!"), MB_OK, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool Is51Out() const { return false; }
|
||||
|
||||
s32 Test() const
|
||||
{
|
||||
if (waveOutGetNumDevs() == 0) {
|
||||
SysMessage("No waveOut Devices Present\n"); return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GetEmptySampleCount() const
|
||||
{
|
||||
int result = 0;
|
||||
for(int i=0;i<MAX_BUFFER_COUNT;i++)
|
||||
{
|
||||
result += (whbuffer[i].dwFlags & WHDR_DONE) ? BufferSize : 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const wchar_t* GetIdent() const
|
||||
{
|
||||
return _T("waveout");
|
||||
}
|
||||
|
||||
const wchar_t* GetLongName() const
|
||||
{
|
||||
return _T("waveOut (Laggy)");
|
||||
}
|
||||
|
||||
} WO;
|
||||
|
||||
SndOutModule *WaveOut=&WO;
|
|
@ -0,0 +1,59 @@
|
|||
; * SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
; * Developed and maintained by the Pcsx2 Development Team.
|
||||
; *
|
||||
; * Originally based on SPU2ghz v1.9 beta, by David Quintana.
|
||||
; *
|
||||
; * 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 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
|
||||
; * 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
|
||||
|
||||
; SPU2-X.def : Declares the module parameters for the DLL.
|
||||
|
||||
;LIBRARY "SPU2-X"
|
||||
|
||||
EXPORTS
|
||||
; Explicit exports can go here
|
||||
PS2EgetLibType @2
|
||||
PS2EgetLibName @3
|
||||
PS2EgetLibVersion2 @4
|
||||
SPU2init @5
|
||||
SPU2shutdown @6
|
||||
SPU2open @7
|
||||
SPU2close @8
|
||||
SPU2write @9
|
||||
SPU2read @10
|
||||
SPU2readDMA4Mem @11
|
||||
SPU2writeDMA4Mem @12
|
||||
SPU2readDMA7Mem @13
|
||||
SPU2writeDMA7Mem @14
|
||||
SPU2async @15
|
||||
SPU2interruptDMA4 @16
|
||||
SPU2interruptDMA7 @17
|
||||
SPU2irqCallback @18
|
||||
|
||||
SPU2setupRecording @19
|
||||
|
||||
SPU2configure @20
|
||||
SPU2test @21
|
||||
SPU2about @22
|
||||
|
||||
SPU2ReadMemAddr @23
|
||||
SPU2WriteMemAddr @24
|
||||
|
||||
SPU2setClockPtr @25
|
||||
|
||||
SPU2setDMABaseAddr @26
|
||||
|
||||
SPU2replay = s2r_replay @27
|
||||
|
||||
SPU2freeze @28
|
|
@ -0,0 +1,299 @@
|
|||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "afxresmw.h"
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (U.S.) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
#ifdef _WIN32
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
#endif //_WIN32
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Dialog
|
||||
//
|
||||
|
||||
IDD_ABOUT DIALOGEX 0, 0, 291, 223
|
||||
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "About SPU2-X"
|
||||
FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "I've Seen Enough",IDOK,91,204,111,14
|
||||
CTEXT "Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]",IDC_STATIC,9,184,273,11
|
||||
CONTROL 114,IDC_STATIC,"Static",SS_BITMAP,45,5,203,64,WS_EX_CLIENTEDGE
|
||||
CTEXT "A Sound Processing Unit 2 plugin for Playstation 2 emulators.",IDC_STATIC,9,71,273,10
|
||||
CTEXT "For updates and news, visit the following links:",IDC_STATIC,9,98,273,10
|
||||
CTEXT "The SPU2-X project is derived from SPU2ghz v1.9beta released in 2008 and later modified and upgraded by the Pcsx2 Playground Team.",IDC_STATIC,25,161,242,17
|
||||
CONTROL "",IDC_STATIC,"Static",SS_BLACKFRAME,20,157,252,41
|
||||
CTEXT "Compile Date, Revision, Etc.",IDC_LABEL_VERSION_INFO,9,80,273,17
|
||||
CTEXT "Pcsx2 Official Svn Repository @ Googlecode",IDC_LINK_GOOGLECODE,9,121,273,10,SS_NOPREFIX | SS_NOTIFY
|
||||
CTEXT "Pcsx2 Official Website and Forums",IDC_LINK_WEBSITE,9,110,273,10,SS_NOPREFIX | SS_NOTIFY
|
||||
CTEXT "Brought to you by the collaborative efforts of the Pcsx2 Development Team.",IDC_STATIC,9,141,273,10
|
||||
END
|
||||
|
||||
IDD_CONFIG DIALOGEX 0, 0, 319, 276
|
||||
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "SPU2-X Settings"
|
||||
FONT 8, "MS Shell Dlg", 400, 0, 0x0
|
||||
BEGIN
|
||||
PUSHBUTTON "OK",IDOK,200,256,54,15,NOT WS_TABSTOP
|
||||
PUSHBUTTON "Cancel",IDCANCEL,259,256,54,15,NOT WS_TABSTOP
|
||||
GROUPBOX "Mixing Settings",IDC_STATIC,6,5,130,99
|
||||
GROUPBOX "Output Settings",IDC_STATIC,142,5,172,247
|
||||
COMBOBOX IDC_OUTPUT,164,26,126,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
PUSHBUTTON "Configure...",IDC_OUTCONF,236,40,54,12
|
||||
COMBOBOX IDC_INTERPOLATE,14,26,114,84,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
CONTROL "Slider2",IDC_LATENCY_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,167,73,116,10
|
||||
CONTROL "Disable Time-stretching",IDC_TS_DISABLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,96,121,11
|
||||
CONTROL "Use a Winamp DSP plugin",IDC_DSP_ENABLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,149,216,126,11
|
||||
CHECKBOX "Disable Effects Processing",IDC_EFFECTS_DISABLE,14,47,112,10,NOT WS_TABSTOP
|
||||
LTEXT "Latency:",IDC_STATIC,181,62,33,9,NOT WS_GROUP
|
||||
LTEXT "Interpolation:",IDC_STATIC,12,16,55,10,NOT WS_GROUP
|
||||
LTEXT "Module:",IDC_STATIC,161,16,50,9,NOT WS_GROUP
|
||||
LTEXT "(speedup!) Skips reverb and stereo delay effects processing, but won't sound as good in most games.",IDC_STATIC,26,60,104,36
|
||||
LTEXT "(currently requires manual configuration via the ini file)",IDC_STATIC,162,229,146,20
|
||||
CTEXT "100 ms (avg)",IDC_LATENCY_LABEL,215,62,58,9
|
||||
LTEXT "(small speedup) Timestretching helps reduce latency and usually eliminates audio skips.",IDC_STATIC,162,109,146,20
|
||||
CONTROL 116,IDC_STATIC,"Static",SS_BITMAP,7,196,117,52,WS_EX_CLIENTEDGE
|
||||
PUSHBUTTON "Advanced...",IDC_OPEN_CONFIG_SOUNDTOUCH,219,130,84,12
|
||||
PUSHBUTTON "Configure Debug Options...",IDC_OPEN_CONFIG_DEBUG,14,131,108,14
|
||||
CHECKBOX "Enable Debug Options",IDC_DEBUG,14,117,104,10,NOT WS_TABSTOP
|
||||
GROUPBOX "",IDC_STATIC,6,107,129,46
|
||||
CONTROL "Disable Audio Expansion",IDC_EXPANSION_DISABLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,150,152,135,10
|
||||
LTEXT "Audio expansion detects extended speaker setups like 5.1 and 2.1 audio, and expand the SPU2's stereo stream so that all speakers are used. Disable this if the detection causes problems on your sound system.",IDC_STATIC,162,164,146,44
|
||||
END
|
||||
|
||||
IDD_DEBUG DIALOGEX 0, 0, 326, 525
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "SPU2-X Debug"
|
||||
FONT 9, "Lucida Console", 400, 0, 0x0
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "Close",IDOK,269,504,50,14
|
||||
END
|
||||
|
||||
IDD_DSOUND DIALOGEX 0, 0, 182, 121
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "DirectSound Output Module Settings"
|
||||
FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "OK",IDOK,67,103,50,14
|
||||
PUSHBUTTON "Cancel",IDCANCEL,127,103,50,14
|
||||
COMBOBOX IDC_DS_DEVICE,4,15,173,62,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
LTEXT "DirectSound Device",IDC_STATIC,4,3,63,8
|
||||
LTEXT "Number of Buffers",IDC_STATIC,4,40,61,11
|
||||
CONTROL "",IDC_BUFFERS_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,74,48,103,10
|
||||
LTEXT "Increase the buffer count if you are experiencing loopy or studdery audio even when games run at high FPS.",IDC_STATIC,8,66,169,27
|
||||
CTEXT "8 (80 ms latency)",IDC_LATENCY_LABEL,78,37,95,11
|
||||
END
|
||||
|
||||
IDD_WAVEOUT DIALOGEX 0, 0, 170, 122
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "waveOut Output Module Settings"
|
||||
FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "OK",IDOK,52,104,50,14
|
||||
PUSHBUTTON "Cancel",IDCANCEL,115,104,50,14
|
||||
COMBOBOX IDC_DS_DEVICE,4,15,161,62,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
LTEXT "waveOut Device",IDC_STATIC,4,3,54,8
|
||||
LTEXT "Number of Buffers",IDC_STATIC,4,39,61,11
|
||||
CONTROL "",IDC_BUFFERS_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,71,48,94,10
|
||||
LTEXT "Use extra buffers if you are experiencing loopy or studdery audio even when games run at high FPS.",IDC_STATIC,8,66,151,27
|
||||
CTEXT "8 (80 ms latency)",IDC_LATENCY_LABEL,70,37,95,11
|
||||
END
|
||||
|
||||
IDD_XAUDIO2 DIALOGEX 0, 0, 202, 116
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "XAudio2 Output Module Settings"
|
||||
FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "OK",IDOK,88,98,50,14
|
||||
PUSHBUTTON "Cancel",IDCANCEL,147,98,50,14
|
||||
COMBOBOX IDC_DS_DEVICE,4,15,193,62,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
|
||||
LTEXT "XAudio Device",IDC_STATIC,4,3,46,8
|
||||
CONTROL "Use Triple Buffering",IDC_XA2_TRIBLE_BUFFER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,40,109,10
|
||||
LTEXT "Increases latency by a few milliseconds. Enable triple buffering you are experiencing loopy or studdery audio even when games run at high FPS.",IDC_STATIC,20,53,175,35
|
||||
END
|
||||
|
||||
IDD_CONFIG_SOUNDTOUCH DIALOGEX 0, 0, 206, 223
|
||||
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "Soundtouch Advanced Configuration - SPU2-X"
|
||||
FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "OK",IDOK,50,205,50,14
|
||||
PUSHBUTTON "Cancel",IDCANCEL,104,205,50,14
|
||||
CTEXT "These are advanced configuration options for fine tuning time stretching behavior. Larger values are better for slowdown, while smaller values are better for speed-up (better than 60fps).\n\nAll options are in milliseconds (ms).",IDC_STATIC,5,5,196,52
|
||||
CONTROL "",IDC_SEQLEN_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,50,98,105,10
|
||||
CTEXT "Sequence Length",IDC_STATIC,72,88,64,9
|
||||
CONTROL "",IDC_SEEKWIN_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,50,136,105,10
|
||||
CTEXT "Seekwindow Size",IDC_STATIC,70,125,66,9
|
||||
CONTROL "",IDC_OVERLAP_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,50,170,105,10
|
||||
CTEXT "Overlap",IDC_STATIC,86,162,34,9
|
||||
LTEXT "30",IDC_STATIC,50,112,9,8
|
||||
LTEXT "90",IDC_STATIC,146,112,9,8
|
||||
LTEXT "10",IDC_STATIC,50,149,9,8
|
||||
LTEXT "32",IDC_STATIC,146,149,9,8
|
||||
LTEXT "3",IDC_STATIC,52,184,8,8
|
||||
LTEXT "15",IDC_STATIC,146,184,9,8
|
||||
PUSHBUTTON "Reset Defaults",IDC_RESET_DEFAULTS,61,62,82,12
|
||||
END
|
||||
|
||||
IDD_CONFIG_DEBUG DIALOGEX 0, 0, 292, 154
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "SPU2-X Debugging Options"
|
||||
FONT 8, "MS Shell Dlg", 400, 0, 0x1
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "OK",IDOK,190,135,46,14
|
||||
PUSHBUTTON "Cancel",IDCANCEL,241,135,46,14
|
||||
GROUPBOX "",IDC_STATIC,5,5,135,93
|
||||
GROUPBOX "Logfile-only Logs",IDC_STATIC,151,5,136,53
|
||||
GROUPBOX "Dumps (on close)",IDC_STATIC,151,60,135,54
|
||||
CONTROL "Show In Console",IDC_MSGSHOW,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,5,69,10
|
||||
CHECKBOX "KeyOn/Off Events",IDC_MSGKEY,17,18,74,9,NOT WS_TABSTOP
|
||||
CONTROL "Voice Stop Events",IDC_MSGVOICE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,31,75,9
|
||||
CONTROL "DMA Operations",IDC_MSGDMA,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,44,68,9
|
||||
CONTROL "AutoDMA Operations",IDC_MSGADMA,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,25,57,83,9
|
||||
CONTROL "Buffer Over/Underruns",IDC_DBG_OVERRUNS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,70,97,9
|
||||
CONTROL "ADPCM Cache Statistics",IDC_DBG_CACHE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,83,114,9
|
||||
CHECKBOX "Dump Core and Voice State",IDC_DUMPCORE,159,74,104,10,NOT WS_TABSTOP
|
||||
CHECKBOX "Dump Memory Contents",IDC_DUMPMEM,159,87,91,10,NOT WS_TABSTOP
|
||||
CHECKBOX "Dump Register Data",IDC_DUMPREGS,159,100,80,10,NOT WS_TABSTOP
|
||||
CHECKBOX "Log Register/DMA Actions",IDC_LOGREGS,159,18,101,10,WS_GROUP | NOT WS_TABSTOP
|
||||
CHECKBOX "Log DMA Writes",IDC_LOGDMA,159,31,68,10,NOT WS_TABSTOP
|
||||
CHECKBOX "Log Audio Output",IDC_LOGWAVE,159,44,71,10,NOT WS_TABSTOP
|
||||
LTEXT "Note: This is a non-devel build. For performance reasons, some logging options are disabled; and only available in devel/debug builds.",IDC_MSG_PUBLIC_BUILD,9,118,174,30
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DESIGNINFO
|
||||
//
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
GUIDELINES DESIGNINFO
|
||||
BEGIN
|
||||
IDD_ABOUT, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 9
|
||||
RIGHTMARGIN, 282
|
||||
TOPMARGIN, 5
|
||||
BOTTOMMARGIN, 218
|
||||
END
|
||||
|
||||
IDD_CONFIG, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 6
|
||||
RIGHTMARGIN, 314
|
||||
BOTTOMMARGIN, 271
|
||||
END
|
||||
|
||||
IDD_DEBUG, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 7
|
||||
RIGHTMARGIN, 319
|
||||
TOPMARGIN, 7
|
||||
BOTTOMMARGIN, 518
|
||||
END
|
||||
|
||||
IDD_DSOUND, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 4
|
||||
RIGHTMARGIN, 177
|
||||
TOPMARGIN, 3
|
||||
BOTTOMMARGIN, 117
|
||||
END
|
||||
|
||||
IDD_WAVEOUT, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 4
|
||||
RIGHTMARGIN, 165
|
||||
TOPMARGIN, 3
|
||||
BOTTOMMARGIN, 118
|
||||
END
|
||||
|
||||
IDD_XAUDIO2, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 4
|
||||
RIGHTMARGIN, 197
|
||||
TOPMARGIN, 3
|
||||
BOTTOMMARGIN, 112
|
||||
END
|
||||
|
||||
IDD_CONFIG_SOUNDTOUCH, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 5
|
||||
RIGHTMARGIN, 201
|
||||
TOPMARGIN, 5
|
||||
BOTTOMMARGIN, 218
|
||||
END
|
||||
|
||||
IDD_CONFIG_DEBUG, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 5
|
||||
RIGHTMARGIN, 287
|
||||
TOPMARGIN, 5
|
||||
BOTTOMMARGIN, 149
|
||||
END
|
||||
END
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Bitmap
|
||||
//
|
||||
|
||||
IDB_SPU2X BITMAP "..\\..\\spu2-x.bmp"
|
||||
IDB_SPU2X_SMALL BITMAP "..\\..\\spu2-x-sm.bmp"
|
||||
#endif // English (U.S.) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Spanish resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ESN)
|
||||
#ifdef _WIN32
|
||||
LANGUAGE LANG_SPANISH, SUBLANG_SPANISH_MODERN
|
||||
#pragma code_page(1252)
|
||||
#endif //_WIN32
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""afxresmw.h""\r\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
#endif // Spanish resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,114 @@
|
|||
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
|
||||
* Developed and maintained by the Pcsx2 Development Team.
|
||||
*
|
||||
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
|
||||
*
|
||||
* 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 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
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include "dialogs.h"
|
||||
|
||||
int SendDialogMsg( HWND hwnd, int dlgId, UINT code, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
return SendMessage( GetDlgItem(hwnd,dlgId), code, wParam, lParam );
|
||||
}
|
||||
|
||||
HRESULT GUIDFromString(const char *str, LPGUID guid)
|
||||
{
|
||||
// "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"
|
||||
|
||||
struct T{ // this is a hack because for some reason sscanf writes too much :/
|
||||
GUID g;
|
||||
int k;
|
||||
} t;
|
||||
|
||||
int r = sscanf_s(str,"{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
|
||||
&t.g.Data1,
|
||||
&t.g.Data2,
|
||||
&t.g.Data3,
|
||||
&t.g.Data4[0],
|
||||
&t.g.Data4[1],
|
||||
&t.g.Data4[2],
|
||||
&t.g.Data4[3],
|
||||
&t.g.Data4[4],
|
||||
&t.g.Data4[5],
|
||||
&t.g.Data4[6],
|
||||
&t.g.Data4[7]
|
||||
);
|
||||
|
||||
if(r!=11) return -1;
|
||||
|
||||
*guid = t.g;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__forceinline void Verifyc(HRESULT hr, const char* fn)
|
||||
{
|
||||
if(FAILED(hr))
|
||||
{
|
||||
assert( 0 );
|
||||
throw std::runtime_error( "DirectSound returned an error from %s" );
|
||||
}
|
||||
}
|
||||
|
||||
void AssignSliderValue( HWND idcwnd, HWND hwndDisplay, int value )
|
||||
{
|
||||
value = min( max( value, 0 ), 512 );
|
||||
SendMessage(idcwnd,TBM_SETPOS,TRUE,value);
|
||||
|
||||
wchar_t tbox[32];
|
||||
swprintf_s( tbox, _T("%d"), value );
|
||||
SetWindowText( hwndDisplay, tbox );
|
||||
}
|
||||
|
||||
void AssignSliderValue( HWND hWnd, int idc, int editbox, int value )
|
||||
{
|
||||
AssignSliderValue( GetDlgItem( hWnd, idc ), GetDlgItem( hWnd, editbox ), value );
|
||||
}
|
||||
|
||||
// Generic slider/scroller message handler. This is succient so long as you
|
||||
// don't need some kind of secondary event handling functionality, such as
|
||||
// updating a custom label.
|
||||
BOOL DoHandleScrollMessage( HWND hwndDisplay, WPARAM wParam, LPARAM lParam )
|
||||
{
|
||||
int wmId = LOWORD(wParam);
|
||||
int wmEvent = HIWORD(wParam);
|
||||
static char temp[64];
|
||||
switch(wmId)
|
||||
{
|
||||
//case TB_ENDTRACK:
|
||||
//case TB_THUMBPOSITION:
|
||||
case TB_LINEUP:
|
||||
case TB_LINEDOWN:
|
||||
case TB_PAGEUP:
|
||||
case TB_PAGEDOWN:
|
||||
wmEvent = (int)SendMessage((HWND)lParam,TBM_GETPOS,0,0);
|
||||
case TB_THUMBTRACK:
|
||||
AssignSliderValue( (HWND)lParam, hwndDisplay, wmEvent );
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int GetSliderValue( HWND hWnd, int idc )
|
||||
{
|
||||
int retval = (int)SendMessage( GetDlgItem( hWnd, idc ), TBM_GETPOS, 0, 0 );
|
||||
return GetClamped( retval, 0, 512 );
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/* Pcsx2 - Pc Ps2 Emulator
|
||||
* Copyright (C) 2002-2008 Pcsx2 Team
|
||||
* Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <winuser.h>
|
||||
|
||||
#define IDC_STATIC (-1)
|
|
@ -0,0 +1,174 @@
|
|||
//GiGaHeRz's SPU2 Driver
|
||||
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
|
||||
//
|
||||
//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
|
||||
//
|
||||
#include "spu2.h"
|
||||
|
||||
extern "C" {
|
||||
#include "dsp.h"
|
||||
|
||||
typedef winampDSPHeader* (*pWinampDSPGetHeader2)();
|
||||
}
|
||||
|
||||
HMODULE hLib = NULL;
|
||||
pWinampDSPGetHeader2 pGetHeader = NULL;
|
||||
winampDSPHeader* pHeader = NULL;
|
||||
|
||||
winampDSPModule* pModule = NULL;
|
||||
|
||||
HWND hTemp;
|
||||
|
||||
#define USE_A_THREAD
|
||||
#ifdef USE_A_THREAD
|
||||
|
||||
HANDLE hUpdateThread;
|
||||
DWORD UpdateThreadId;
|
||||
|
||||
bool running;
|
||||
|
||||
DWORD WINAPI DspUpdateThread(PVOID param);
|
||||
#endif
|
||||
s32 DspLoadLibrary(wchar_t *fileName, int modNum)
|
||||
#ifdef USE_A_THREAD
|
||||
{
|
||||
if(!dspPluginEnabled) return -1;
|
||||
|
||||
running=true;
|
||||
hUpdateThread = CreateThread(NULL,0,DspUpdateThread,NULL,0,&UpdateThreadId);
|
||||
return (hUpdateThread==INVALID_HANDLE_VALUE);
|
||||
}
|
||||
|
||||
s32 DspLoadLibrary2( wchar_t *fileName, int modNum )
|
||||
#endif
|
||||
{
|
||||
if( !dspPluginEnabled ) return -1;
|
||||
|
||||
hLib = LoadLibraryW( fileName );
|
||||
if(!hLib)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
pGetHeader = (pWinampDSPGetHeader2)GetProcAddress(hLib,"winampDSPGetHeader2");
|
||||
|
||||
if(!pGetHeader)
|
||||
{
|
||||
FreeLibrary(hLib);
|
||||
hLib=NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
pHeader = pGetHeader();
|
||||
|
||||
pModule = pHeader->getModule(modNum);
|
||||
|
||||
if(!pModule)
|
||||
{
|
||||
pGetHeader=NULL;
|
||||
pHeader=NULL;
|
||||
FreeLibrary(hLib);
|
||||
hLib=NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
pModule->hDllInstance = hLib;
|
||||
pModule->hwndParent=0;
|
||||
pModule->Init(pModule);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DspCloseLibrary()
|
||||
#ifdef USE_A_THREAD
|
||||
{
|
||||
if(!dspPluginEnabled) return ;
|
||||
|
||||
PostThreadMessage(UpdateThreadId,WM_QUIT,0,0);
|
||||
running=false;
|
||||
if(WaitForSingleObject(hUpdateThread,1000)==WAIT_TIMEOUT)
|
||||
{
|
||||
TerminateThread(hUpdateThread,1);
|
||||
}
|
||||
}
|
||||
|
||||
void DspCloseLibrary2()
|
||||
#endif
|
||||
{
|
||||
if(!dspPluginEnabled) return ;
|
||||
|
||||
if(hLib)
|
||||
{
|
||||
pModule->Quit(pModule);
|
||||
FreeLibrary(hLib);
|
||||
}
|
||||
pModule=NULL;
|
||||
pHeader=NULL;
|
||||
pGetHeader=NULL;
|
||||
hLib=NULL;
|
||||
}
|
||||
|
||||
int DspProcess(s16 *buffer, int samples)
|
||||
{
|
||||
if(!dspPluginEnabled) return samples;
|
||||
|
||||
if(hLib)
|
||||
{
|
||||
return pModule->ModifySamples(pModule,buffer,samples,16,2,SampleRate);
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
|
||||
void DspUpdate()
|
||||
#ifdef USE_A_THREAD
|
||||
{
|
||||
}
|
||||
|
||||
DWORD WINAPI DspUpdateThread(PVOID param)
|
||||
{
|
||||
if( !dspPluginEnabled ) return -1;
|
||||
|
||||
if( DspLoadLibrary2( dspPlugin, dspPluginModule ) )
|
||||
return -1;
|
||||
|
||||
MSG msg;
|
||||
while(running)
|
||||
{
|
||||
GetMessage(&msg,0,0,0);
|
||||
if((msg.hwnd==NULL)&&(msg.message==WM_QUIT))
|
||||
{
|
||||
break;
|
||||
}
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
DspCloseLibrary2();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
{
|
||||
if(!dspPluginEnabled) return;
|
||||
|
||||
MSG msg;
|
||||
while(PeekMessage(&msg,0,0,0,PM_REMOVE))
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,56 @@
|
|||
//GiGaHeRz's SPU2 Driver
|
||||
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
|
||||
//
|
||||
//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
|
||||
//
|
||||
|
||||
// DSP plugin interface
|
||||
|
||||
// notes:
|
||||
// any window that remains in foreground should optimally pass unused
|
||||
// keystrokes to the parent (winamp's) window, so that the user
|
||||
// can still control it. As for storing configuration,
|
||||
// Configuration data should be stored in <dll directory>\plugin.ini
|
||||
// (look at the vis plugin for configuration code)
|
||||
|
||||
typedef struct winampDSPModule {
|
||||
char *description; // description
|
||||
HWND hwndParent; // parent window (filled in by calling app)
|
||||
HINSTANCE hDllInstance; // instance handle to this DLL (filled in by calling app)
|
||||
|
||||
void (*Config)(struct winampDSPModule *this_mod); // configuration dialog (if needed)
|
||||
int (*Init)(struct winampDSPModule *this_mod); // 0 on success, creates window, etc (if needed)
|
||||
|
||||
// modify waveform samples: returns number of samples to actually write
|
||||
// (typically numsamples, but no more than twice numsamples, and no less than half numsamples)
|
||||
// numsamples should always be at least 128. should, but I'm not sure
|
||||
int (*ModifySamples)(struct winampDSPModule *this_mod, short int *samples, int numsamples, int bps, int nch, int srate);
|
||||
|
||||
void (*Quit)(struct winampDSPModule *this_mod); // called when unloading
|
||||
|
||||
void *userData; // user data, optional
|
||||
} winampDSPModule;
|
||||
|
||||
typedef struct {
|
||||
int version; // DSP_HDRVER
|
||||
char *description; // description of library
|
||||
winampDSPModule* (*getModule)(int); // module retrieval function
|
||||
} winampDSPHeader;
|
||||
|
||||
// exported symbols
|
||||
typedef winampDSPHeader* (*winampDSPGetHeaderType)();
|
||||
|
||||
// header version: 0x20 == 0.20 == winamp 2.0
|
||||
#define DSP_HDRVER 0x20
|
|
@ -0,0 +1,68 @@
|
|||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by Spu2-X.rc
|
||||
//
|
||||
#define IDD_CONFIG 9
|
||||
#define IDD_DEBUG 105
|
||||
#define IDD_DSOUND 106
|
||||
#define IDD_WAVEOUT 109
|
||||
#define IDD_ABOUT 110
|
||||
#define IDD_XAUDIO2 111
|
||||
#define IDB_SPU2X 114
|
||||
#define IDB_SPU2X_SMALL 116
|
||||
#define IDD_CONFIG_SOUNDTOUCH 117
|
||||
#define IDD_CONFIG_DEBUG 118
|
||||
#define IDC_EFFECTS 1001
|
||||
#define IDC_EFFECTS_DISABLE 1001
|
||||
#define IDC_DUMPREGS 1003
|
||||
#define IDC_DUMPMEM 1004
|
||||
#define IDC_DUMPCORE 1005
|
||||
#define IDC_LOGWAVE 1006
|
||||
#define IDC_LOGDMA 1007
|
||||
#define IDC_LOGREGS 1008
|
||||
#define IDC_DEBUG 1010
|
||||
#define IDC_INTERPOLATE 1011
|
||||
#define IDC_OUTPUT 1013
|
||||
#define IDC_BUFFERS_SLIDER 1014
|
||||
#define IDC_TEXT 1019
|
||||
#define IDC_MSGKEY 1020
|
||||
#define IDC_MSGDMA 1021
|
||||
#define IDC_MSGADMA 1022
|
||||
#define IDC_MSGVOICE 1023
|
||||
#define IDC_MSGSHOW 1024
|
||||
#define IDC_OUTCONF 1028
|
||||
#define IDC_DSP_ENABLE 1029
|
||||
#define IDC_TS_ENABLE 1030
|
||||
#define IDC_TS_DISABLE 1030
|
||||
#define IDC_DS_DEVICE 1032
|
||||
#define IDC_DBG_OVERRUNS 1038
|
||||
#define IDC_DBG_CACHE 1039
|
||||
#define IDC_DEBUG_GROUP 1040
|
||||
#define IDC_LATENCY_SLIDER 1041
|
||||
#define IDC_LATENCY_LABEL 1042
|
||||
#define ICD_LR_CENTER_SLIDER 1042
|
||||
#define IDC_SEQLEN_SLIDER 1043
|
||||
#define IDC_SEEKWIN_SLIDER 1044
|
||||
#define IDC_OVERLAP_SLIDER 1045
|
||||
#define IDC_VOLBOOST 1047
|
||||
#define IDC_MSG_PUBLIC_BUILD 1048
|
||||
#define IDC_XA2_TRIBLE_BUFFER 1050
|
||||
#define IDC_CHECK2 1051
|
||||
#define IDC_EXPANSION_DISABLE 1051
|
||||
#define IDC_LABEL_VERSION_INFO 1054
|
||||
#define IDC_LINK_WEBSITE 1055
|
||||
#define IDC_LINK_GOOGLECODE 1056
|
||||
#define IDC_RESET_DEFAULTS 1057
|
||||
#define IDC_OPEN_CONFIG_SOUNDTOUCH 1058
|
||||
#define IDC_OPEN_CONFIG_DEBUG 1059
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 119
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1060
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="windows-1250"?>
|
||||
<VisualStudioPropertySheet
|
||||
ProjectType="Visual C++"
|
||||
Version="8.00"
|
||||
Name="plugins"
|
||||
OutputDirectory="$(ProjectDir)\bin"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
>
|
||||
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
CommandLine=".\vsprops\preBuild.cmd "$(ProjectDir)\.." "$(ProjectDir)vsprops""
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
CommandLine=".\vsprops\postBuild.cmd "$(TargetPath)" "$(SolutionDir)" "$(TargetName)" $(TargetExt)"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalIncludeDirectories="..\;"$(ProjectDir)";..\common;..\3rdparty"
|
||||
PreprocessorDefinitions="__WIN32__;WIN32;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE"
|
||||
StructMemberAlignment="5"
|
||||
RuntimeTypeInfo="false"
|
||||
WarningLevel="3"
|
||||
DebugInformationFormat="3"
|
||||
CompileAs="0"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
LinkIncremental="1"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="2"
|
||||
/>
|
||||
</VisualStudioPropertySheet>
|
|
@ -0,0 +1,24 @@
|
|||
@echo off
|
||||
rem
|
||||
rem Usage: postBuild.cmd SourcePath DestDir DestFile DestExt
|
||||
rem
|
||||
rem SourcePath - $(TargetPath) - Fully qualified path of the generated target file.
|
||||
rem DestDir - $(SolutionDir) - Directory of the destination, usually the same as the solution.
|
||||
rem DestFile - Base filename of the target/dest, without extension!
|
||||
rem DestExt - Extension of the target/dest!
|
||||
|
||||
set pcsxoutdir=%2\bin\plugins
|
||||
set pcsxoutname=%pcsxoutdir%\%3%4
|
||||
set pcsxnewname=%pcsxoutdir%\%3-r$WCREV$$WCMODS?m:$%4
|
||||
|
||||
IF NOT EXIST %pcsxoutdir% (
|
||||
md %pcsxoutdir%
|
||||
)
|
||||
|
||||
copy /Y %1 %pcsxoutname%
|
||||
copy /Y %1 %pcsxnewname%
|
||||
|
||||
if ERRORLEVEL 0 (
|
||||
echo Target copied to %pcsxnewname%
|
||||
)
|
||||
exit 0
|
|
@ -0,0 +1,21 @@
|
|||
@echo off
|
||||
rem
|
||||
rem Usage: postBuild.cmd SourcePath DestDir DestFile DestExt
|
||||
rem
|
||||
rem SourcePath - $(TargetPath) - Fully qualified path of the generated target file.
|
||||
rem DestDir - $(SolutionDir) - Directory of the destination, usually the same as the solution.
|
||||
rem DestFile - Base filename of the target/dest, without extension!
|
||||
rem DestExt - Extension of the target/dest!
|
||||
|
||||
set pcsxoutdir=%2\bin\plugins
|
||||
set pcsxoutname=%pcsxoutdir%%3%4
|
||||
|
||||
IF NOT EXIST %pcsxoutdir% (
|
||||
md %pcsxoutdir%
|
||||
)
|
||||
|
||||
copy /Y %1 %pcsxoutname%
|
||||
if ERRORLEVEL 0 (
|
||||
echo Target copied to %pcsxoutname%
|
||||
)
|
||||
set ERRORLEVEL=0
|
|
@ -0,0 +1,20 @@
|
|||
@echo off
|
||||
|
||||
rem Usage: preBuild.cmd ProjectSrcDir VspropsDir
|
||||
rem
|
||||
rem ProjectSrcDir - $(ProjectDir)\.. - Directory of project source code.
|
||||
rem VspropsDir - $(PrjectDir)\vsprops - Directory of this script and its counterparts.
|
||||
|
||||
SubWCRev.exe %1 %2\svnrev_template.h %1\svnrev.h
|
||||
if %ERRORLEVEL% NEQ 0 (
|
||||
echo Automatic revision update unavailable, using generic template instead.
|
||||
echo You can safely ignore this message - see svnrev.h for details.
|
||||
copy /Y %2\svnrev_unknown.h %1\svnrev.h
|
||||
copy /Y %2\postBuild.unknown %2\postBuild.cmd
|
||||
) else (
|
||||
SubWCRev.exe %1 %2\postBuild.tmpl %2\postBuild.cmd
|
||||
)
|
||||
|
||||
rem Always return an errorlevel of 0 -- this allows compilation to continue if SubWCRev failed.
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,18 @@
|
|||
// svnrev_template.h --> svnrev.h
|
||||
//
|
||||
// This file acts as a template for the automatic SVN revision/version tag.
|
||||
// It is used by the utility SubWCrev.exe to create an "svnrev.h" file for
|
||||
// whichever project is being compiled (as indicated by command line options
|
||||
// passed to SubWCRev.exe during the project's pre-build step).
|
||||
//
|
||||
// The SubWCRev.exe utility is part of TortoiseSVN and requires several DLLs
|
||||
// installed by TortoiseSVN, so it will only be available if you have TortoiseSVN
|
||||
// installed on your system. If you do not have it installed, a generic template
|
||||
// is used instead (see svnrev_generic.h). Having TortoiseSVN is handy but not
|
||||
// necessary. If you do not have it installed, everything will still compile
|
||||
// fine except without the SVN revision tagged to the application/dll version.
|
||||
//
|
||||
// TortoiseSVN can be downloaded from http://tortoisesvn.tigris.org
|
||||
|
||||
#define SVN_REV $WCREV$
|
||||
#define SVN_MODS $WCMODS?1:0$
|
|
@ -0,0 +1,23 @@
|
|||
// svnrev_genric.h --> svnrev.h
|
||||
//
|
||||
// This file acts as a placebo for people who do not have TortoiseSVN installed.
|
||||
// It provides "empty" revision information to the Pcsx2 Playground projects in
|
||||
// the absence of real revisions derived from the repository being built.
|
||||
//
|
||||
// This file does not affect application/dll builds in any significant manner,
|
||||
// other than the lack of automatic revision tags inserted into the app (which
|
||||
// is very convenient but hardly necessary).
|
||||
//
|
||||
// See svn_template.h for more information on how the process of revision
|
||||
// templating works.
|
||||
//
|
||||
// If you would like to enable automatic revisin tagging, TortoiseSVN can be
|
||||
// downloaded from http://tortoisesvn.tigris.org
|
||||
|
||||
#define SVN_REV_UNKNOWN
|
||||
|
||||
// The following defines are included so that code will still compile even if it
|
||||
// doesn't check for the SVN_REV_UNKNOWN define.
|
||||
|
||||
#define SVN_REV 0
|
||||
#define SVN_MODS ""
|
|
@ -0,0 +1,885 @@
|
|||
/* Pcsx2 - Pc Ps2 Emulator
|
||||
* Copyright (C) 2002-2008 Pcsx2 Team
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
#ifndef __PS2EDEFS_H__
|
||||
#define __PS2EDEFS_H__
|
||||
|
||||
/*
|
||||
* PS2E Definitions v0.6.2 (beta)
|
||||
*
|
||||
* Author: linuzappz@hotmail.com
|
||||
* shadowpcsx2@yahoo.gr
|
||||
* florinsasu@hotmail.com
|
||||
*/
|
||||
|
||||
/*
|
||||
Notes:
|
||||
* Since this is still beta things may change.
|
||||
|
||||
* OSflags:
|
||||
__LINUX__ (linux OS)
|
||||
_WIN32 (win32 OS)
|
||||
|
||||
* common return values (for ie. GSinit):
|
||||
0 - success
|
||||
-1 - error
|
||||
|
||||
* reserved keys:
|
||||
F1 to F10 are reserved for the emulator
|
||||
|
||||
* plugins should NOT change the current
|
||||
working directory.
|
||||
(on win32, add flag OFN_NOCHANGEDIR for
|
||||
GetOpenFileName)
|
||||
|
||||
*/
|
||||
|
||||
#include "PS2Etypes.h"
|
||||
|
||||
|
||||
/* common defines */
|
||||
#ifndef C_ASSERT
|
||||
#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1]
|
||||
#endif
|
||||
|
||||
#if defined(GSdefs) || defined(PADdefs) || defined(SIOdefs) || \
|
||||
defined(SPU2defs) || defined(CDVDdefs) || defined(DEV9defs) || \
|
||||
defined(USBdefs) || defined(FWdefs)
|
||||
#define COMMONdefs
|
||||
#endif
|
||||
|
||||
// PS2EgetLibType returns (may be OR'd)
|
||||
#define PS2E_LT_GS 0x01
|
||||
#define PS2E_LT_PAD 0x02 // -=[ OBSOLETE ]=-
|
||||
#define PS2E_LT_SPU2 0x04
|
||||
#define PS2E_LT_CDVD 0x08
|
||||
#define PS2E_LT_DEV9 0x10
|
||||
#define PS2E_LT_USB 0x20
|
||||
#define PS2E_LT_FW 0x40
|
||||
#define PS2E_LT_SIO 0x80
|
||||
|
||||
// PS2EgetLibVersion2 (high 16 bits)
|
||||
#define PS2E_GS_VERSION 0x0006
|
||||
#define PS2E_PAD_VERSION 0x0002 // -=[ OBSOLETE ]=-
|
||||
#define PS2E_SPU2_VERSION 0x0005
|
||||
#define PS2E_CDVD_VERSION 0x0005
|
||||
#define PS2E_DEV9_VERSION 0x0003
|
||||
#define PS2E_USB_VERSION 0x0003
|
||||
#define PS2E_FW_VERSION 0x0002
|
||||
#define PS2E_SIO_VERSION 0x0001
|
||||
#ifdef COMMONdefs
|
||||
|
||||
u32 CALLBACK PS2EgetLibType(void);
|
||||
u32 CALLBACK PS2EgetLibVersion2(u32 type);
|
||||
char* CALLBACK PS2EgetLibName(void);
|
||||
|
||||
#endif
|
||||
|
||||
// key values:
|
||||
/* key values must be OS dependant:
|
||||
win32: the VK_XXX will be used (WinUser)
|
||||
linux: the XK_XXX will be used (XFree86)
|
||||
*/
|
||||
|
||||
// event values:
|
||||
#define KEYPRESS 1
|
||||
#define KEYRELEASE 2
|
||||
|
||||
typedef struct _keyEvent {
|
||||
u32 key;
|
||||
u32 evt;
|
||||
} keyEvent;
|
||||
|
||||
// for 64bit compilers
|
||||
typedef char __keyEvent_Size__[(sizeof(keyEvent) == 8)?1:-1];
|
||||
|
||||
// plugin types
|
||||
#define SIO_TYPE_PAD 0x00000001
|
||||
#define SIO_TYPE_MTAP 0x00000004
|
||||
#define SIO_TYPE_RM 0x00000040
|
||||
#define SIO_TYPE_MC 0x00000100
|
||||
|
||||
typedef int (CALLBACK * SIOchangeSlotCB)(int slot);
|
||||
|
||||
typedef struct _cdvdSubQ {
|
||||
u8 ctrl:4; // control and mode bits
|
||||
u8 mode:4; // control and mode bits
|
||||
u8 trackNum; // current track number (1 to 99)
|
||||
u8 trackIndex; // current index within track (0 to 99)
|
||||
u8 trackM; // current minute location on the disc (BCD encoded)
|
||||
u8 trackS; // current sector location on the disc (BCD encoded)
|
||||
u8 trackF; // current frame location on the disc (BCD encoded)
|
||||
u8 pad; // unused
|
||||
u8 discM; // current minute offset from first track (BCD encoded)
|
||||
u8 discS; // current sector offset from first track (BCD encoded)
|
||||
u8 discF; // current frame offset from first track (BCD encoded)
|
||||
} cdvdSubQ;
|
||||
|
||||
typedef struct _cdvdTD { // NOT bcd coded
|
||||
u32 lsn;
|
||||
u8 type;
|
||||
} cdvdTD;
|
||||
|
||||
typedef struct _cdvdTN {
|
||||
u8 strack; //number of the first track (usually 1)
|
||||
u8 etrack; //number of the last track
|
||||
} cdvdTN;
|
||||
|
||||
// CDVDreadTrack mode values:
|
||||
#define CDVD_MODE_2352 0 // full 2352 bytes
|
||||
#define CDVD_MODE_2340 1 // skip sync (12) bytes
|
||||
#define CDVD_MODE_2328 2 // skip sync+head+sub (24) bytes
|
||||
#define CDVD_MODE_2048 3 // skip sync+head+sub (24) bytes
|
||||
#define CDVD_MODE_2368 4 // full 2352 bytes + 16 subq
|
||||
|
||||
// CDVDgetDiskType returns:
|
||||
#define CDVD_TYPE_ILLEGAL 0xff // Illegal Disc
|
||||
#define CDVD_TYPE_DVDV 0xfe // DVD Video
|
||||
#define CDVD_TYPE_CDDA 0xfd // Audio CD
|
||||
#define CDVD_TYPE_PS2DVD 0x14 // PS2 DVD
|
||||
#define CDVD_TYPE_PS2CDDA 0x13 // PS2 CD (with audio)
|
||||
#define CDVD_TYPE_PS2CD 0x12 // PS2 CD
|
||||
#define CDVD_TYPE_PSCDDA 0x11 // PS CD (with audio)
|
||||
#define CDVD_TYPE_PSCD 0x10 // PS CD
|
||||
#define CDVD_TYPE_UNKNOWN 0x05 // Unknown
|
||||
#define CDVD_TYPE_DETCTDVDD 0x04 // Detecting Dvd Dual Sided
|
||||
#define CDVD_TYPE_DETCTDVDS 0x03 // Detecting Dvd Single Sided
|
||||
#define CDVD_TYPE_DETCTCD 0x02 // Detecting Cd
|
||||
#define CDVD_TYPE_DETCT 0x01 // Detecting
|
||||
#define CDVD_TYPE_NODISC 0x00 // No Disc
|
||||
|
||||
// CDVDgetTrayStatus returns:
|
||||
#define CDVD_TRAY_CLOSE 0x00
|
||||
#define CDVD_TRAY_OPEN 0x01
|
||||
|
||||
// cdvdTD.type (track types for cds)
|
||||
#define CDVD_AUDIO_TRACK 0x01
|
||||
#define CDVD_MODE1_TRACK 0x41
|
||||
#define CDVD_MODE2_TRACK 0x61
|
||||
|
||||
#define CDVD_AUDIO_MASK 0x00
|
||||
#define CDVD_DATA_MASK 0x40
|
||||
// CDROM_DATA_TRACK 0x04 //do not enable this! (from linux kernel)
|
||||
|
||||
typedef void (*DEV9callback)(int cycles);
|
||||
typedef int (*DEV9handler)(void);
|
||||
|
||||
typedef void (*USBcallback)(int cycles);
|
||||
typedef int (*USBhandler)(void);
|
||||
|
||||
// freeze modes:
|
||||
#define FREEZE_LOAD 0
|
||||
#define FREEZE_SAVE 1
|
||||
#define FREEZE_SIZE 2
|
||||
|
||||
typedef struct _GSdriverInfo {
|
||||
char name[8];
|
||||
void *common;
|
||||
} GSdriverInfo;
|
||||
|
||||
#ifdef _WINDOWS_
|
||||
typedef struct _winInfo { // unsupported values must be set to zero
|
||||
HWND hWnd;
|
||||
HMENU hMenu;
|
||||
HWND hStatusWnd;
|
||||
} winInfo;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* GS plugin API */
|
||||
|
||||
// if this file is included with this define
|
||||
// the next api will not be skipped by the compiler
|
||||
#ifdef GSdefs
|
||||
|
||||
// basic funcs
|
||||
|
||||
s32 CALLBACK GSinit();
|
||||
s32 CALLBACK GSopen(void *pDsp, char *Title, int multithread);
|
||||
void CALLBACK GSclose();
|
||||
void CALLBACK GSshutdown();
|
||||
void CALLBACK GSvsync(int field);
|
||||
void CALLBACK GSgifTransfer1(u32 *pMem, u32 addr);
|
||||
void CALLBACK GSgifTransfer2(u32 *pMem, u32 size);
|
||||
void CALLBACK GSgifTransfer3(u32 *pMem, u32 size);
|
||||
void CALLBACK GSgetLastTag(u64* ptag); // returns the last tag processed (64 bits)
|
||||
void CALLBACK GSgifSoftReset(u32 mask);
|
||||
void CALLBACK GSreadFIFO(u64 *mem);
|
||||
void CALLBACK GSreadFIFO2(u64 *mem, int qwc);
|
||||
|
||||
// extended funcs
|
||||
|
||||
// GSkeyEvent gets called when there is a keyEvent from the PAD plugin
|
||||
void CALLBACK GSkeyEvent(keyEvent *ev);
|
||||
void CALLBACK GSchangeSaveState(int, const char* filename);
|
||||
void CALLBACK GSmakeSnapshot(char *path);
|
||||
void CALLBACK GSmakeSnapshot2(char *pathname, int* snapdone, int savejpg);
|
||||
void CALLBACK GSirqCallback(void (*callback)());
|
||||
void CALLBACK GSprintf(int timeout, char *fmt, ...);
|
||||
void CALLBACK GSsetBaseMem(void*);
|
||||
void CALLBACK GSsetGameCRC(int crc, int gameoptions);
|
||||
|
||||
// controls frame skipping in the GS, if this routine isn't present, frame skipping won't be done
|
||||
void CALLBACK GSsetFrameSkip(int frameskip);
|
||||
|
||||
// if start is 1, starts recording spu2 data, else stops
|
||||
// returns a non zero value if successful
|
||||
// for now, pData is not used
|
||||
int CALLBACK GSsetupRecording(int start, void* pData);
|
||||
|
||||
void CALLBACK GSreset();
|
||||
void CALLBACK GSwriteCSR(u32 value);
|
||||
void CALLBACK GSgetDriverInfo(GSdriverInfo *info);
|
||||
#ifdef _WIN32
|
||||
s32 CALLBACK GSsetWindowInfo(winInfo *info);
|
||||
#endif
|
||||
s32 CALLBACK GSfreeze(int mode, freezeData *data);
|
||||
void CALLBACK GSconfigure();
|
||||
void CALLBACK GSabout();
|
||||
s32 CALLBACK GStest();
|
||||
|
||||
#endif
|
||||
|
||||
/* PAD plugin API -=[ OBSOLETE ]=- */
|
||||
|
||||
// if this file is included with this define
|
||||
// the next api will not be skipped by the compiler
|
||||
#ifdef PADdefs
|
||||
|
||||
// basic funcs
|
||||
|
||||
s32 CALLBACK PADinit(u32 flags);
|
||||
s32 CALLBACK PADopen(void *pDsp);
|
||||
void CALLBACK PADclose();
|
||||
void CALLBACK PADshutdown();
|
||||
// PADkeyEvent is called every vsync (return NULL if no event)
|
||||
keyEvent* CALLBACK PADkeyEvent();
|
||||
u8 CALLBACK PADstartPoll(int pad);
|
||||
u8 CALLBACK PADpoll(u8 value);
|
||||
// returns: 1 if supported pad1
|
||||
// 2 if supported pad2
|
||||
// 3 if both are supported
|
||||
u32 CALLBACK PADquery();
|
||||
|
||||
// call to give a hint to the PAD plugin to query for the keyboard state. A
|
||||
// good plugin will query the OS for keyboard state ONLY in this function.
|
||||
// This function is necessary when multithreading because otherwise
|
||||
// the PAD plugin can get into deadlocks with the thread that really owns
|
||||
// the window (and input). Note that PADupdate can be called from a different
|
||||
// thread than the other functions, so mutex or other multithreading primitives
|
||||
// have to be added to maintain data integrity.
|
||||
void CALLBACK PADupdate(int pad);
|
||||
|
||||
// extended funcs
|
||||
|
||||
void CALLBACK PADgsDriverInfo(GSdriverInfo *info);
|
||||
void CALLBACK PADconfigure();
|
||||
void CALLBACK PADabout();
|
||||
s32 CALLBACK PADtest();
|
||||
|
||||
#endif
|
||||
|
||||
/* SIO plugin API */
|
||||
|
||||
// if this file is included with this define
|
||||
// the next api will not be skipped by the compiler
|
||||
#ifdef SIOdefs
|
||||
|
||||
// basic funcs
|
||||
|
||||
s32 CALLBACK SIOinit(u32 port, u32 slot, SIOchangeSlotCB f);
|
||||
s32 CALLBACK SIOopen(void *pDsp);
|
||||
void CALLBACK SIOclose();
|
||||
void CALLBACK SIOshutdown();
|
||||
u8 CALLBACK SIOstartPoll(u8 value);
|
||||
u8 CALLBACK SIOpoll(u8 value);
|
||||
// returns: SIO_TYPE_{PAD,MTAP,RM,MC}
|
||||
u32 CALLBACK SIOquery();
|
||||
|
||||
// extended funcs
|
||||
|
||||
void CALLBACK SIOconfigure();
|
||||
void CALLBACK SIOabout();
|
||||
s32 CALLBACK SIOtest();
|
||||
|
||||
#endif
|
||||
|
||||
/* SPU2 plugin API */
|
||||
|
||||
// if this file is included with this define
|
||||
// the next api will not be skipped by the compiler
|
||||
#ifdef SPU2defs
|
||||
|
||||
// basic funcs
|
||||
|
||||
s32 CALLBACK SPU2init();
|
||||
s32 CALLBACK SPU2open(void *pDsp);
|
||||
void CALLBACK SPU2close();
|
||||
void CALLBACK SPU2shutdown();
|
||||
void CALLBACK SPU2write(u32 mem, u16 value);
|
||||
u16 CALLBACK SPU2read(u32 mem);
|
||||
void CALLBACK SPU2readDMA4Mem(u16 *pMem, int size);
|
||||
void CALLBACK SPU2writeDMA4Mem(u16 *pMem, int size);
|
||||
void CALLBACK SPU2interruptDMA4();
|
||||
void CALLBACK SPU2readDMA7Mem(u16* pMem, int size);
|
||||
void CALLBACK SPU2writeDMA7Mem(u16 *pMem, int size);
|
||||
|
||||
// all addresses passed by dma will be pointers to the array starting at baseaddr
|
||||
// This function is necessary to successfully save and reload the spu2 state
|
||||
void CALLBACK SPU2setDMABaseAddr(uptr baseaddr);
|
||||
|
||||
void CALLBACK SPU2interruptDMA7();
|
||||
u32 CALLBACK SPU2ReadMemAddr(int core);
|
||||
void CALLBACK SPU2WriteMemAddr(int core,u32 value);
|
||||
void CALLBACK SPU2irqCallback(void (*SPU2callback)(),void (*DMA4callback)(),void (*DMA7callback)());
|
||||
|
||||
// extended funcs
|
||||
// if start is 1, starts recording spu2 data, else stops
|
||||
// returns a non zero value if successful
|
||||
// for now, pData is not used
|
||||
int CALLBACK SPU2setupRecording(int start, void* pData);
|
||||
|
||||
void CALLBACK SPU2setClockPtr(u32* ptr);
|
||||
void CALLBACK SPU2setTimeStretcher(short int enable);
|
||||
|
||||
void CALLBACK SPU2async(u32 cycles);
|
||||
s32 CALLBACK SPU2freeze(int mode, freezeData *data);
|
||||
void CALLBACK SPU2configure();
|
||||
void CALLBACK SPU2about();
|
||||
s32 CALLBACK SPU2test();
|
||||
|
||||
#endif
|
||||
|
||||
/* CDVD plugin API */
|
||||
|
||||
// if this file is included with this define
|
||||
// the next api will not be skipped by the compiler
|
||||
#ifdef CDVDdefs
|
||||
|
||||
// basic funcs
|
||||
|
||||
s32 CALLBACK CDVDinit();
|
||||
s32 CALLBACK CDVDopen(const char* pTitleFilename);
|
||||
void CALLBACK CDVDclose();
|
||||
void CALLBACK CDVDshutdown();
|
||||
s32 CALLBACK CDVDreadTrack(u32 lsn, int mode);
|
||||
|
||||
// return can be NULL (for async modes)
|
||||
u8* CALLBACK CDVDgetBuffer();
|
||||
|
||||
s32 CALLBACK CDVDreadSubQ(u32 lsn, cdvdSubQ* subq);//read subq from disc (only cds have subq data)
|
||||
s32 CALLBACK CDVDgetTN(cdvdTN *Buffer); //disk information
|
||||
s32 CALLBACK CDVDgetTD(u8 Track, cdvdTD *Buffer); //track info: min,sec,frame,type
|
||||
s32 CALLBACK CDVDgetTOC(void* toc); //gets ps2 style toc from disc
|
||||
s32 CALLBACK CDVDgetDiskType(); //CDVD_TYPE_xxxx
|
||||
s32 CALLBACK CDVDgetTrayStatus(); //CDVD_TRAY_xxxx
|
||||
s32 CALLBACK CDVDctrlTrayOpen(); //open disc tray
|
||||
s32 CALLBACK CDVDctrlTrayClose(); //close disc tray
|
||||
|
||||
// extended funcs
|
||||
|
||||
void CALLBACK CDVDconfigure();
|
||||
void CALLBACK CDVDabout();
|
||||
s32 CALLBACK CDVDtest();
|
||||
void CALLBACK CDVDnewDiskCB(void (*callback)());
|
||||
|
||||
#endif
|
||||
|
||||
/* DEV9 plugin API */
|
||||
|
||||
// if this file is included with this define
|
||||
// the next api will not be skipped by the compiler
|
||||
#ifdef DEV9defs
|
||||
|
||||
// basic funcs
|
||||
|
||||
// NOTE: The read/write functions CANNOT use XMM/MMX regs
|
||||
// If you want to use them, need to save and restore current ones
|
||||
s32 CALLBACK DEV9init();
|
||||
s32 CALLBACK DEV9open(void *pDsp);
|
||||
void CALLBACK DEV9close();
|
||||
void CALLBACK DEV9shutdown();
|
||||
u8 CALLBACK DEV9read8(u32 addr);
|
||||
u16 CALLBACK DEV9read16(u32 addr);
|
||||
u32 CALLBACK DEV9read32(u32 addr);
|
||||
void CALLBACK DEV9write8(u32 addr, u8 value);
|
||||
void CALLBACK DEV9write16(u32 addr, u16 value);
|
||||
void CALLBACK DEV9write32(u32 addr, u32 value);
|
||||
void CALLBACK DEV9readDMA8Mem(u32 *pMem, int size);
|
||||
void CALLBACK DEV9writeDMA8Mem(u32 *pMem, int size);
|
||||
// cycles = IOP cycles before calling callback,
|
||||
// if callback returns 1 the irq is triggered, else not
|
||||
void CALLBACK DEV9irqCallback(DEV9callback callback);
|
||||
DEV9handler CALLBACK DEV9irqHandler(void);
|
||||
|
||||
// extended funcs
|
||||
|
||||
s32 CALLBACK DEV9freeze(int mode, freezeData *data);
|
||||
void CALLBACK DEV9configure();
|
||||
void CALLBACK DEV9about();
|
||||
s32 CALLBACK DEV9test();
|
||||
|
||||
#endif
|
||||
|
||||
/* USB plugin API */
|
||||
|
||||
// if this file is included with this define
|
||||
// the next api will not be skipped by the compiler
|
||||
#ifdef USBdefs
|
||||
|
||||
// basic funcs
|
||||
|
||||
s32 CALLBACK USBinit();
|
||||
s32 CALLBACK USBopen(void *pDsp);
|
||||
void CALLBACK USBclose();
|
||||
void CALLBACK USBshutdown();
|
||||
u8 CALLBACK USBread8(u32 addr);
|
||||
u16 CALLBACK USBread16(u32 addr);
|
||||
u32 CALLBACK USBread32(u32 addr);
|
||||
void CALLBACK USBwrite8(u32 addr, u8 value);
|
||||
void CALLBACK USBwrite16(u32 addr, u16 value);
|
||||
void CALLBACK USBwrite32(u32 addr, u32 value);
|
||||
void CALLBACK USBasync(u32 cycles);
|
||||
|
||||
// cycles = IOP cycles before calling callback,
|
||||
// if callback returns 1 the irq is triggered, else not
|
||||
void CALLBACK USBirqCallback(USBcallback callback);
|
||||
USBhandler CALLBACK USBirqHandler(void);
|
||||
void CALLBACK USBsetRAM(void *mem);
|
||||
|
||||
// extended funcs
|
||||
|
||||
s32 CALLBACK USBfreeze(int mode, freezeData *data);
|
||||
void CALLBACK USBconfigure();
|
||||
void CALLBACK USBabout();
|
||||
s32 CALLBACK USBtest();
|
||||
|
||||
#endif
|
||||
|
||||
/* FW plugin API */
|
||||
|
||||
// if this file is included with this define
|
||||
// the next api will not be skipped by the compiler
|
||||
#ifdef FWdefs
|
||||
// basic funcs
|
||||
|
||||
// NOTE: The read/write functions CANNOT use XMM/MMX regs
|
||||
// If you want to use them, need to save and restore current ones
|
||||
s32 CALLBACK FWinit();
|
||||
s32 CALLBACK FWopen(void *pDsp);
|
||||
void CALLBACK FWclose();
|
||||
void CALLBACK FWshutdown();
|
||||
u32 CALLBACK FWread32(u32 addr);
|
||||
void CALLBACK FWwrite32(u32 addr, u32 value);
|
||||
void CALLBACK FWirqCallback(void (*callback)());
|
||||
|
||||
// extended funcs
|
||||
|
||||
s32 CALLBACK FWfreeze(int mode, freezeData *data);
|
||||
void CALLBACK FWconfigure();
|
||||
void CALLBACK FWabout();
|
||||
s32 CALLBACK FWtest();
|
||||
#endif
|
||||
|
||||
// might be useful for emulators
|
||||
#ifdef PLUGINtypedefs
|
||||
|
||||
typedef u32 (CALLBACK* _PS2EgetLibType)(void);
|
||||
typedef u32 (CALLBACK* _PS2EgetLibVersion2)(u32 type);
|
||||
typedef char*(CALLBACK* _PS2EgetLibName)(void);
|
||||
|
||||
// GS
|
||||
// NOTE: GSreadFIFOX/GSwriteCSR functions CANNOT use XMM/MMX regs
|
||||
// If you want to use them, need to save and restore current ones
|
||||
typedef s32 (CALLBACK* _GSinit)();
|
||||
typedef s32 (CALLBACK* _GSopen)(void *pDsp, char *Title, int multithread);
|
||||
typedef void (CALLBACK* _GSclose)();
|
||||
typedef void (CALLBACK* _GSshutdown)();
|
||||
typedef void (CALLBACK* _GSvsync)(int field);
|
||||
typedef void (CALLBACK* _GSgifTransfer1)(u32 *pMem, u32 addr);
|
||||
typedef void (CALLBACK* _GSgifTransfer2)(u32 *pMem, u32 size);
|
||||
typedef void (CALLBACK* _GSgifTransfer3)(u32 *pMem, u32 size);
|
||||
typedef void (CALLBACK* _GSgetLastTag)(u64* ptag); // returns the last tag processed (64 bits)
|
||||
typedef void (CALLBACK* _GSgifSoftReset)(u32 mask);
|
||||
typedef void (CALLBACK* _GSreadFIFO)(u64 *pMem);
|
||||
typedef void (CALLBACK* _GSreadFIFO2)(u64 *pMem, int qwc);
|
||||
|
||||
typedef void (CALLBACK* _GSkeyEvent)(keyEvent* ev);
|
||||
typedef void (CALLBACK* _GSchangeSaveState)(int, const char* filename);
|
||||
typedef void (CALLBACK* _GSirqCallback)(void (*callback)());
|
||||
typedef void (CALLBACK* _GSprintf)(int timeout, char *fmt, ...);
|
||||
typedef void (CALLBACK* _GSsetBaseMem)(void*);
|
||||
typedef void (CALLBACK* _GSsetGameCRC)(int, int);
|
||||
typedef void (CALLBACK* _GSsetFrameSkip)(int frameskip);
|
||||
typedef int (CALLBACK* _GSsetupRecording)(int, void*);
|
||||
typedef void (CALLBACK* _GSreset)();
|
||||
typedef void (CALLBACK* _GSwriteCSR)(u32 value);
|
||||
typedef void (CALLBACK* _GSgetDriverInfo)(GSdriverInfo *info);
|
||||
#ifdef _WINDOWS_
|
||||
typedef s32 (CALLBACK* _GSsetWindowInfo)(winInfo *info);
|
||||
#endif
|
||||
typedef void (CALLBACK* _GSmakeSnapshot)(const char *path);
|
||||
typedef void (CALLBACK* _GSmakeSnapshot2)(const char *path, int*, int);
|
||||
typedef s32 (CALLBACK* _GSfreeze)(int mode, freezeData *data);
|
||||
typedef void (CALLBACK* _GSconfigure)();
|
||||
typedef s32 (CALLBACK* _GStest)();
|
||||
typedef void (CALLBACK* _GSabout)();
|
||||
|
||||
// PAD
|
||||
typedef s32 (CALLBACK* _PADinit)(u32 flags);
|
||||
typedef s32 (CALLBACK* _PADopen)(void *pDsp);
|
||||
typedef void (CALLBACK* _PADclose)();
|
||||
typedef void (CALLBACK* _PADshutdown)();
|
||||
typedef keyEvent* (CALLBACK* _PADkeyEvent)();
|
||||
typedef u8 (CALLBACK* _PADstartPoll)(int pad);
|
||||
typedef u8 (CALLBACK* _PADpoll)(u8 value);
|
||||
typedef u32 (CALLBACK* _PADquery)();
|
||||
typedef void (CALLBACK* _PADupdate)(int pad);
|
||||
|
||||
typedef void (CALLBACK* _PADgsDriverInfo)(GSdriverInfo *info);
|
||||
typedef void (CALLBACK* _PADconfigure)();
|
||||
typedef s32 (CALLBACK* _PADtest)();
|
||||
typedef void (CALLBACK* _PADabout)();
|
||||
|
||||
// SIO
|
||||
typedef s32 (CALLBACK* _SIOinit)(u32 port, u32 slot, SIOchangeSlotCB f);
|
||||
typedef s32 (CALLBACK* _SIOopen)(void *pDsp);
|
||||
typedef void (CALLBACK* _SIOclose)();
|
||||
typedef void (CALLBACK* _SIOshutdown)();
|
||||
typedef u8 (CALLBACK* _SIOstartPoll)(u8 value);
|
||||
typedef u8 (CALLBACK* _SIOpoll)(u8 value);
|
||||
typedef u32 (CALLBACK* _SIOquery)();
|
||||
|
||||
typedef void (CALLBACK* _SIOconfigure)();
|
||||
typedef s32 (CALLBACK* _SIOtest)();
|
||||
typedef void (CALLBACK* _SIOabout)();
|
||||
|
||||
// SPU2
|
||||
// NOTE: The read/write functions CANNOT use XMM/MMX regs
|
||||
// If you want to use them, need to save and restore current ones
|
||||
typedef s32 (CALLBACK* _SPU2init)();
|
||||
typedef s32 (CALLBACK* _SPU2open)(void *pDsp);
|
||||
typedef void (CALLBACK* _SPU2close)();
|
||||
typedef void (CALLBACK* _SPU2shutdown)();
|
||||
typedef void (CALLBACK* _SPU2write)(u32 mem, u16 value);
|
||||
typedef u16 (CALLBACK* _SPU2read)(u32 mem);
|
||||
typedef void (CALLBACK* _SPU2readDMA4Mem)(u16 *pMem, int size);
|
||||
typedef void (CALLBACK* _SPU2writeDMA4Mem)(u16 *pMem, int size);
|
||||
typedef void (CALLBACK* _SPU2interruptDMA4)();
|
||||
typedef void (CALLBACK* _SPU2readDMA7Mem)(u16 *pMem, int size);
|
||||
typedef void (CALLBACK* _SPU2writeDMA7Mem)(u16 *pMem, int size);
|
||||
typedef void (CALLBACK* _SPU2setDMABaseAddr)(uptr baseaddr);
|
||||
typedef void (CALLBACK* _SPU2interruptDMA7)();
|
||||
typedef void (CALLBACK* _SPU2irqCallback)(void (*SPU2callback)(),void (*DMA4callback)(),void (*DMA7callback)());
|
||||
typedef int (CALLBACK* _SPU2setupRecording)(int, void*);
|
||||
|
||||
typedef void (CALLBACK* _SPU2setClockPtr)(u32*ptr);
|
||||
typedef void (CALLBACK* _SPU2setTimeStretcher)(short int enable);
|
||||
|
||||
typedef u32 (CALLBACK* _SPU2ReadMemAddr)(int core);
|
||||
typedef void (CALLBACK* _SPU2WriteMemAddr)(int core,u32 value);
|
||||
typedef void (CALLBACK* _SPU2async)(u32 cycles);
|
||||
typedef s32 (CALLBACK* _SPU2freeze)(int mode, freezeData *data);
|
||||
typedef void (CALLBACK* _SPU2configure)();
|
||||
typedef s32 (CALLBACK* _SPU2test)();
|
||||
typedef void (CALLBACK* _SPU2about)();
|
||||
|
||||
|
||||
// CDVD
|
||||
// NOTE: The read/write functions CANNOT use XMM/MMX regs
|
||||
// If you want to use them, need to save and restore current ones
|
||||
typedef s32 (CALLBACK* _CDVDinit)();
|
||||
typedef s32 (CALLBACK* _CDVDopen)(const char* pTitleFilename);
|
||||
typedef void (CALLBACK* _CDVDclose)();
|
||||
typedef void (CALLBACK* _CDVDshutdown)();
|
||||
typedef s32 (CALLBACK* _CDVDreadTrack)(u32 lsn, int mode);
|
||||
typedef u8* (CALLBACK* _CDVDgetBuffer)();
|
||||
typedef s32 (CALLBACK* _CDVDreadSubQ)(u32 lsn, cdvdSubQ* subq);
|
||||
typedef s32 (CALLBACK* _CDVDgetTN)(cdvdTN *Buffer);
|
||||
typedef s32 (CALLBACK* _CDVDgetTD)(u8 Track, cdvdTD *Buffer);
|
||||
typedef s32 (CALLBACK* _CDVDgetTOC)(void* toc);
|
||||
typedef s32 (CALLBACK* _CDVDgetDiskType)();
|
||||
typedef s32 (CALLBACK* _CDVDgetTrayStatus)();
|
||||
typedef s32 (CALLBACK* _CDVDctrlTrayOpen)();
|
||||
typedef s32 (CALLBACK* _CDVDctrlTrayClose)();
|
||||
|
||||
typedef void (CALLBACK* _CDVDconfigure)();
|
||||
typedef s32 (CALLBACK* _CDVDtest)();
|
||||
typedef void (CALLBACK* _CDVDabout)();
|
||||
typedef void (CALLBACK* _CDVDnewDiskCB)(void (*callback)());
|
||||
|
||||
// DEV9
|
||||
// NOTE: The read/write functions CANNOT use XMM/MMX regs
|
||||
// If you want to use them, need to save and restore current ones
|
||||
typedef s32 (CALLBACK* _DEV9init)();
|
||||
typedef s32 (CALLBACK* _DEV9open)(void *pDsp);
|
||||
typedef void (CALLBACK* _DEV9close)();
|
||||
typedef void (CALLBACK* _DEV9shutdown)();
|
||||
typedef u8 (CALLBACK* _DEV9read8)(u32 mem);
|
||||
typedef u16 (CALLBACK* _DEV9read16)(u32 mem);
|
||||
typedef u32 (CALLBACK* _DEV9read32)(u32 mem);
|
||||
typedef void (CALLBACK* _DEV9write8)(u32 mem, u8 value);
|
||||
typedef void (CALLBACK* _DEV9write16)(u32 mem, u16 value);
|
||||
typedef void (CALLBACK* _DEV9write32)(u32 mem, u32 value);
|
||||
typedef void (CALLBACK* _DEV9readDMA8Mem)(u32 *pMem, int size);
|
||||
typedef void (CALLBACK* _DEV9writeDMA8Mem)(u32 *pMem, int size);
|
||||
typedef void (CALLBACK* _DEV9irqCallback)(DEV9callback callback);
|
||||
typedef DEV9handler (CALLBACK* _DEV9irqHandler)(void);
|
||||
|
||||
typedef s32 (CALLBACK* _DEV9freeze)(int mode, freezeData *data);
|
||||
typedef void (CALLBACK* _DEV9configure)();
|
||||
typedef s32 (CALLBACK* _DEV9test)();
|
||||
typedef void (CALLBACK* _DEV9about)();
|
||||
|
||||
// USB
|
||||
// NOTE: The read/write functions CANNOT use XMM/MMX regs
|
||||
// If you want to use them, need to save and restore current ones
|
||||
typedef s32 (CALLBACK* _USBinit)();
|
||||
typedef s32 (CALLBACK* _USBopen)(void *pDsp);
|
||||
typedef void (CALLBACK* _USBclose)();
|
||||
typedef void (CALLBACK* _USBshutdown)();
|
||||
typedef u8 (CALLBACK* _USBread8)(u32 mem);
|
||||
typedef u16 (CALLBACK* _USBread16)(u32 mem);
|
||||
typedef u32 (CALLBACK* _USBread32)(u32 mem);
|
||||
typedef void (CALLBACK* _USBwrite8)(u32 mem, u8 value);
|
||||
typedef void (CALLBACK* _USBwrite16)(u32 mem, u16 value);
|
||||
typedef void (CALLBACK* _USBwrite32)(u32 mem, u32 value);
|
||||
typedef void (CALLBACK* _USBasync)(u32 cycles);
|
||||
|
||||
|
||||
typedef void (CALLBACK* _USBirqCallback)(USBcallback callback);
|
||||
typedef USBhandler (CALLBACK* _USBirqHandler)(void);
|
||||
typedef void (CALLBACK* _USBsetRAM)(void *mem);
|
||||
|
||||
typedef s32 (CALLBACK* _USBfreeze)(int mode, freezeData *data);
|
||||
typedef void (CALLBACK* _USBconfigure)();
|
||||
typedef s32 (CALLBACK* _USBtest)();
|
||||
typedef void (CALLBACK* _USBabout)();
|
||||
|
||||
//FW
|
||||
typedef s32 (CALLBACK* _FWinit)();
|
||||
typedef s32 (CALLBACK* _FWopen)(void *pDsp);
|
||||
typedef void (CALLBACK* _FWclose)();
|
||||
typedef void (CALLBACK* _FWshutdown)();
|
||||
typedef u32 (CALLBACK* _FWread32)(u32 mem);
|
||||
typedef void (CALLBACK* _FWwrite32)(u32 mem, u32 value);
|
||||
typedef void (CALLBACK* _FWirqCallback)(void (*callback)());
|
||||
|
||||
typedef s32 (CALLBACK* _FWfreeze)(int mode, freezeData *data);
|
||||
typedef void (CALLBACK* _FWconfigure)();
|
||||
typedef s32 (CALLBACK* _FWtest)();
|
||||
typedef void (CALLBACK* _FWabout)();
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef PLUGINfuncs
|
||||
|
||||
// GS
|
||||
extern _GSinit GSinit;
|
||||
extern _GSopen GSopen;
|
||||
extern _GSclose GSclose;
|
||||
extern _GSshutdown GSshutdown;
|
||||
extern _GSvsync GSvsync;
|
||||
extern _GSgifTransfer1 GSgifTransfer1;
|
||||
extern _GSgifTransfer2 GSgifTransfer2;
|
||||
extern _GSgifTransfer3 GSgifTransfer3;
|
||||
extern _GSgetLastTag GSgetLastTag;
|
||||
extern _GSgifSoftReset GSgifSoftReset;
|
||||
extern _GSreadFIFO GSreadFIFO;
|
||||
extern _GSreadFIFO2 GSreadFIFO2;
|
||||
|
||||
extern _GSkeyEvent GSkeyEvent;
|
||||
extern _GSchangeSaveState GSchangeSaveState;
|
||||
extern _GSmakeSnapshot GSmakeSnapshot;
|
||||
extern _GSmakeSnapshot2 GSmakeSnapshot2;
|
||||
extern _GSirqCallback GSirqCallback;
|
||||
extern _GSprintf GSprintf;
|
||||
extern _GSsetBaseMem GSsetBaseMem;
|
||||
extern _GSsetGameCRC GSsetGameCRC;
|
||||
extern _GSsetFrameSkip GSsetFrameSkip;
|
||||
extern _GSsetupRecording GSsetupRecording;
|
||||
extern _GSreset GSreset;
|
||||
extern _GSwriteCSR GSwriteCSR;
|
||||
extern _GSgetDriverInfo GSgetDriverInfo;
|
||||
#ifdef _WINDOWS_
|
||||
extern _GSsetWindowInfo GSsetWindowInfo;
|
||||
#endif
|
||||
extern _GSfreeze GSfreeze;
|
||||
extern _GSconfigure GSconfigure;
|
||||
extern _GStest GStest;
|
||||
extern _GSabout GSabout;
|
||||
|
||||
// PAD1
|
||||
extern _PADinit PAD1init;
|
||||
extern _PADopen PAD1open;
|
||||
extern _PADclose PAD1close;
|
||||
extern _PADshutdown PAD1shutdown;
|
||||
extern _PADkeyEvent PAD1keyEvent;
|
||||
extern _PADstartPoll PAD1startPoll;
|
||||
extern _PADpoll PAD1poll;
|
||||
extern _PADquery PAD1query;
|
||||
extern _PADupdate PAD1update;
|
||||
|
||||
extern _PADgsDriverInfo PAD1gsDriverInfo;
|
||||
extern _PADconfigure PAD1configure;
|
||||
extern _PADtest PAD1test;
|
||||
extern _PADabout PAD1about;
|
||||
|
||||
// PAD2
|
||||
extern _PADinit PAD2init;
|
||||
extern _PADopen PAD2open;
|
||||
extern _PADclose PAD2close;
|
||||
extern _PADshutdown PAD2shutdown;
|
||||
extern _PADkeyEvent PAD2keyEvent;
|
||||
extern _PADstartPoll PAD2startPoll;
|
||||
extern _PADpoll PAD2poll;
|
||||
extern _PADquery PAD2query;
|
||||
extern _PADupdate PAD2update;
|
||||
|
||||
extern _PADgsDriverInfo PAD2gsDriverInfo;
|
||||
extern _PADconfigure PAD2configure;
|
||||
extern _PADtest PAD2test;
|
||||
extern _PADabout PAD2about;
|
||||
|
||||
// SIO[2]
|
||||
extern _SIOinit SIOinit[2][9];
|
||||
extern _SIOopen SIOopen[2][9];
|
||||
extern _SIOclose SIOclose[2][9];
|
||||
extern _SIOshutdown SIOshutdown[2][9];
|
||||
extern _SIOstartPoll SIOstartPoll[2][9];
|
||||
extern _SIOpoll SIOpoll[2][9];
|
||||
extern _SIOquery SIOquery[2][9];
|
||||
|
||||
extern _SIOconfigure SIOconfigure[2][9];
|
||||
extern _SIOtest SIOtest[2][9];
|
||||
extern _SIOabout SIOabout[2][9];
|
||||
|
||||
// SPU2
|
||||
extern _SPU2init SPU2init;
|
||||
extern _SPU2open SPU2open;
|
||||
extern _SPU2close SPU2close;
|
||||
extern _SPU2shutdown SPU2shutdown;
|
||||
extern _SPU2write SPU2write;
|
||||
extern _SPU2read SPU2read;
|
||||
extern _SPU2readDMA4Mem SPU2readDMA4Mem;
|
||||
extern _SPU2writeDMA4Mem SPU2writeDMA4Mem;
|
||||
extern _SPU2interruptDMA4 SPU2interruptDMA4;
|
||||
extern _SPU2readDMA7Mem SPU2readDMA7Mem;
|
||||
extern _SPU2writeDMA7Mem SPU2writeDMA7Mem;
|
||||
extern _SPU2setDMABaseAddr SPU2setDMABaseAddr;
|
||||
extern _SPU2interruptDMA7 SPU2interruptDMA7;
|
||||
extern _SPU2ReadMemAddr SPU2ReadMemAddr;
|
||||
extern _SPU2setupRecording SPU2setupRecording;
|
||||
extern _SPU2WriteMemAddr SPU2WriteMemAddr;
|
||||
extern _SPU2irqCallback SPU2irqCallback;
|
||||
|
||||
extern _SPU2setClockPtr SPU2setClockPtr;
|
||||
extern _SPU2setTimeStretcher SPU2setTimeStretcher;
|
||||
|
||||
extern _SPU2async SPU2async;
|
||||
extern _SPU2freeze SPU2freeze;
|
||||
extern _SPU2configure SPU2configure;
|
||||
extern _SPU2test SPU2test;
|
||||
extern _SPU2about SPU2about;
|
||||
|
||||
// CDVD
|
||||
extern _CDVDinit CDVDinit;
|
||||
extern _CDVDopen CDVDopen;
|
||||
extern _CDVDclose CDVDclose;
|
||||
extern _CDVDshutdown CDVDshutdown;
|
||||
extern _CDVDreadTrack CDVDreadTrack;
|
||||
extern _CDVDgetBuffer CDVDgetBuffer;
|
||||
extern _CDVDreadSubQ CDVDreadSubQ;
|
||||
extern _CDVDgetTN CDVDgetTN;
|
||||
extern _CDVDgetTD CDVDgetTD;
|
||||
extern _CDVDgetTOC CDVDgetTOC;
|
||||
extern _CDVDgetDiskType CDVDgetDiskType;
|
||||
extern _CDVDgetTrayStatus CDVDgetTrayStatus;
|
||||
extern _CDVDctrlTrayOpen CDVDctrlTrayOpen;
|
||||
extern _CDVDctrlTrayClose CDVDctrlTrayClose;
|
||||
|
||||
extern _CDVDconfigure CDVDconfigure;
|
||||
extern _CDVDtest CDVDtest;
|
||||
extern _CDVDabout CDVDabout;
|
||||
extern _CDVDnewDiskCB CDVDnewDiskCB;
|
||||
|
||||
// DEV9
|
||||
extern _DEV9init DEV9init;
|
||||
extern _DEV9open DEV9open;
|
||||
extern _DEV9close DEV9close;
|
||||
extern _DEV9shutdown DEV9shutdown;
|
||||
extern _DEV9read8 DEV9read8;
|
||||
extern _DEV9read16 DEV9read16;
|
||||
extern _DEV9read32 DEV9read32;
|
||||
extern _DEV9write8 DEV9write8;
|
||||
extern _DEV9write16 DEV9write16;
|
||||
extern _DEV9write32 DEV9write32;
|
||||
extern _DEV9readDMA8Mem DEV9readDMA8Mem;
|
||||
extern _DEV9writeDMA8Mem DEV9writeDMA8Mem;
|
||||
extern _DEV9irqCallback DEV9irqCallback;
|
||||
extern _DEV9irqHandler DEV9irqHandler;
|
||||
|
||||
extern _DEV9configure DEV9configure;
|
||||
extern _DEV9freeze DEV9freeze;
|
||||
extern _DEV9test DEV9test;
|
||||
extern _DEV9about DEV9about;
|
||||
|
||||
// USB
|
||||
extern _USBinit USBinit;
|
||||
extern _USBopen USBopen;
|
||||
extern _USBclose USBclose;
|
||||
extern _USBshutdown USBshutdown;
|
||||
extern _USBread8 USBread8;
|
||||
extern _USBread16 USBread16;
|
||||
extern _USBread32 USBread32;
|
||||
extern _USBwrite8 USBwrite8;
|
||||
extern _USBwrite16 USBwrite16;
|
||||
extern _USBwrite32 USBwrite32;
|
||||
extern _USBasync USBasync;
|
||||
|
||||
extern _USBirqCallback USBirqCallback;
|
||||
extern _USBirqHandler USBirqHandler;
|
||||
extern _USBsetRAM USBsetRAM;
|
||||
|
||||
extern _USBconfigure USBconfigure;
|
||||
extern _USBfreeze USBfreeze;
|
||||
extern _USBtest USBtest;
|
||||
extern _USBabout USBabout;
|
||||
|
||||
// FW
|
||||
extern _FWinit FWinit;
|
||||
extern _FWopen FWopen;
|
||||
extern _FWclose FWclose;
|
||||
extern _FWshutdown FWshutdown;
|
||||
extern _FWread32 FWread32;
|
||||
extern _FWwrite32 FWwrite32;
|
||||
extern _FWirqCallback FWirqCallback;
|
||||
|
||||
extern _FWconfigure FWconfigure;
|
||||
extern _FWfreeze FWfreeze;
|
||||
extern _FWtest FWtest;
|
||||
extern _FWabout FWabout;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // End extern "C"
|
||||
#endif
|
||||
|
||||
#endif /* __PS2EDEFS_H__ */
|
|
@ -0,0 +1,219 @@
|
|||
/* Pcsx2 - Pc Ps2 Emulator
|
||||
* Copyright (C) 2002-2008 Pcsx2 Team
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
#ifndef __PS2ETYPES_H__
|
||||
#define __PS2ETYPES_H__
|
||||
|
||||
#if defined (__linux__) && !defined(__LINUX__) // some distributions are lower case
|
||||
#define __LINUX__
|
||||
#endif
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
#define __LINUX__
|
||||
#endif
|
||||
|
||||
#ifndef ARRAYSIZE
|
||||
#define ARRAYSIZE(x) (sizeof(x)/sizeof((x)[0]))
|
||||
#endif
|
||||
|
||||
#ifdef __LINUX__
|
||||
#define CALLBACK
|
||||
#else
|
||||
#define CALLBACK __stdcall
|
||||
#endif
|
||||
|
||||
|
||||
// jASSUME - give hints to the optimizer
|
||||
// This is primarily useful for the default case switch optimizer, which enables VC to
|
||||
// generate more compact switches.
|
||||
|
||||
#ifdef NDEBUG
|
||||
# define jBREAKPOINT() ((void) 0)
|
||||
# ifdef _MSC_VER
|
||||
# define jASSUME(exp) (__assume(exp))
|
||||
# else
|
||||
# define jASSUME(exp) ((void) sizeof(exp))
|
||||
# endif
|
||||
#else
|
||||
# if defined(_MSC_VER)
|
||||
# define jBREAKPOINT() do { __asm int 3 } while(0)
|
||||
# else
|
||||
# define jBREAKPOINT() ((void) *(volatile char *) 0)
|
||||
# endif
|
||||
# define jASSUME(exp) if(exp) ; else jBREAKPOINT()
|
||||
#endif
|
||||
|
||||
// disable the default case in a switch
|
||||
#define jNO_DEFAULT \
|
||||
{ \
|
||||
break; \
|
||||
\
|
||||
default: \
|
||||
jASSUME(0); \
|
||||
break; \
|
||||
}
|
||||
|
||||
|
||||
// Basic types
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
typedef __int8 s8;
|
||||
typedef __int16 s16;
|
||||
typedef __int32 s32;
|
||||
typedef __int64 s64;
|
||||
|
||||
typedef unsigned __int8 u8;
|
||||
typedef unsigned __int16 u16;
|
||||
typedef unsigned __int32 u32;
|
||||
typedef unsigned __int64 u64;
|
||||
|
||||
typedef unsigned int uint;
|
||||
|
||||
#define PCSX2_ALIGNED(alig,x) __declspec(align(alig)) x
|
||||
#define PCSX2_ALIGNED16(x) __declspec(align(16)) x
|
||||
#define PCSX2_ALIGNED16_DECL(x) __declspec(align(16)) x
|
||||
|
||||
#define __naked __declspec(naked)
|
||||
|
||||
#else // _MSC_VER
|
||||
|
||||
#ifdef __LINUX__
|
||||
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include "stdint.h"
|
||||
|
||||
typedef int8_t s8;
|
||||
typedef int16_t s16;
|
||||
typedef int32_t s32;
|
||||
typedef int64_t s64;
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
typedef uintptr_t uptr;
|
||||
typedef intptr_t sptr;
|
||||
|
||||
#else // HAVE_STDINT_H
|
||||
|
||||
typedef char s8;
|
||||
typedef short s16;
|
||||
typedef int s32;
|
||||
typedef long long s64;
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
|
||||
#endif // HAVE_STDINT_H
|
||||
|
||||
typedef unsigned int uint;
|
||||
|
||||
#define LONG long
|
||||
typedef union _LARGE_INTEGER
|
||||
{
|
||||
long long QuadPart;
|
||||
} LARGE_INTEGER;
|
||||
|
||||
#define __fastcall __attribute__((fastcall))
|
||||
#define __unused __attribute__((unused))
|
||||
#define _inline __inline__ __attribute__((unused))
|
||||
#define __forceinline __attribute__((always_inline,unused))
|
||||
#define __naked // GCC lacks the naked specifier
|
||||
|
||||
#endif // __LINUX__
|
||||
|
||||
#define PCSX2_ALIGNED(alig,x) x __attribute((aligned(alig)))
|
||||
#define PCSX2_ALIGNED16(x) x __attribute((aligned(16)))
|
||||
|
||||
#define PCSX2_ALIGNED16_DECL(x) x
|
||||
|
||||
#endif // _MSC_VER
|
||||
|
||||
#if !defined(__LINUX__) || !defined(HAVE_STDINT_H)
|
||||
#if defined(__x86_64__)
|
||||
typedef u64 uptr;
|
||||
typedef s64 sptr;
|
||||
#else
|
||||
typedef u32 uptr;
|
||||
typedef s32 sptr;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// A rough-and-ready cross platform 128-bit datatype, Non-SSE style.
|
||||
#ifdef __cplusplus
|
||||
struct u128
|
||||
{
|
||||
u64 lo;
|
||||
u64 hi;
|
||||
|
||||
// Implicit conversion from u64
|
||||
u128( u64 src ) :
|
||||
lo( src )
|
||||
, hi( 0 ) {}
|
||||
|
||||
// Implicit conversion from u32
|
||||
u128( u32 src ) :
|
||||
lo( src )
|
||||
, hi( 0 ) {}
|
||||
};
|
||||
|
||||
struct s128
|
||||
{
|
||||
s64 lo;
|
||||
s64 hi;
|
||||
|
||||
// Implicit conversion from u64
|
||||
s128( s64 src ) :
|
||||
lo( src )
|
||||
, hi( 0 ) {}
|
||||
|
||||
// Implicit conversion from u32
|
||||
s128( s32 src ) :
|
||||
lo( src )
|
||||
, hi( 0 ) {}
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
typedef union _u128_t
|
||||
{
|
||||
u64 lo;
|
||||
u64 hi;
|
||||
} u128;
|
||||
|
||||
typedef union _s128_t
|
||||
{
|
||||
s64 lo;
|
||||
s64 hi;
|
||||
} s128;
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int size;
|
||||
s8 *data;
|
||||
} freezeData;
|
||||
|
||||
/* common defines */
|
||||
#ifndef C_ASSERT
|
||||
#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1]
|
||||
#endif
|
||||
|
||||
#endif /* __PS2ETYPES_H__ */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue