Merge remote-tracking branch 'remotes/flamefire/RecordImprovements' into develop
This commit is contained in:
commit
c191284030
149 files changed
+7740
-5038
No files matched your search
+5
-8
@@ -14,13 +14,13 @@ Output/Songs
|
||||
/*.ctl
|
||||
/Output/Registration.xml
|
||||
/Vocaluxe.suo
|
||||
/Vocaluxe/Vocaluxe.csproj.user
|
||||
*.csproj.user
|
||||
*.vcxproj.user
|
||||
/Output/Config.xml
|
||||
*.ipch
|
||||
*.log
|
||||
/Output/Playlists/*.xml
|
||||
/Output/x64/gstreamer/GstreamerHelper.dll
|
||||
/Output/x86/gstreamer/GstreamerHelper.dll
|
||||
GstreamerHelper.dll
|
||||
*.tlog
|
||||
*.suo
|
||||
*.obj
|
||||
@@ -39,11 +39,7 @@ Output/Songs
|
||||
*.Cache
|
||||
/GstreamerHelper/bin/*
|
||||
/VocaluxeLib/bin
|
||||
/ClientServerLib/ClientServerLib/ClientServerLib.csproj.user
|
||||
/ClientServerLib/ClientServerLib/bin/Debug/ClientServerLib.dll
|
||||
/ClientServerLib/ClientTest/bin/Debug/ClientServerLib.dll
|
||||
/ClientServerLib/ClientTest/bin/Release/ClientServerLib.dll
|
||||
/ClientServerLib/ClientServerLib/bin/ClientServerLib.dll
|
||||
ClientServerLib.dll
|
||||
ClientTest.exe
|
||||
*.vshost.*
|
||||
*.userprefs
|
||||
@@ -63,3 +59,4 @@ Output/Vocaluxe_x64.exe
|
||||
/Output/VocaluxeServerConfig.exe.config
|
||||
/PhoneGap/
|
||||
/Output/Profiles
|
||||
PitchTracker.dll
|
||||
@@ -18,7 +18,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.ServiceModel;
|
||||
using System.ServiceModel.Web;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
|
||||
namespace ServerLib
|
||||
@@ -191,10 +191,15 @@ namespace ServerLib
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
};
|
||||
Process p = Process.Start(info);
|
||||
p.WaitForExit();
|
||||
p.Close();
|
||||
|
||||
try
|
||||
{
|
||||
using (Process p = Process.Start(info))
|
||||
{
|
||||
p.WaitForExit();
|
||||
p.Close();
|
||||
}
|
||||
}
|
||||
catch (Exception) {}
|
||||
#else
|
||||
|
||||
//Required?
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#endregion
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// Allgemeine Informationen über eine Assembly werden über die folgenden
|
||||
|
||||
@@ -95,8 +95,7 @@
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugWin|Win32'">
|
||||
<LinkIncremental>
|
||||
</LinkIncremental>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>bin\$(Configuration).$(Platform)\</OutDir>
|
||||
<IncludePath>$(MSBuildProjectDirectory)\x86\lib\glib-2.0\include;$(MSBuildProjectDirectory)\x86\include\gstreamer-1.0;$(MSBuildProjectDirectory)\x86\include\glib-2.0;$(MSBuildProjectDirectory)\x86\include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(MSBuildProjectDirectory)\x86\lib;$(LibraryPath)</LibraryPath>
|
||||
@@ -104,12 +103,12 @@
|
||||
<IntDir>obj\$(Configuration).$(Platform)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugWin|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>bin\$(Configuration).$(Platform)\</OutDir>
|
||||
<IncludePath>$(MSBuildProjectDirectory)\x64\lib\glib-2.0\include;$(MSBuildProjectDirectory)\x64\include\gstreamer-1.0;$(MSBuildProjectDirectory)\x64\include\glib-2.0;$(MSBuildProjectDirectory)\x64\include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(MSBuildProjectDirectory)\x64\lib;$(LibraryPath)</LibraryPath>
|
||||
<CodeAnalysisRuleSet>..\CheckingRules.ruleset</CodeAnalysisRuleSet>
|
||||
<IntDir>obj\$(Configuration).$(Platform)\</IntDir>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseWin|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
+1
-54
@@ -618,57 +618,4 @@ an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
END OF TERMS AND CONDITIONS
|
||||
@@ -0,0 +1,18 @@
|
||||
AForge.NET Framework
|
||||
http://www.aforgenet.com/framework
|
||||
==================================
|
||||
|
||||
License agreement
|
||||
|
||||
Due to some external dependencies, the framework does not have a common
|
||||
single license. The AForge.Video.FFMPEG component is GPL v3 licensed, because
|
||||
it depends on the GPL build of the FFMPEG library. The rest of the framework
|
||||
is LGPL v3 licensed.
|
||||
|
||||
Both licenses can be found in the gpl-3.0.txt and lgpl-3.0.txt files, or
|
||||
the next web pages:
|
||||
http://www.gnu.org/licenses/gpl.html
|
||||
http://www.gnu.org/licenses/lgpl.html
|
||||
|
||||
Copyright © AForge.NET, 2006-2013
|
||||
contacts@aforgenet.com
|
||||
@@ -0,0 +1,7 @@
|
||||
http://code.google.com/p/aforge
|
||||
|
||||
Version: 2.2.5
|
||||
|
||||
Files:
|
||||
AForge.Video.dll
|
||||
AForge.Video.DirectShow.dll
|
||||
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If 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 convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
@@ -0,0 +1,166 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
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 that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU 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 as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
@@ -0,0 +1,7 @@
|
||||
https://code.google.com/p/portaudiosharp/
|
||||
|
||||
Version: rev44 (Feb 26, 2012)
|
||||
|
||||
Files:
|
||||
PortAudioSharp.dll
|
||||
PortAudioSharp.dll.config
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,81 @@
|
||||
Portable header file to contain:
|
||||
>>>>>
|
||||
/*
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* PortAudio API Header File
|
||||
* Latest version available at: http://www.portaudio.com
|
||||
*
|
||||
* Copyright (c) 1999-2006 Ross Bencina and Phil Burk
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The text above constitutes the entire PortAudio license; however,
|
||||
* the PortAudio community also makes the following non-binding requests:
|
||||
*
|
||||
* Any person wishing to distribute modifications to the Software is
|
||||
* requested to send the modifications to the original developer so that
|
||||
* they can be incorporated into the canonical version. It is also
|
||||
* requested that these non-binding requests be included along with the
|
||||
* license above.
|
||||
*/
|
||||
<<<<<
|
||||
|
||||
|
||||
Implementation files to contain:
|
||||
>>>>>
|
||||
/*
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* Latest version at: http://www.portaudio.com
|
||||
* <platform> Implementation
|
||||
* Copyright (c) 1999-2000 <author(s)>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The text above constitutes the entire PortAudio license; however,
|
||||
* the PortAudio community also makes the following non-binding requests:
|
||||
*
|
||||
* Any person wishing to distribute modifications to the Software is
|
||||
* requested to send the modifications to the original developer so that
|
||||
* they can be incorporated into the canonical version. It is also
|
||||
* requested that these non-binding requests be included along with the
|
||||
* license above.
|
||||
*/
|
||||
<<<<<
|
||||
@@ -0,0 +1,98 @@
|
||||
README for PortAudio
|
||||
|
||||
/*
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* Latest Version at: http://www.portaudio.com
|
||||
*
|
||||
* Copyright (c) 1999-2008 Phil Burk and Ross Bencina
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The text above constitutes the entire PortAudio license; however,
|
||||
* the PortAudio community also makes the following non-binding requests:
|
||||
*
|
||||
* Any person wishing to distribute modifications to the Software is
|
||||
* requested to send the modifications to the original developer so that
|
||||
* they can be incorporated into the canonical version. It is also
|
||||
* requested that these non-binding requests be included along with the
|
||||
* license above.
|
||||
*/
|
||||
|
||||
|
||||
PortAudio is a portable audio I/O library designed for cross-platform
|
||||
support of audio. It uses either a callback mechanism to request audio
|
||||
processing, or blocking read/write calls to buffer data between the
|
||||
native audio subsystem and the client. Audio can be processed in various
|
||||
formats, including 32 bit floating point, and will be converted to the
|
||||
native format internally.
|
||||
|
||||
Documentation:
|
||||
Documentation is available in "/doc/html/index.html"
|
||||
Also see "src/common/portaudio.h" for API spec.
|
||||
Also see http://www.portaudio.com/docs/
|
||||
And see the "test/" directory for many examples of usage
|
||||
(we suggest "test/patest_saw.c" for an example)
|
||||
|
||||
For information on compiling programs with PortAudio, please see the
|
||||
tutorial at:
|
||||
|
||||
http://portaudio.com/trac/wiki/TutorialDir/TutorialStart
|
||||
|
||||
We have an active mailing list for user and developer discussions.
|
||||
Please feel free to join. See http://www.portaudio.com for details.
|
||||
|
||||
|
||||
Important Files and Folders:
|
||||
include/portaudio.h = header file for PortAudio API. Specifies API.
|
||||
src/common/ = platform independant code, host independant
|
||||
code for all implementations.
|
||||
src/os = os specific (but host api neutral) code
|
||||
src/hostapi = implementations for different host apis
|
||||
|
||||
|
||||
Host API Implementations:
|
||||
src/hostapi/alsa = Advanced Linux Sound Architecture (ALSA)
|
||||
src/hostapi/asihpi = AudioScience HPI
|
||||
src/hostapi/asio = ASIO for Windows and Macintosh
|
||||
src/hostapi/coreaudio = Macintosh Core Audio for OS X
|
||||
src/hostapi/dsound = Windows Direct Sound
|
||||
src/hostapi/jack = JACK Audio Connection Kit
|
||||
src/hostapi/oss = Unix Open Sound System (OSS)
|
||||
src/hostapi/wasapi = Windows Vista WASAPI
|
||||
src/hostapi/wdmks = Windows WDM Kernel Streaming
|
||||
src/hostapi/wmme = Windows MultiMedia Extensions (MME)
|
||||
|
||||
|
||||
Test Programs:
|
||||
test/pa_fuzz.c = guitar fuzz box
|
||||
test/pa_devs.c = print a list of available devices
|
||||
test/pa_minlat.c = determine minimum latency for your machine
|
||||
test/paqa_devs.c = self test that opens all devices
|
||||
test/paqa_errs.c = test error detection and reporting
|
||||
test/patest_clip.c = hear a sine wave clipped and unclipped
|
||||
test/patest_dither.c = hear effects of dithering (extremely subtle)
|
||||
test/patest_pink.c = fun with pink noise
|
||||
test/patest_record.c = record and playback some audio
|
||||
test/patest_maxsines.c = how many sine waves can we play? Tests Pa_GetCPULoad().
|
||||
test/patest_sine.c = output a sine wave in a simple PA app
|
||||
test/patest_sync.c = test syncronization of audio and video
|
||||
test/patest_wire.c = pass input to output, wire simulator
|
||||
@@ -0,0 +1,7 @@
|
||||
http://www.portaudio.com/
|
||||
|
||||
Version: January 30, 2014, SVN rev 1919
|
||||
|
||||
Files:
|
||||
x86/PortAudio.dll
|
||||
x64/PortAudio.dll
|
||||
@@ -1,5 +1,3 @@
|
||||
// QRCode.dll from https://github.com/codebude/QRCoder/
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Raffael Herrmann
|
||||
@@ -0,0 +1,6 @@
|
||||
https://github.com/codebude/QRCoder/
|
||||
|
||||
Version: master 19.01.2014
|
||||
|
||||
Files:
|
||||
QRCoder.dll
|
||||
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2007-2012 SlimDX Group
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,7 @@
|
||||
http://slimdx.org/download.php
|
||||
|
||||
SVN r2171 (Feb 4, 2012) -> Equal to Jan 12 release but recompiled with VS2012
|
||||
|
||||
Files:
|
||||
x86/SlimDx.dll
|
||||
x64/SlimDx.dll
|
||||
Binary file not shown.
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration><startup><supportedRuntime version="v2.0.50727" /></startup>
|
||||
<dllmap dll="PortAudio.dll" target="libportaudio.so.2" />
|
||||
</configuration>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -17,10 +17,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using VocaluxeLib.Menu;
|
||||
using VocaluxeLib.Profile;
|
||||
|
||||
namespace VocaluxeLib.PartyModes.Challenge
|
||||
{
|
||||
|
||||
@@ -17,10 +17,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using VocaluxeLib.Menu;
|
||||
using VocaluxeLib.Profile;
|
||||
|
||||
namespace VocaluxeLib.PartyModes.ChallengeMedley
|
||||
{
|
||||
|
||||
@@ -17,10 +17,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using VocaluxeLib.Menu;
|
||||
using VocaluxeLib.Profile;
|
||||
|
||||
namespace VocaluxeLib.PartyModes.TicTacToe
|
||||
{
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
#include "PitchWrapper.h"
|
||||
#include <stdlib.h>
|
||||
#include <cmath>
|
||||
|
||||
float* short2FloatArray(short* in, size_t len){
|
||||
const float maxShort = 32768.0; //maximum abs value of a short
|
||||
|
||||
float* result = static_cast<float*>(malloc(len * sizeof(float)));
|
||||
float* curOut = result;
|
||||
for(size_t i=0; i<len; i++){
|
||||
*curOut = *in / maxShort;
|
||||
curOut++;
|
||||
in++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
double* short2DoubleArray(short* in, size_t len, bool scale = true){
|
||||
const double maxShort = 32768.0; //maximum abs value of a short
|
||||
|
||||
double* result = static_cast<double*>(malloc(len * sizeof(double)));
|
||||
double* curOut = result;
|
||||
for(size_t i=0; i<len; i++){
|
||||
*curOut = *in;
|
||||
if(scale)
|
||||
*curOut /= maxShort;
|
||||
curOut++;
|
||||
in++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void freeFloatArray(float* floats){
|
||||
free(floats);
|
||||
}
|
||||
|
||||
void freeDoubleArray(double* floats){
|
||||
free(floats);
|
||||
}
|
||||
|
||||
double FreqToNote(double freq){
|
||||
return 12.0 * std::log(freq / 127.09) / std::log(2.0) + 27.5 - 16; // 16 = C2
|
||||
}
|
||||
|
||||
/*
|
||||
namespace Native{
|
||||
namespace PitchTracking{
|
||||
CTone^ ConvertToneToManaged(Tone* tone){
|
||||
CTone^ result = gcnew CTone();
|
||||
result->Freq = tone->freq;
|
||||
result->DB = tone->db;
|
||||
result->Stabledb = tone->stabledb;
|
||||
result->Age = static_cast<int>(tone->age);
|
||||
result->NoteExact = 12.0 * std::log(tone->freq / 127.09) / std::log(2.0) + 27.5 - 16; // 16 = C2
|
||||
result->Note = static_cast<int>(std::round(result->NoteExact));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,192 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{54A48AF7-515C-4EDD-B395-2BFFA096050A}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>PitchTracker</RootNamespace>
|
||||
<ProjectName>PitchTracker</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v110_xp</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v110_xp</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v110_xp</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v110_xp</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>bin\$(Configuration).$(Platform)\</OutDir>
|
||||
<IntDir>obj\$(Configuration).$(Platform)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>bin\$(Configuration).$(Platform)\</OutDir>
|
||||
<IntDir>obj\$(Configuration).$(Platform)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>bin\$(Configuration).$(Platform)\</OutDir>
|
||||
<IntDir>obj\$(Configuration).$(Platform)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>bin\$(Configuration).$(Platform)\</OutDir>
|
||||
<IntDir>obj\$(Configuration).$(Platform)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy "$(OutDir)*.dll" "$(SolutionDir)Output\x64\" /I /Y</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy "$(OutDir)*.dll" "$(SolutionDir)Output\x86\" /I /Y</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
<OmitFramePointers>false</OmitFramePointers>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy "$(OutDir)*.dll" "$(SolutionDir)Output\x64\" /I /Y</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy "$(OutDir)*.dll" "$(SolutionDir)Output\x86\" /I /Y</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="compatibility.cpp" />
|
||||
<ClCompile Include="performous\pitch.cc" />
|
||||
<ClCompile Include="ptAKF.cpp" />
|
||||
<ClCompile Include="PitchWrapper.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="compatibility.h" />
|
||||
<ClInclude Include="Helper.h" />
|
||||
<ClInclude Include="performous\libda\fft.hpp" />
|
||||
<ClInclude Include="performous\libda\sample.hpp" />
|
||||
<ClInclude Include="ptAKF.h" />
|
||||
<ClInclude Include="PitchWrapper.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="performous\pitch.hh" />
|
||||
<None Include="performous\util.hh" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Quelldateien">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Headerdateien">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Ressourcendateien">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Quelldateien\performous">
|
||||
<UniqueIdentifier>{a47c9d33-78d2-453d-bb59-e5724cd85f67}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Headerdateien\performous">
|
||||
<UniqueIdentifier>{4c72400a-ff2c-4842-8c20-54deb5df606d}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Headerdateien\performous\libda">
|
||||
<UniqueIdentifier>{d71782a0-47fc-4fe1-8569-0a0ce8fe9b45}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="PitchWrapper.cpp">
|
||||
<Filter>Quelldateien</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="compatibility.cpp">
|
||||
<Filter>Quelldateien</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="performous\pitch.cc">
|
||||
<Filter>Quelldateien\performous</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ptAKF.cpp">
|
||||
<Filter>Quelldateien</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="compatibility.h">
|
||||
<Filter>Headerdateien</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PitchWrapper.h">
|
||||
<Filter>Headerdateien</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Helper.h">
|
||||
<Filter>Headerdateien</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="performous\libda\fft.hpp">
|
||||
<Filter>Headerdateien\performous\libda</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="performous\libda\sample.hpp">
|
||||
<Filter>Headerdateien\performous\libda</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ptAKF.h">
|
||||
<Filter>Headerdateien</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="performous\pitch.hh">
|
||||
<Filter>Headerdateien\performous</Filter>
|
||||
</None>
|
||||
<None Include="performous\util.hh">
|
||||
<Filter>Headerdateien\performous</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,182 @@
|
||||
#include "PitchWrapper.h"
|
||||
#include "Helper.h"
|
||||
|
||||
|
||||
Analyzer* Analyzer_Create(unsigned step){
|
||||
return new Analyzer(44100, "", step);
|
||||
}
|
||||
|
||||
void Analyzer_Free(Analyzer* analyzer){
|
||||
if(analyzer)
|
||||
delete analyzer;
|
||||
}
|
||||
|
||||
void Analyzer_InputFloat(Analyzer* analyzer, float* data, int sampleCt){
|
||||
if(sampleCt == 0 || !analyzer)
|
||||
return;
|
||||
analyzer->input(data, data + sampleCt);
|
||||
}
|
||||
|
||||
void Analyzer_InputShort(Analyzer* analyzer, short* data, int sampleCt){
|
||||
if(sampleCt == 0 || !analyzer)
|
||||
return;
|
||||
float* dataFloat = short2FloatArray(data, sampleCt);
|
||||
analyzer->input(dataFloat, dataFloat + sampleCt);
|
||||
freeFloatArray(dataFloat);
|
||||
}
|
||||
|
||||
void Analyzer_InputByte(Analyzer* analyzer, char* data, int sampleCt){
|
||||
if(sampleCt <= 0 || !analyzer)
|
||||
return;
|
||||
float* dataFloat = short2FloatArray(static_cast<short*>(static_cast<void*>(data)), sampleCt);
|
||||
analyzer->input(dataFloat, dataFloat + sampleCt);
|
||||
freeFloatArray(dataFloat);
|
||||
}
|
||||
|
||||
void Analyzer_Process(Analyzer* analyzer){
|
||||
if(!analyzer)
|
||||
return;
|
||||
analyzer->process();
|
||||
}
|
||||
|
||||
double Analyzer_GetPeak(Analyzer* analyzer){
|
||||
if(!analyzer)
|
||||
return -999;
|
||||
return analyzer->getPeak();
|
||||
}
|
||||
|
||||
double Analyzer_FindNote(Analyzer* analyzer, double minFreq, double maxFreq){
|
||||
if(!analyzer)
|
||||
return -1;
|
||||
const Tone* tone = analyzer->findTone(minFreq, maxFreq);
|
||||
return (tone == NULL) ? -1 : FreqToNote(tone->freq);
|
||||
}
|
||||
|
||||
bool Analyzer_OutputFloat(Analyzer* analyzer, float* data, int sampleCt, float rate){
|
||||
if(!analyzer)
|
||||
return false;
|
||||
return analyzer->output(data, data + sampleCt, rate);
|
||||
}
|
||||
|
||||
DllExport PtAKF* PtAKF_Create(){
|
||||
return new PtAKF();
|
||||
}
|
||||
|
||||
DllExport void PtAKF_Free(PtAKF* analyzer){
|
||||
if(analyzer)
|
||||
delete analyzer;
|
||||
}
|
||||
|
||||
DllExport int PtAKF_GetNumHalfTones(){
|
||||
return PtAKF::GetNumHalfTones();
|
||||
}
|
||||
|
||||
DllExport void PtAKF_InputByte(PtAKF* analyzer, char* data, int sampleCt){
|
||||
if(sampleCt <= 0 || !analyzer)
|
||||
return;
|
||||
short* dataShort = static_cast<short*>(static_cast<void*>(data));
|
||||
analyzer->input(dataShort, dataShort + sampleCt);
|
||||
}
|
||||
|
||||
DllExport int PtAKF_GetNote(PtAKF* analyzer, double* maxVolume, float* weights){
|
||||
if(!analyzer)
|
||||
return -1;
|
||||
return analyzer->GetNote(maxVolume, weights);
|
||||
}
|
||||
|
||||
/*
|
||||
namespace Native{
|
||||
namespace PitchTracking{
|
||||
|
||||
//CAnalyzer
|
||||
|
||||
void CAnalyzer::Input(array<float>^ data){
|
||||
if(data->Length == 0)
|
||||
return;
|
||||
pin_ptr<float> dataPtr = &data[0];
|
||||
float* dataPtr2 = dataPtr;
|
||||
m_analyzer->input(dataPtr2, dataPtr2 + data->Length);
|
||||
}
|
||||
|
||||
void CAnalyzer::Input(array<short>^ data){
|
||||
if(data->Length == 0)
|
||||
return;
|
||||
pin_ptr<short> dataPtr = &data[0];
|
||||
float* dataFloat = short2FloatArray(dataPtr, data->Length);
|
||||
m_analyzer->input(dataFloat, dataFloat + data->Length);
|
||||
freeFloatArray(dataFloat);
|
||||
}
|
||||
|
||||
void CAnalyzer::Input(array<Byte>^ data){
|
||||
if(data->Length == 0)
|
||||
return;
|
||||
pin_ptr<Byte> dataPtr = &data[0];
|
||||
float* dataFloat = short2FloatArray(static_cast<short*>(static_cast<void*>(dataPtr)), data->Length / 2);
|
||||
m_analyzer->input(dataFloat, dataFloat + data->Length);
|
||||
freeFloatArray(dataFloat);
|
||||
}
|
||||
|
||||
void CAnalyzer::Process(){
|
||||
m_analyzer->process();
|
||||
}
|
||||
|
||||
double CAnalyzer::GetPeak(){
|
||||
return m_analyzer->getPeak();
|
||||
}
|
||||
|
||||
array<CTone^>^ CAnalyzer::GetTones(){
|
||||
Analyzer::tones_t umTones = m_analyzer->getTones();
|
||||
array<CTone^>^ tones = gcnew array<CTone^>(static_cast<int>(umTones.size()));
|
||||
int i = 0;
|
||||
for(auto it=umTones.begin(); it!=umTones.end(); it++, i++){
|
||||
tones[i]=ConvertToneToManaged(*it);
|
||||
}
|
||||
return tones;
|
||||
}
|
||||
|
||||
CTone^ CAnalyzer::FindTone(){
|
||||
return FindTone(65.0);
|
||||
}
|
||||
|
||||
CTone^ CAnalyzer::FindTone(double minFreq){
|
||||
return FindTone(minFreq, 1000.0);
|
||||
}
|
||||
|
||||
CTone^ CAnalyzer::FindTone(double minFreq, double maxFreq){
|
||||
const Tone* tone = m_analyzer->findTone(minFreq, maxFreq);
|
||||
return (tone == NULL) ? nullptr : ConvertToneToManaged(tone);
|
||||
}
|
||||
|
||||
void CAnalyzer::Output(array<float>^ data, float rate){
|
||||
pin_ptr<float> dataPtr = &data[0];
|
||||
m_analyzer->output(dataPtr, dataPtr + data->Length, rate);
|
||||
}
|
||||
|
||||
String^ CAnalyzer::GetId(){
|
||||
return m_id;
|
||||
}
|
||||
|
||||
|
||||
//CPitchTracker
|
||||
|
||||
void CPitchTracker::Init(double baseToneFrequency, int minHalfTone, int maxHalfTone){
|
||||
PitchTracker::Init(baseToneFrequency, minHalfTone, maxHalfTone);
|
||||
}
|
||||
|
||||
void CPitchTracker::DeInit(){
|
||||
PitchTracker::DeInit();
|
||||
}
|
||||
|
||||
int CPitchTracker::GetTone(array<short>^ samples, array<float>^ weights){
|
||||
if(samples->Length == 0)
|
||||
return -1;
|
||||
pin_ptr<short> dataPtr = &samples[0];
|
||||
pin_ptr<float> weightsPtr = &weights[0];
|
||||
double* dataDouble = short2DoubleArray(dataPtr, samples->Length, false);
|
||||
int result = PitchTracker::GetTone(dataDouble, samples->Length, weightsPtr, true);
|
||||
freeDoubleArray(dataDouble);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include "performous/pitch.hh"
|
||||
#include "ptAKF.h"
|
||||
|
||||
#define DllExport extern "C" __declspec(dllexport)
|
||||
|
||||
DllExport Analyzer* Analyzer_Create(unsigned step);
|
||||
DllExport void Analyzer_Free(Analyzer* analyzer);
|
||||
DllExport void Analyzer_InputFloat(Analyzer* analyzer, float* data, int sampleCt);
|
||||
DllExport void Analyzer_InputShort(Analyzer* analyzer, short* data, int sampleCt);
|
||||
DllExport void Analyzer_InputByte(Analyzer* analyzer, char* data, int sampleCt);
|
||||
DllExport void Analyzer_Process(Analyzer* analyzer);
|
||||
DllExport double Analyzer_GetPeak(Analyzer* analyzer);
|
||||
DllExport double Analyzer_FindNote(Analyzer* analyzer, double minFreq, double maxFreq);
|
||||
DllExport bool Analyzer_OutputFloat(Analyzer* analyzer, float* data, int sampleCt, float rate);
|
||||
|
||||
DllExport PtAKF* PtAKF_Create();
|
||||
DllExport void PtAKF_Free(PtAKF* analyzer);
|
||||
DllExport int PtAKF_GetNumHalfTones();
|
||||
DllExport void PtAKF_InputByte(PtAKF* analyzer, char* data, int sampleCt);
|
||||
DllExport int PtAKF_GetNote(PtAKF* analyzer, double* maxVolume, float* weights);
|
||||
|
||||
/*
|
||||
public ref class CTone {
|
||||
public:
|
||||
static const int MAXHARM = 48; ///< The maximum number of harmonics tracked
|
||||
static const int MINAGE = 2; ///< The minimum age required for a tone to be output
|
||||
double Freq; ///< Frequency (Hz)
|
||||
double DB; ///< Level (dB)
|
||||
double Stabledb; ///< Stable level, useful for graphics rendering
|
||||
int Age; ///< How many times the tone has been detected in row
|
||||
double NoteExact;
|
||||
int Note;
|
||||
/// Less-than compare by levels (instead of frequencies like operator< does)
|
||||
static bool dbCompare(CTone l, CTone r) { return l.DB < r.DB; }
|
||||
};
|
||||
|
||||
public ref class CAnalyzer {
|
||||
public:
|
||||
CAnalyzer(double rate, String^ id) {
|
||||
m_analyzer = new Analyzer(rate,"");
|
||||
m_id = id;
|
||||
}
|
||||
CAnalyzer(double rate, String^ id, unsigned step) {
|
||||
m_analyzer = new Analyzer(rate,"",step);
|
||||
m_id = id;
|
||||
}
|
||||
~CAnalyzer() { this->!CAnalyzer(); }
|
||||
!CAnalyzer() { delete m_analyzer; }
|
||||
|
||||
void Input(array<float>^ data);
|
||||
void Input(array<short>^ data);
|
||||
void Input(array<Byte>^ data);
|
||||
void Process();
|
||||
double GetPeak();
|
||||
array<CTone^>^ GetTones();
|
||||
CTone^ FindTone();
|
||||
CTone^ FindTone(double minFreq);
|
||||
CTone^ FindTone(double minFreq, double maxFreq);
|
||||
void Output(array<float>^ data, float rate);
|
||||
String^ GetId();
|
||||
|
||||
private:
|
||||
Analyzer* m_analyzer;
|
||||
String^ m_id;
|
||||
};
|
||||
|
||||
public ref class CPitchTracker abstract sealed{
|
||||
public:
|
||||
static void Init(double baseToneFrequency, int minHalfTone, int maxHalfTone);
|
||||
static void DeInit();
|
||||
static int GetTone(array<short>^ samples, array<float>^ weights);
|
||||
};
|
||||
*/
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "compatibility.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#if(_MSC_VER < 1800) // < VS2013
|
||||
namespace std
|
||||
{
|
||||
float round(float d)
|
||||
{
|
||||
return static_cast<float>(floor(d + 0.5));
|
||||
}
|
||||
|
||||
double round(double d)
|
||||
{
|
||||
return floor(d + 0.5);
|
||||
}
|
||||
|
||||
long double round(long double d)
|
||||
{
|
||||
return floor(d + 0.5);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
double remainder(double num, double denom){
|
||||
return num - std::round(num/denom) * denom;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,17 @@
|
||||
//Contains some definitions to make the code work with compilers that do not support all feautures
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define constexpr const
|
||||
#if(_MSC_VER < 1800) // < VS2013
|
||||
namespace std
|
||||
{
|
||||
float round(float d);
|
||||
double round(double d);
|
||||
long double round(long double d);
|
||||
}
|
||||
|
||||
double remainder(double num, double denom);
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
code is based on Performous: https://github.com/performous/performous/tree/master/game
|
||||
Some modifications were made to make it compile with VS2012, fix warnings and make it suitable for our needs
|
||||
From 25.03.2014
|
||||
@@ -0,0 +1,346 @@
|
||||
GNU GPLv2 or later. Some parts of the project may have more liberal licenses.
|
||||
See Authors.txt for information on copyright holders and licenses of specific
|
||||
parts. Fonts are licensed under SIL OFL.
|
||||
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @file fft.hpp FFT and related facilities.
|
||||
*/
|
||||
|
||||
#include "sample.hpp"
|
||||
#include <complex>
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.141592653589793
|
||||
#endif
|
||||
|
||||
#define M_TAU (2.0 * M_PI)
|
||||
|
||||
namespace da {
|
||||
|
||||
// With g++ optimization -fcx-limited-range should be used for 5x performance boost.
|
||||
|
||||
// Based on the description of Volodymyr Myrnyy in
|
||||
// http://www.dspdesignline.com/showArticle.jhtml?printableArticle=true&articleId=199903272
|
||||
template<unsigned P, typename T> struct DanielsonLanczos {
|
||||
static void apply(std::complex<T>* data) {
|
||||
constexpr std::size_t N = 1 << P;
|
||||
constexpr std::size_t M = N / 2;
|
||||
// Compute even and odd halves
|
||||
DanielsonLanczos<P - 1, T>().apply(data);
|
||||
DanielsonLanczos<P - 1, T>().apply(data + M);
|
||||
// Combine the results
|
||||
std::complex<T> w(1.0);
|
||||
for (std::size_t i = 0; i < M; ++i) {
|
||||
const std::complex<T> temp = data[i + M] * w;
|
||||
data[M + i] = data[i] - temp;
|
||||
data[i] += temp;
|
||||
w *= std::polar(1.0, -M_TAU / N);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> struct DanielsonLanczos<0, T> { static void apply(std::complex<T>*) {} };
|
||||
|
||||
/** Perform FFT on data. **/
|
||||
template<unsigned P, typename T> void fft(std::complex<T>* data) {
|
||||
// Perform bit-reversal sorting of sample data.
|
||||
constexpr std::size_t N = 1 << P;
|
||||
std::size_t j = 0;
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
if (i < j) std::swap(data[i], data[j]);
|
||||
std::size_t m = N / 2;
|
||||
while (m > 1 && m <= j) { j -= m; m >>= 1; }
|
||||
j += m;
|
||||
}
|
||||
// Do the actual calculation
|
||||
DanielsonLanczos<P, T>::apply(data);
|
||||
}
|
||||
|
||||
/** Perform FFT on data from floating point iterator, windowing the input. **/
|
||||
template<unsigned P, typename InIt, typename Window> std::vector<std::complex<float> > fft(InIt begin, Window window) {
|
||||
std::vector<std::complex<float> > data(1 << P);
|
||||
// Perform bit-reversal sorting of sample data.
|
||||
constexpr std::size_t N = 1 << P;
|
||||
std::size_t j = 0;
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
data[j] = *begin++ * window[i];
|
||||
std::size_t m = N / 2;
|
||||
while (m > 1 && m <= j) { j -= m; m >>= 1; }
|
||||
j += m;
|
||||
}
|
||||
// Do the actual calculation
|
||||
DanielsonLanczos<P, float>::apply(&data[0]);
|
||||
return data;
|
||||
}
|
||||
|
||||
template<unsigned P, typename T> void ifft(std::complex<T>* data) {
|
||||
constexpr std::size_t N = 1 << P;
|
||||
for (std::size_t i = 0; i < N; ++i) data[i] = std::conj(data[i]); // Invert phase so that we can use FFT to do IFFT
|
||||
fft<P>(data);
|
||||
constexpr std::complex<T> scale(1.0/N, 0.0);
|
||||
for (std::size_t i = 0; i < N; ++i) data[i] = scale * std::conj(data[i]); // Invert back, and apply IFFT scaling
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include "../compatibility.h"
|
||||
|
||||
/**
|
||||
* @file sample.hpp Sample format definition and format conversions.
|
||||
*/
|
||||
|
||||
namespace da {
|
||||
|
||||
using std::round;
|
||||
|
||||
// Should be a floating-point type
|
||||
typedef float sample_t;
|
||||
|
||||
// A helper function for clamping a value to a certain range
|
||||
template <typename T> T clamp(T val, T min, T max) {
|
||||
if (val < min) val = min;
|
||||
if (val > max) val = max;
|
||||
return val;
|
||||
}
|
||||
|
||||
constexpr sample_t max_s16(32767), min_s16 = -max_s16 - sample_t(1);
|
||||
constexpr sample_t max_s24(8388607), min_s24 = -max_s24 - sample_t(1);
|
||||
constexpr sample_t max_s32(2147483647), min_s32= -max_s32 - sample_t(1);
|
||||
constexpr double tau(6.2831853071795864769252867665590057683943387987502116); ///< One full circle
|
||||
constexpr double pi = tau / sample_t(2); ///< Half circle
|
||||
constexpr double eps(1e-10); ///< Tiny positive value
|
||||
|
||||
/// Mathematical sinc function
|
||||
template<typename Float> static inline Float msinc(Float x) { return std::abs(x) < Float(eps) ? Float(1) : std::sin(x) / x; }
|
||||
|
||||
/// Normalized (signal processing) sinc function
|
||||
template<typename Float> static inline Float sinc(Float x) { return msinc(Float(pi) * x); }
|
||||
|
||||
/// Lanczos kernel of size A
|
||||
template<unsigned A, typename Float> static inline Float lanc(Float x) {
|
||||
return std::abs(x) < A ? sinc(x) * sinc(x / A) : Float();
|
||||
}
|
||||
|
||||
// The following conversions provide lossless conversions between floats
|
||||
// and integers. Be sure to use only these conversions or otherwise the
|
||||
// conversions may not be lossless, due to different scaling factors being
|
||||
// used by different libraries.
|
||||
|
||||
// The negative minimum integer value produces sample_t value slightly
|
||||
// more negative than -1.0 but this is necessary in order to prevent
|
||||
// clipping in the float-to-int conversions. Now amplitude 1.0 in floating
|
||||
// point produces -32767 .. 32767 symmetrical non-clipping range in s16.
|
||||
|
||||
static inline sample_t conv_from_s16(int s) { return s / max_s16; }
|
||||
static inline sample_t conv_from_s24(int s) { return s / max_s24; }
|
||||
static inline sample_t conv_from_s32(int s) { return s / max_s32; }
|
||||
// The rounding is strictly not necessary, but it greatly improves
|
||||
// the error tolerance if any floating point calculations are done.
|
||||
// The ugly static_casts are required to avoid warnings in MSVC.
|
||||
static inline int conv_to_s16(sample_t s) { return clamp(static_cast<int>(round(s * max_s16)), static_cast<int>(min_s16), static_cast<int>(max_s16)); }
|
||||
static inline int conv_to_s24(sample_t s) { return clamp(static_cast<int>(round(s * max_s24)), static_cast<int>(min_s24), static_cast<int>(max_s24)); }
|
||||
static inline int conv_to_s32(sample_t s) { return static_cast<int>(clamp(round(s * max_s32), min_s32, max_s32 )); }
|
||||
// Non-rounding non-clamping versions are provided for very low end devices (still lossless)
|
||||
static inline int conv_to_s16_fast(sample_t s) { return static_cast<int>(s * max_s16); }
|
||||
static inline int conv_to_s24_fast(sample_t s) { return static_cast<int>(s * max_s24); }
|
||||
static inline int conv_to_s32_fast(sample_t s) { return static_cast<int>(s * max_s32); }
|
||||
|
||||
template <typename ValueType> class step_iterator: public std::iterator<std::random_access_iterator_tag, ValueType> {
|
||||
ValueType* m_pos;
|
||||
std::ptrdiff_t m_step;
|
||||
public:
|
||||
step_iterator(ValueType* pos, std::ptrdiff_t step): m_pos(pos), m_step(step) {}
|
||||
ValueType& operator*() { return *m_pos; }
|
||||
step_iterator operator+(std::ptrdiff_t rhs) { return step_iterator(m_pos + m_step * rhs, m_step); }
|
||||
step_iterator& operator++() { m_pos += m_step; return *this; }
|
||||
step_iterator operator++(int) { step_iterator ret = *this; ++*this; return ret; }
|
||||
bool operator!=(step_iterator const& rhs) const { return m_pos != rhs.m_pos; }
|
||||
std::ptrdiff_t operator-(step_iterator const& rhs) const { return (m_pos - rhs.m_pos) / m_step; }
|
||||
// TODO: more operators
|
||||
};
|
||||
|
||||
typedef step_iterator<sample_t> sample_iterator;
|
||||
typedef step_iterator<sample_t const> sample_const_iterator;
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
#include "pitch.hh"
|
||||
|
||||
#include "util.hh"
|
||||
#include "libda/fft.hpp"
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
// Limit the range to avoid noise and useless computation
|
||||
static const double FFT_MINFREQ = 45.0;
|
||||
static const double FFT_MAXFREQ = 5000.0;
|
||||
|
||||
Tone::Tone():
|
||||
freq(0.0),
|
||||
db(-getInf()),
|
||||
stabledb(-getInf()),
|
||||
age()
|
||||
{
|
||||
for (auto& h: harmonics) h = -getInf();
|
||||
}
|
||||
|
||||
void Tone::print() const {
|
||||
if (age < Tone::MINAGE) return;
|
||||
std::cout << std::fixed << std::setprecision(1) << freq << " Hz, age " << age << ", " << db << " dB:";
|
||||
for (std::size_t i = 0; i < 8; ++i) std::cout << " " << harmonics[i];
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
bool Tone::operator==(double f) const {
|
||||
return std::abs(freq / f - 1.0) < 0.05;
|
||||
}
|
||||
|
||||
Analyzer::Analyzer(double rate, std::string id, unsigned step):
|
||||
m_step(step),
|
||||
m_resampleFactor(1.0),
|
||||
m_resamplePos(),
|
||||
m_rate(rate),
|
||||
m_id(id),
|
||||
m_window(FFT_N),
|
||||
m_fftLastPhase(FFT_N / 2),
|
||||
m_peak(0.0),
|
||||
m_oldfreq(0.0)
|
||||
{
|
||||
if (m_step > FFT_N) throw std::logic_error("Analyzer step is larger that FFT_N (ideally it should be less than a fourth of FFT_N).");
|
||||
// Hamming window
|
||||
for (size_t i=0; i < FFT_N; i++) {
|
||||
m_window[i] = static_cast<float>(0.53836 - 0.46164 * std::cos(2.0 * M_PI * i / (FFT_N - 1)));
|
||||
}
|
||||
}
|
||||
|
||||
bool Analyzer::output(float* begin, float* end, double rate) {
|
||||
constexpr unsigned a = 2;
|
||||
const size_t size = m_passthrough.size();
|
||||
const unsigned out = static_cast<unsigned>((end - begin) / 2 /* stereo */);
|
||||
if (out == 0) return false;
|
||||
const unsigned in = static_cast<unsigned>(m_resampleFactor * (m_rate / rate) * out + 2 * a /* lanczos kernel */ + 5 /* safety margin for rounding errors */);
|
||||
float pcm[m_passthrough.capacity];
|
||||
if(!m_passthrough.read(pcm, pcm + in + 4))
|
||||
return false;
|
||||
for (unsigned i = 0; i < out; ++i) {
|
||||
double s = 0.0;
|
||||
unsigned k = static_cast<unsigned>(m_resamplePos);
|
||||
double x = m_resamplePos - k;
|
||||
// Lanczos sampling of input at m_resamplePos
|
||||
for (unsigned j = 0; j <= 2 * a; ++j) s += pcm[k + j] * da::lanc<a>(x - j + a);
|
||||
s *= 5.0;
|
||||
begin[i * 2] += (float) s;
|
||||
begin[i * 2 + 1] += (float) s;
|
||||
m_resamplePos += m_resampleFactor;
|
||||
}
|
||||
unsigned num = static_cast<unsigned>(m_resamplePos);
|
||||
m_resamplePos -= num;
|
||||
if (size > 3000) {
|
||||
// Reset
|
||||
m_passthrough.pop(size - 700);
|
||||
m_resampleFactor = 1.0;
|
||||
} else {
|
||||
m_passthrough.pop(num);
|
||||
m_resampleFactor = 0.99 * m_resampleFactor + 0.01 * (size > 700 ? 1.02 : 0.98);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
struct Peak {
|
||||
double freq;
|
||||
double db;
|
||||
bool harm[Tone::MAXHARM];
|
||||
Peak(double _freq = 0.0, double _db = -getInf()):
|
||||
freq(_freq), db(_db)
|
||||
{
|
||||
for (auto& h: harm) h = false;
|
||||
}
|
||||
void clear() {
|
||||
freq = 0.0;
|
||||
db = -getInf();
|
||||
}
|
||||
};
|
||||
|
||||
Peak& match(std::vector<Peak>& peaks, std::size_t pos) {
|
||||
std::size_t best = pos;
|
||||
if (peaks[pos - 1].db > peaks[best].db) best = pos - 1;
|
||||
if (peaks[pos + 1].db > peaks[best].db) best = pos + 1;
|
||||
return peaks[best];
|
||||
}
|
||||
}
|
||||
|
||||
bool Analyzer::calcFFT() {
|
||||
float pcm[FFT_N];
|
||||
// Read FFT_N samples, move forward by m_step samples
|
||||
if (!m_buf.read(pcm, pcm + FFT_N)) return false;
|
||||
m_buf.pop(m_step);
|
||||
// Peak level calculation of the most recent m_step samples (the rest is overlap)
|
||||
for (float const* ptr = pcm + FFT_N - m_step; ptr != pcm + FFT_N; ++ptr) {
|
||||
float s = *ptr;
|
||||
float p = s * s;
|
||||
if (p > m_peak) m_peak = p; else m_peak *= 0.999;
|
||||
}
|
||||
// Calculate FFT
|
||||
m_fft = da::fft<FFT_P>(pcm, m_window);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Analyzer::calcTones() {
|
||||
// Precalculated constants
|
||||
const double freqPerBin = m_rate / FFT_N;
|
||||
const double phaseStep = 2.0 * M_PI * m_step / FFT_N;
|
||||
const double normCoeff = 1.0 / FFT_N;
|
||||
const double minMagnitude = pow(10, -100.0 / 20.0) / normCoeff; // -100 dB
|
||||
// Limit frequency range of processing
|
||||
const size_t kMin = std::max(size_t(1), size_t(FFT_MINFREQ / freqPerBin));
|
||||
const size_t kMax = std::min(FFT_N / 2, size_t(FFT_MAXFREQ / freqPerBin));
|
||||
std::vector<Peak> peaks(kMax + 1); // One extra to simplify loops
|
||||
for (size_t k = 1; k <= kMax; ++k) {
|
||||
double magnitude = std::abs(m_fft[k]);
|
||||
double phase = std::arg(m_fft[k]);
|
||||
// process phase difference
|
||||
double delta = phase - m_fftLastPhase[k];
|
||||
m_fftLastPhase[k] = static_cast<float>(phase);
|
||||
delta -= k * phaseStep; // subtract expected phase difference
|
||||
delta = remainder(delta, 2.0 * M_PI); // map delta phase into +/- M_PI interval
|
||||
delta /= phaseStep; // calculate diff from bin center frequency
|
||||
double freq = (k + delta) * freqPerBin; // calculate the true frequency
|
||||
if (freq > 1.0 && magnitude > minMagnitude) {
|
||||
peaks[k].freq = freq;
|
||||
peaks[k].db = 20.0 * log10(normCoeff * magnitude);
|
||||
}
|
||||
}
|
||||
// Prefilter peaks
|
||||
double prevdb = peaks[0].db;
|
||||
for (size_t k = 1; k < kMax; ++k) {
|
||||
double db = peaks[k].db;
|
||||
if (db > prevdb) peaks[k - 1].clear();
|
||||
if (db < prevdb) peaks[k].clear();
|
||||
prevdb = db;
|
||||
}
|
||||
// Find the tones (collections of harmonics) from the array of peaks
|
||||
tones_t tones;
|
||||
for (size_t k = kMax - 1; k >= kMin; --k) {
|
||||
if (peaks[k].db < -70.0) continue;
|
||||
// Find the best divider for getting the fundamental from peaks[k]
|
||||
std::size_t bestDiv = 1;
|
||||
int bestScore = 0;
|
||||
for (std::size_t div = 2; div <= Tone::MAXHARM && k / div > 1; ++div) {
|
||||
double freq = peaks[k].freq / div; // Fundamental
|
||||
int score = 0;
|
||||
for (std::size_t n = 1; n < div && n < 8; ++n) {
|
||||
Peak& p = match(peaks, k * n / div);
|
||||
--score;
|
||||
if (p.db < -90.0 || std::abs(p.freq / n / freq - 1.0) > .03) continue;
|
||||
if (n == 1) score += 4; // Extra for fundamental
|
||||
score += 2;
|
||||
}
|
||||
if (score > bestScore) {
|
||||
bestScore = score;
|
||||
bestDiv = div;
|
||||
}
|
||||
}
|
||||
// Construct a Tone by combining the fundamental frequency (freq) and all harmonics
|
||||
Tone t;
|
||||
std::size_t count = 0;
|
||||
double freq = peaks[k].freq / bestDiv;
|
||||
t.db = peaks[k].db;
|
||||
for (std::size_t n = 1; n <= bestDiv; ++n) {
|
||||
// Find the peak for n'th harmonic
|
||||
Peak& p = match(peaks, k * n / bestDiv);
|
||||
if (std::abs(p.freq / n / freq - 1.0) > .03) continue; // Does it match the fundamental freq?
|
||||
if (p.db > t.db - 10.0) {
|
||||
t.db = std::max(t.db, p.db);
|
||||
++count;
|
||||
t.freq += p.freq / n;
|
||||
}
|
||||
t.harmonics[n - 1] = p.db;
|
||||
p.clear();
|
||||
}
|
||||
t.freq /= count;
|
||||
// If the tone seems strong enough, add it (-3 dB compensation for each harmonic)
|
||||
if (t.db > -50.0 - 3.0 * count) {
|
||||
t.stabledb = t.db;
|
||||
tones.push_back(t);
|
||||
}
|
||||
}
|
||||
mergeWithOld(tones);
|
||||
m_tones.swap(tones);
|
||||
}
|
||||
|
||||
void Analyzer::mergeWithOld(tones_t& tones) const {
|
||||
tones.sort();
|
||||
auto it = tones.begin();
|
||||
// Iterate over old tones
|
||||
for (auto const& old: m_tones) {
|
||||
// Try to find a matching new tone
|
||||
while (it != tones.end() && *it < old) ++it;
|
||||
// If match found
|
||||
if (it != tones.end() && *it == old) {
|
||||
// Merge the old tone into the new tone
|
||||
it->age = old.age + 1;
|
||||
it->stabledb = 0.8 * old.stabledb + 0.2 * it->db;
|
||||
it->freq = 0.5 * old.freq + 0.5 * it->freq;
|
||||
} else if (old.db > -80.0) {
|
||||
// Insert a decayed version of the old tone into new tones
|
||||
Tone& t = *tones.insert(it, old);
|
||||
t.db -= 5.0;
|
||||
t.stabledb -= 0.1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Analyzer::process() {
|
||||
// Try calculating FFT and calculate tones until no more data in input buffer
|
||||
while (calcFFT()) calcTones();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
#pragma once
|
||||
|
||||
#include <complex>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include "../compatibility.h"
|
||||
|
||||
/// struct to represent tones
|
||||
struct Tone {
|
||||
static const std::size_t MAXHARM = 48; ///< The maximum number of harmonics tracked
|
||||
static const std::size_t MINAGE = 2; ///< The minimum age required for a tone to be output
|
||||
double freq; ///< Frequency (Hz)
|
||||
double db; ///< Level (dB)
|
||||
double stabledb; ///< Stable level, useful for graphics rendering
|
||||
double harmonics[MAXHARM]; ///< Harmonics' levels
|
||||
std::size_t age; ///< How many times the tone has been detected in row
|
||||
Tone();
|
||||
void print() const; ///< Prints Tone to std::cout
|
||||
bool operator==(double f) const; ///< Compare for rough frequency match
|
||||
/// Less-than compare by levels (instead of frequencies like operator< does)
|
||||
static bool dbCompare(Tone const& l, Tone const& r) { return l.db < r.db; }
|
||||
};
|
||||
|
||||
static inline bool operator==(Tone const& lhs, Tone const& rhs) { return lhs == rhs.freq; }
|
||||
static inline bool operator!=(Tone const& lhs, Tone const& rhs) { return !(lhs == rhs); }
|
||||
static inline bool operator<=(Tone const& lhs, Tone const& rhs) { return lhs.freq < rhs.freq || lhs == rhs; }
|
||||
static inline bool operator>=(Tone const& lhs, Tone const& rhs) { return lhs.freq > rhs.freq || lhs == rhs; }
|
||||
static inline bool operator<(Tone const& lhs, Tone const& rhs) { return lhs.freq < rhs.freq && lhs != rhs; }
|
||||
static inline bool operator>(Tone const& lhs, Tone const& rhs) { return lhs.freq > rhs.freq && lhs != rhs; }
|
||||
|
||||
static const unsigned FFT_P = 10;
|
||||
static const std::size_t FFT_N = 1 << FFT_P;
|
||||
|
||||
/// Lock-free ring buffer. Discards oldest data on overflow (not strictly thread-safe).
|
||||
template <size_t SIZE> class RingBuffer {
|
||||
public:
|
||||
constexpr static size_t capacity = SIZE;
|
||||
RingBuffer(): m_read(), m_write() {} ///< Initialize empty buffer
|
||||
template <typename InIt> void insert(InIt begin, InIt end) {
|
||||
size_t r = m_read; // The read position
|
||||
size_t w = m_write; // The write position
|
||||
bool overflow = (static_cast<size_t>(end - begin) >= SIZE - size());
|
||||
while (begin != end) {
|
||||
m_buf[w] = *begin++; // Copy sample
|
||||
w = modulo(w + 1); // Update cursor
|
||||
}
|
||||
m_write = w;
|
||||
if (overflow) m_read = modulo(w + 1); // Reset read pointer on overflow
|
||||
}
|
||||
/// Read data from current position if there is enough data to fill the range (otherwise return false). Does not move read pointer.
|
||||
template <typename OutIt> bool read(OutIt begin, OutIt end) {
|
||||
size_t r = m_read;
|
||||
if (size() < static_cast<size_t>(end - begin)) return false; // Not enough audio available
|
||||
while (begin != end) *begin++ = m_buf[r++ % SIZE]; // Copy audio to output iterator
|
||||
return true;
|
||||
}
|
||||
void pop(size_t n) { m_read = modulo(m_read + n); } ///< Move reading pointer forward.
|
||||
size_t size() const { return modulo(m_write - m_read); }
|
||||
private:
|
||||
static size_t modulo(size_t idx) { return idx % SIZE; } ///< Modulo operation with proper rounding (handles slightly "negative" idx as well)
|
||||
float m_buf[SIZE];
|
||||
volatile size_t m_read, m_write; ///< The indices of the next read/write operations. read == write implies that buffer is empty.
|
||||
};
|
||||
|
||||
/// analyzer class
|
||||
/** class to analyze input audio and transform it into useable data
|
||||
*/
|
||||
class Analyzer {
|
||||
public:
|
||||
/// fast fourier transform vector
|
||||
typedef std::vector<std::complex<float> > fft_t;
|
||||
/// list of tones
|
||||
typedef std::list<Tone> tones_t;
|
||||
/// constructor
|
||||
Analyzer(double rate, std::string id, unsigned step = 200);
|
||||
/** Add input data to buffer. This is thread-safe (against other functions). **/
|
||||
template <typename InIt> void input(InIt begin, InIt end) {
|
||||
m_buf.insert(begin, end);
|
||||
m_passthrough.insert(begin, end);
|
||||
}
|
||||
/** Call this to process all data input so far. **/
|
||||
void process();
|
||||
/** Get the raw FFT. **/
|
||||
fft_t const& getFFT() const { return m_fft; }
|
||||
/** Get the peak level in dB (negative value, 0.0 = clipping). **/
|
||||
double getPeak() const { return 10.0 * log10(m_peak); }
|
||||
/** Get a list of all tones detected. **/
|
||||
tones_t const& getTones() const { return m_tones; }
|
||||
/** Find a tone within the singing range; prefers strong tones around 200-400 Hz. **/
|
||||
Tone const* findTone(double minfreq = 65.0, double maxfreq = 1000.0) const {
|
||||
if (m_tones.empty()) { m_oldfreq = 0.0; return NULL; }
|
||||
double db = std::max_element(m_tones.begin(), m_tones.end(), Tone::dbCompare)->db;
|
||||
Tone const* best = NULL;
|
||||
double bestscore = 0;
|
||||
for (tones_t::const_iterator it = m_tones.begin(); it != m_tones.end(); ++it) {
|
||||
if (it->db < db - 20.0 || it->freq < minfreq || it->age < Tone::MINAGE) continue;
|
||||
if (it->freq > maxfreq) break;
|
||||
double score = it->db - std::max(180.0, std::abs(it->freq - 300.0)) / 10.0;
|
||||
if (m_oldfreq != 0.0 && std::fabs(it->freq/m_oldfreq - 1.0) < 0.05) score += 10.0;
|
||||
if (best && bestscore > score) break;
|
||||
best = &*it;
|
||||
bestscore = score;
|
||||
}
|
||||
m_oldfreq = (best ? best->freq : 0.0);
|
||||
return best;
|
||||
}
|
||||
/** Give data away for mic pass-through */
|
||||
bool output(float* begin, float* end, double rate);
|
||||
/** Returns the id (color name) of the mic */
|
||||
std::string const& getId() const { return m_id; }
|
||||
|
||||
private:
|
||||
const unsigned m_step;
|
||||
RingBuffer<4 * FFT_N> m_buf; // Twice the FFT size should give enough room for sliding window and for engine delays
|
||||
RingBuffer<4096> m_passthrough;
|
||||
double m_resampleFactor;
|
||||
double m_resamplePos;
|
||||
double m_rate;
|
||||
std::string m_id;
|
||||
std::vector<float> m_window;
|
||||
fft_t m_fft;
|
||||
std::vector<float> m_fftLastPhase;
|
||||
double m_peak;
|
||||
tones_t m_tones;
|
||||
mutable double m_oldfreq;
|
||||
bool calcFFT();
|
||||
void calcTones();
|
||||
void mergeWithOld(tones_t& tones) const;
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
/** Limit val to range [min, max] **/
|
||||
template <typename T> T clamp(T val, T min = 0, T max = 1) {
|
||||
if (min > max) throw std::logic_error("min > max");
|
||||
if (val < min) return min;
|
||||
if (val > max) return max;
|
||||
return val;
|
||||
}
|
||||
|
||||
template <typename Numeric> struct MinMax {
|
||||
Numeric min, max;
|
||||
explicit MinMax(Numeric min = std::numeric_limits<Numeric>::min(), Numeric max = std::numeric_limits<Numeric>::max()): min(min), max(max) {}
|
||||
bool matches(Numeric val) const { return val >= min && val <= max; }
|
||||
};
|
||||
|
||||
/** A convenient way for getting NaNs **/
|
||||
static inline double getNaN() { return std::numeric_limits<double>::quiet_NaN(); }
|
||||
|
||||
/** A convenient way for getting infs **/
|
||||
static inline double getInf() { return std::numeric_limits<double>::infinity(); }
|
||||
|
||||
static inline bool isPow2(unsigned int val) {
|
||||
if (val == 0) return false;
|
||||
if ((val & (val-1)) == 0) return true; // From Wikipedia: Power_of_two
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline unsigned int nextPow2(unsigned int val) {
|
||||
unsigned int ret = 1;
|
||||
while (ret < val) ret *= 2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline unsigned int prevPow2(unsigned int val) {
|
||||
unsigned int ret = 1;
|
||||
while ((ret*2) < val) ret *= 2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
// This file is part of Vocaluxe.
|
||||
//
|
||||
// Vocaluxe 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Vocaluxe 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 Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "ptAKF.h"
|
||||
#include <math.h>
|
||||
|
||||
static constexpr double BaseToneFrequency = 65.4064; // lowest (half-)tone to analyze (C2 = 65.4064 Hz)
|
||||
static constexpr double HalftoneBase = 1.05946309436; // 2^(1/12) -> HalftoneBase^12 = 2 (one octave)
|
||||
int PtAKF::_InitCount = 0;
|
||||
float* PtAKF::_SamplesPerPeriodPerTone = NULL;
|
||||
|
||||
PtAKF::PtAKF(){
|
||||
_InitCount++;
|
||||
if(_InitCount == 1){
|
||||
_SamplesPerPeriodPerTone = new float[_MaxHalfTone + 1];
|
||||
//Init Array to avoid costly calculations
|
||||
for (int toneIndex = 0; toneIndex <= _MaxHalfTone; toneIndex++)
|
||||
{
|
||||
double freq = BaseToneFrequency * pow(HalftoneBase, toneIndex);
|
||||
_SamplesPerPeriodPerTone[toneIndex] = static_cast<float>(44100.0 / freq); // samples in one period
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PtAKF::~PtAKF(){
|
||||
_InitCount--;
|
||||
if(!_InitCount){
|
||||
delete[] _SamplesPerPeriodPerTone;
|
||||
_SamplesPerPeriodPerTone = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int PtAKF::GetNote(double* maxVolume, float* weights){
|
||||
float AnaylsisBuf[_SampleCt];
|
||||
size_t size = _AnalysisBuf.size();
|
||||
if(size > _SampleCt)
|
||||
_AnalysisBuf.pop(size - _SampleCt);
|
||||
if(!_AnalysisBuf.read(AnaylsisBuf, AnaylsisBuf + _SampleCt))
|
||||
return -1;
|
||||
return _GetNote(AnaylsisBuf, maxVolume, weights);
|
||||
}
|
||||
|
||||
int PtAKF::_GetNote(float* samples, double* maxVolume, float* weights){
|
||||
float maxVolumeL = 0;
|
||||
for(int i = 0; i < _SampleCt; i++){
|
||||
float vol = abs(samples[i]);
|
||||
if(vol > maxVolumeL)
|
||||
maxVolumeL = vol;
|
||||
}
|
||||
maxVolumeL /= 32767.;
|
||||
*maxVolume = maxVolumeL;
|
||||
|
||||
float maxWeight = -1;
|
||||
float minWeight = 1;
|
||||
int maxTone = -1;
|
||||
for (int toneIndex = _MinHalfTone; toneIndex <= _MaxHalfTone; toneIndex++)
|
||||
{
|
||||
float curWeight = _AnalyzeToneFunc(samples, toneIndex);
|
||||
|
||||
if (curWeight > maxWeight)
|
||||
{
|
||||
maxWeight = curWeight;
|
||||
maxTone = toneIndex;
|
||||
}
|
||||
|
||||
if (curWeight < minWeight)
|
||||
minWeight = curWeight;
|
||||
|
||||
weights[toneIndex - _MinHalfTone] = curWeight;
|
||||
}
|
||||
|
||||
if(maxWeight - minWeight > 0.01){
|
||||
return maxTone;
|
||||
}else return -1;
|
||||
}
|
||||
|
||||
float PtAKF::_AnalyzeToneFunc(float* samples, int toneIndex){
|
||||
// Use method by Kobayashi and Shimamura (1995): Combine AKF and AMDF to a new f(z)=AKF(z)/(AMDF(z)+k) with k=1
|
||||
|
||||
float samplesPerPeriodD = _SamplesPerPeriodPerTone[toneIndex]; // samples in one period
|
||||
int samplesPerPeriod = static_cast<int>(samplesPerPeriodD);
|
||||
float fHigh = samplesPerPeriodD - samplesPerPeriod;
|
||||
float fLow = 1.0f - fHigh;
|
||||
|
||||
float accumDistAKF = 0; // accumulated distances
|
||||
float accumDistAMDF = 0; // accumulated distances
|
||||
|
||||
// compare correlating samples
|
||||
int sampleIndex = 0; // index of sample to analyze
|
||||
// Start value= index of sample one period ahead
|
||||
for (int correlatingSampleIndex = sampleIndex + samplesPerPeriod; correlatingSampleIndex + 1 < _SampleCt; correlatingSampleIndex++, sampleIndex++)
|
||||
{
|
||||
// calc distance to corresponding sample in next period
|
||||
float xn = samples[sampleIndex];
|
||||
float xnt = samples[correlatingSampleIndex] * fLow + samples[correlatingSampleIndex + 1] * fHigh;
|
||||
accumDistAKF += xn * xnt;
|
||||
accumDistAMDF += abs(xn - xnt);
|
||||
}
|
||||
|
||||
accumDistAKF /= _SampleCt;
|
||||
accumDistAMDF /= sampleIndex;
|
||||
|
||||
return accumDistAKF / (accumDistAMDF * 32767.f + 32767.f); // Need to scale AKF by MAX^2 and AMDF by MAX, so do some maths to divide only once
|
||||
}
|
||||
/*
|
||||
|
||||
|
||||
|
||||
namespace PitchTrackerAKF{
|
||||
static double *SamplesPerPeriodPerTone = NULL;
|
||||
static double BaseToneFrequency = -1;
|
||||
static int MinHalfTone = -1, MaxHalfTone = -1;
|
||||
static const double HalftoneBase = 1.05946309436; // 2^(1/12) -> HalftoneBase^12 = 2 (one octave)
|
||||
|
||||
void Init(double baseToneFrequency, int minHalfTone, int maxHalfTone){
|
||||
if(SamplesPerPeriodPerTone && abs(baseToneFrequency - BaseToneFrequency) < 1. && minHalfTone == MinHalfTone && maxHalfTone == MaxHalfTone)
|
||||
return;
|
||||
BaseToneFrequency = baseToneFrequency;
|
||||
MinHalfTone = minHalfTone;
|
||||
MaxHalfTone = maxHalfTone;
|
||||
DeInit();
|
||||
if(minHalfTone < 0 || maxHalfTone < minHalfTone)
|
||||
return;
|
||||
SamplesPerPeriodPerTone = (double*) malloc((MaxHalfTone + 1) * sizeof(double));
|
||||
//Init Array to avoid costly calculations
|
||||
for (int toneIndex = 0; toneIndex <= MaxHalfTone; toneIndex++)
|
||||
{
|
||||
double freq = baseToneFrequency * pow(HalftoneBase, toneIndex);
|
||||
SamplesPerPeriodPerTone[toneIndex] = 44100.0 / freq; // samples in one period
|
||||
}
|
||||
}
|
||||
|
||||
void DeInit(){
|
||||
if(SamplesPerPeriodPerTone){
|
||||
free(SamplesPerPeriodPerTone);
|
||||
SamplesPerPeriodPerTone=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static T _AnalyzeToneFunc(T *samples, int sampleCt, int toneIndex, bool scale){
|
||||
// Use method by Kobayashi and Shimamura (1995): Combine AKF and AMDF to a new f(z)=AKF(z)/(AMDF(z)+k) with k=1
|
||||
double samplesPerPeriodD = SamplesPerPeriodPerTone[toneIndex]; // samples in one period
|
||||
int samplesPerPeriod = static_cast<int>(samplesPerPeriodD);
|
||||
T fHigh = static_cast<T>(samplesPerPeriodD - samplesPerPeriod);
|
||||
T fLow = static_cast<T>(1.0 - fHigh);
|
||||
|
||||
T accumDistAKF = 0; // accumulated distances
|
||||
T accumDistAMDF = 0; // accumulated distances
|
||||
|
||||
// compare correlating samples
|
||||
int sampleIndex = 0; // index of sample to analyze
|
||||
// Start value= index of sample one period ahead
|
||||
for (int correlatingSampleIndex = sampleIndex + samplesPerPeriod; correlatingSampleIndex + 1 < sampleCt; correlatingSampleIndex++, sampleIndex++)
|
||||
{
|
||||
// calc distance to corresponding sample in next period
|
||||
T xn = samples[sampleIndex];
|
||||
T xnt = samples[correlatingSampleIndex] * fLow + samples[correlatingSampleIndex + 1] * fHigh;
|
||||
accumDistAKF += xn * xnt;
|
||||
accumDistAMDF += abs(xn - xnt);
|
||||
}
|
||||
|
||||
accumDistAKF /= sampleCt;
|
||||
accumDistAMDF /= sampleIndex;
|
||||
|
||||
if(scale)
|
||||
return accumDistAKF / (accumDistAMDF * 32767.f + 32767.f); // Need to scale AKF by MAX^2 and AMDF by MAX, so do some maths to divide only once
|
||||
else
|
||||
return accumDistAKF / (accumDistAMDF + 1.f);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int GetTone(T *samples, int sampleCt, double* maxVolume, float *weights, bool scale){
|
||||
if(!SamplesPerPeriodPerTone)
|
||||
return -1;
|
||||
T maxVolumeL = 0;
|
||||
for(int i = 0; i < sampleCt; i++){
|
||||
T vol = abs(samples[i]);
|
||||
if(vol > maxVolumeL)
|
||||
maxVolumeL = vol;
|
||||
}
|
||||
if(scale)
|
||||
maxVolumeL /= 32767.;
|
||||
*maxVolume = maxVolumeL;
|
||||
|
||||
T maxWeight = -1;
|
||||
T minWeight = 1;
|
||||
int maxTone = -1;
|
||||
for (int toneIndex = MinHalfTone; toneIndex <= MaxHalfTone; toneIndex++)
|
||||
{
|
||||
T curWeight = _AnalyzeToneFunc<T>(samples, sampleCt, toneIndex, scale);
|
||||
|
||||
if (curWeight > maxWeight)
|
||||
{
|
||||
maxWeight = curWeight;
|
||||
maxTone = toneIndex;
|
||||
}
|
||||
|
||||
if (curWeight < minWeight)
|
||||
minWeight = curWeight;
|
||||
|
||||
weights[toneIndex - MinHalfTone] = static_cast<float>(curWeight);
|
||||
}
|
||||
|
||||
if(maxWeight - minWeight > 0.01){
|
||||
return maxTone;
|
||||
}else return -1;
|
||||
}
|
||||
|
||||
template int GetTone(float *samples, int sampleCt, double* maxVolume, float *weights, bool scale);
|
||||
template int GetTone(double *samples, int sampleCt, double* maxVolume, float *weights, bool scale);
|
||||
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "performous/pitch.hh"
|
||||
|
||||
// A pitch detection that is based on the AKF and AMDF (initially from original Vocaluxe/USDx)
|
||||
class PtAKF{
|
||||
public:
|
||||
PtAKF();
|
||||
~PtAKF();
|
||||
|
||||
void input(short* begin, short* end){
|
||||
_AnalysisBuf.insert(begin, end);
|
||||
}
|
||||
|
||||
int GetNote(double* maxVolume, float* weights);
|
||||
static int GetNumHalfTones(){ return _MaxHalfTone - _MinHalfTone + 1;}
|
||||
private:
|
||||
static int _InitCount;
|
||||
static float* _SamplesPerPeriodPerTone;
|
||||
static constexpr int _MinHalfTone = 0; //C2
|
||||
static constexpr int _MaxHalfTone = 47; //B5
|
||||
|
||||
constexpr static size_t _SampleCt = 4096;
|
||||
RingBuffer<_SampleCt * 2> _AnalysisBuf;
|
||||
|
||||
static int _GetNote(float* samples, double* maxVolume, float* weights);
|
||||
static float _AnalyzeToneFunc(float* samples, int toneIndex);
|
||||
};
|
||||
@@ -9,6 +9,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vocaluxe", "Vocaluxe\Vocalu
|
||||
{EC5167E4-CCFB-4CEE-987C-CF816F1062A3} = {EC5167E4-CCFB-4CEE-987C-CF816F1062A3}
|
||||
{D2B6DDEF-E0E1-4DDF-A73A-E6ACEF5C95EC} = {D2B6DDEF-E0E1-4DDF-A73A-E6ACEF5C95EC}
|
||||
{5A4053F1-5AD5-441F-B254-D97A088127B6} = {5A4053F1-5AD5-441F-B254-D97A088127B6}
|
||||
{54A48AF7-515C-4EDD-B395-2BFFA096050A} = {54A48AF7-515C-4EDD-B395-2BFFA096050A}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VocaluxeLib", "VocaluxeLib\VocaluxeLib.csproj", "{D2B6DDEF-E0E1-4DDF-A73A-E6ACEF5C95EC}"
|
||||
@@ -25,6 +26,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServerLib", "ClientServerLi
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebserverInitalConfig", "WebserverInitalConfig\WebserverInitalConfig.csproj", "{55F89808-4E79-4FFE-BC13-48258913F5C9}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PitchTracker", "PitchTracker\PitchTracker.vcxproj", "{54A48AF7-515C-4EDD-B395-2BFFA096050A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
DebugLinux|x64 = DebugLinux|x64
|
||||
@@ -108,7 +111,9 @@ Global
|
||||
{5A4053F1-5AD5-441F-B254-D97A088127B6}.DebugWin|x86.ActiveCfg = DebugWin|x86
|
||||
{5A4053F1-5AD5-441F-B254-D97A088127B6}.DebugWin|x86.Build.0 = DebugWin|x86
|
||||
{5A4053F1-5AD5-441F-B254-D97A088127B6}.InstallerWin|x64.ActiveCfg = ReleaseWin|x64
|
||||
{5A4053F1-5AD5-441F-B254-D97A088127B6}.InstallerWin|x64.Build.0 = ReleaseWin|x64
|
||||
{5A4053F1-5AD5-441F-B254-D97A088127B6}.InstallerWin|x86.ActiveCfg = ReleaseWin|x86
|
||||
{5A4053F1-5AD5-441F-B254-D97A088127B6}.InstallerWin|x86.Build.0 = ReleaseWin|x86
|
||||
{5A4053F1-5AD5-441F-B254-D97A088127B6}.ReleaseLinux|x64.ActiveCfg = ReleaseLinux|x64
|
||||
{5A4053F1-5AD5-441F-B254-D97A088127B6}.ReleaseLinux|x64.Build.0 = ReleaseLinux|x64
|
||||
{5A4053F1-5AD5-441F-B254-D97A088127B6}.ReleaseLinux|x86.ActiveCfg = ReleaseLinux|x86
|
||||
@@ -126,7 +131,9 @@ Global
|
||||
{6F3E1874-F28A-40E9-AFBD-31934D593678}.DebugWin|x86.ActiveCfg = DebugWin|x86
|
||||
{6F3E1874-F28A-40E9-AFBD-31934D593678}.DebugWin|x86.Build.0 = DebugWin|x86
|
||||
{6F3E1874-F28A-40E9-AFBD-31934D593678}.InstallerWin|x64.ActiveCfg = ReleaseWin|x64
|
||||
{6F3E1874-F28A-40E9-AFBD-31934D593678}.InstallerWin|x64.Build.0 = ReleaseWin|x64
|
||||
{6F3E1874-F28A-40E9-AFBD-31934D593678}.InstallerWin|x86.ActiveCfg = ReleaseWin|x86
|
||||
{6F3E1874-F28A-40E9-AFBD-31934D593678}.InstallerWin|x86.Build.0 = ReleaseWin|x86
|
||||
{6F3E1874-F28A-40E9-AFBD-31934D593678}.ReleaseLinux|x64.ActiveCfg = ReleaseLinux|x64
|
||||
{6F3E1874-F28A-40E9-AFBD-31934D593678}.ReleaseLinux|x64.Build.0 = ReleaseLinux|x64
|
||||
{6F3E1874-F28A-40E9-AFBD-31934D593678}.ReleaseLinux|x86.ActiveCfg = ReleaseLinux|x86
|
||||
@@ -142,7 +149,9 @@ Global
|
||||
{ED007796-367F-4142-AA55-5450C7645C2C}.DebugWin|x86.ActiveCfg = DebugWin|Win32
|
||||
{ED007796-367F-4142-AA55-5450C7645C2C}.DebugWin|x86.Build.0 = DebugWin|Win32
|
||||
{ED007796-367F-4142-AA55-5450C7645C2C}.InstallerWin|x64.ActiveCfg = ReleaseWin|x64
|
||||
{ED007796-367F-4142-AA55-5450C7645C2C}.InstallerWin|x64.Build.0 = ReleaseWin|x64
|
||||
{ED007796-367F-4142-AA55-5450C7645C2C}.InstallerWin|x86.ActiveCfg = ReleaseWin|Win32
|
||||
{ED007796-367F-4142-AA55-5450C7645C2C}.InstallerWin|x86.Build.0 = ReleaseWin|Win32
|
||||
{ED007796-367F-4142-AA55-5450C7645C2C}.ReleaseLinux|x64.ActiveCfg = ReleaseWin|x64
|
||||
{ED007796-367F-4142-AA55-5450C7645C2C}.ReleaseLinux|x86.ActiveCfg = ReleaseWin|x64
|
||||
{ED007796-367F-4142-AA55-5450C7645C2C}.ReleaseWin|x64.ActiveCfg = ReleaseWin|x64
|
||||
@@ -152,18 +161,23 @@ Global
|
||||
{EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.DebugLinux|x64.ActiveCfg = DebugLinux|Any CPU
|
||||
{EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.DebugLinux|x64.Build.0 = DebugLinux|Any CPU
|
||||
{EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.DebugLinux|x86.ActiveCfg = DebugLinux|Any CPU
|
||||
{EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.DebugLinux|x86.Build.0 = DebugLinux|Any CPU
|
||||
{EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.DebugWin|x64.ActiveCfg = DebugWin|Any CPU
|
||||
{EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.DebugWin|x64.Build.0 = DebugWin|Any CPU
|
||||
{EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.DebugWin|x86.ActiveCfg = DebugWin|Any CPU
|
||||
{EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.DebugWin|x86.Build.0 = DebugWin|Any CPU
|
||||
{EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.InstallerWin|x64.ActiveCfg = ReleaseWin|Any CPU
|
||||
{EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.InstallerWin|x64.Build.0 = ReleaseWin|Any CPU
|
||||
{EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.InstallerWin|x86.ActiveCfg = ReleaseLinux|Any CPU
|
||||
{EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.InstallerWin|x86.Build.0 = ReleaseLinux|Any CPU
|
||||
{EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.ReleaseLinux|x64.ActiveCfg = ReleaseLinux|Any CPU
|
||||
{EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.ReleaseLinux|x64.Build.0 = ReleaseLinux|Any CPU
|
||||
{EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.ReleaseLinux|x86.ActiveCfg = ReleaseLinux|Any CPU
|
||||
{EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.ReleaseLinux|x86.Build.0 = ReleaseLinux|Any CPU
|
||||
{EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.ReleaseWin|x64.ActiveCfg = ReleaseWin|Any CPU
|
||||
{EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.ReleaseWin|x64.Build.0 = ReleaseWin|Any CPU
|
||||
{EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.ReleaseWin|x86.ActiveCfg = ReleaseWin|Any CPU
|
||||
{EC5167E4-CCFB-4CEE-987C-CF816F1062A3}.ReleaseWin|x86.Build.0 = ReleaseWin|Any CPU
|
||||
{55F89808-4E79-4FFE-BC13-48258913F5C9}.DebugLinux|x64.ActiveCfg = Debug|Any CPU
|
||||
{55F89808-4E79-4FFE-BC13-48258913F5C9}.DebugLinux|x86.ActiveCfg = Debug|Any CPU
|
||||
{55F89808-4E79-4FFE-BC13-48258913F5C9}.DebugWin|x64.ActiveCfg = Debug|Any CPU
|
||||
@@ -173,12 +187,33 @@ Global
|
||||
{55F89808-4E79-4FFE-BC13-48258913F5C9}.InstallerWin|x64.ActiveCfg = Release|Any CPU
|
||||
{55F89808-4E79-4FFE-BC13-48258913F5C9}.InstallerWin|x64.Build.0 = Release|Any CPU
|
||||
{55F89808-4E79-4FFE-BC13-48258913F5C9}.InstallerWin|x86.ActiveCfg = Release|Any CPU
|
||||
{55F89808-4E79-4FFE-BC13-48258913F5C9}.InstallerWin|x86.Build.0 = Release|Any CPU
|
||||
{55F89808-4E79-4FFE-BC13-48258913F5C9}.ReleaseLinux|x64.ActiveCfg = Release|Any CPU
|
||||
{55F89808-4E79-4FFE-BC13-48258913F5C9}.ReleaseLinux|x86.ActiveCfg = Release|Any CPU
|
||||
{55F89808-4E79-4FFE-BC13-48258913F5C9}.ReleaseWin|x64.ActiveCfg = Release|Any CPU
|
||||
{55F89808-4E79-4FFE-BC13-48258913F5C9}.ReleaseWin|x64.Build.0 = Release|Any CPU
|
||||
{55F89808-4E79-4FFE-BC13-48258913F5C9}.ReleaseWin|x86.ActiveCfg = Release|Any CPU
|
||||
{55F89808-4E79-4FFE-BC13-48258913F5C9}.ReleaseWin|x86.Build.0 = Release|Any CPU
|
||||
{54A48AF7-515C-4EDD-B395-2BFFA096050A}.DebugLinux|x64.ActiveCfg = Debug|x64
|
||||
{54A48AF7-515C-4EDD-B395-2BFFA096050A}.DebugLinux|x64.Build.0 = Debug|x64
|
||||
{54A48AF7-515C-4EDD-B395-2BFFA096050A}.DebugLinux|x86.ActiveCfg = Debug|Win32
|
||||
{54A48AF7-515C-4EDD-B395-2BFFA096050A}.DebugLinux|x86.Build.0 = Debug|Win32
|
||||
{54A48AF7-515C-4EDD-B395-2BFFA096050A}.DebugWin|x64.ActiveCfg = Debug|x64
|
||||
{54A48AF7-515C-4EDD-B395-2BFFA096050A}.DebugWin|x64.Build.0 = Debug|x64
|
||||
{54A48AF7-515C-4EDD-B395-2BFFA096050A}.DebugWin|x86.ActiveCfg = Debug|Win32
|
||||
{54A48AF7-515C-4EDD-B395-2BFFA096050A}.DebugWin|x86.Build.0 = Debug|Win32
|
||||
{54A48AF7-515C-4EDD-B395-2BFFA096050A}.InstallerWin|x64.ActiveCfg = Release|x64
|
||||
{54A48AF7-515C-4EDD-B395-2BFFA096050A}.InstallerWin|x64.Build.0 = Release|x64
|
||||
{54A48AF7-515C-4EDD-B395-2BFFA096050A}.InstallerWin|x86.ActiveCfg = Release|Win32
|
||||
{54A48AF7-515C-4EDD-B395-2BFFA096050A}.InstallerWin|x86.Build.0 = Release|Win32
|
||||
{54A48AF7-515C-4EDD-B395-2BFFA096050A}.ReleaseLinux|x64.ActiveCfg = Release|x64
|
||||
{54A48AF7-515C-4EDD-B395-2BFFA096050A}.ReleaseLinux|x64.Build.0 = Release|x64
|
||||
{54A48AF7-515C-4EDD-B395-2BFFA096050A}.ReleaseLinux|x86.ActiveCfg = Release|Win32
|
||||
{54A48AF7-515C-4EDD-B395-2BFFA096050A}.ReleaseLinux|x86.Build.0 = Release|Win32
|
||||
{54A48AF7-515C-4EDD-B395-2BFFA096050A}.ReleaseWin|x64.ActiveCfg = Release|x64
|
||||
{54A48AF7-515C-4EDD-B395-2BFFA096050A}.ReleaseWin|x64.Build.0 = Release|x64
|
||||
{54A48AF7-515C-4EDD-B395-2BFFA096050A}.ReleaseWin|x86.ActiveCfg = Release|Win32
|
||||
{54A48AF7-515C-4EDD-B395-2BFFA096050A}.ReleaseWin|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
+228
-407
@@ -17,8 +17,8 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using VocaluxeLib;
|
||||
using VocaluxeLib.Songs;
|
||||
using VocaluxeLib.Draw;
|
||||
@@ -27,25 +27,28 @@ namespace Vocaluxe.Base
|
||||
{
|
||||
static class CBackgroundMusic
|
||||
{
|
||||
private static int _CurrentMusicStream = -1;
|
||||
private static int _PreviousMusicIndex;
|
||||
private static CPlaylistElement _CurrentPlaylistElement = new CPlaylistElement();
|
||||
private static bool _Initialized;
|
||||
|
||||
private static readonly List<CPlaylistElement> _AllFileNames = new List<CPlaylistElement>();
|
||||
private static readonly List<CPlaylistElement> _NotPlayedFileNames = new List<CPlaylistElement>();
|
||||
private static readonly List<CPlaylistElement> _BGMusicFileNames = new List<CPlaylistElement>();
|
||||
private static readonly List<CPlaylistElement> _PreviousFileNames = new List<CPlaylistElement>();
|
||||
private static bool _OwnSongsAvailable;
|
||||
private static EBackgroundMusicSource _MusicSource;
|
||||
private static float _Volume;
|
||||
private static bool _VideoEnabled;
|
||||
|
||||
private static CPlaylistElement _CurrentPlaylistElement; //Currently played music
|
||||
private static int _CurrentMusicStream = -1; //Current active music stream
|
||||
private static int _PreviousMusicIndex; //Index of _CurrentPlaylistElement in _PreviousFiles (kept for performance)
|
||||
|
||||
private static readonly List<CPlaylistElement> _BGMusicFiles = new List<CPlaylistElement>(); //Background music files
|
||||
private static readonly List<CPlaylistElement> _NotPlayedFiles = new List<CPlaylistElement>(); //Not played files
|
||||
private static readonly List<CPlaylistElement> _PreviousFiles = new List<CPlaylistElement>(); //Played files
|
||||
|
||||
private static int _Video = -1;
|
||||
private static CTexture _CurrentVideoTexture;
|
||||
private static CFading _VideoFading;
|
||||
private static bool _VideoEnabled;
|
||||
|
||||
private static bool _OwnMusicAdded;
|
||||
private static bool _BackgroundMusicAdded;
|
||||
private static bool _Disabled;
|
||||
private static bool _CanSing;
|
||||
private static bool _RepeatSong;
|
||||
|
||||
public static bool VideoEnabled
|
||||
{
|
||||
@@ -54,16 +57,19 @@ namespace Vocaluxe.Base
|
||||
{
|
||||
if (_VideoEnabled == value)
|
||||
return;
|
||||
ToggleVideo();
|
||||
_VideoEnabled = value;
|
||||
if (_VideoEnabled)
|
||||
_LoadVideo();
|
||||
else if (_Video != -1)
|
||||
{
|
||||
CVideo.Close(_Video);
|
||||
CDraw.RemoveTexture(ref _CurrentVideoTexture);
|
||||
_Video = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CanSing
|
||||
{
|
||||
get { return _CanSing; }
|
||||
set { _CanSing = value; }
|
||||
}
|
||||
public static bool CanSing { get; private set; }
|
||||
|
||||
public static bool Disabled
|
||||
{
|
||||
@@ -78,20 +84,16 @@ namespace Vocaluxe.Base
|
||||
}
|
||||
}
|
||||
|
||||
public static bool RepeatSong
|
||||
{
|
||||
get { return _RepeatSong; }
|
||||
set { _RepeatSong = value; }
|
||||
}
|
||||
public static bool RepeatSong { get; set; }
|
||||
|
||||
public static int SongID
|
||||
{
|
||||
get { return _CurrentPlaylistElement.SongID; }
|
||||
get { return _CurrentPlaylistElement == null ? -1 : _CurrentPlaylistElement.SongID; }
|
||||
}
|
||||
|
||||
public static bool SongHasVideo
|
||||
{
|
||||
get { return File.Exists(_CurrentPlaylistElement.VideoFilePath); }
|
||||
get { return _CurrentPlaylistElement != null && File.Exists(_CurrentPlaylistElement.VideoFilePath); }
|
||||
}
|
||||
|
||||
public static bool IsPlaying { get; private set; }
|
||||
@@ -100,6 +102,8 @@ namespace Vocaluxe.Base
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_CurrentPlaylistElement == null)
|
||||
return "";
|
||||
if (_CurrentPlaylistElement.Artist != "" && _CurrentPlaylistElement.Title != "")
|
||||
return _CurrentPlaylistElement.Artist + " - " + _CurrentPlaylistElement.Title;
|
||||
return Path.GetFileNameWithoutExtension(_CurrentPlaylistElement.MusicFilePath);
|
||||
@@ -108,50 +112,63 @@ namespace Vocaluxe.Base
|
||||
|
||||
public static CTexture Cover
|
||||
{
|
||||
get { return _CurrentPlaylistElement.Cover; }
|
||||
get { return _CurrentPlaylistElement == null ? CCover.NoCover : _CurrentPlaylistElement.Cover; }
|
||||
}
|
||||
|
||||
//Use this to set whether own songs are available for access
|
||||
public static bool OwnSongsAvailable
|
||||
{
|
||||
get { return _OwnSongsAvailable; }
|
||||
set
|
||||
{
|
||||
_OwnSongsAvailable = value;
|
||||
if (_OwnSongsAvailable && _MusicSource != EBackgroundMusicSource.TR_CONFIG_NO_OWN_MUSIC)
|
||||
_AddOwnMusic();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the background music with values from config
|
||||
/// Has to be called before any other method is used
|
||||
/// </summary>
|
||||
public static void Init()
|
||||
{
|
||||
var templist = new List<string>();
|
||||
if (_Initialized)
|
||||
return;
|
||||
|
||||
foreach (string ending in CSettings.MusicFileTypes)
|
||||
templist.AddRange(CHelper.ListFiles(CSettings.FolderBackgroundMusic, ending, true, true));
|
||||
List<string> soundFiles = CHelper.ListSoundFiles(CSettings.FolderBackgroundMusic, true, true);
|
||||
|
||||
foreach (string path in templist)
|
||||
_BGMusicFileNames.Add(new CPlaylistElement(path));
|
||||
|
||||
if (CConfig.BackgroundMusicSource != EBackgroundMusicSource.TR_CONFIG_ONLY_OWN_MUSIC)
|
||||
AddBackgroundMusic();
|
||||
|
||||
_VideoEnabled = (CConfig.VideoBackgrounds == EOffOn.TR_CONFIG_ON && CConfig.VideosToBackground == EOffOn.TR_CONFIG_ON);
|
||||
foreach (string path in soundFiles)
|
||||
_BGMusicFiles.Add(new CPlaylistElement(path));
|
||||
//Set a default to have a consistent starting point, use SetMusicSource afterwards
|
||||
_MusicSource = EBackgroundMusicSource.TR_CONFIG_NO_OWN_MUSIC;
|
||||
_AddBackgroundMusic();
|
||||
|
||||
_PreviousMusicIndex = -1;
|
||||
IsPlaying = false;
|
||||
_Volume = CConfig.BackgroundMusicVolume;
|
||||
_VideoEnabled = (CConfig.VideoBackgrounds == EOffOn.TR_CONFIG_ON && CConfig.VideosToBackground == EOffOn.TR_CONFIG_ON);
|
||||
SetMusicSource(CConfig.BackgroundMusicSource);
|
||||
_Initialized = true;
|
||||
}
|
||||
|
||||
public static void Play()
|
||||
{
|
||||
if (IsPlaying)
|
||||
if (IsPlaying || CConfig.BackgroundMusic == EOffOn.TR_CONFIG_OFF)
|
||||
return;
|
||||
|
||||
if (CConfig.BackgroundMusic == EOffOn.TR_CONFIG_ON)
|
||||
if (_CurrentMusicStream != -1)
|
||||
{
|
||||
if (_AllFileNames.Count > 0)
|
||||
{
|
||||
if (_CurrentMusicStream != -1)
|
||||
{
|
||||
CSound.Fade(_CurrentMusicStream, 100f, CSettings.BackgroundMusicFadeTime);
|
||||
CSound.Play(_CurrentMusicStream);
|
||||
if (_VideoEnabled && _Video != -1)
|
||||
CVideo.Resume(_Video);
|
||||
IsPlaying = true;
|
||||
}
|
||||
else
|
||||
Next();
|
||||
|
||||
_CanSing = !_IsBackgroundFile(_CurrentPlaylistElement);
|
||||
}
|
||||
//Resume
|
||||
CSound.SetStreamVolume(_CurrentMusicStream, 0f);
|
||||
CSound.Fade(_CurrentMusicStream, _Volume, CSettings.BackgroundMusicFadeTime);
|
||||
CSound.Play(_CurrentMusicStream);
|
||||
if (_VideoEnabled && _Video != -1)
|
||||
CVideo.Resume(_Video);
|
||||
IsPlaying = true;
|
||||
}
|
||||
else
|
||||
Next();
|
||||
}
|
||||
|
||||
public static void Stop()
|
||||
@@ -159,7 +176,7 @@ namespace Vocaluxe.Base
|
||||
if (!IsPlaying)
|
||||
return;
|
||||
|
||||
if (_VideoEnabled && _Video != -1)
|
||||
if (_Video != -1)
|
||||
{
|
||||
CVideo.Close(_Video);
|
||||
CDraw.RemoveTexture(ref _CurrentVideoTexture);
|
||||
@@ -168,7 +185,7 @@ namespace Vocaluxe.Base
|
||||
CSound.FadeAndClose(_CurrentMusicStream, 0f, CSettings.BackgroundMusicFadeTime);
|
||||
_CurrentMusicStream = -1;
|
||||
|
||||
_CurrentPlaylistElement = new CPlaylistElement();
|
||||
_CurrentPlaylistElement = null;
|
||||
IsPlaying = false;
|
||||
}
|
||||
|
||||
@@ -177,7 +194,7 @@ namespace Vocaluxe.Base
|
||||
if (!IsPlaying)
|
||||
return;
|
||||
|
||||
if (_VideoEnabled && _Video != -1)
|
||||
if (_Video != -1)
|
||||
{
|
||||
CVideo.Pause(_Video);
|
||||
CVideo.Skip(_Video, CSound.GetPosition(_CurrentMusicStream) + CSettings.BackgroundMusicFadeTime, _CurrentPlaylistElement.VideoGap);
|
||||
@@ -186,277 +203,204 @@ namespace Vocaluxe.Base
|
||||
IsPlaying = false;
|
||||
}
|
||||
|
||||
public static void Update()
|
||||
{
|
||||
if (_AllFileNames.Count > 0 && _CurrentMusicStream != -1)
|
||||
{
|
||||
float timeToPlay;
|
||||
if (Math.Abs(_CurrentPlaylistElement.Finish) < 0.001) //No End-Tag defined
|
||||
timeToPlay = CSound.GetLength(_CurrentMusicStream) - CSound.GetPosition(_CurrentMusicStream);
|
||||
else //End-Tag found
|
||||
timeToPlay = _CurrentPlaylistElement.Finish - CSound.GetPosition(_CurrentMusicStream);
|
||||
|
||||
bool finished = CSound.IsFinished(_CurrentMusicStream);
|
||||
if (IsPlaying && ((timeToPlay <= CSettings.BackgroundMusicFadeTime && timeToPlay > 0) || finished))
|
||||
{
|
||||
if (_RepeatSong)
|
||||
{
|
||||
//Seek to #Start-Tag, if found
|
||||
if (_CurrentPlaylistElement.Start > 0.001 && CConfig.BackgroundMusicUseStart == EOffOn.TR_CONFIG_ON)
|
||||
CSound.SetPosition(_CurrentMusicStream, _CurrentPlaylistElement.Start);
|
||||
else
|
||||
CSound.SetPosition(_CurrentMusicStream, 0);
|
||||
if (_VideoEnabled && _Video != -1)
|
||||
{
|
||||
if (_CurrentPlaylistElement.Start > 0.001 && CConfig.BackgroundMusicUseStart == EOffOn.TR_CONFIG_ON)
|
||||
CVideo.Skip(_Video, _CurrentPlaylistElement.Start, _CurrentPlaylistElement.VideoGap);
|
||||
else
|
||||
CVideo.Skip(_Video, 0f, _CurrentPlaylistElement.VideoGap);
|
||||
}
|
||||
}
|
||||
else
|
||||
Next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Next()
|
||||
{
|
||||
if (_AllFileNames.Count > 0)
|
||||
Stop(); //stop last song if any
|
||||
if (_PreviousMusicIndex < _PreviousFiles.Count - 2)
|
||||
{
|
||||
if (_PreviousMusicIndex == _PreviousFileNames.Count - 1 || _PreviousFileNames.Count == 0)
|
||||
{
|
||||
//We are not currently in the previous list
|
||||
Stop();
|
||||
if (_NotPlayedFileNames.Count == 0)
|
||||
_NotPlayedFileNames.AddRange(_AllFileNames);
|
||||
|
||||
_CurrentPlaylistElement = _NotPlayedFileNames[CGame.Rand.Next(_NotPlayedFileNames.Count)];
|
||||
_NotPlayedFileNames.Remove(_CurrentPlaylistElement);
|
||||
|
||||
_PreviousFileNames.Add(_CurrentPlaylistElement);
|
||||
_PreviousMusicIndex = _PreviousFileNames.Count - 1;
|
||||
}
|
||||
else if (_PreviousFileNames.Count > 0)
|
||||
{
|
||||
//We are in the previous list
|
||||
Stop();
|
||||
_PreviousMusicIndex++;
|
||||
|
||||
_CurrentPlaylistElement = _PreviousFileNames[_PreviousMusicIndex];
|
||||
}
|
||||
_CurrentMusicStream = CSound.Load(_CurrentPlaylistElement.MusicFilePath);
|
||||
CSound.SetStreamVolumeMax(_CurrentMusicStream, CConfig.BackgroundMusicVolume);
|
||||
|
||||
//Seek to #Start-Tag, if found
|
||||
if (_CurrentPlaylistElement.Start > 0.001 && CConfig.BackgroundMusicUseStart == EOffOn.TR_CONFIG_ON)
|
||||
CSound.SetPosition(_CurrentMusicStream, _CurrentPlaylistElement.Start);
|
||||
|
||||
if (_VideoEnabled)
|
||||
_LoadVideo();
|
||||
CSound.SetStreamVolume(_CurrentMusicStream, 0f);
|
||||
Play();
|
||||
//We are in the previous list and next element exists
|
||||
_PreviousMusicIndex++;
|
||||
_CurrentPlaylistElement = _PreviousFiles[_PreviousMusicIndex];
|
||||
}
|
||||
else
|
||||
Stop();
|
||||
{
|
||||
//We are not in the previous list (anymore)
|
||||
if (_NotPlayedFiles.Count == 0)
|
||||
{
|
||||
if (_PreviousFiles.Count == 0)
|
||||
return; //No songs to play
|
||||
_NotPlayedFiles.AddRange(_PreviousFiles);
|
||||
}
|
||||
|
||||
_CurrentPlaylistElement = _NotPlayedFiles[CGame.Rand.Next(_NotPlayedFiles.Count)];
|
||||
_NotPlayedFiles.Remove(_CurrentPlaylistElement);
|
||||
|
||||
_PreviousFiles.Add(_CurrentPlaylistElement);
|
||||
_PreviousMusicIndex = _PreviousFiles.Count - 1;
|
||||
}
|
||||
_StartSong();
|
||||
CanSing = !_IsBackgroundFile(_CurrentPlaylistElement);
|
||||
}
|
||||
|
||||
public static void Previous()
|
||||
{
|
||||
if (_PreviousFileNames.Count > 0 && _PreviousMusicIndex >= 0)
|
||||
if (_PreviousMusicIndex < 0)
|
||||
return;
|
||||
Debug.Assert(_CurrentMusicStream != -1 && _CurrentPlaylistElement != null);
|
||||
if (CSound.GetPosition(_CurrentMusicStream) <= 1.5f && _PreviousMusicIndex > 0)
|
||||
{
|
||||
if (CSound.GetPosition(_CurrentMusicStream) >= 1.5f)
|
||||
{
|
||||
//Seek to #Start-Tag, if found
|
||||
if (_CurrentPlaylistElement.Start > 0.001 && CConfig.BackgroundMusicUseStart == EOffOn.TR_CONFIG_ON)
|
||||
CSound.SetPosition(_CurrentMusicStream, _CurrentPlaylistElement.Start);
|
||||
else
|
||||
CSound.SetPosition(_CurrentMusicStream, 0);
|
||||
if (_VideoEnabled && _Video != -1)
|
||||
{
|
||||
if (_CurrentPlaylistElement.Start > 0.001 && CConfig.BackgroundMusicUseStart == EOffOn.TR_CONFIG_ON)
|
||||
CVideo.Skip(_Video, _CurrentPlaylistElement.Start, _CurrentPlaylistElement.VideoGap);
|
||||
else
|
||||
CVideo.Skip(_Video, 0f, _CurrentPlaylistElement.VideoGap);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Stop();
|
||||
_PreviousMusicIndex--;
|
||||
if (_PreviousMusicIndex < 0)
|
||||
_PreviousMusicIndex = 0; //No previous songs left, so play the first
|
||||
Stop(); //stop last song
|
||||
_PreviousMusicIndex--;
|
||||
|
||||
_CurrentPlaylistElement = _PreviousFileNames[_PreviousMusicIndex];
|
||||
|
||||
_CurrentMusicStream = CSound.Load(_CurrentPlaylistElement.MusicFilePath);
|
||||
CSound.SetStreamVolumeMax(_CurrentMusicStream, CConfig.BackgroundMusicVolume);
|
||||
if (_VideoEnabled)
|
||||
_LoadVideo();
|
||||
CSound.SetStreamVolume(_CurrentMusicStream, 0f);
|
||||
Play();
|
||||
}
|
||||
_CurrentPlaylistElement = _PreviousFiles[_PreviousMusicIndex];
|
||||
CanSing = !_IsBackgroundFile(_CurrentPlaylistElement);
|
||||
}
|
||||
else if (_CurrentMusicStream != -1)
|
||||
_StartSong();
|
||||
}
|
||||
|
||||
public static void Update()
|
||||
{
|
||||
if (!IsPlaying)
|
||||
return;
|
||||
Debug.Assert(_CurrentMusicStream != -1 && _CurrentPlaylistElement != null);
|
||||
|
||||
float timeToPlay;
|
||||
if (Math.Abs(_CurrentPlaylistElement.Finish) < 0.001) //No End-Tag defined
|
||||
timeToPlay = CSound.GetLength(_CurrentMusicStream) - CSound.GetPosition(_CurrentMusicStream);
|
||||
else //End-Tag found
|
||||
timeToPlay = _CurrentPlaylistElement.Finish - CSound.GetPosition(_CurrentMusicStream);
|
||||
|
||||
bool finished = CSound.IsFinished(_CurrentMusicStream);
|
||||
if (timeToPlay <= CSettings.BackgroundMusicFadeTime || finished)
|
||||
{
|
||||
//Seek to #Start-Tag, if found
|
||||
if (_CurrentPlaylistElement.Start > 0.001 && CConfig.BackgroundMusicUseStart == EOffOn.TR_CONFIG_ON)
|
||||
CSound.SetPosition(_CurrentMusicStream, _CurrentPlaylistElement.Start);
|
||||
if (RepeatSong)
|
||||
_StartSong();
|
||||
else
|
||||
CSound.SetPosition(_CurrentMusicStream, 0);
|
||||
if (_VideoEnabled && _Video != -1)
|
||||
Next();
|
||||
}
|
||||
}
|
||||
|
||||
public static CTexture GetVideoTexture()
|
||||
{
|
||||
if (_Video == -1)
|
||||
return null;
|
||||
float vtime;
|
||||
if (CVideo.GetFrame(_Video, ref _CurrentVideoTexture, CSound.GetPosition(_CurrentMusicStream), out vtime))
|
||||
{
|
||||
if (_VideoFading != null)
|
||||
{
|
||||
bool finished;
|
||||
_CurrentVideoTexture.Color.A = _VideoFading.GetValue(out finished);
|
||||
if (finished)
|
||||
_VideoFading = null;
|
||||
}
|
||||
return _CurrentVideoTexture;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void SetMusicSource(EBackgroundMusicSource source)
|
||||
{
|
||||
if (_MusicSource == source)
|
||||
return;
|
||||
_MusicSource = source;
|
||||
switch (_MusicSource)
|
||||
{
|
||||
case EBackgroundMusicSource.TR_CONFIG_NO_OWN_MUSIC:
|
||||
_AddBackgroundMusic();
|
||||
_RemoveOwnMusic();
|
||||
break;
|
||||
|
||||
case EBackgroundMusicSource.TR_CONFIG_ONLY_OWN_MUSIC:
|
||||
_AddOwnMusic();
|
||||
_RemoveBackgroundMusic();
|
||||
break;
|
||||
case EBackgroundMusicSource.TR_CONFIG_OWN_MUSIC:
|
||||
_AddBackgroundMusic();
|
||||
_AddOwnMusic();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetVolume(float volume)
|
||||
{
|
||||
_Volume = volume;
|
||||
if (_CurrentMusicStream >= 0)
|
||||
CSound.SetStreamVolume(_CurrentMusicStream, _Volume);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// (Re-)Starts the _CurrentPlaylistElement
|
||||
/// </summary>
|
||||
private static void _StartSong()
|
||||
{
|
||||
Debug.Assert(_CurrentPlaylistElement != null);
|
||||
//If we have an active stream, reuse it
|
||||
if (_CurrentMusicStream < 0)
|
||||
{
|
||||
//otherwhise load
|
||||
_CurrentMusicStream = CSound.Load(_CurrentPlaylistElement.MusicFilePath);
|
||||
if (_CurrentMusicStream < 0)
|
||||
return;
|
||||
CSound.SetStreamVolume(_CurrentMusicStream, 0f);
|
||||
CSound.Fade(_CurrentMusicStream, _Volume, CSettings.BackgroundMusicFadeTime);
|
||||
}
|
||||
//Seek to #Start-Tag, if found
|
||||
if (_CurrentPlaylistElement.Start > 0.001 && CConfig.BackgroundMusicUseStart == EOffOn.TR_CONFIG_ON)
|
||||
CSound.SetPosition(_CurrentMusicStream, _CurrentPlaylistElement.Start);
|
||||
|
||||
if (_VideoEnabled)
|
||||
{
|
||||
if (_Video != -1)
|
||||
{
|
||||
if (_CurrentPlaylistElement.Start > 0.001 && CConfig.BackgroundMusicUseStart == EOffOn.TR_CONFIG_ON)
|
||||
CVideo.Skip(_Video, _CurrentPlaylistElement.Start, _CurrentPlaylistElement.VideoGap);
|
||||
CVideo.Skip(_Video, 0f, _CurrentPlaylistElement.VideoGap + _CurrentPlaylistElement.Start);
|
||||
else
|
||||
CVideo.Skip(_Video, 0f, _CurrentPlaylistElement.VideoGap);
|
||||
}
|
||||
else
|
||||
_LoadVideo();
|
||||
}
|
||||
}
|
||||
|
||||
public static void ApplyVolume()
|
||||
private static void _AddOwnMusic()
|
||||
{
|
||||
CSound.SetStreamVolumeMax(_CurrentMusicStream, CConfig.BackgroundMusicVolume);
|
||||
}
|
||||
|
||||
public static void AddOwnMusic()
|
||||
{
|
||||
if (!_OwnMusicAdded)
|
||||
{
|
||||
foreach (CSong song in CSongs.AllSongs)
|
||||
{
|
||||
_AllFileNames.Add(new CPlaylistElement(song));
|
||||
_NotPlayedFileNames.Add(new CPlaylistElement(song));
|
||||
}
|
||||
}
|
||||
if (_OwnMusicAdded || !_OwnSongsAvailable)
|
||||
return;
|
||||
foreach (CSong song in CSongs.AllSongs)
|
||||
_NotPlayedFiles.Add(new CPlaylistElement(song));
|
||||
_OwnMusicAdded = true;
|
||||
}
|
||||
|
||||
public static void RemoveOwnMusic()
|
||||
private static void _RemoveOwnMusic()
|
||||
{
|
||||
_AllFileNames.Clear();
|
||||
_NotPlayedFileNames.Clear();
|
||||
if (!_OwnMusicAdded)
|
||||
return;
|
||||
_NotPlayedFiles.RemoveAll(el => el.SongID >= 0);
|
||||
_PreviousFiles.RemoveAll(el => el.SongID >= 0);
|
||||
_PreviousMusicIndex = _PreviousFiles.IndexOf(_CurrentPlaylistElement);
|
||||
|
||||
if (_BackgroundMusicAdded)
|
||||
{
|
||||
_AllFileNames.AddRange(_BGMusicFileNames);
|
||||
_NotPlayedFileNames.AddRange(_BGMusicFileNames);
|
||||
}
|
||||
|
||||
if (IsPlaying && !_IsBackgroundFile(_CurrentPlaylistElement) || _AllFileNames.Count == 0)
|
||||
if (IsPlaying && !_IsBackgroundFile(_CurrentPlaylistElement))
|
||||
Next();
|
||||
|
||||
_OwnMusicAdded = false;
|
||||
}
|
||||
|
||||
public static void AddBackgroundMusic()
|
||||
{
|
||||
if (!_BackgroundMusicAdded)
|
||||
{
|
||||
_AllFileNames.AddRange(_BGMusicFileNames);
|
||||
_NotPlayedFileNames.AddRange(_BGMusicFileNames);
|
||||
_BackgroundMusicAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemoveBackgroundMusic()
|
||||
private static void _AddBackgroundMusic()
|
||||
{
|
||||
if (_BackgroundMusicAdded)
|
||||
{
|
||||
_AllFileNames.Clear();
|
||||
_OwnMusicAdded = false;
|
||||
AddOwnMusic();
|
||||
_BackgroundMusicAdded = false;
|
||||
|
||||
if (IsPlaying && _IsBackgroundFile(_CurrentPlaylistElement) || _AllFileNames.Count == 0)
|
||||
Next();
|
||||
}
|
||||
return;
|
||||
_NotPlayedFiles.AddRange(_BGMusicFiles);
|
||||
_BackgroundMusicAdded = true;
|
||||
}
|
||||
|
||||
public static void CheckAndApplyConfig(EOffOn newOnOff, EBackgroundMusicSource newSource, float newVolume)
|
||||
private static void _RemoveBackgroundMusic()
|
||||
{
|
||||
if (CConfig.BackgroundMusicSource != newSource || (newOnOff == EOffOn.TR_CONFIG_ON && !_OwnMusicAdded && !_BackgroundMusicAdded))
|
||||
{
|
||||
CConfig.BackgroundMusicSource = newSource;
|
||||
if (!_BackgroundMusicAdded)
|
||||
return;
|
||||
_NotPlayedFiles.RemoveAll(el => el.SongID < 0);
|
||||
_PreviousFiles.RemoveAll(el => el.SongID < 0);
|
||||
_PreviousMusicIndex = _PreviousFiles.IndexOf(_CurrentPlaylistElement);
|
||||
|
||||
switch (newSource)
|
||||
{
|
||||
case EBackgroundMusicSource.TR_CONFIG_NO_OWN_MUSIC:
|
||||
if (!_BackgroundMusicAdded)
|
||||
AddBackgroundMusic();
|
||||
if (IsPlaying && _IsBackgroundFile(_CurrentPlaylistElement))
|
||||
Next();
|
||||
|
||||
RemoveOwnMusic();
|
||||
break;
|
||||
|
||||
case EBackgroundMusicSource.TR_CONFIG_ONLY_OWN_MUSIC:
|
||||
if (!_OwnMusicAdded)
|
||||
AddOwnMusic();
|
||||
|
||||
RemoveBackgroundMusic();
|
||||
break;
|
||||
case EBackgroundMusicSource.TR_CONFIG_OWN_MUSIC:
|
||||
if (!_BackgroundMusicAdded)
|
||||
AddBackgroundMusic();
|
||||
|
||||
if (!_OwnMusicAdded)
|
||||
AddOwnMusic();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (CConfig.BackgroundMusic != newOnOff)
|
||||
{
|
||||
CConfig.BackgroundMusic = newOnOff;
|
||||
if (newOnOff == EOffOn.TR_CONFIG_ON)
|
||||
Play();
|
||||
else
|
||||
Pause();
|
||||
}
|
||||
|
||||
if (Math.Abs(CConfig.BackgroundMusicVolume - newVolume) > 0.01)
|
||||
{
|
||||
CConfig.BackgroundMusicVolume = (int)newVolume;
|
||||
ApplyVolume();
|
||||
}
|
||||
|
||||
CConfig.SaveConfig();
|
||||
}
|
||||
|
||||
public static void ToggleVideo()
|
||||
{
|
||||
if (_Video != -1)
|
||||
{
|
||||
CVideo.Close(_Video);
|
||||
CDraw.RemoveTexture(ref _CurrentVideoTexture);
|
||||
_Video = -1;
|
||||
}
|
||||
else
|
||||
_LoadVideo();
|
||||
}
|
||||
|
||||
public static CTexture GetVideoTexture()
|
||||
{
|
||||
if (_Video != -1)
|
||||
{
|
||||
float vtime;
|
||||
if (CVideo.GetFrame(_Video, ref _CurrentVideoTexture, CSound.GetPosition(_CurrentMusicStream), out vtime))
|
||||
{
|
||||
if (_VideoFading != null)
|
||||
{
|
||||
bool finished;
|
||||
_CurrentVideoTexture.Color.A = _VideoFading.GetValue(out finished);
|
||||
if (finished)
|
||||
_VideoFading = null;
|
||||
}
|
||||
return _CurrentVideoTexture;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
_BackgroundMusicAdded = false;
|
||||
}
|
||||
|
||||
private static bool _IsBackgroundFile(CPlaylistElement element)
|
||||
{
|
||||
return _BGMusicFileNames.Any(elements => elements.MusicFilePath == element.MusicFilePath);
|
||||
return _BGMusicFiles.Contains(element);
|
||||
}
|
||||
|
||||
private static void _LoadVideo()
|
||||
@@ -464,134 +408,11 @@ namespace Vocaluxe.Base
|
||||
if (_Video != -1 || String.IsNullOrEmpty(_CurrentPlaylistElement.VideoFilePath))
|
||||
return;
|
||||
_Video = CVideo.Load(_CurrentPlaylistElement.VideoFilePath);
|
||||
if (_CurrentPlaylistElement.Start > 0.001 && CConfig.BackgroundMusicUseStart == EOffOn.TR_CONFIG_ON)
|
||||
CVideo.Skip(_Video, _CurrentPlaylistElement.Start, _CurrentPlaylistElement.VideoGap);
|
||||
if (CConfig.BackgroundMusicUseStart == EOffOn.TR_CONFIG_ON)
|
||||
CVideo.Skip(_Video, 0f, _CurrentPlaylistElement.VideoGap + _CurrentPlaylistElement.Start); //Use gap, otherwhise we will overwrite position in GetVideoTexture
|
||||
else
|
||||
CVideo.Skip(_Video, 0f, _CurrentPlaylistElement.VideoGap);
|
||||
_VideoFading = new CFading(0f, 1f, 3f);
|
||||
}
|
||||
}
|
||||
|
||||
class CPlaylistElement
|
||||
{
|
||||
private readonly CSong _Song;
|
||||
|
||||
private readonly int _SongID;
|
||||
public int SongID
|
||||
{
|
||||
get { return _SongID; }
|
||||
}
|
||||
|
||||
private string _MusicFilePath = String.Empty;
|
||||
|
||||
public string MusicFilePath
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Song != null)
|
||||
return _Song.GetMP3();
|
||||
|
||||
return _MusicFilePath;
|
||||
}
|
||||
|
||||
set { _MusicFilePath = value; }
|
||||
}
|
||||
|
||||
public string VideoFilePath
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Song != null)
|
||||
return _Song.GetVideo();
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public string Title
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Song != null)
|
||||
return _Song.Title;
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public string Artist
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Song != null)
|
||||
return _Song.Artist;
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public float Start
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Song != null)
|
||||
return _Song.Start;
|
||||
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public float Finish
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Song != null)
|
||||
return _Song.Finish;
|
||||
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public CTexture Cover
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Song != null)
|
||||
return _Song.CoverTextureSmall;
|
||||
|
||||
return CCover.NoCover;
|
||||
}
|
||||
}
|
||||
|
||||
public float VideoGap
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Song != null)
|
||||
return _Song.VideoGap;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public CPlaylistElement(CSong song)
|
||||
{
|
||||
MusicFilePath = string.Empty;
|
||||
_SongID = song.ID;
|
||||
|
||||
_Song = song;
|
||||
}
|
||||
|
||||
public CPlaylistElement(string filePath)
|
||||
{
|
||||
MusicFilePath = filePath;
|
||||
_SongID = -1;
|
||||
}
|
||||
|
||||
public CPlaylistElement()
|
||||
{
|
||||
MusicFilePath = string.Empty;
|
||||
_SongID = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
+68
-62
@@ -24,7 +24,7 @@ using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
using System.Xml;
|
||||
using Vocaluxe.Lib.Sound;
|
||||
using Vocaluxe.Lib.Sound.Record;
|
||||
using Vocaluxe.Lib.Webcam;
|
||||
using VocaluxeLib;
|
||||
using VocaluxeLib.Profile;
|
||||
@@ -43,6 +43,15 @@ namespace Vocaluxe.Base
|
||||
ConformanceLevel = ConformanceLevel.Document
|
||||
};
|
||||
|
||||
private static bool _Initialized;
|
||||
|
||||
// Base file and folder names (formerly from CSettings but they can be changed)
|
||||
private const string _FolderProfiles = "Profiles"; //Use ProfileFolders for access
|
||||
private const string _FolderSongs = "Songs"; //Use SongFolders for access
|
||||
public static string FolderPlaylists = "Playlists";
|
||||
public static string FileHighscoreDB = "HighscoreDB.sqlite";
|
||||
private static string _FileConfig = "Config.xml";
|
||||
|
||||
// Debug
|
||||
public static EDebugLevel DebugLevel = EDebugLevel.TR_CONFIG_OFF;
|
||||
public static EOffOn SaveModifiedSongs = EOffOn.TR_CONFIG_OFF;
|
||||
@@ -87,7 +96,6 @@ namespace Vocaluxe.Base
|
||||
|
||||
// Sound
|
||||
public static EPlaybackLib PlayBackLib = EPlaybackLib.Gstreamer;
|
||||
public static ERecordLib RecordLib = ERecordLib.PortAudio;
|
||||
public static EBufferSize AudioBufferSize = EBufferSize.B2048;
|
||||
public static int AudioLatency;
|
||||
private static int _BackgroundMusicVolume = 30;
|
||||
@@ -97,8 +105,28 @@ namespace Vocaluxe.Base
|
||||
public static int PreviewMusicVolume = 50;
|
||||
public static int GameMusicVolume = 80;
|
||||
|
||||
//Folders
|
||||
public static readonly List<string> SongFolders = new List<string>()
|
||||
{
|
||||
Path.Combine(Directory.GetCurrentDirectory(), _FolderSongs)
|
||||
|
||||
#if INSTALLER
|
||||
,Path.Combine(CSettings.DataPath, CSettings.FolderSongs);
|
||||
#endif
|
||||
};
|
||||
/// <summary>
|
||||
/// Folders with profiles
|
||||
/// First one is used for new profiles
|
||||
/// </summary>
|
||||
public static readonly List<string> ProfileFolders = new List<string>
|
||||
{
|
||||
#if INSTALLER
|
||||
Path.Combine(Environment.CurrentDirectory, CSettings.FolderProfiles),
|
||||
#endif
|
||||
Path.Combine(CSettings.DataPath, _FolderProfiles)
|
||||
};
|
||||
|
||||
// Game
|
||||
public static readonly List<string> SongFolder = new List<string>();
|
||||
public static ESongMenu SongMenu = ESongMenu.TR_CONFIG_TILE_BOARD;
|
||||
public static ESongSorting SongSorting = ESongSorting.TR_CONFIG_ARTIST;
|
||||
public static EOffOn IgnoreArticles = EOffOn.TR_CONFIG_ON;
|
||||
@@ -120,8 +148,11 @@ namespace Vocaluxe.Base
|
||||
public static EOffOn VideosToBackground = EOffOn.TR_CONFIG_OFF;
|
||||
|
||||
// Record
|
||||
public static SMicConfig[] MicConfig;
|
||||
public static int MicDelay = 300; //[ms]
|
||||
public static ERecordLib RecordLib = ERecordLib.PortAudio;
|
||||
public static readonly SMicConfig[] MicConfig = new SMicConfig[CSettings.MaxNumPlayer];
|
||||
public static int MicDelay = 200; //[ms]
|
||||
|
||||
// Webcam
|
||||
public static EWebcamLib WebcamLib = EWebcamLib.OpenCV;
|
||||
public static SWebcamConfig WebcamConfig;
|
||||
|
||||
@@ -135,8 +166,6 @@ namespace Vocaluxe.Base
|
||||
private static readonly List<string> _Params = new List<string>();
|
||||
private static readonly List<string> _Values = new List<string>();
|
||||
|
||||
//Variables to save old values for commandline-parameters
|
||||
private static readonly List<string> _SongFolderOld = new List<string>();
|
||||
public static int BackgroundMusicVolume
|
||||
{
|
||||
get { return _BackgroundMusicVolume; }
|
||||
@@ -154,27 +183,21 @@ namespace Vocaluxe.Base
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
SongFolder.Add(Path.Combine(Directory.GetCurrentDirectory(), CSettings.FolderSongs));
|
||||
|
||||
#if INSTALLER
|
||||
SongFolder.Add(Path.Combine(CSettings.DataPath, CSettings.FolderSongs));
|
||||
#endif
|
||||
|
||||
foreach (string folder in SongFolder)
|
||||
CSettings.CreateFolder(folder);
|
||||
|
||||
MicConfig = new SMicConfig[CSettings.MaxNumPlayer];
|
||||
if (_Initialized)
|
||||
return;
|
||||
|
||||
// Init config file
|
||||
if (!File.Exists(Path.Combine(CSettings.DataPath, CSettings.FileConfig)))
|
||||
if (!File.Exists(Path.Combine(CSettings.DataPath, _FileConfig)))
|
||||
SaveConfig();
|
||||
else
|
||||
_LoadConfig();
|
||||
|
||||
_LoadConfig();
|
||||
_Initialized = true;
|
||||
}
|
||||
|
||||
private static void _LoadConfig()
|
||||
{
|
||||
CXMLReader xmlReader = CXMLReader.OpenFile(Path.Combine(CSettings.DataPath, CSettings.FileConfig));
|
||||
CXMLReader xmlReader = CXMLReader.OpenFile(Path.Combine(CSettings.DataPath, _FileConfig));
|
||||
if (xmlReader == null)
|
||||
return;
|
||||
|
||||
@@ -231,15 +254,14 @@ namespace Vocaluxe.Base
|
||||
|
||||
#region Game
|
||||
// Songfolder
|
||||
string value = string.Empty;
|
||||
string value = String.Empty;
|
||||
int i = 1;
|
||||
while (xmlReader.GetValue("//root/Game/SongFolder" + i, out value, value))
|
||||
{
|
||||
if (i == 1)
|
||||
SongFolder.Clear();
|
||||
|
||||
SongFolder.Add(value);
|
||||
value = string.Empty;
|
||||
SongFolders.Clear();
|
||||
SongFolders.Add(value);
|
||||
value = String.Empty;
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -271,7 +293,7 @@ namespace Vocaluxe.Base
|
||||
|
||||
//Read players from config
|
||||
for (i = 1; i <= CSettings.MaxNumPlayer; i++)
|
||||
xmlReader.GetValue("//root/Game/Players/Player" + i, out Players[i - 1], string.Empty);
|
||||
xmlReader.GetValue("//root/Game/Players/Player" + i, out Players[i - 1], String.Empty);
|
||||
#endregion Game
|
||||
|
||||
#region Video
|
||||
@@ -290,7 +312,6 @@ namespace Vocaluxe.Base
|
||||
#endregion Video
|
||||
|
||||
#region Record
|
||||
MicConfig = new SMicConfig[CSettings.MaxNumPlayer];
|
||||
for (int p = 1; p <= CSettings.MaxNumPlayer; p++)
|
||||
{
|
||||
MicConfig[p - 1] = new SMicConfig(0);
|
||||
@@ -319,7 +340,7 @@ namespace Vocaluxe.Base
|
||||
XmlWriter writer = null;
|
||||
try
|
||||
{
|
||||
writer = XmlWriter.Create(Path.Combine(CSettings.DataPath, CSettings.FileConfig), XMLSettings);
|
||||
writer = XmlWriter.Create(Path.Combine(CSettings.DataPath, _FileConfig), XMLSettings);
|
||||
writer.WriteStartDocument();
|
||||
writer.WriteStartElement("root");
|
||||
|
||||
@@ -470,21 +491,9 @@ namespace Vocaluxe.Base
|
||||
writer.WriteElementString("Language", Language);
|
||||
|
||||
// SongFolder
|
||||
// Check, if program was started with songfolder-parameters
|
||||
if (_SongFolderOld.Count > 0)
|
||||
{
|
||||
//Write "old" song-folders to config.
|
||||
writer.WriteComment("SongFolder: SongFolder1, SongFolder2, SongFolder3, ...");
|
||||
for (int i = 0; i < _SongFolderOld.Count; i++)
|
||||
writer.WriteElementString("SongFolder" + (i + 1), _SongFolderOld[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Write "normal" song-folders to config.
|
||||
writer.WriteComment("SongFolder: SongFolder1, SongFolder2, SongFolder3, ...");
|
||||
for (int i = 0; i < SongFolder.Count; i++)
|
||||
writer.WriteElementString("SongFolder" + (i + 1), SongFolder[i]);
|
||||
}
|
||||
writer.WriteComment("SongFolder: SongFolder1, SongFolder2, SongFolder3, ...");
|
||||
for (int i = 0; i < SongFolders.Count; i++)
|
||||
writer.WriteElementString("SongFolder" + (i + 1), SongFolders[i]);
|
||||
|
||||
writer.WriteComment("SongMenu: " + CHelper.ListStrings(Enum.GetNames(typeof(ESongMenu))));
|
||||
writer.WriteElementString("SongMenu", Enum.GetName(typeof(ESongMenu), SongMenu));
|
||||
@@ -626,7 +635,7 @@ namespace Vocaluxe.Base
|
||||
/// <returns></returns>
|
||||
public static bool IsMicConfig()
|
||||
{
|
||||
ReadOnlyCollection<CRecordDevice> devices = CSound.RecordGetDevices();
|
||||
ReadOnlyCollection<CRecordDevice> devices = CRecord.GetDevices();
|
||||
if (devices == null)
|
||||
return false;
|
||||
|
||||
@@ -640,7 +649,7 @@ namespace Vocaluxe.Base
|
||||
/// <returns></returns>
|
||||
public static bool IsMicConfig(int player)
|
||||
{
|
||||
ReadOnlyCollection<CRecordDevice> devices = CSound.RecordGetDevices();
|
||||
ReadOnlyCollection<CRecordDevice> devices = CRecord.GetDevices();
|
||||
if (devices == null)
|
||||
return false;
|
||||
|
||||
@@ -666,8 +675,8 @@ namespace Vocaluxe.Base
|
||||
public static bool AutoAssignMics()
|
||||
{
|
||||
//Look for (usb-)mic
|
||||
//SRecordDevice[] Devices = new SRecordDevice[CSound.RecordGetDevices().Length];
|
||||
ReadOnlyCollection<CRecordDevice> devices = CSound.RecordGetDevices();
|
||||
//SRecordDevice[] Devices = new SRecordDevice[CRecord.RecordGetDevices().Length];
|
||||
ReadOnlyCollection<CRecordDevice> devices = CRecord.GetDevices();
|
||||
if (devices == null)
|
||||
return false;
|
||||
|
||||
@@ -731,7 +740,7 @@ namespace Vocaluxe.Base
|
||||
Regex spliterParam = new Regex(@"-{1,2}|\/", RegexOptions.IgnoreCase);
|
||||
|
||||
//Complete argument string
|
||||
string arguments = args.Aggregate(string.Empty, (current, arg) => current + (arg + " "));
|
||||
string arguments = args.Aggregate(String.Empty, (current, arg) => current + arg + " ");
|
||||
|
||||
args = spliterParam.Split(arguments);
|
||||
|
||||
@@ -792,24 +801,22 @@ namespace Vocaluxe.Base
|
||||
case "configfile":
|
||||
//Check if value is valid
|
||||
if (_CheckFile(value))
|
||||
CSettings.FileConfig = value;
|
||||
_FileConfig = value;
|
||||
break;
|
||||
|
||||
case "scorefile":
|
||||
//Check if value is valid
|
||||
if (_CheckFile(value))
|
||||
CSettings.FileHighscoreDB = value;
|
||||
FileHighscoreDB = value;
|
||||
break;
|
||||
|
||||
case "playlistfolder":
|
||||
CSettings.CreateFolder(value);
|
||||
CSettings.FolderPlaylists = value;
|
||||
FolderPlaylists = value;
|
||||
break;
|
||||
|
||||
case "profilefolder":
|
||||
CSettings.CreateFolder(value);
|
||||
CSettings.FoldersProfiles.Clear();
|
||||
CSettings.FoldersProfiles.Add(value);
|
||||
ProfileFolders.Clear();
|
||||
ProfileFolders.Add(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -820,6 +827,7 @@ namespace Vocaluxe.Base
|
||||
/// </summary>
|
||||
public static void UseCommandLineParamsAfter()
|
||||
{
|
||||
bool songFoldersOverwritten = false;
|
||||
//Check each parameter
|
||||
for (int i = 0; i < _Params.Count; i++)
|
||||
{
|
||||
@@ -834,15 +842,13 @@ namespace Vocaluxe.Base
|
||||
{
|
||||
case "songfolder":
|
||||
case "songpath":
|
||||
//Check if SongFolders from config.xml are saved
|
||||
if (_SongFolderOld.Count == 0)
|
||||
if (!songFoldersOverwritten)
|
||||
{
|
||||
_SongFolderOld.Clear();
|
||||
_SongFolderOld.AddRange(SongFolder);
|
||||
SongFolder.Clear();
|
||||
SongFolders.Clear();
|
||||
songFoldersOverwritten = true;
|
||||
}
|
||||
//Add parameter-value to SongFolder
|
||||
SongFolder.Add(value);
|
||||
if (!SongFolders.Contains(value))
|
||||
SongFolders.Add(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,19 +36,26 @@ namespace Vocaluxe.Base
|
||||
_KeysPool = new List<SKeyEvent>();
|
||||
_MousePool = new List<SMouseEvent>();
|
||||
|
||||
_Controller.Add(new CWiiMote());
|
||||
_Controller.Add(CVocaluxeServer.Controller);
|
||||
_AddController(new CWiiMote());
|
||||
_AddController(CVocaluxeServer.Controller);
|
||||
}
|
||||
|
||||
foreach (IController controller in _Controller)
|
||||
controller.Init();
|
||||
private static void _AddController(IController controller)
|
||||
{
|
||||
if (controller.Init())
|
||||
_Controller.Add(controller);
|
||||
}
|
||||
|
||||
public static void Close()
|
||||
{
|
||||
foreach (IController controller in _Controller)
|
||||
controller.Close();
|
||||
|
||||
_Controller = new List<IController>();
|
||||
if (_Controller != null)
|
||||
{
|
||||
foreach (IController controller in _Controller)
|
||||
controller.Close();
|
||||
_Controller = null;
|
||||
_KeysPool = null;
|
||||
_MousePool = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Connect()
|
||||
|
||||
+34
-35
@@ -36,11 +36,9 @@ namespace Vocaluxe.Base
|
||||
static class CCover
|
||||
{
|
||||
private static readonly XmlWriterSettings _Settings = new XmlWriterSettings();
|
||||
private static readonly Dictionary<string, CTexture> _Cover = new Dictionary<string, CTexture>();
|
||||
private static readonly Dictionary<string, CTexture> _Covers = new Dictionary<string, CTexture>();
|
||||
private static readonly List<SCoverTheme> _CoverThemes = new List<SCoverTheme>();
|
||||
|
||||
private static readonly Object _MutexCover = new Object();
|
||||
|
||||
public static CTexture NoCover { get; private set; }
|
||||
|
||||
public static void Init()
|
||||
@@ -87,24 +85,22 @@ namespace Vocaluxe.Base
|
||||
/// </summary>
|
||||
public static CTexture Cover(string name)
|
||||
{
|
||||
lock (_MutexCover)
|
||||
lock (_Covers)
|
||||
{
|
||||
if (!_CoverExists(name))
|
||||
return NoCover;
|
||||
|
||||
return _Cover[name];
|
||||
return _Covers[name];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if a cover with the given name exists.
|
||||
/// MUST HOLD _Covers lock at this point
|
||||
/// </summary>
|
||||
private static bool _CoverExists(string name)
|
||||
{
|
||||
lock (_MutexCover)
|
||||
{
|
||||
return _Cover.ContainsKey(name);
|
||||
}
|
||||
return _Covers.ContainsKey(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -112,14 +108,15 @@ namespace Vocaluxe.Base
|
||||
/// </summary>
|
||||
public static void ReloadCover()
|
||||
{
|
||||
List<string> keys = _Cover.Keys.ToList();
|
||||
|
||||
foreach (string key in keys)
|
||||
lock (_Covers)
|
||||
{
|
||||
CTexture texture = _Cover[key];
|
||||
CDraw.RemoveTexture(ref texture);
|
||||
foreach (string key in _Covers.Keys.ToList())
|
||||
{
|
||||
CTexture texture = _Covers[key];
|
||||
CDraw.RemoveTexture(ref texture);
|
||||
}
|
||||
_Covers.Clear();
|
||||
}
|
||||
_Cover.Clear();
|
||||
_LoadCover(CConfig.CoverTheme);
|
||||
}
|
||||
|
||||
@@ -178,12 +175,16 @@ namespace Vocaluxe.Base
|
||||
return;
|
||||
|
||||
var ignoreList = new List<string>();
|
||||
|
||||
string coverPath = Path.Combine(CSettings.FolderCover, coverTheme.Folder);
|
||||
List<string> files = CHelper.ListImageFiles(coverPath, true, true);
|
||||
|
||||
CXMLReader xmlReader = CXMLReader.OpenFile(Path.Combine(CSettings.FolderCover, coverTheme.File));
|
||||
if (xmlReader != null)
|
||||
lock (_Covers)
|
||||
{
|
||||
lock (_MutexCover)
|
||||
if (xmlReader != null)
|
||||
{
|
||||
_Cover.Clear();
|
||||
_Covers.Clear();
|
||||
List<string> cover = xmlReader.GetValues("Cover");
|
||||
foreach (string coverName in cover)
|
||||
{
|
||||
@@ -199,38 +200,36 @@ namespace Vocaluxe.Base
|
||||
ignoreList.Add(Path.GetFileName(coverFilePath));
|
||||
|
||||
if (name == "NoCover")
|
||||
NoCover = _Cover[name];
|
||||
NoCover = _Covers[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var files = new List<string>();
|
||||
|
||||
string coverPath = Path.Combine(CSettings.FolderCover, coverTheme.Folder);
|
||||
files.AddRange(CHelper.ListFiles(coverPath, "*.png", true, true));
|
||||
files.AddRange(CHelper.ListFiles(coverPath, "*.jpg", true, true));
|
||||
files.AddRange(CHelper.ListFiles(coverPath, "*.jpeg", true, true));
|
||||
files.AddRange(CHelper.ListFiles(coverPath, "*.bmp", true, true));
|
||||
|
||||
|
||||
foreach (string file in files)
|
||||
{
|
||||
if (!ignoreList.Contains(Path.GetFileName(file)))
|
||||
_AddCover(Path.GetFileNameWithoutExtension(file), file);
|
||||
foreach (string file in files)
|
||||
{
|
||||
if (!ignoreList.Contains(Path.GetFileName(file)))
|
||||
_AddCover(Path.GetFileNameWithoutExtension(file), file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ads a cover with the given name if it does not exist yet
|
||||
/// MUST HOLD _Covers lock at this point
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the cover</param>
|
||||
/// <param name="file">Filename of image file</param>
|
||||
private static void _AddCover(string name, string file)
|
||||
{
|
||||
CTexture texture = CDraw.AddTexture(file);
|
||||
|
||||
if (!_CoverExists(name))
|
||||
_Cover.Add(name, texture);
|
||||
_Covers.Add(name, texture);
|
||||
else
|
||||
{
|
||||
CTexture tex = _Cover[name];
|
||||
CTexture tex = _Covers[name];
|
||||
CDraw.RemoveTexture(ref tex);
|
||||
_Cover[name] = texture;
|
||||
_Covers[name] = texture;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+16
-19
@@ -24,6 +24,7 @@ namespace Vocaluxe.Base
|
||||
{
|
||||
class CCursor
|
||||
{
|
||||
private int _MouseMoveDiffMin = CSettings.MouseMoveDiffMinActive;
|
||||
private CFading _Fading;
|
||||
private CTexture _Cursor;
|
||||
|
||||
@@ -49,12 +50,8 @@ namespace Vocaluxe.Base
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
if (_Movetimer.IsRunning && _Movetimer.ElapsedMilliseconds > CSettings.MouseMoveOffTime)
|
||||
{
|
||||
_Movetimer.Stop();
|
||||
_Fade(0f, 0.5f);
|
||||
}
|
||||
|
||||
if (_Movetimer.ElapsedMilliseconds > CSettings.MouseMoveOffTime)
|
||||
Deactivate();
|
||||
|
||||
if (_Fading != null)
|
||||
{
|
||||
@@ -64,21 +61,15 @@ namespace Vocaluxe.Base
|
||||
_Fading = null;
|
||||
}
|
||||
|
||||
if (Visible && (CSettings.GameState == EGameState.EditTheme || ShowCursor))
|
||||
if (Visible && (CSettings.ProgramState == EProgramState.EditTheme || ShowCursor))
|
||||
CDraw.DrawTexture(_Cursor);
|
||||
}
|
||||
|
||||
public void UpdatePosition(int x, int y)
|
||||
{
|
||||
if (Math.Abs(_Cursor.Rect.X - x) > CSettings.MouseMoveDiffMin ||
|
||||
Math.Abs(_Cursor.Rect.Y - y) > CSettings.MouseMoveDiffMin)
|
||||
{
|
||||
if (!IsActive)
|
||||
_Fade(1f, 0.2f);
|
||||
|
||||
_Movetimer.Restart();
|
||||
CSettings.MouseActive();
|
||||
}
|
||||
if (Math.Abs(_Cursor.Rect.X - x) > _MouseMoveDiffMin ||
|
||||
Math.Abs(_Cursor.Rect.Y - y) > _MouseMoveDiffMin)
|
||||
Activate();
|
||||
|
||||
_Cursor.Rect.X = x;
|
||||
_Cursor.Rect.Y = y;
|
||||
@@ -109,14 +100,20 @@ namespace Vocaluxe.Base
|
||||
UpdatePosition(x, y);
|
||||
}
|
||||
|
||||
public void FadeOut()
|
||||
public void Deactivate()
|
||||
{
|
||||
_Movetimer.Stop();
|
||||
if (!IsActive)
|
||||
return;
|
||||
_MouseMoveDiffMin = CSettings.MouseMoveDiffMinInactive;
|
||||
_Movetimer.Reset();
|
||||
_Fade(0f, 0.5f);
|
||||
}
|
||||
|
||||
public void FadeIn()
|
||||
public void Activate()
|
||||
{
|
||||
if (IsActive)
|
||||
return;
|
||||
_MouseMoveDiffMin = CSettings.MouseMoveDiffMinActive;
|
||||
_Movetimer.Restart();
|
||||
_Fade(1f, 0.2f);
|
||||
}
|
||||
|
||||
+152
-125
@@ -20,9 +20,13 @@ using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Community.CsharpSqlite;
|
||||
using VocaluxeLib;
|
||||
using VocaluxeLib.Draw;
|
||||
using VocaluxeLib.Songs;
|
||||
#if WIN
|
||||
using System.Data.SQLite;
|
||||
#else
|
||||
@@ -32,10 +36,6 @@ using SQLiteTransaction = Mono.Data.Sqlite.SqliteTransaction;
|
||||
using SQLiteCommand = Mono.Data.Sqlite.SqliteCommand;
|
||||
using SQLiteDataReader = Mono.Data.Sqlite.SqliteDataReader;
|
||||
#endif
|
||||
using Community.CsharpSqlite;
|
||||
using VocaluxeLib;
|
||||
using VocaluxeLib.Songs;
|
||||
using VocaluxeLib.Draw;
|
||||
|
||||
namespace Vocaluxe.Base
|
||||
{
|
||||
@@ -53,31 +53,37 @@ namespace Vocaluxe.Base
|
||||
private static string _CoverFilePath;
|
||||
private static string _CreditsRessourcesFilePath;
|
||||
|
||||
//You have to lock all actions using cover connection or transaction, otherwhise order is not guaranted
|
||||
private static readonly object _CoverMutex = new object();
|
||||
private static SQLiteConnection _ConnectionCover;
|
||||
private static SQLiteTransaction _TransactionCover;
|
||||
|
||||
public static void Init()
|
||||
public static bool Init()
|
||||
{
|
||||
_HighscoreFilePath = Path.Combine(Environment.CurrentDirectory, CSettings.FileHighscoreDB);
|
||||
_HighscoreFilePath = Path.Combine(Environment.CurrentDirectory, CConfig.FileHighscoreDB);
|
||||
_CoverFilePath = Path.Combine(Environment.CurrentDirectory, CSettings.FileCoverDB);
|
||||
_CreditsRessourcesFilePath = Path.Combine(Environment.CurrentDirectory, CSettings.FileCreditsRessourcesDB);
|
||||
|
||||
_InitHighscoreDB();
|
||||
if (!_InitHighscoreDB())
|
||||
{
|
||||
CLog.LogError("Error initializing Highscore-DB", true, true);
|
||||
return false;
|
||||
}
|
||||
if (!_InitCoverDB())
|
||||
{
|
||||
CLog.LogError("Error initializing Cover-DB", true, true);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (!_InitCreditsRessourcesDB())
|
||||
{
|
||||
CLog.LogError("Error initializing Cover-DB", true, true);
|
||||
return;
|
||||
CLog.LogError("Error initializing Credits-DB", true, true);
|
||||
return false;
|
||||
}
|
||||
GC.Collect();
|
||||
return true;
|
||||
}
|
||||
|
||||
#region Highscores
|
||||
public static bool GetDataBaseSongInfos(string artist, string title, out int numPlayed, out string dateAdded, out int highscoreID)
|
||||
public static bool GetDataBaseSongInfos(string artist, string title, out int numPlayed, out DateTime dateAdded, out int highscoreID)
|
||||
{
|
||||
string sArtist;
|
||||
string sTitle;
|
||||
@@ -378,12 +384,12 @@ namespace Vocaluxe.Base
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static bool _GetDataBaseSongInfos(int songID, out string artist, out string title, out int numPlayed, out string dateAdded, string filePath)
|
||||
private static bool _GetDataBaseSongInfos(int songID, out string artist, out string title, out int numPlayed, out DateTime dateAdded, string filePath)
|
||||
{
|
||||
artist = String.Empty;
|
||||
title = String.Empty;
|
||||
numPlayed = 0;
|
||||
dateAdded = String.Empty;
|
||||
dateAdded = DateTime.Today;
|
||||
|
||||
using (var connection = new SQLiteConnection())
|
||||
{
|
||||
@@ -419,7 +425,7 @@ namespace Vocaluxe.Base
|
||||
artist = reader.GetString(0);
|
||||
title = reader.GetString(1);
|
||||
numPlayed = reader.GetInt32(2);
|
||||
dateAdded = new DateTime(reader.GetInt64(3)).ToString("dd/MM/yyyy");
|
||||
dateAdded = new DateTime(reader.GetInt64(3));
|
||||
reader.Dispose();
|
||||
return true;
|
||||
}
|
||||
@@ -431,7 +437,7 @@ namespace Vocaluxe.Base
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void _InitHighscoreDB()
|
||||
private static bool _InitHighscoreDB()
|
||||
{
|
||||
string oldDBFilePath = Path.Combine(Environment.CurrentDirectory, CSettings.FileOldHighscoreDB);
|
||||
if (File.Exists(oldDBFilePath))
|
||||
@@ -441,17 +447,17 @@ namespace Vocaluxe.Base
|
||||
if (!_CreateOrConvert(oldDBFilePath))
|
||||
{
|
||||
CLog.LogError("Cannot init Highscore DB: Error opening database: " + oldDBFilePath, true, true);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (!_CreateOrConvert(_HighscoreFilePath))
|
||||
{
|
||||
CLog.LogError("Cannot init Highscore DB: Error opening database: " + _HighscoreFilePath, true, true);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (!_ImportData(oldDBFilePath))
|
||||
{
|
||||
CLog.LogError("Cannot init Highscore DB: Error importing data", true, true);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -460,13 +466,17 @@ namespace Vocaluxe.Base
|
||||
if (!_CreateOrConvert(_HighscoreFilePath))
|
||||
{
|
||||
CLog.LogError("Cannot init Highscore DB: Error opening database: " + _HighscoreFilePath, true, true);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
File.Delete(oldDBFilePath);
|
||||
}
|
||||
else if (!_CreateOrConvert(_HighscoreFilePath))
|
||||
{
|
||||
CLog.LogError("Cannot init Highscore DB: Error opening database: " + _HighscoreFilePath, true, true);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void _CreateHighscoreDB(string filePath)
|
||||
@@ -1026,7 +1036,8 @@ namespace Vocaluxe.Base
|
||||
int shortsong = source.GetInt32(7);
|
||||
int diff = source.GetInt32(8);
|
||||
|
||||
string artist, title, dateadded;
|
||||
string artist, title;
|
||||
DateTime dateadded;
|
||||
int numplayed;
|
||||
if (_GetDataBaseSongInfos(songid, out artist, out title, out numplayed, out dateadded, sourceDBPath))
|
||||
AddScore(player, score, linenr, date, medley, duet, shortsong, diff, artist, title, numplayed, _HighscoreFilePath);
|
||||
@@ -1050,134 +1061,150 @@ namespace Vocaluxe.Base
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_ConnectionCover == null)
|
||||
lock (_CoverMutex)
|
||||
{
|
||||
_ConnectionCover = new SQLiteConnection {ConnectionString = "Data Source=" + _CoverFilePath};
|
||||
_ConnectionCover.Open();
|
||||
}
|
||||
|
||||
using (var command = new SQLiteCommand(_ConnectionCover))
|
||||
{
|
||||
command.CommandText = "SELECT id, width, height FROM Cover WHERE [Path] = @path";
|
||||
command.Parameters.Add("@path", DbType.String, 0).Value = coverPath;
|
||||
|
||||
SQLiteDataReader reader = command.ExecuteReader();
|
||||
|
||||
if (reader != null && reader.HasRows)
|
||||
if (_ConnectionCover == null)
|
||||
{
|
||||
reader.Read();
|
||||
int id = reader.GetInt32(0);
|
||||
int w = reader.GetInt32(1);
|
||||
int h = reader.GetInt32(2);
|
||||
reader.Close();
|
||||
|
||||
command.CommandText = "SELECT Data FROM CoverData WHERE CoverID = @id";
|
||||
command.Parameters.Add("@id", DbType.Int32).Value = id;
|
||||
reader = command.ExecuteReader();
|
||||
|
||||
if (reader.HasRows)
|
||||
{
|
||||
reader.Read();
|
||||
byte[] data = _GetBytes(reader);
|
||||
reader.Dispose();
|
||||
tex = CDraw.EnqueueTexture(w, h, data);
|
||||
return true;
|
||||
}
|
||||
_ConnectionCover = new SQLiteConnection {ConnectionString = "Data Source=" + _CoverFilePath};
|
||||
_ConnectionCover.Open();
|
||||
}
|
||||
else
|
||||
|
||||
using (var command = new SQLiteCommand(_ConnectionCover))
|
||||
{
|
||||
if (reader != null)
|
||||
reader.Close();
|
||||
|
||||
if (_TransactionCover == null)
|
||||
_TransactionCover = _ConnectionCover.BeginTransaction();
|
||||
|
||||
Bitmap origin;
|
||||
try
|
||||
{
|
||||
origin = new Bitmap(coverPath);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
CLog.LogError("Error loading Texture: " + coverPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
int w = maxSize;
|
||||
int h = maxSize;
|
||||
byte[] data;
|
||||
|
||||
try
|
||||
{
|
||||
if (origin.Width >= origin.Height && origin.Width > w)
|
||||
h = (int)Math.Round((float)w / origin.Width * origin.Height);
|
||||
else if (origin.Height > origin.Width && origin.Height > h)
|
||||
w = (int)Math.Round((float)h / origin.Height * origin.Width);
|
||||
|
||||
using (var bmp = new Bitmap(w, h))
|
||||
{
|
||||
using (Graphics g = Graphics.FromImage(bmp))
|
||||
g.DrawImage(origin, new Rectangle(0, 0, w, h));
|
||||
|
||||
data = new byte[w * h * 4];
|
||||
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
|
||||
Marshal.Copy(bmpData.Scan0, data, 0, w * h * 4);
|
||||
bmp.UnlockBits(bmpData);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
origin.Dispose();
|
||||
}
|
||||
|
||||
tex = CDraw.EnqueueTexture(w, h, data);
|
||||
|
||||
command.CommandText = "INSERT INTO Cover (Path, width, height) VALUES (@path, @w, @h)";
|
||||
command.Parameters.Add("@w", DbType.Int32).Value = w;
|
||||
command.Parameters.Add("@h", DbType.Int32).Value = h;
|
||||
command.CommandText = "SELECT id, width, height FROM Cover WHERE [Path] = @path";
|
||||
command.Parameters.Add("@path", DbType.String, 0).Value = coverPath;
|
||||
command.ExecuteNonQuery();
|
||||
|
||||
command.CommandText = "SELECT id FROM Cover WHERE [Path] = @path";
|
||||
command.Parameters.Add("@path", DbType.String, 0).Value = coverPath;
|
||||
reader = command.ExecuteReader();
|
||||
SQLiteDataReader reader = command.ExecuteReader();
|
||||
|
||||
if (reader != null)
|
||||
if (reader != null && reader.HasRows)
|
||||
{
|
||||
reader.Read();
|
||||
int id = reader.GetInt32(0);
|
||||
reader.Dispose();
|
||||
command.CommandText = "INSERT INTO CoverData (CoverID, Data) VALUES (@id, @data)";
|
||||
int w = reader.GetInt32(1);
|
||||
int h = reader.GetInt32(2);
|
||||
reader.Close();
|
||||
|
||||
command.CommandText = "SELECT Data FROM CoverData WHERE CoverID = @id";
|
||||
command.Parameters.Add("@id", DbType.Int32).Value = id;
|
||||
command.Parameters.Add("@data", DbType.Binary, 20).Value = data;
|
||||
command.ExecuteReader();
|
||||
return true;
|
||||
reader = command.ExecuteReader();
|
||||
|
||||
if (reader.HasRows)
|
||||
{
|
||||
reader.Read();
|
||||
byte[] data = _GetBytes(reader);
|
||||
reader.Dispose();
|
||||
tex = CDraw.EnqueueTexture(w, h, data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (reader != null)
|
||||
reader.Close();
|
||||
|
||||
if (_TransactionCover == null)
|
||||
_TransactionCover = _ConnectionCover.BeginTransaction();
|
||||
|
||||
Bitmap origin;
|
||||
try
|
||||
{
|
||||
origin = new Bitmap(coverPath);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
CLog.LogError("Error loading Texture: " + coverPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
int w = maxSize;
|
||||
int h = maxSize;
|
||||
byte[] data;
|
||||
|
||||
try
|
||||
{
|
||||
if (origin.Width >= origin.Height && origin.Width > w)
|
||||
h = (int)Math.Round((float)w / origin.Width * origin.Height);
|
||||
else if (origin.Height > origin.Width && origin.Height > h)
|
||||
w = (int)Math.Round((float)h / origin.Height * origin.Width);
|
||||
|
||||
using (var bmp = new Bitmap(w, h))
|
||||
{
|
||||
using (Graphics g = Graphics.FromImage(bmp))
|
||||
g.DrawImage(origin, new Rectangle(0, 0, w, h));
|
||||
|
||||
data = new byte[w * h * 4];
|
||||
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
|
||||
Marshal.Copy(bmpData.Scan0, data, 0, w * h * 4);
|
||||
bmp.UnlockBits(bmpData);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
origin.Dispose();
|
||||
}
|
||||
|
||||
tex = CDraw.EnqueueTexture(w, h, data);
|
||||
|
||||
command.CommandText = "INSERT INTO Cover (Path, width, height) VALUES (@path, @w, @h)";
|
||||
command.Parameters.Add("@w", DbType.Int32).Value = w;
|
||||
command.Parameters.Add("@h", DbType.Int32).Value = h;
|
||||
command.Parameters.Add("@path", DbType.String, 0).Value = coverPath;
|
||||
command.ExecuteNonQuery();
|
||||
|
||||
command.CommandText = "SELECT id FROM Cover WHERE [Path] = @path";
|
||||
command.Parameters.Add("@path", DbType.String, 0).Value = coverPath;
|
||||
reader = command.ExecuteReader();
|
||||
|
||||
if (reader != null)
|
||||
{
|
||||
reader.Read();
|
||||
int id = reader.GetInt32(0);
|
||||
reader.Dispose();
|
||||
command.CommandText = "INSERT INTO CoverData (CoverID, Data) VALUES (@id, @data)";
|
||||
command.Parameters.Add("@id", DbType.Int32).Value = id;
|
||||
command.Parameters.Add("@data", DbType.Binary, 20).Value = data;
|
||||
command.ExecuteReader();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void CommitCovers()
|
||||
{
|
||||
if (_TransactionCover != null)
|
||||
lock (_CoverMutex)
|
||||
{
|
||||
_TransactionCover.Commit();
|
||||
_TransactionCover = null;
|
||||
GC.Collect();
|
||||
_CommitCovers();
|
||||
}
|
||||
}
|
||||
|
||||
public static void CloseConnections()
|
||||
/// <summary>
|
||||
/// You have to hold the CoverMutex when calling this!
|
||||
/// </summary>
|
||||
private static void _CommitCovers()
|
||||
{
|
||||
CommitCovers();
|
||||
|
||||
if (_ConnectionCover != null)
|
||||
if (_TransactionCover != null)
|
||||
{
|
||||
_ConnectionCover.Close();
|
||||
_ConnectionCover.Dispose();
|
||||
_ConnectionCover = null;
|
||||
_TransactionCover.Commit();
|
||||
_TransactionCover.Dispose();
|
||||
_TransactionCover = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Close()
|
||||
{
|
||||
//Do commit and close atomicly otherwhise we may loose changes
|
||||
lock (_CoverMutex)
|
||||
{
|
||||
CommitCovers();
|
||||
|
||||
if (_ConnectionCover != null)
|
||||
{
|
||||
_ConnectionCover.Dispose();
|
||||
_ConnectionCover = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-5
@@ -28,8 +28,10 @@ namespace Vocaluxe.Base
|
||||
{
|
||||
private static IDraw _Draw;
|
||||
|
||||
public static void InitDraw()
|
||||
public static bool InitDraw()
|
||||
{
|
||||
if (_Draw != null)
|
||||
return false;
|
||||
switch (CConfig.Renderer)
|
||||
{
|
||||
case ERenderer.TR_CONFIG_SOFTWARE:
|
||||
@@ -45,6 +47,7 @@ namespace Vocaluxe.Base
|
||||
{
|
||||
CLog.LogError(e.Message + " - Error in initializing of OpenGL. Please check whether" +
|
||||
" your graphic card drivers are up to date.", true, true);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -59,6 +62,7 @@ namespace Vocaluxe.Base
|
||||
CLog.LogError(e.Message + " - Error in initializing of Direct3D. Please check if " +
|
||||
"your DirectX redistributables and graphic card drivers are up to date. You can " +
|
||||
"download the DirectX runtimes at http://www.microsoft.com/download/en/details.aspx?id=8109", true, true);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
@@ -67,8 +71,7 @@ namespace Vocaluxe.Base
|
||||
_Draw = new CDrawWinForm();
|
||||
break;
|
||||
}
|
||||
if (!_Draw.Init())
|
||||
CLog.LogError("Could not init drawing interface!", true, true);
|
||||
return _Draw.Init();
|
||||
}
|
||||
|
||||
public static void MainLoop()
|
||||
@@ -79,8 +82,11 @@ namespace Vocaluxe.Base
|
||||
|
||||
public static void Unload()
|
||||
{
|
||||
_Draw.Unload();
|
||||
_Draw = null;
|
||||
if (_Draw != null)
|
||||
{
|
||||
_Draw.Unload();
|
||||
_Draw = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetScreenWidth()
|
||||
|
||||
+13
-7
@@ -15,6 +15,9 @@
|
||||
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
//Uncomment to make the engine hit every note
|
||||
//#define DEBUG_HIT
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Vocaluxe.SongQueue;
|
||||
@@ -208,8 +211,6 @@ namespace Vocaluxe.Base
|
||||
|
||||
public static void UpdatePoints(float time)
|
||||
{
|
||||
const bool DEBUG_HIT = false;
|
||||
|
||||
CSong song = _SongQueue.GetSong();
|
||||
|
||||
if (song == null)
|
||||
@@ -224,7 +225,7 @@ namespace Vocaluxe.Base
|
||||
MidRecordedBeat = -0.5f + GetBeatFromTime(time, song.BPM, song.Gap + CConfig.MicDelay / 1000f);
|
||||
|
||||
for (int p = 0; p < _NumPlayer; p++)
|
||||
CSound.AnalyzeBuffer(p);
|
||||
CRecord.AnalyzeBuffer(p);
|
||||
|
||||
if (_LastEvalBeat >= RecordedBeat)
|
||||
return;
|
||||
@@ -270,10 +271,14 @@ namespace Vocaluxe.Base
|
||||
if (line == lines.Length - 1 && beat == lines[line].LastNoteBeat)
|
||||
Players[p].SongFinished = true;
|
||||
|
||||
if (notes[note].PointsForBeat > 0 && (CSound.RecordToneValid(p) || DEBUG_HIT))
|
||||
if (notes[note].PointsForBeat > 0 && (CRecord.ToneValid(p)
|
||||
#if DEBUG_HIT
|
||||
|| true
|
||||
#endif
|
||||
))
|
||||
{
|
||||
int tone = notes[note].Tone;
|
||||
int tonePlayer = CSound.RecordGetTone(p);
|
||||
int tonePlayer = CRecord.GetTone(p);
|
||||
|
||||
while (tonePlayer - tone > 6)
|
||||
tonePlayer -= 12;
|
||||
@@ -281,8 +286,9 @@ namespace Vocaluxe.Base
|
||||
while (tonePlayer - tone < -6)
|
||||
tonePlayer += 12;
|
||||
|
||||
if (DEBUG_HIT)
|
||||
#if DEBUG_HIT
|
||||
tonePlayer = tone;
|
||||
#endif
|
||||
|
||||
Players[p].NoteDiff = Math.Abs(tone - tonePlayer);
|
||||
bool hit = Players[p].NoteDiff <= (2 - (int)CProfiles.GetDifficulty(Players[p].ProfileID));
|
||||
@@ -290,7 +296,7 @@ namespace Vocaluxe.Base
|
||||
if (hit)
|
||||
{
|
||||
// valid
|
||||
//CSound.RecordSetTone(p, Tone);
|
||||
//CRecord.RecordSetTone(p, Tone);
|
||||
double points = (CSettings.MaxScore - CSettings.LinebonusScore) * (double)notes[note].PointsForBeat /
|
||||
song.Notes.GetVoice(Players[p].VoiceNr).Points;
|
||||
if (notes[note].Type == ENoteType.Golden)
|
||||
|
||||
+21
-29
@@ -61,10 +61,10 @@ namespace Vocaluxe.Base
|
||||
}
|
||||
|
||||
#region public methods
|
||||
public static void InitGraphics()
|
||||
public static void Init()
|
||||
{
|
||||
// Add Screens, must be the same order as in EScreens!
|
||||
CLog.StartBenchmark(1, "Build Screen List");
|
||||
CLog.StartBenchmark("Build Screen List");
|
||||
|
||||
_Screens.Add(new CScreenTest());
|
||||
_Screens.Add(new CScreenLoad());
|
||||
@@ -91,7 +91,7 @@ namespace Vocaluxe.Base
|
||||
_PopupScreens.Add(new CPopupScreenVolumeControl());
|
||||
_PopupScreens.Add(new CPopupScreenServerQR());
|
||||
|
||||
CLog.StopBenchmark(1, "Build Screen List");
|
||||
CLog.StopBenchmark("Build Screen List");
|
||||
|
||||
_CurrentScreen = EScreens.ScreenLoad;
|
||||
_NextScreen = EScreens.ScreenNull;
|
||||
@@ -101,9 +101,9 @@ namespace Vocaluxe.Base
|
||||
_GlobalAlpha = 1f;
|
||||
_ZOffset = 0f;
|
||||
|
||||
CLog.StartBenchmark(0, "Load Theme");
|
||||
CLog.StartBenchmark("Load Theme");
|
||||
LoadTheme();
|
||||
CLog.StopBenchmark(0, "Load Theme");
|
||||
CLog.StopBenchmark("Load Theme");
|
||||
}
|
||||
|
||||
public static void LoadTheme()
|
||||
@@ -112,10 +112,10 @@ namespace Vocaluxe.Base
|
||||
|
||||
for (int i = 0; i < _Screens.Count; i++)
|
||||
{
|
||||
CLog.StartBenchmark(1, "Load Theme " + Enum.GetNames(typeof(EScreens))[i]);
|
||||
CLog.StartBenchmark("Load Theme " + Enum.GetNames(typeof(EScreens))[i]);
|
||||
_Screens[i].Init();
|
||||
_Screens[i].LoadTheme(CTheme.GetThemeScreensPath(-1));
|
||||
CLog.StopBenchmark(1, "Load Theme " + Enum.GetNames(typeof(EScreens))[i]);
|
||||
CLog.StopBenchmark("Load Theme " + Enum.GetNames(typeof(EScreens))[i]);
|
||||
}
|
||||
|
||||
foreach (IMenu popup in _PopupScreens)
|
||||
@@ -178,7 +178,7 @@ namespace Vocaluxe.Base
|
||||
if (CConfig.CoverLoading == ECoverLoading.TR_CONFIG_COVERLOADING_DYNAMIC && _CurrentScreen != EScreens.ScreenSing)
|
||||
CSongs.LoadCover();
|
||||
|
||||
if (CSettings.GameState != EGameState.EditTheme)
|
||||
if (CSettings.ProgramState != EProgramState.EditTheme)
|
||||
{
|
||||
run &= _HandleInputs(keys, mouse);
|
||||
run &= _Update();
|
||||
@@ -236,8 +236,6 @@ namespace Vocaluxe.Base
|
||||
_Screens[(int)_CurrentScreen].OnClose();
|
||||
_CurrentScreen = _NextScreen;
|
||||
_NextScreen = EScreens.ScreenNull;
|
||||
if (CBackgroundMusic.IsPlaying)
|
||||
CBackgroundMusic.Play();
|
||||
_Screens[(int)_CurrentScreen].OnShowFinish();
|
||||
_Screens[(int)_CurrentScreen].ProcessMouseMove(_Cursor.X, _Cursor.Y);
|
||||
|
||||
@@ -346,8 +344,7 @@ namespace Vocaluxe.Base
|
||||
if (keyEvent.Key == Keys.Left || keyEvent.Key == Keys.Right || keyEvent.Key == Keys.Up || keyEvent.Key == Keys.Down || keyEvent.Key == Keys.NumPad0 ||
|
||||
keyEvent.Key == Keys.D0)
|
||||
{
|
||||
CSettings.MouseInactive();
|
||||
_Cursor.FadeOut();
|
||||
_Cursor.Deactivate();
|
||||
|
||||
if (keyEvent.ModAlt && keyEvent.ModCtrl && keyEvent.ModShift)
|
||||
{
|
||||
@@ -431,7 +428,7 @@ namespace Vocaluxe.Base
|
||||
else
|
||||
{
|
||||
CConfig.BackgroundMusicVolume += diff;
|
||||
CBackgroundMusic.ApplyVolume();
|
||||
CBackgroundMusic.SetVolume(CConfig.BackgroundMusicVolume);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -442,7 +439,7 @@ namespace Vocaluxe.Base
|
||||
|
||||
default:
|
||||
CConfig.BackgroundMusicVolume += diff;
|
||||
CBackgroundMusic.ApplyVolume();
|
||||
CBackgroundMusic.SetVolume(CConfig.BackgroundMusicVolume);
|
||||
break;
|
||||
}
|
||||
CConfig.SaveConfig();
|
||||
@@ -453,14 +450,12 @@ namespace Vocaluxe.Base
|
||||
ShowPopup(EPopupScreens.PopupVolumeControl);
|
||||
_VolumePopupTimer.Restart();
|
||||
}
|
||||
|
||||
CBackgroundMusic.ApplyVolume();
|
||||
}
|
||||
|
||||
if (keyEvent.ModShift && (keyEvent.Key == Keys.F1))
|
||||
CSettings.GameState = EGameState.EditTheme;
|
||||
CSettings.ProgramState = EProgramState.EditTheme;
|
||||
else if (keyEvent.ModAlt && (keyEvent.Key == Keys.Enter))
|
||||
CSettings.IsFullScreen = !CSettings.IsFullScreen;
|
||||
CConfig.FullScreen = (CConfig.FullScreen == EOffOn.TR_CONFIG_ON) ? EOffOn.TR_CONFIG_OFF : EOffOn.TR_CONFIG_ON;
|
||||
else if (keyEvent.ModAlt && (keyEvent.Key == Keys.P))
|
||||
CDraw.MakeScreenShot();
|
||||
else
|
||||
@@ -488,10 +483,7 @@ namespace Vocaluxe.Base
|
||||
mouseEvent = inputMouseEvent;
|
||||
|
||||
if (mouseEvent.Wheel != 0)
|
||||
{
|
||||
CSettings.MouseActive();
|
||||
_Cursor.FadeIn();
|
||||
}
|
||||
_Cursor.Activate();
|
||||
|
||||
_UpdateMousePosition(mouseEvent.X, mouseEvent.Y);
|
||||
|
||||
@@ -554,11 +546,11 @@ namespace Vocaluxe.Base
|
||||
{
|
||||
if (keyEvent.ModShift && (keyEvent.Key == Keys.F1))
|
||||
{
|
||||
CSettings.GameState = EGameState.Normal;
|
||||
CSettings.ProgramState = EProgramState.Normal;
|
||||
_Screens[(int)_CurrentScreen].NextInteraction();
|
||||
}
|
||||
else if (keyEvent.ModAlt && (keyEvent.Key == Keys.Enter))
|
||||
CSettings.IsFullScreen = !CSettings.IsFullScreen;
|
||||
CConfig.FullScreen = (CConfig.FullScreen == EOffOn.TR_CONFIG_ON) ? EOffOn.TR_CONFIG_OFF : EOffOn.TR_CONFIG_ON;
|
||||
else if (keyEvent.ModAlt && (keyEvent.Key == Keys.P))
|
||||
CDraw.MakeScreenShot();
|
||||
else
|
||||
@@ -592,7 +584,7 @@ namespace Vocaluxe.Base
|
||||
}
|
||||
|
||||
if (_VolumePopupTimer.IsRunning)
|
||||
_Cursor.FadeIn();
|
||||
_Cursor.Activate();
|
||||
|
||||
if (_CurrentPopupScreen != EPopupScreens.NoPopup)
|
||||
_PopupScreens[(int)_CurrentPopupScreen].UpdateGame();
|
||||
@@ -616,10 +608,10 @@ namespace Vocaluxe.Base
|
||||
|
||||
if (CConfig.DebugLevel >= EDebugLevel.TR_CONFIG_LEVEL2)
|
||||
{
|
||||
debugOutput.Add(CSound.RecordGetToneAbs(0).ToString(CLanguage.Translate("TR_DEBUG_TONE_ABS") + " P1: 00"));
|
||||
debugOutput.Add(CSound.RecordGetMaxVolume(0).ToString(CLanguage.Translate("TR_DEBUG_MAX_VOLUME") + " P1: 0.000"));
|
||||
debugOutput.Add(CSound.RecordGetToneAbs(1).ToString(CLanguage.Translate("TR_DEBUG_TONE_ABS") + " P2: 00"));
|
||||
debugOutput.Add(CSound.RecordGetMaxVolume(1).ToString(CLanguage.Translate("TR_DEBUG_MAX_VOLUME") + " P2: 0.000"));
|
||||
debugOutput.Add(CRecord.GetToneAbs(0).ToString(CLanguage.Translate("TR_DEBUG_TONE_ABS") + " P1: 00"));
|
||||
debugOutput.Add(CRecord.GetMaxVolume(0).ToString(CLanguage.Translate("TR_DEBUG_MAX_VOLUME") + " P1: 0.000"));
|
||||
debugOutput.Add(CRecord.GetToneAbs(1).ToString(CLanguage.Translate("TR_DEBUG_TONE_ABS") + " P2: 00"));
|
||||
debugOutput.Add(CRecord.GetMaxVolume(1).ToString(CLanguage.Translate("TR_DEBUG_MAX_VOLUME") + " P2: 0.000"));
|
||||
|
||||
if (CConfig.DebugLevel >= EDebugLevel.TR_CONFIG_LEVEL3)
|
||||
{
|
||||
|
||||
+34
-18
@@ -103,6 +103,8 @@ namespace Vocaluxe.Base
|
||||
static class CLog
|
||||
{
|
||||
private const int _MaxBenchmarks = 10;
|
||||
private static int _BenchmarksRunning;
|
||||
private static bool _Initialized;
|
||||
|
||||
private static CLogFile _ErrorLog;
|
||||
private static CLogFile _PerformanceLog;
|
||||
@@ -124,15 +126,26 @@ namespace Vocaluxe.Base
|
||||
_BenchmarkTimer = new Stopwatch[_MaxBenchmarks];
|
||||
for (int i = 0; i < _BenchmarkTimer.Length; i++)
|
||||
_BenchmarkTimer[i] = new Stopwatch();
|
||||
_BenchmarksRunning = 0;
|
||||
_Initialized = true;
|
||||
}
|
||||
|
||||
public static void CloseAll()
|
||||
{
|
||||
_ErrorLog.Close();
|
||||
_PerformanceLog.Close();
|
||||
_BenchmarkLog.Close();
|
||||
_DebugLog.Close();
|
||||
_SongInfoLog.Close();
|
||||
if (_Initialized)
|
||||
{
|
||||
_ErrorLog.Dispose();
|
||||
_ErrorLog = null;
|
||||
_PerformanceLog.Dispose();
|
||||
_PerformanceLog = null;
|
||||
_BenchmarkLog.Dispose();
|
||||
_BenchmarkLog = null;
|
||||
_DebugLog.Dispose();
|
||||
_DebugLog = null;
|
||||
_SongInfoLog.Dispose();
|
||||
_SongInfoLog = null;
|
||||
_Initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
#region LogError
|
||||
@@ -165,41 +178,44 @@ namespace Vocaluxe.Base
|
||||
}
|
||||
|
||||
#region LogBenchmark
|
||||
public static void StartBenchmark(int benchmarkNr, string text)
|
||||
public static void StartBenchmark(string text)
|
||||
{
|
||||
if (benchmarkNr >= 0 && benchmarkNr < _MaxBenchmarks)
|
||||
if (_BenchmarksRunning < _MaxBenchmarks)
|
||||
{
|
||||
_BenchmarkTimer[benchmarkNr].Stop();
|
||||
_BenchmarkTimer[benchmarkNr].Reset();
|
||||
|
||||
string space = String.Empty;
|
||||
for (int i = 0; i < benchmarkNr; i++)
|
||||
for (int i = 0; i < _BenchmarksRunning; i++)
|
||||
space += " ";
|
||||
_BenchmarkLog.Add(space + "Start " + text);
|
||||
|
||||
_BenchmarkTimer[benchmarkNr].Start();
|
||||
_BenchmarkTimer[_BenchmarksRunning].Restart();
|
||||
}
|
||||
else
|
||||
LogError("Tried to start to many benchmarks at once"); //Log and ignore
|
||||
_BenchmarksRunning++; //Inc even if not started to correct right Stop() call
|
||||
}
|
||||
|
||||
public static void StopBenchmark(int benchmarkNr, string text)
|
||||
public static void StopBenchmark(string text)
|
||||
{
|
||||
if (benchmarkNr >= 0 && benchmarkNr < _MaxBenchmarks)
|
||||
if (_BenchmarksRunning > 0 && _BenchmarksRunning <= _MaxBenchmarks)
|
||||
{
|
||||
_BenchmarkTimer[benchmarkNr].Stop();
|
||||
_BenchmarksRunning--;
|
||||
_BenchmarkTimer[_BenchmarksRunning].Stop();
|
||||
|
||||
string space = String.Empty;
|
||||
for (int i = 0; i < benchmarkNr; i++)
|
||||
for (int i = 0; i < _BenchmarksRunning; i++)
|
||||
space += " ";
|
||||
|
||||
float ms;
|
||||
if (Stopwatch.IsHighResolution && _NanosecPerTick > 0)
|
||||
ms = (float)((_NanosecPerTick * _BenchmarkTimer[benchmarkNr].ElapsedTicks) / (1000.0 * 1000.0));
|
||||
ms = (float)((_NanosecPerTick * _BenchmarkTimer[_BenchmarksRunning].ElapsedTicks) / (1000.0 * 1000.0));
|
||||
else
|
||||
ms = _BenchmarkTimer[benchmarkNr].ElapsedMilliseconds;
|
||||
ms = _BenchmarkTimer[_BenchmarksRunning].ElapsedMilliseconds;
|
||||
|
||||
_BenchmarkLog.Add(space + "Stop " + text + ", Elapsed Time: " + ms.ToString("0.000") + "ms");
|
||||
_BenchmarkLog.Add(String.Empty);
|
||||
}
|
||||
else
|
||||
_BenchmarksRunning--;
|
||||
}
|
||||
#endregion LogBenchmark
|
||||
}
|
||||
|
||||
+11
-11
@@ -118,7 +118,7 @@ namespace Vocaluxe.Base
|
||||
|
||||
public IEnumerable<string> GetSongFolders()
|
||||
{
|
||||
return CConfig.SongFolder;
|
||||
return CConfig.SongFolders;
|
||||
}
|
||||
|
||||
public ESongSorting GetSongSorting()
|
||||
@@ -179,9 +179,9 @@ namespace Vocaluxe.Base
|
||||
return CSettings.ZNear;
|
||||
}
|
||||
|
||||
public EGameState GetGameState()
|
||||
public EProgramState GetProgramState()
|
||||
{
|
||||
return CSettings.GameState;
|
||||
return CSettings.ProgramState;
|
||||
}
|
||||
|
||||
public int GetToneMin()
|
||||
@@ -226,7 +226,7 @@ namespace Vocaluxe.Base
|
||||
|
||||
public string GetFolderProfiles()
|
||||
{
|
||||
return CSettings.FolderProfiles;
|
||||
return CConfig.ProfileFolders[0];
|
||||
}
|
||||
|
||||
public string GetDataPath()
|
||||
@@ -360,11 +360,6 @@ namespace Vocaluxe.Base
|
||||
CBackgroundMusic.Play();
|
||||
}
|
||||
|
||||
public void ApplyVolume()
|
||||
{
|
||||
CBackgroundMusic.ApplyVolume();
|
||||
}
|
||||
|
||||
public CTexture GetVideoTexture()
|
||||
{
|
||||
return CBackgroundMusic.GetVideoTexture();
|
||||
@@ -606,6 +601,11 @@ namespace Vocaluxe.Base
|
||||
return CProfiles.GetAvatarTextureFromProfile(profileID);
|
||||
}
|
||||
|
||||
public CAvatar GetAvatarByFilename(string fileName)
|
||||
{
|
||||
return CProfiles.GetAvatarByFilename(fileName);
|
||||
}
|
||||
|
||||
public bool IsProfileIDValid(int profileID)
|
||||
{
|
||||
return CProfiles.IsProfileIDValid(profileID);
|
||||
@@ -626,7 +626,7 @@ namespace Vocaluxe.Base
|
||||
{
|
||||
public int GetToneAbs(int playerNr)
|
||||
{
|
||||
return CSound.RecordGetToneAbs(playerNr);
|
||||
return CRecord.GetToneAbs(playerNr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -839,7 +839,7 @@ namespace Vocaluxe.Base
|
||||
return CDataBase.GetCover(fileName, ref texture, coverSize);
|
||||
}
|
||||
|
||||
public bool GetDataBaseSongInfos(string artist, string title, out int numPlayed, out string dateAdded, out int highscoreID)
|
||||
public bool GetDataBaseSongInfos(string artist, string title, out int numPlayed, out DateTime dateAdded, out int highscoreID)
|
||||
{
|
||||
return CDataBase.GetDataBaseSongInfos(artist, title, out numPlayed, out dateAdded, out highscoreID);
|
||||
}
|
||||
|
||||
@@ -223,6 +223,7 @@ namespace Vocaluxe.Base
|
||||
foreach (string file in files)
|
||||
{
|
||||
SPartyMode pm = _LoadPartyMode(file);
|
||||
if(pm.NoErrors)
|
||||
_PartyModes.Add(pm.PartyModeID, pm);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using VocaluxeLib.Draw;
|
||||
using VocaluxeLib.Songs;
|
||||
|
||||
namespace Vocaluxe.Base
|
||||
{
|
||||
class CPlaylistElement
|
||||
{
|
||||
private readonly CSong _Song;
|
||||
private readonly string _MusicFilePath = String.Empty;
|
||||
|
||||
public int SongID
|
||||
{
|
||||
get { return (_Song == null) ? -1 : _Song.ID; }
|
||||
}
|
||||
|
||||
public string MusicFilePath
|
||||
{
|
||||
get { return _Song != null ? _Song.GetMP3() : _MusicFilePath; }
|
||||
}
|
||||
|
||||
public string VideoFilePath
|
||||
{
|
||||
get { return _Song != null ? _Song.GetVideo() : string.Empty; }
|
||||
}
|
||||
|
||||
public string Title
|
||||
{
|
||||
get { return _Song != null ? _Song.Title : ""; }
|
||||
}
|
||||
|
||||
public string Artist
|
||||
{
|
||||
get { return _Song != null ? _Song.Artist : ""; }
|
||||
}
|
||||
|
||||
public float Start
|
||||
{
|
||||
get { return _Song != null ? _Song.Start : 0f; }
|
||||
}
|
||||
|
||||
public float Finish
|
||||
{
|
||||
get { return _Song != null ? _Song.Finish : 0f; }
|
||||
}
|
||||
|
||||
public CTexture Cover
|
||||
{
|
||||
get { return _Song != null ? _Song.CoverTextureSmall : CCover.NoCover; }
|
||||
}
|
||||
|
||||
public float VideoGap
|
||||
{
|
||||
get { return _Song != null ? _Song.VideoGap : 0; }
|
||||
}
|
||||
|
||||
public CPlaylistElement(CSong song)
|
||||
{
|
||||
_Song = song;
|
||||
}
|
||||
|
||||
public CPlaylistElement(string filePath)
|
||||
{
|
||||
_MusicFilePath = filePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,7 @@ namespace Vocaluxe.Base
|
||||
private static void _ConvertUSDXPlaylists()
|
||||
{
|
||||
var files = new List<string>();
|
||||
files.AddRange(CHelper.ListFiles(Path.Combine(CSettings.DataPath, CSettings.FolderPlaylists), "*.upl", true, true));
|
||||
files.AddRange(CHelper.ListFiles(Path.Combine(CSettings.DataPath, CConfig.FolderPlaylists), "*.upl", true, true));
|
||||
|
||||
foreach (string file in files)
|
||||
{
|
||||
@@ -73,7 +73,7 @@ namespace Vocaluxe.Base
|
||||
_Playlists = new List<CPlaylistFile>();
|
||||
|
||||
var files = new List<string>();
|
||||
files.AddRange(CHelper.ListFiles(Path.Combine(CSettings.DataPath, CSettings.FolderPlaylists), "*.xml", true, true));
|
||||
files.AddRange(CHelper.ListFiles(Path.Combine(CSettings.DataPath, CConfig.FolderPlaylists), "*.xml", true, true));
|
||||
|
||||
foreach (string file in files)
|
||||
{
|
||||
|
||||
+27
-27
@@ -17,10 +17,11 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using VocaluxeLib;
|
||||
using VocaluxeLib.Profile;
|
||||
using VocaluxeLib.Draw;
|
||||
using VocaluxeLib.Profile;
|
||||
|
||||
namespace Vocaluxe.Base
|
||||
{
|
||||
@@ -111,13 +112,21 @@ namespace Vocaluxe.Base
|
||||
break;
|
||||
|
||||
case EAction.AddProfile:
|
||||
if (change.Profile == null)
|
||||
CProfile newProf = change.Profile;
|
||||
if (newProf == null)
|
||||
break;
|
||||
|
||||
change.Profile.ID = _ProfileIDs.Dequeue();
|
||||
change.Profile.Avatar = _GetAvatar(change.Profile.AvatarFileName);
|
||||
change.Profile.SaveProfile();
|
||||
_Profiles.Add(change.Profile.ID, change.Profile);
|
||||
newProf.ID = _ProfileIDs.Dequeue();
|
||||
if (newProf.Avatar == null)
|
||||
newProf.Avatar = _Avatars.Values.First();
|
||||
else if (newProf.Avatar.ID < 0)
|
||||
{
|
||||
newProf.Avatar.ID = _AvatarIDs.Dequeue();
|
||||
_Avatars.Add(newProf.Avatar.ID, newProf.Avatar);
|
||||
_AvatarsChanged = true;
|
||||
}
|
||||
newProf.SaveProfile();
|
||||
_Profiles.Add(newProf.ID, newProf);
|
||||
|
||||
_ProfilesChanged = true;
|
||||
break;
|
||||
@@ -318,7 +327,7 @@ namespace Vocaluxe.Base
|
||||
{
|
||||
var profile = new CProfile
|
||||
{
|
||||
FileName = fileName != "" ? Path.Combine(CSettings.DataPath, CSettings.FolderProfiles, fileName) : String.Empty
|
||||
FileName = fileName != "" ? Path.Combine(CConfig.ProfileFolders[0], fileName) : String.Empty
|
||||
};
|
||||
|
||||
if (File.Exists(profile.FileName))
|
||||
@@ -332,8 +341,8 @@ namespace Vocaluxe.Base
|
||||
|
||||
public static int NewAvatar(string fileName)
|
||||
{
|
||||
var avatar = new CAvatar(-1);
|
||||
if (!avatar.LoadFromFile(fileName))
|
||||
CAvatar avatar = CAvatar.GetAvatar(fileName);
|
||||
if (avatar == null)
|
||||
return -1;
|
||||
|
||||
avatar.ID = _AvatarIDs.Dequeue();
|
||||
@@ -474,7 +483,7 @@ namespace Vocaluxe.Base
|
||||
|
||||
public static int GetAvatarID(int profileID)
|
||||
{
|
||||
if (!IsProfileIDValid(profileID))
|
||||
if (!IsProfileIDValid(profileID) || _Profiles[profileID].Avatar == null)
|
||||
return -1;
|
||||
|
||||
return _Profiles[profileID].Avatar.ID;
|
||||
@@ -505,7 +514,7 @@ namespace Vocaluxe.Base
|
||||
|
||||
public static CTexture GetAvatarTextureFromProfile(int profileID)
|
||||
{
|
||||
if (!IsProfileIDValid(profileID))
|
||||
if (!IsProfileIDValid(profileID) || _Profiles[profileID].Avatar == null)
|
||||
return null;
|
||||
|
||||
return _Profiles[profileID].Avatar.Texture;
|
||||
@@ -525,10 +534,7 @@ namespace Vocaluxe.Base
|
||||
foreach (int id in ids)
|
||||
{
|
||||
if (_Profiles[id].LoadProfile())
|
||||
{
|
||||
_Profiles[id].Avatar = _GetAvatar(_Profiles[id].AvatarFileName);
|
||||
knownFiles.Add(Path.GetFileName(_Profiles[id].FileName));
|
||||
}
|
||||
else
|
||||
_Profiles.Remove(id);
|
||||
}
|
||||
@@ -536,7 +542,7 @@ namespace Vocaluxe.Base
|
||||
|
||||
|
||||
var files = new List<string>();
|
||||
foreach (string path in CSettings.FoldersProfiles)
|
||||
foreach (string path in CConfig.ProfileFolders)
|
||||
files.AddRange(CHelper.ListFiles(path, "*.xml", true, true));
|
||||
|
||||
foreach (string file in files)
|
||||
@@ -552,7 +558,6 @@ namespace Vocaluxe.Base
|
||||
|
||||
if (profile.LoadProfile())
|
||||
{
|
||||
profile.Avatar = _GetAvatar(profile.AvatarFileName);
|
||||
profile.ID = _ProfileIDs.Dequeue();
|
||||
_Profiles.Add(profile.ID, profile);
|
||||
}
|
||||
@@ -577,21 +582,16 @@ namespace Vocaluxe.Base
|
||||
}
|
||||
|
||||
var files = new List<string>();
|
||||
foreach (string path in CSettings.FoldersProfiles)
|
||||
{
|
||||
files.AddRange(CHelper.ListFiles(path, "*.png", true, true));
|
||||
files.AddRange(CHelper.ListFiles(path, "*.jpg", true, true));
|
||||
files.AddRange(CHelper.ListFiles(path, "*.jpeg", true, true));
|
||||
files.AddRange(CHelper.ListFiles(path, "*.bmp", true, true));
|
||||
}
|
||||
foreach (string path in CConfig.ProfileFolders)
|
||||
files.AddRange(CHelper.ListImageFiles(path, true, true));
|
||||
|
||||
foreach (string file in files)
|
||||
{
|
||||
if (knownFiles.Contains(Path.GetFileName(file)))
|
||||
continue;
|
||||
|
||||
var avatar = new CAvatar(-1);
|
||||
if (avatar.LoadFromFile(file))
|
||||
CAvatar avatar = CAvatar.GetAvatar(file);
|
||||
if (avatar != null)
|
||||
{
|
||||
avatar.ID = _AvatarIDs.Dequeue();
|
||||
_Avatars.Add(avatar.ID, avatar);
|
||||
@@ -655,7 +655,7 @@ namespace Vocaluxe.Base
|
||||
return String.CompareOrdinal(a.PlayerName, b.PlayerName);
|
||||
}
|
||||
|
||||
private static CAvatar _GetAvatar(string fileName)
|
||||
public static CAvatar GetAvatarByFilename(string fileName)
|
||||
{
|
||||
string name = Path.GetFileName(fileName);
|
||||
|
||||
@@ -664,7 +664,7 @@ namespace Vocaluxe.Base
|
||||
if (Path.GetFileName(_Avatars[id].FileName) == name)
|
||||
return _Avatars[id];
|
||||
}
|
||||
return new CAvatar(-1);
|
||||
return null;
|
||||
}
|
||||
#endregion private methods
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using Vocaluxe.Lib.Sound.Record;
|
||||
using Vocaluxe.Lib.Sound.Record.DirectSound;
|
||||
using Vocaluxe.Lib.Sound.Record.PortAudio;
|
||||
using VocaluxeLib;
|
||||
|
||||
namespace Vocaluxe.Base
|
||||
{
|
||||
static class CRecord
|
||||
{
|
||||
private static IRecord _Record;
|
||||
|
||||
public static bool Init()
|
||||
{
|
||||
if (_Record != null)
|
||||
return false;
|
||||
switch (CConfig.RecordLib)
|
||||
{
|
||||
#if WIN
|
||||
case ERecordLib.DirectSound:
|
||||
_Record = new CDirectSoundRecord();
|
||||
break;
|
||||
#endif
|
||||
|
||||
// case ERecordLib.PortAudio:
|
||||
default:
|
||||
_Record = new CPortAudioRecord();
|
||||
break;
|
||||
}
|
||||
return _Record.Init();
|
||||
}
|
||||
|
||||
public static void Close()
|
||||
{
|
||||
if (_Record != null)
|
||||
{
|
||||
_Record.Close();
|
||||
_Record = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Start()
|
||||
{
|
||||
return _Record.Start();
|
||||
}
|
||||
|
||||
public static bool Stop()
|
||||
{
|
||||
return _Record.Stop();
|
||||
}
|
||||
|
||||
public static void AnalyzeBuffer(int player)
|
||||
{
|
||||
_Record.AnalyzeBuffer(player);
|
||||
}
|
||||
|
||||
public static int GetToneAbs(int player)
|
||||
{
|
||||
return _Record.GetToneAbs(player);
|
||||
}
|
||||
|
||||
public static int GetTone(int player)
|
||||
{
|
||||
return _Record.GetTone(player);
|
||||
}
|
||||
|
||||
public static void SetTone(int player, int tone)
|
||||
{
|
||||
_Record.SetTone(player, tone);
|
||||
}
|
||||
|
||||
public static bool ToneValid(int player)
|
||||
{
|
||||
return _Record.ToneValid(player);
|
||||
}
|
||||
|
||||
public static float GetMaxVolume(int player)
|
||||
{
|
||||
return _Record.GetMaxVolume(player);
|
||||
}
|
||||
|
||||
public static int NumHalfTones(int player)
|
||||
{
|
||||
return _Record.NumHalfTones();
|
||||
}
|
||||
|
||||
public static float[] ToneWeigth(int player)
|
||||
{
|
||||
return _Record.ToneWeigth(player);
|
||||
}
|
||||
|
||||
public static ReadOnlyCollection<CRecordDevice> GetDevices()
|
||||
{
|
||||
ReadOnlyCollection<CRecordDevice> devices = _Record.RecordDevices();
|
||||
|
||||
if (devices != null)
|
||||
{
|
||||
foreach (CRecordDevice device in devices)
|
||||
{
|
||||
device.PlayerChannel1 = _GetPlayerFromMicConfig(device.Name, device.Driver, 1);
|
||||
device.PlayerChannel2 = _GetPlayerFromMicConfig(device.Name, device.Driver, 2);
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int _GetPlayerFromMicConfig(string device, string devicedriver, int channel)
|
||||
{
|
||||
for (int p = 0; p < CSettings.MaxNumPlayer; p++)
|
||||
{
|
||||
if (CConfig.MicConfig[p].Channel != 0 &&
|
||||
CConfig.MicConfig[p].DeviceName == device &&
|
||||
CConfig.MicConfig[p].DeviceDriver == devicedriver &&
|
||||
CConfig.MicConfig[p].Channel == channel)
|
||||
return p + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
-47
@@ -33,6 +33,10 @@ namespace Vocaluxe.Base
|
||||
// ReSharper restore UnusedMember.Global
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class contains settings for the program
|
||||
/// These are not editable, everything that can be changed by the user is in CConfig
|
||||
/// </summary>
|
||||
static class CSettings
|
||||
{
|
||||
#if ARCH_X86
|
||||
@@ -43,7 +47,9 @@ namespace Vocaluxe.Base
|
||||
private const String _Arch = "x64";
|
||||
#endif
|
||||
|
||||
public static EGameState GameState = EGameState.Start;
|
||||
//TODO: This should not be here as it can change
|
||||
//State of the program
|
||||
public static EProgramState ProgramState = EProgramState.Start;
|
||||
|
||||
//Adjusting of programName and version now in the assembly config.
|
||||
//I'd use the major and minor for Main releases, build number for every public release and revision for every bugfix version without any features
|
||||
@@ -63,23 +69,20 @@ namespace Vocaluxe.Base
|
||||
public const int ZNear = -100;
|
||||
public const int ZFar = 100;
|
||||
|
||||
public static bool IsFullScreen;
|
||||
public const int VertexBufferElements = 10000;
|
||||
|
||||
#if INSTALLER
|
||||
public static string DataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Vocaluxe");
|
||||
public static readonly string DataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Vocaluxe");
|
||||
#else
|
||||
public static string DataPath = Environment.CurrentDirectory;
|
||||
public static readonly string DataPath = Environment.CurrentDirectory;
|
||||
#endif
|
||||
|
||||
public const string Icon = "Vocaluxe.ico";
|
||||
public const string Logo = "Logo.png";
|
||||
public const string FallbackLanguage = "English";
|
||||
public static string FileConfig = "Config.xml";
|
||||
public const string FileFonts = "Fonts.xml";
|
||||
|
||||
public const string FileOldHighscoreDB = "Ultrastar.db";
|
||||
public static string FileHighscoreDB = "HighscoreDB.sqlite";
|
||||
public const string FileCoverDB = "CoverDB.sqlite";
|
||||
public const string FileCreditsRessourcesDB = "CreditsRessourcesDB.sqlite";
|
||||
public const string FilePerformanceLog = "Performance.log";
|
||||
@@ -96,21 +99,11 @@ namespace Vocaluxe.Base
|
||||
public const string FolderThemes = "Themes";
|
||||
public const string FolderThemeFonts = "Fonts";
|
||||
public const string FolderScreens = "Screens";
|
||||
public const string FolderProfiles = "Profiles";
|
||||
public static List<string> FoldersProfiles = new List<string>
|
||||
{
|
||||
#if INSTALLER
|
||||
Path.Combine(Environment.CurrentDirectory, FolderProfiles),
|
||||
#endif
|
||||
Path.Combine(DataPath, FolderProfiles)
|
||||
};
|
||||
public const string FolderPhotos = "Photos";
|
||||
public const string FolderSongs = "Songs";
|
||||
public const string FolderSounds = "Sounds";
|
||||
public const string FolderLanguages = "Languages";
|
||||
public const string FolderScreenshots = "Screenshots";
|
||||
public const string FolderBackgroundMusic = "BackgroundMusic";
|
||||
public static string FolderPlaylists = "Playlists";
|
||||
|
||||
public const string FolderPartyModes = "PartyModes";
|
||||
public const string FolderPartyModeCode = "Code";
|
||||
@@ -128,8 +121,10 @@ namespace Vocaluxe.Base
|
||||
public const int ToneMax = 89;
|
||||
|
||||
public const int NumNoteLines = 11;
|
||||
public static int MouseMoveDiffMin = 2;
|
||||
|
||||
public const int MouseMoveOffTime = 3000; //in ms
|
||||
public const int MouseMoveDiffMinActive = 3;
|
||||
public const int MouseMoveDiffMinInactive = 15;
|
||||
|
||||
public const int MaxNumPlayer = 6;
|
||||
public const int MaxScore = 10000;
|
||||
@@ -153,14 +148,6 @@ namespace Vocaluxe.Base
|
||||
public const float SlideShowImageTime = 3500f;
|
||||
public const float SlideShowFadeTime = 500f;
|
||||
|
||||
public static readonly List<string> MusicFileTypes = new List<string>
|
||||
{
|
||||
"*.mp3",
|
||||
"*.wma",
|
||||
"*.ogg",
|
||||
"*.wav"
|
||||
};
|
||||
|
||||
private static AssemblyName _Assembly
|
||||
{
|
||||
get { return Assembly.GetExecutingAssembly().GetName(); }
|
||||
@@ -220,40 +207,37 @@ namespace Vocaluxe.Base
|
||||
return RenderW / (float)RenderH;
|
||||
}
|
||||
|
||||
public static void MouseInactive()
|
||||
{
|
||||
MouseMoveDiffMin = 15;
|
||||
}
|
||||
|
||||
public static void MouseActive()
|
||||
{
|
||||
MouseMoveDiffMin = 3;
|
||||
}
|
||||
|
||||
public static void CreateFolders()
|
||||
{
|
||||
var folders = new List<string>
|
||||
{
|
||||
FolderCover,
|
||||
FolderFonts,
|
||||
Path.Combine(Environment.CurrentDirectory, FolderCover),
|
||||
Path.Combine(Environment.CurrentDirectory, FolderFonts),
|
||||
Path.Combine(DataPath, FolderScreenshots),
|
||||
FolderBackgroundMusic,
|
||||
FolderSounds,
|
||||
Path.Combine(DataPath, FolderPlaylists)
|
||||
Path.Combine(Environment.CurrentDirectory, FolderBackgroundMusic),
|
||||
Path.Combine(Environment.CurrentDirectory, FolderSounds),
|
||||
Path.Combine(DataPath, CConfig.FolderPlaylists)
|
||||
};
|
||||
folders.AddRange(FoldersProfiles);
|
||||
folders.AddRange(CConfig.ProfileFolders);
|
||||
folders.AddRange(CConfig.SongFolders);
|
||||
|
||||
foreach (string folder in folders)
|
||||
{
|
||||
string path = Path.Combine(Environment.CurrentDirectory, folder);
|
||||
CreateFolder(path);
|
||||
}
|
||||
_CreateFolder(folder);
|
||||
}
|
||||
|
||||
public static void CreateFolder(string path)
|
||||
private static void _CreateFolder(string path)
|
||||
{
|
||||
if (!Directory.Exists(path))
|
||||
Directory.CreateDirectory(path);
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CLog.LogError("Cannot create directory \"" + path + "\": " + e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
using VocaluxeLib;
|
||||
using VocaluxeLib.Songs;
|
||||
|
||||
@@ -61,36 +61,10 @@ namespace Vocaluxe.Base
|
||||
_SetChanged();
|
||||
}
|
||||
|
||||
private void _CreateCategoriesLetter()
|
||||
private void _CreateCategories()
|
||||
{
|
||||
CCategory lastCategory = null;
|
||||
CCategory notLetterCat = null;
|
||||
foreach (CSongPointer songPointer in CSongs.Sorter.SortedSongs)
|
||||
{
|
||||
Char firstLetter = Char.ToUpper(songPointer.SortString.Normalize(NormalizationForm.FormD)[0]);
|
||||
if (Char.IsLetter(firstLetter))
|
||||
{
|
||||
if (lastCategory == null || firstLetter.ToString() != lastCategory.Name)
|
||||
{
|
||||
lastCategory = new CCategory(firstLetter.ToString());
|
||||
_Categories.Add(lastCategory);
|
||||
}
|
||||
lastCategory.Songs.Add(songPointer);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (notLetterCat == null)
|
||||
{
|
||||
notLetterCat = new CCategory("#");
|
||||
_Categories.Add(notLetterCat);
|
||||
}
|
||||
notLetterCat.Songs.Add(songPointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
_AdjustCategoryNames();
|
||||
|
||||
private void _CreateCategoriesNormal(string noCategoryName)
|
||||
{
|
||||
CCategory lastCategory = null;
|
||||
CCategory noCategory = null;
|
||||
foreach (CSongPointer songPointer in CSongs.Sorter.SortedSongs)
|
||||
@@ -108,7 +82,7 @@ namespace Vocaluxe.Base
|
||||
{
|
||||
if (noCategory == null)
|
||||
{
|
||||
noCategory = new CCategory(noCategoryName);
|
||||
noCategory = new CCategory(_GetNoCategoryName());
|
||||
_Categories.Add(noCategory);
|
||||
}
|
||||
noCategory.Songs.Add(songPointer);
|
||||
@@ -116,37 +90,15 @@ namespace Vocaluxe.Base
|
||||
}
|
||||
}
|
||||
|
||||
private void _CreateCategories()
|
||||
/// <summary>
|
||||
/// Sets the SortStrings of the songpointers to the actual category names
|
||||
/// </summary>
|
||||
private static void _AdjustCategoryNames()
|
||||
{
|
||||
string noCategoryName = "";
|
||||
|
||||
ESongSorting sorting = CSongs.Sorter.SongSorting;
|
||||
|
||||
switch (sorting)
|
||||
{
|
||||
case ESongSorting.TR_CONFIG_EDITION:
|
||||
noCategoryName = CLanguage.Translate("TR_SCREENSONG_NOEDITION");
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_GENRE:
|
||||
noCategoryName = CLanguage.Translate("TR_SCREENSONG_NOGENRE");
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_DECADE:
|
||||
case ESongSorting.TR_CONFIG_YEAR:
|
||||
noCategoryName = CLanguage.Translate("TR_SCREENSONG_NOYEAR");
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_LANGUAGE:
|
||||
noCategoryName = CLanguage.Translate("TR_SCREENSONG_NOLANGUAGE");
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_NONE:
|
||||
noCategoryName = CLanguage.Translate("TR_SCREENSONG_ALLSONGS");
|
||||
break;
|
||||
}
|
||||
if (sorting == ESongSorting.TR_CONFIG_ARTIST_LETTER || sorting == ESongSorting.TR_CONFIG_TITLE_LETTER)
|
||||
_CreateCategoriesLetter();
|
||||
else
|
||||
{
|
||||
if (sorting == ESongSorting.TR_CONFIG_DECADE)
|
||||
{
|
||||
foreach (CSongPointer songPointer in CSongs.Sorter.SortedSongs)
|
||||
{
|
||||
string year = songPointer.SortString;
|
||||
@@ -156,11 +108,59 @@ namespace Vocaluxe.Base
|
||||
songPointer.SortString = year + "0 - " + year + "9";
|
||||
}
|
||||
}
|
||||
}
|
||||
_CreateCategoriesNormal(noCategoryName);
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_TITLE_LETTER:
|
||||
case ESongSorting.TR_CONFIG_ARTIST_LETTER:
|
||||
foreach (CSongPointer songPointer in CSongs.Sorter.SortedSongs)
|
||||
songPointer.SortString = (songPointer.SortString.Length == 0 || !Char.IsLetter(songPointer.SortString, 0)) ? "#" : songPointer.SortString[0].ToString();
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_DATEADDED:
|
||||
foreach (CSongPointer songPointer in CSongs.Sorter.SortedSongs)
|
||||
songPointer.SortString = CSongs.GetSong(songPointer.SongID).DateAdded.ToString("dd/MM/yyyy");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static string _GetNoCategoryName()
|
||||
{
|
||||
string noCategoryName;
|
||||
switch (CSongs.Sorter.SongSorting)
|
||||
{
|
||||
case ESongSorting.TR_CONFIG_NONE:
|
||||
noCategoryName = CLanguage.Translate("TR_SCREENSONG_ALLSONGS");
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_FOLDER:
|
||||
case ESongSorting.TR_CONFIG_ARTIST:
|
||||
case ESongSorting.TR_CONFIG_ARTIST_LETTER:
|
||||
case ESongSorting.TR_CONFIG_TITLE_LETTER:
|
||||
noCategoryName = "";
|
||||
Debug.Assert(false, "Should not have an uncategorized song");
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_EDITION:
|
||||
noCategoryName = CLanguage.Translate("TR_SCREENSONG_NOEDITION");
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_GENRE:
|
||||
noCategoryName = CLanguage.Translate("TR_SCREENSONG_NOGENRE");
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_LANGUAGE:
|
||||
noCategoryName = CLanguage.Translate("TR_SCREENSONG_NOLANGUAGE");
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_DECADE:
|
||||
case ESongSorting.TR_CONFIG_YEAR:
|
||||
noCategoryName = CLanguage.Translate("TR_SCREENSONG_NOYEAR");
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_DATEADDED:
|
||||
noCategoryName = "";
|
||||
Debug.Assert(false, "Should not have an uncategorized song");
|
||||
break;
|
||||
default:
|
||||
noCategoryName = "";
|
||||
Debug.Assert(false, "Forgot category option");
|
||||
break;
|
||||
}
|
||||
return noCategoryName;
|
||||
}
|
||||
|
||||
private void _FillCategories()
|
||||
{
|
||||
if (!_Changed)
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using VocaluxeLib;
|
||||
using VocaluxeLib.Songs;
|
||||
|
||||
@@ -110,83 +111,65 @@ namespace Vocaluxe.Base
|
||||
return res;
|
||||
}
|
||||
|
||||
private List<CSongPointer> _CreateSortList(string fieldName)
|
||||
private void _AddSongToList(CSong song, List<CSongPointer> list)
|
||||
{
|
||||
var sortList = new List<CSongPointer>();
|
||||
if (fieldName == "")
|
||||
CSongs.Filter.FilteredSongs.ForEach(song => sortList.Add(new CSongPointer(song.ID, "")));
|
||||
string value = null;
|
||||
List<string> values = null;
|
||||
switch (_SongSorting)
|
||||
{
|
||||
case ESongSorting.TR_CONFIG_NONE:
|
||||
value = "";
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_FOLDER:
|
||||
value = song.FolderName;
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_ARTIST:
|
||||
case ESongSorting.TR_CONFIG_ARTIST_LETTER:
|
||||
value = _IgnoreArticles == EOffOn.TR_CONFIG_ON ? song.ArtistSorting : song.Artist;
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_TITLE_LETTER:
|
||||
value = _IgnoreArticles == EOffOn.TR_CONFIG_ON ? song.TitleSorting : song.Title;
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_EDITION:
|
||||
values = song.Editions;
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_GENRE:
|
||||
values = song.Genres;
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_LANGUAGE:
|
||||
values = song.Languages;
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_DECADE:
|
||||
case ESongSorting.TR_CONFIG_YEAR:
|
||||
value = song.Year;
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_DATEADDED:
|
||||
value = song.DateAdded.ToString("yyyyMMdd");
|
||||
break;
|
||||
default:
|
||||
Debug.Assert(false, "Forgot sorting option");
|
||||
break;
|
||||
}
|
||||
Debug.Assert(value != null || values != null, "Sorting implementation faulty");
|
||||
if (value != null)
|
||||
list.Add(new CSongPointer(song.ID, value));
|
||||
else
|
||||
{
|
||||
FieldInfo field = typeof(CSong).GetField(fieldName, BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public);
|
||||
if (field == null)
|
||||
{
|
||||
CLog.LogError("Unknow sorting field: " + fieldName);
|
||||
return _CreateSortList("");
|
||||
}
|
||||
bool isString = field.FieldType == typeof(string);
|
||||
if (!isString && field.FieldType != typeof(List<String>))
|
||||
throw new Exception("Unkown sort field type");
|
||||
foreach (CSong song in CSongs.Filter.FilteredSongs)
|
||||
{
|
||||
object value = field.GetValue(song);
|
||||
if (isString)
|
||||
sortList.Add(new CSongPointer(song.ID, (String)value));
|
||||
else
|
||||
{
|
||||
var values = (List<String>)value;
|
||||
if (values.Count == 0)
|
||||
sortList.Add(new CSongPointer(song.ID, ""));
|
||||
else
|
||||
{
|
||||
// ReSharper disable LoopCanBeConvertedToQuery
|
||||
foreach (String sortString in values)
|
||||
// ReSharper restore LoopCanBeConvertedToQuery
|
||||
sortList.Add(new CSongPointer(song.ID, sortString));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (list.Count == 0)
|
||||
list.Add(new CSongPointer(song.ID, ""));
|
||||
else
|
||||
list.AddRange(values.Select(val => new CSongPointer(song.ID, val)));
|
||||
}
|
||||
return sortList;
|
||||
}
|
||||
|
||||
private void _SortSongs()
|
||||
{
|
||||
if (!_Changed)
|
||||
return;
|
||||
String fieldName;
|
||||
switch (_SongSorting)
|
||||
{
|
||||
case ESongSorting.TR_CONFIG_EDITION:
|
||||
fieldName = "Editions";
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_GENRE:
|
||||
fieldName = "Genres";
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_FOLDER:
|
||||
fieldName = "FolderName";
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_ARTIST_LETTER:
|
||||
case ESongSorting.TR_CONFIG_ARTIST:
|
||||
fieldName = _IgnoreArticles == EOffOn.TR_CONFIG_ON ? "ArtistSorting" : "Artist";
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_TITLE_LETTER:
|
||||
fieldName = _IgnoreArticles == EOffOn.TR_CONFIG_ON ? "TitleSorting" : "Title";
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_YEAR:
|
||||
case ESongSorting.TR_CONFIG_DECADE:
|
||||
fieldName = "Year";
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_LANGUAGE:
|
||||
fieldName = "Languages";
|
||||
break;
|
||||
case ESongSorting.TR_CONFIG_DATEADDED:
|
||||
fieldName = "DateAdded";
|
||||
break;
|
||||
default:
|
||||
fieldName = "";
|
||||
break;
|
||||
}
|
||||
List<CSongPointer> sortList = _CreateSortList(fieldName);
|
||||
|
||||
List<CSongPointer> sortList = new List<CSongPointer>();
|
||||
foreach (CSong song in CSongs.Filter.FilteredSongs)
|
||||
_AddSongToList(song, sortList);
|
||||
switch (_SongSorting)
|
||||
{
|
||||
case ESongSorting.TR_CONFIG_ARTIST_LETTER:
|
||||
|
||||
+11
-13
@@ -332,21 +332,21 @@ namespace Vocaluxe.Base
|
||||
|
||||
public static void LoadSongs()
|
||||
{
|
||||
CLog.StartBenchmark(1, "Load Songs");
|
||||
CLog.StartBenchmark("Load Songs");
|
||||
SongsLoaded = false;
|
||||
_Songs.Clear();
|
||||
|
||||
CLog.StartBenchmark(2, "List Songs");
|
||||
CLog.StartBenchmark("List Songs");
|
||||
var files = new List<string>();
|
||||
foreach (string p in CConfig.SongFolder)
|
||||
foreach (string p in CConfig.SongFolders)
|
||||
{
|
||||
string path = p;
|
||||
files.AddRange(CHelper.ListFiles(path, "*.txt", true, true));
|
||||
files.AddRange(CHelper.ListFiles(path, "*.txd", true, true));
|
||||
}
|
||||
CLog.StopBenchmark(2, "List Songs");
|
||||
CLog.StopBenchmark("List Songs");
|
||||
|
||||
CLog.StartBenchmark(2, "Read TXTs");
|
||||
CLog.StartBenchmark("Read TXTs");
|
||||
foreach (string file in files)
|
||||
{
|
||||
CSong song = CSong.LoadSong(file);
|
||||
@@ -356,25 +356,24 @@ namespace Vocaluxe.Base
|
||||
if (song.LoadNotes())
|
||||
_Songs.Add(song);
|
||||
}
|
||||
CLog.StopBenchmark(2, "Read TXTs");
|
||||
CLog.StopBenchmark("Read TXTs");
|
||||
|
||||
CLog.StartBenchmark(2, "Sort Songs");
|
||||
CLog.StartBenchmark("Sort Songs");
|
||||
Sorter.SongSorting = CConfig.SongSorting;
|
||||
Sorter.IgnoreArticles = CConfig.IgnoreArticles;
|
||||
Categorizer.Tabs = CConfig.Tabs;
|
||||
Categorizer.ObjectChanged += _HandleCategoriesChanged;
|
||||
CLog.StopBenchmark(2, "Sort Songs");
|
||||
CLog.StopBenchmark("Sort Songs");
|
||||
Category = -1;
|
||||
SongsLoaded = true;
|
||||
|
||||
if (CConfig.CoverLoading == ECoverLoading.TR_CONFIG_COVERLOADING_ATSTART)
|
||||
{
|
||||
CLog.StartBenchmark(2, "Load Covers/Notes");
|
||||
CLog.StartBenchmark("Load Covers/Notes");
|
||||
_LoadCover();
|
||||
CLog.StopBenchmark(2, "Load Covers/Notes");
|
||||
CLog.StopBenchmark("Load Covers/Notes");
|
||||
}
|
||||
CLog.StopBenchmark(1, "Load Songs ");
|
||||
GC.Collect();
|
||||
CLog.StopBenchmark("Load Songs ");
|
||||
}
|
||||
|
||||
public static void LoadCover()
|
||||
@@ -419,7 +418,6 @@ namespace Vocaluxe.Base
|
||||
song.LoadSmallCover();
|
||||
NumSongsWithCoverLoaded++;
|
||||
}
|
||||
GC.Collect();
|
||||
_CoverLoaded = true;
|
||||
CDataBase.CommitCovers();
|
||||
}
|
||||
|
||||
+15
-109
@@ -16,9 +16,12 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using Vocaluxe.Lib.Sound;
|
||||
using Vocaluxe.Lib.Sound.Playback;
|
||||
using Vocaluxe.Lib.Sound.Playback.Gstreamer;
|
||||
using Vocaluxe.Lib.Sound.Playback.GstreamerSharp;
|
||||
using Vocaluxe.Lib.Sound.Playback.OpenAL;
|
||||
using Vocaluxe.Lib.Sound.Playback.PortAudio;
|
||||
using VocaluxeLib;
|
||||
|
||||
namespace Vocaluxe.Base
|
||||
@@ -35,6 +38,8 @@ namespace Vocaluxe.Base
|
||||
|
||||
public static bool Init()
|
||||
{
|
||||
if (_Playback != null)
|
||||
return false;
|
||||
switch (CConfig.PlayBackLib)
|
||||
{
|
||||
case EPlaybackLib.PortAudio:
|
||||
@@ -72,7 +77,14 @@ namespace Vocaluxe.Base
|
||||
|
||||
public static void CloseAllStreams()
|
||||
{
|
||||
_Playback.CloseAll();
|
||||
if (_Playback != null)
|
||||
_Playback.CloseAll();
|
||||
}
|
||||
|
||||
public static void Close()
|
||||
{
|
||||
CloseAllStreams();
|
||||
_Playback = null;
|
||||
}
|
||||
|
||||
#region Stream Handling
|
||||
@@ -203,111 +215,5 @@ namespace Vocaluxe.Base
|
||||
return stream;
|
||||
}
|
||||
#endregion Sounds
|
||||
|
||||
#region Record
|
||||
private static IRecord _Record;
|
||||
|
||||
public static void RecordInit()
|
||||
{
|
||||
switch (CConfig.RecordLib)
|
||||
{
|
||||
#if WIN
|
||||
case ERecordLib.DirectSound:
|
||||
_Record = new CDirectSoundRecord();
|
||||
break;
|
||||
#endif
|
||||
|
||||
// case ERecordLib.PortAudio:
|
||||
default:
|
||||
_Record = new CPortAudioRecord();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void RecordCloseAll()
|
||||
{
|
||||
_Record.CloseAll();
|
||||
}
|
||||
|
||||
public static bool RecordStart()
|
||||
{
|
||||
return _Record.Start();
|
||||
}
|
||||
|
||||
public static bool RecordStop()
|
||||
{
|
||||
return _Record.Stop();
|
||||
}
|
||||
|
||||
public static void AnalyzeBuffer(int player)
|
||||
{
|
||||
_Record.AnalyzeBuffer(player);
|
||||
}
|
||||
|
||||
public static int RecordGetToneAbs(int player)
|
||||
{
|
||||
return _Record.GetToneAbs(player);
|
||||
}
|
||||
|
||||
public static int RecordGetTone(int player)
|
||||
{
|
||||
return _Record.GetTone(player);
|
||||
}
|
||||
|
||||
public static void RecordSetTone(int player, int tone)
|
||||
{
|
||||
_Record.SetTone(player, tone);
|
||||
}
|
||||
|
||||
public static bool RecordToneValid(int player)
|
||||
{
|
||||
return _Record.ToneValid(player);
|
||||
}
|
||||
|
||||
public static float RecordGetMaxVolume(int player)
|
||||
{
|
||||
return _Record.GetMaxVolume(player);
|
||||
}
|
||||
|
||||
public static int NumHalfTones(int player)
|
||||
{
|
||||
return _Record.NumHalfTones(player);
|
||||
}
|
||||
|
||||
public static float[] ToneWeigth(int player)
|
||||
{
|
||||
return _Record.ToneWeigth(player);
|
||||
}
|
||||
|
||||
public static ReadOnlyCollection<CRecordDevice> RecordGetDevices()
|
||||
{
|
||||
ReadOnlyCollection<CRecordDevice> devices = _Record.RecordDevices();
|
||||
|
||||
if (devices != null)
|
||||
{
|
||||
foreach (CRecordDevice device in devices)
|
||||
{
|
||||
device.PlayerChannel1 = _GetPlayerFromMicConfig(device.Name, device.Driver, 1);
|
||||
device.PlayerChannel2 = _GetPlayerFromMicConfig(device.Name, device.Driver, 2);
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int _GetPlayerFromMicConfig(string device, string devicedriver, int channel)
|
||||
{
|
||||
for (int p = 0; p < CSettings.MaxNumPlayer; p++)
|
||||
{
|
||||
if (CConfig.MicConfig[p].Channel != 0 &&
|
||||
CConfig.MicConfig[p].DeviceName == device &&
|
||||
CConfig.MicConfig[p].DeviceDriver == devicedriver &&
|
||||
CConfig.MicConfig[p].Channel == channel)
|
||||
return p + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endregion Record
|
||||
}
|
||||
}
|
||||
+16
-7
@@ -142,22 +142,30 @@ namespace Vocaluxe.Base
|
||||
#endregion Vars
|
||||
|
||||
#region Theme and Skin loading and writing
|
||||
public static void InitTheme()
|
||||
public static bool Init()
|
||||
{
|
||||
_ListThemes();
|
||||
ListSkins();
|
||||
|
||||
LoadSkins();
|
||||
LoadTheme();
|
||||
return LoadSkins() && LoadTheme();
|
||||
}
|
||||
|
||||
public static void LoadSkins()
|
||||
public static bool LoadSkins()
|
||||
{
|
||||
//Fail if loading of any skin failed or no skin was loaded
|
||||
//TODO: implement handlers that failing skins are logged and ignored
|
||||
bool result = false;
|
||||
for (int index = 0; index < _Skins.Count; index++)
|
||||
{
|
||||
if (_Skins[index].PartyModeID == -1)
|
||||
LoadSkin(index);
|
||||
{
|
||||
if (LoadSkin(index))
|
||||
result = true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static bool LoadSkin(int skinIndex)
|
||||
@@ -225,13 +233,14 @@ namespace Vocaluxe.Base
|
||||
}
|
||||
}
|
||||
|
||||
public static void LoadTheme()
|
||||
public static bool LoadTheme()
|
||||
{
|
||||
for (int i = 0; i < _Themes.Count; i++)
|
||||
{
|
||||
if (_Themes[i].Name == CConfig.Theme && _Themes[i].PartyModeID == -1)
|
||||
LoadTheme(i);
|
||||
return LoadTheme(i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool LoadTheme(int themeIndex)
|
||||
|
||||
+10
-4
@@ -29,8 +29,10 @@ namespace Vocaluxe.Base
|
||||
private static IVideoDecoder _VideoDecoder;
|
||||
|
||||
#region Init
|
||||
public static void Init()
|
||||
public static bool Init()
|
||||
{
|
||||
if (_VideoDecoder != null)
|
||||
return false;
|
||||
switch (CConfig.VideoDecoder)
|
||||
{
|
||||
case EVideoDecoder.FFmpeg:
|
||||
@@ -44,14 +46,18 @@ namespace Vocaluxe.Base
|
||||
break;
|
||||
}
|
||||
|
||||
_VideoDecoder.Init();
|
||||
return _VideoDecoder.Init();
|
||||
}
|
||||
#endregion Init
|
||||
|
||||
#region Interface
|
||||
public static void CloseAll()
|
||||
public static void Close()
|
||||
{
|
||||
_VideoDecoder.CloseAll();
|
||||
if (_VideoDecoder != null)
|
||||
{
|
||||
_VideoDecoder.CloseAll();
|
||||
_VideoDecoder = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetNumStreams()
|
||||
|
||||
@@ -26,8 +26,10 @@ namespace Vocaluxe.Base
|
||||
{
|
||||
private static IWebcam _Webcam;
|
||||
|
||||
public static void Init()
|
||||
public static bool Init()
|
||||
{
|
||||
if (_Webcam != null)
|
||||
return false;
|
||||
switch (CConfig.WebcamLib)
|
||||
{
|
||||
case EWebcamLib.AForgeNet:
|
||||
@@ -38,11 +40,13 @@ namespace Vocaluxe.Base
|
||||
_Webcam = new CAForgeNet();
|
||||
break;
|
||||
}
|
||||
_Webcam.Init();
|
||||
if (!_Webcam.Init())
|
||||
return false;
|
||||
_Webcam.Select(CConfig.WebcamConfig);
|
||||
|
||||
CConfig.WebcamConfig = _Webcam.GetConfig();
|
||||
CConfig.SaveConfig();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool GetFrame(ref CTexture tex)
|
||||
@@ -57,7 +61,11 @@ namespace Vocaluxe.Base
|
||||
|
||||
public static void Close()
|
||||
{
|
||||
_Webcam.Close();
|
||||
if (_Webcam != null)
|
||||
{
|
||||
_Webcam.Close();
|
||||
_Webcam = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Pause()
|
||||
|
||||
@@ -361,9 +361,9 @@ namespace Vocaluxe.Base.Fonts
|
||||
public static void LoadThemeFonts(string themeName, string fontFolder, CXMLReader xmlReader)
|
||||
{
|
||||
_LoadFontFiles(xmlReader, fontFolder, themeName);
|
||||
CLog.StartBenchmark(1, "BuildGlyphs");
|
||||
CLog.StartBenchmark("BuildGlyphs");
|
||||
_BuildGlyphs();
|
||||
CLog.StopBenchmark(1, "BuildGlyphs");
|
||||
CLog.StopBenchmark("BuildGlyphs");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -372,9 +372,9 @@ namespace Vocaluxe.Base.Fonts
|
||||
public static void LoadPartyModeFonts(int partyModeID, string fontFolder, CXMLReader xmlReader)
|
||||
{
|
||||
_LoadFontFiles(xmlReader, fontFolder, "", partyModeID);
|
||||
CLog.StartBenchmark(1, "BuildGlyphs");
|
||||
CLog.StartBenchmark("BuildGlyphs");
|
||||
_BuildGlyphs();
|
||||
CLog.StopBenchmark(1, "BuildGlyphs");
|
||||
CLog.StopBenchmark("BuildGlyphs");
|
||||
}
|
||||
|
||||
public static void SaveThemeFonts(string themeName, XmlWriter writer)
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
//Uncomment to make the font write the glyphs to file
|
||||
//#define FONT_DEBUG_OUTPUT
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
@@ -119,19 +122,15 @@ namespace Vocaluxe.Base.Fonts
|
||||
_DrawBounding.Y *= _Texture.Width / _DrawBounding.Width;
|
||||
_DrawBounding.Width = _Texture.Width;
|
||||
_DrawBounding.Height = _Texture.Height;*/
|
||||
#pragma warning disable 162
|
||||
// ReSharper disable HeuristicUnreachableCode
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalse
|
||||
if (false && Char.IsLetterOrDigit(chr))
|
||||
#if FONT_DEBUG_OUTPUT
|
||||
if (Char.IsLetterOrDigit(chr))
|
||||
{
|
||||
if (outline > 0)
|
||||
bmpCropped.Save("font_" + chr + "o" + CFonts.Style + "2.png", ImageFormat.Png);
|
||||
else
|
||||
bmpCropped.Save("font_" + chr + CFonts.Style + "2.png", ImageFormat.Png);
|
||||
}
|
||||
// ReSharper restore ConditionIsAlwaysTrueOrFalse
|
||||
// ReSharper restore HeuristicUnreachableCode
|
||||
#pragma warning restore 162
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using ServerLib;
|
||||
@@ -29,16 +30,34 @@ using VocaluxeLib;
|
||||
using VocaluxeLib.Menu;
|
||||
using VocaluxeLib.Profile;
|
||||
using VocaluxeLib.Songs;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Vocaluxe.Base.Server
|
||||
{
|
||||
static class CVocaluxeServer
|
||||
{
|
||||
private class CServerController : CControllerFramework
|
||||
{
|
||||
public override string GetName()
|
||||
{
|
||||
return "App controller";
|
||||
}
|
||||
|
||||
public override void Connect() {}
|
||||
|
||||
public override void Disconnect() {}
|
||||
|
||||
public override bool IsConnected()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void SetRumble(float duration) {}
|
||||
}
|
||||
|
||||
private static CServer _Server;
|
||||
//private static CDiscover _Discover;
|
||||
|
||||
public static readonly CControllerFramework Controller = new CControllerFramework();
|
||||
public static readonly CControllerFramework Controller = new CServerController();
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
@@ -69,7 +88,6 @@ namespace Vocaluxe.Base.Server
|
||||
CServer.AddPlaylist = _AddPlaylist;
|
||||
|
||||
//_Discover = new CDiscover(CConfig.ServerPort, CCommands.BroadcastKeyword);
|
||||
Controller.Init();
|
||||
}
|
||||
|
||||
public static void Start()
|
||||
@@ -83,8 +101,12 @@ namespace Vocaluxe.Base.Server
|
||||
|
||||
public static void Close()
|
||||
{
|
||||
_Server.Stop();
|
||||
//_Discover.Stop();
|
||||
if (_Server != null)
|
||||
{
|
||||
_Server.Stop();
|
||||
_Server = null;
|
||||
//_Discover.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetServerAddress()
|
||||
@@ -148,7 +170,7 @@ namespace Vocaluxe.Base.Server
|
||||
else
|
||||
keyChar = c;
|
||||
|
||||
if ((keyChar >= 0x30 && keyChar <= 0x39) || (keyChar >= 0x41 && keyChar <= 0x5A))
|
||||
if ((keyChar >= '0' && keyChar <= '9') || (keyChar >= 'A' && keyChar <= 'Z'))
|
||||
{
|
||||
Keys keys = (Keys)keyChar;
|
||||
if (shift)
|
||||
@@ -185,7 +207,6 @@ namespace Vocaluxe.Base.Server
|
||||
ID = existingProfile.ID,
|
||||
FileName = existingProfile.FileName,
|
||||
Active = existingProfile.Active,
|
||||
AvatarFileName = existingProfile.AvatarFileName,
|
||||
Avatar = existingProfile.Avatar,
|
||||
Difficulty = existingProfile.Difficulty,
|
||||
UserRole = existingProfile.UserRole,
|
||||
@@ -295,10 +316,10 @@ namespace Vocaluxe.Base.Server
|
||||
{
|
||||
try
|
||||
{
|
||||
string filename = _SaveImage(avatarData, "snapshot", CSettings.FolderProfiles);
|
||||
string filename = _SaveImage(avatarData, "snapshot", CConfig.ProfileFolders[0]);
|
||||
|
||||
var avatar = new CAvatar(-1);
|
||||
if (avatar.LoadFromFile(filename))
|
||||
var avatar = CAvatar.GetAvatar(filename);
|
||||
if (avatar!=null)
|
||||
{
|
||||
CProfiles.AddAvatar(avatar);
|
||||
return avatar;
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Vocaluxe
|
||||
{
|
||||
static class COSFunctions
|
||||
{
|
||||
#if WIN
|
||||
private static bool _DefaultDllDirSet;
|
||||
private static bool _UsePath;
|
||||
private static bool _UseSetDllDir;
|
||||
|
||||
private static class CWindowsFunctions
|
||||
{
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
|
||||
public static extern void AddDllDirectory(string lpPathName);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
|
||||
public static extern void SetDllDirectory(string lpPathName);
|
||||
|
||||
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool SetDefaultDllDirectories(int directoryFlags);
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
public const int LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000;
|
||||
// ReSharper restore InconsistentNaming
|
||||
}
|
||||
#endif
|
||||
|
||||
public static void AddEnvironmentPath(string path)
|
||||
{
|
||||
string newPath = Environment.GetEnvironmentVariable("PATH") ?? string.Empty;
|
||||
if (newPath.Length > 0)
|
||||
newPath += ";";
|
||||
newPath += path;
|
||||
|
||||
Environment.SetEnvironmentVariable("PATH", newPath);
|
||||
}
|
||||
|
||||
public static void AddLibrarySearchpath(string path)
|
||||
{
|
||||
#if WIN
|
||||
if (!_DefaultDllDirSet)
|
||||
{
|
||||
try
|
||||
{
|
||||
CWindowsFunctions.SetDefaultDllDirectories(CWindowsFunctions.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_UsePath = true;
|
||||
_UseSetDllDir = true;
|
||||
}
|
||||
_DefaultDllDirSet = true;
|
||||
}
|
||||
if (_UsePath)
|
||||
AddEnvironmentPath(path);
|
||||
if (!_UseSetDllDir)
|
||||
{
|
||||
try
|
||||
{
|
||||
CWindowsFunctions.AddDllDirectory(path);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_UseSetDllDir = true;
|
||||
}
|
||||
}
|
||||
if (_UseSetDllDir)
|
||||
CWindowsFunctions.SetDllDirectory(path);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void SetForegroundWindow(IntPtr hWnd)
|
||||
{
|
||||
#if WIN
|
||||
CWindowsFunctions.SetForegroundWindow(hWnd);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Security;
|
||||
using Microsoft.Win32;
|
||||
using Vocaluxe.Base;
|
||||
@@ -49,6 +50,16 @@ namespace Vocaluxe
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool _KeyExists(string key)
|
||||
{
|
||||
using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(key))
|
||||
{
|
||||
if (rk != null)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a given program is installed by checking the Uninstall registry key.
|
||||
/// </summary>
|
||||
@@ -56,10 +67,11 @@ namespace Vocaluxe
|
||||
/// <returns></returns>
|
||||
private static bool _IsProgramInstalled(string name)
|
||||
{
|
||||
const string uninstallKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
|
||||
const string uninstallKeyx64 = @"SOFTWARE\WoW6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
|
||||
const string baseKey = @"SOFTWARE\";
|
||||
const string baseKey64 = @"SOFTWARE\WoW6432Node\";
|
||||
const string uninstallKey = @"Microsoft\Windows\CurrentVersion\Uninstall";
|
||||
name = name.ToLower();
|
||||
return _CheckUninstallKey(name, uninstallKey) || _CheckUninstallKey(name, uninstallKeyx64);
|
||||
return _CheckUninstallKey(name, baseKey + uninstallKey) || (_KeyExists(baseKey64) && _CheckUninstallKey(name, baseKey64 + uninstallKey));
|
||||
}
|
||||
|
||||
private static bool _SystemDllExists(string dllName)
|
||||
@@ -93,6 +105,7 @@ namespace Vocaluxe
|
||||
true, true);
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
bool vc2008Installed = _IsVC2008Installed();
|
||||
if (!vc2008Installed)
|
||||
{
|
||||
@@ -106,7 +119,21 @@ namespace Vocaluxe
|
||||
true, true);
|
||||
return false;
|
||||
}
|
||||
* */
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
#if ARCH_X86
|
||||
string path = "x86";
|
||||
#endif
|
||||
#if ARCH_X64
|
||||
string path = "x64";
|
||||
#endif
|
||||
path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + Path.DirectorySeparatorChar + path;
|
||||
COSFunctions.AddLibrarySearchpath(path);
|
||||
COSFunctions.AddLibrarySearchpath(path + Path.DirectorySeparatorChar + "gstreamer");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,9 +38,6 @@ using System.Diagnostics.CodeAnalysis;
|
||||
[assembly: SuppressMessage("Microsoft.Reliability", "CA2000:Objekte verwerfen, bevor Bereich verloren geht", Scope = "member", Target = "Vocaluxe.Lib.Draw.CDirect3D.#CopyScreen()")
|
||||
]
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Scope = "type", Target = "Vocaluxe.Lib.Input.WiiMote.InputReport")]
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Scope = "type", Target = "PortAudioSharp.PortAudio+PaDeviceIndex")]
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Scope = "type", Target = "PortAudioSharp.PortAudio+PaSampleFormat")]
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Scope = "type", Target = "PortAudioSharp.PortAudio+PaStreamCallbackFlags")]
|
||||
[assembly:
|
||||
SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope = "member",
|
||||
Target = "Vocaluxe.Lib.Video.Gstreamer.CGstreamerVideoWrapper.#GetFrame(System.Int32,System.Single)")]
|
||||
@@ -49,19 +46,4 @@ using System.Diagnostics.CodeAnalysis;
|
||||
Target = "Vocaluxe.Lib.Input.CHIDAPI.#ReadTimeout(System.IntPtr,System.Byte[]&,System.Int32,System.Int32)")]
|
||||
[assembly:
|
||||
SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope = "member",
|
||||
Target = "Vocaluxe.Lib.Input.CHIDAPI.#Read(System.IntPtr,System.Byte[]&,System.Int32)")]
|
||||
[assembly: SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope = "member", Target = "PortAudioSharp.PortAudio.#Pa_GetVersionText()")]
|
||||
[assembly:
|
||||
SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope = "member",
|
||||
Target = "PortAudioSharp.PortAudio.#Pa_GetErrorText(PortAudioSharp.PortAudio+PaError)")]
|
||||
[assembly:
|
||||
SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope = "member",
|
||||
Target = "PortAudioSharp.PortAudio.#Pa_GetHostApiInfo(System.Int32)")]
|
||||
[assembly:
|
||||
SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope = "member", Target = "PortAudioSharp.PortAudio.#Pa_GetLastHostErrorInfo()")]
|
||||
[assembly:
|
||||
SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope = "member",
|
||||
Target = "PortAudioSharp.PortAudio.#Pa_GetDeviceInfo(System.Int32)")]
|
||||
[assembly:
|
||||
SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope = "member",
|
||||
Target = "PortAudioSharp.PortAudio.#Pa_GetStreamInfo(System.IntPtr)")]
|
||||
Target = "Vocaluxe.Lib.Input.CHIDAPI.#Read(System.IntPtr,System.Byte[]&,System.Int32)")]
|
||||
@@ -311,7 +311,6 @@ namespace Vocaluxe.Lib.Draw
|
||||
FormBorderStyle = FormBorderStyle.None;
|
||||
CenterToScreen();
|
||||
_Fullscreen = true;
|
||||
CConfig.FullScreen = EOffOn.TR_CONFIG_ON;
|
||||
|
||||
if (WindowState == FormWindowState.Maximized)
|
||||
{
|
||||
@@ -321,8 +320,7 @@ namespace Vocaluxe.Lib.Draw
|
||||
}
|
||||
else
|
||||
_DoResize();
|
||||
|
||||
CConfig.SaveConfig();
|
||||
;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -334,9 +332,6 @@ namespace Vocaluxe.Lib.Draw
|
||||
FormBorderStyle = FormBorderStyle.Sizable;
|
||||
CenterToScreen();
|
||||
_Fullscreen = false;
|
||||
CConfig.FullScreen = EOffOn.TR_CONFIG_OFF;
|
||||
|
||||
CConfig.SaveConfig();
|
||||
}
|
||||
#endregion resize
|
||||
|
||||
@@ -486,10 +481,7 @@ namespace Vocaluxe.Lib.Draw
|
||||
Show();
|
||||
|
||||
if (CConfig.FullScreen == EOffOn.TR_CONFIG_ON)
|
||||
{
|
||||
CSettings.IsFullScreen = true;
|
||||
_EnterFullScreen();
|
||||
}
|
||||
|
||||
//Resize window if aspect ratio is incorrect
|
||||
if (Math.Abs(_W / (float)_H - CSettings.GetRenderAspect()) > 0.01)
|
||||
@@ -530,7 +522,7 @@ namespace Vocaluxe.Lib.Draw
|
||||
}
|
||||
}
|
||||
//Apply fullscreen mode
|
||||
if (CSettings.IsFullScreen != _Fullscreen)
|
||||
if ((CConfig.FullScreen == EOffOn.TR_CONFIG_ON) != _Fullscreen)
|
||||
{
|
||||
if (!_Fullscreen)
|
||||
_EnterFullScreen();
|
||||
|
||||
@@ -127,9 +127,6 @@ namespace Vocaluxe.Lib.Draw
|
||||
targetForm.Bounds = Screen.PrimaryScreen.Bounds;
|
||||
targetForm.TopMost = true;
|
||||
_Fullscreen = true;
|
||||
|
||||
CConfig.FullScreen = EOffOn.TR_CONFIG_ON;
|
||||
CConfig.SaveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,9 +143,6 @@ namespace Vocaluxe.Lib.Draw
|
||||
targetForm.TopMost = _TopMost;
|
||||
targetForm.Bounds = _Bounds;
|
||||
_Fullscreen = false;
|
||||
|
||||
CConfig.FullScreen = EOffOn.TR_CONFIG_OFF;
|
||||
CConfig.SaveConfig();
|
||||
}
|
||||
#endregion FullScreenStuff
|
||||
|
||||
@@ -232,10 +226,7 @@ namespace Vocaluxe.Lib.Draw
|
||||
Show();
|
||||
|
||||
if (CConfig.FullScreen == EOffOn.TR_CONFIG_ON)
|
||||
{
|
||||
CSettings.IsFullScreen = true;
|
||||
_Maximize(this);
|
||||
}
|
||||
|
||||
while (_Run)
|
||||
{
|
||||
@@ -247,7 +238,7 @@ namespace Vocaluxe.Lib.Draw
|
||||
_Run = CGraphics.UpdateGameLogic(_Keys, _Mouse);
|
||||
_FlipBuffer();
|
||||
|
||||
if ((CSettings.IsFullScreen && !_Fullscreen) || (!CSettings.IsFullScreen && _Fullscreen))
|
||||
if ((CConfig.FullScreen == EOffOn.TR_CONFIG_ON) != _Fullscreen)
|
||||
_ToggleFullScreen();
|
||||
|
||||
if (CTime.IsRunning())
|
||||
|
||||
@@ -235,7 +235,6 @@ namespace Vocaluxe.Lib.Draw
|
||||
private void _EnterFullScreen()
|
||||
{
|
||||
_Fullscreen = true;
|
||||
CConfig.FullScreen = EOffOn.TR_CONFIG_ON;
|
||||
|
||||
_Restore.Location = Location;
|
||||
_Restore.Width = Width;
|
||||
@@ -261,19 +260,14 @@ namespace Vocaluxe.Lib.Draw
|
||||
}
|
||||
else
|
||||
_DoResize();
|
||||
|
||||
CConfig.SaveConfig();
|
||||
}
|
||||
|
||||
private void _LeaveFullScreen()
|
||||
{
|
||||
_Fullscreen = false;
|
||||
CConfig.FullScreen = EOffOn.TR_CONFIG_OFF;
|
||||
|
||||
FormBorderStyle = FormBorderStyle.Sizable;
|
||||
DesktopBounds = new Rectangle(_Restore.Location, new Size(_Restore.Width, _Restore.Height));
|
||||
|
||||
CConfig.SaveConfig();
|
||||
}
|
||||
#endregion Helpers
|
||||
|
||||
@@ -433,10 +427,7 @@ namespace Vocaluxe.Lib.Draw
|
||||
Show();
|
||||
|
||||
if (CConfig.FullScreen == EOffOn.TR_CONFIG_ON)
|
||||
{
|
||||
CSettings.IsFullScreen = true;
|
||||
_EnterFullScreen();
|
||||
}
|
||||
|
||||
while (_Run)
|
||||
{
|
||||
@@ -448,7 +439,7 @@ namespace Vocaluxe.Lib.Draw
|
||||
|
||||
_Control.SwapBuffers();
|
||||
|
||||
if (CSettings.IsFullScreen != _Fullscreen)
|
||||
if ((CConfig.FullScreen == EOffOn.TR_CONFIG_ON) != _Fullscreen)
|
||||
_ToggleFullScreen();
|
||||
|
||||
_CheckQueue();
|
||||
|
||||
@@ -15,51 +15,63 @@
|
||||
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using VocaluxeLib;
|
||||
|
||||
namespace Vocaluxe.Lib.Input
|
||||
{
|
||||
public class CControllerFramework : IController
|
||||
public abstract class CControllerFramework : IController
|
||||
{
|
||||
private List<SKeyEvent> _KeysPool;
|
||||
protected bool _Initialized;
|
||||
private readonly List<SKeyEvent> _KeysPool = new List<SKeyEvent>();
|
||||
private List<SKeyEvent> _CurrentKeysPool;
|
||||
private readonly Object _KeyCopyLock = new Object();
|
||||
|
||||
private List<SMouseEvent> _MousePool;
|
||||
private readonly List<SMouseEvent> _MousePool = new List<SMouseEvent>();
|
||||
private List<SMouseEvent> _CurrentMousePool;
|
||||
private readonly Object _MouseCopyLock = new Object();
|
||||
|
||||
public virtual void Init()
|
||||
public abstract string GetName();
|
||||
|
||||
public virtual bool Init()
|
||||
{
|
||||
_KeysPool = new List<SKeyEvent>();
|
||||
if (_Initialized)
|
||||
return false;
|
||||
_CurrentKeysPool = new List<SKeyEvent>();
|
||||
_MousePool = new List<SMouseEvent>();
|
||||
_CurrentMousePool = new List<SMouseEvent>();
|
||||
}
|
||||
|
||||
public virtual void Close() {}
|
||||
|
||||
public virtual void Connect() {}
|
||||
|
||||
public virtual void Disconnect() {}
|
||||
|
||||
public virtual bool IsConnected()
|
||||
{
|
||||
_Initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual void Close()
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
_CurrentKeysPool = null;
|
||||
_CurrentMousePool = null;
|
||||
lock (_KeysPool)
|
||||
_KeysPool.Clear();
|
||||
lock (_MousePool)
|
||||
_MousePool.Clear();
|
||||
_Initialized = false;
|
||||
}
|
||||
|
||||
public abstract void Connect();
|
||||
|
||||
public abstract void Disconnect();
|
||||
|
||||
public abstract bool IsConnected();
|
||||
|
||||
public virtual void Update()
|
||||
{
|
||||
lock (_KeyCopyLock)
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_KeysPool)
|
||||
{
|
||||
foreach (SKeyEvent e in _KeysPool)
|
||||
_CurrentKeysPool.Add(e);
|
||||
_KeysPool.Clear();
|
||||
}
|
||||
|
||||
lock (_MouseCopyLock)
|
||||
lock (_MousePool)
|
||||
{
|
||||
foreach (SMouseEvent e in _MousePool)
|
||||
_CurrentMousePool.Add(e);
|
||||
@@ -69,6 +81,8 @@ namespace Vocaluxe.Lib.Input
|
||||
|
||||
public virtual bool PollKeyEvent(ref SKeyEvent keyEvent)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return false;
|
||||
if (_CurrentKeysPool.Count > 0)
|
||||
{
|
||||
keyEvent = _CurrentKeysPool[0];
|
||||
@@ -80,6 +94,8 @@ namespace Vocaluxe.Lib.Input
|
||||
|
||||
public virtual bool PollMouseEvent(ref SMouseEvent mouseEvent)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return false;
|
||||
if (_CurrentMousePool.Count > 0)
|
||||
{
|
||||
mouseEvent = _CurrentMousePool[0];
|
||||
@@ -89,11 +105,13 @@ namespace Vocaluxe.Lib.Input
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual void SetRumble(float duration) {}
|
||||
public abstract void SetRumble(float duration);
|
||||
|
||||
public void AddKeyEvent(SKeyEvent keyEvent)
|
||||
{
|
||||
lock (_KeyCopyLock)
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_KeysPool)
|
||||
{
|
||||
_KeysPool.Add(keyEvent);
|
||||
}
|
||||
@@ -101,6 +119,8 @@ namespace Vocaluxe.Lib.Input
|
||||
|
||||
public void AddMouseEvent(SMouseEvent mouseEvent)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
lock (_MousePool)
|
||||
{
|
||||
_MousePool.Add(mouseEvent);
|
||||
|
||||
@@ -32,7 +32,8 @@ namespace Vocaluxe.Lib.Input
|
||||
|
||||
interface IController
|
||||
{
|
||||
void Init();
|
||||
string GetName();
|
||||
bool Init();
|
||||
void Close();
|
||||
void Connect();
|
||||
void Disconnect();
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
@@ -25,99 +24,79 @@ using VocaluxeLib;
|
||||
|
||||
namespace Vocaluxe.Lib.Input.WiiMote
|
||||
{
|
||||
class CWiiMote : IController
|
||||
class CWiiMote : CControllerFramework
|
||||
{
|
||||
private CWiiMoteLib _WiiMote;
|
||||
|
||||
private List<SKeyEvent> _KeysPool;
|
||||
private List<SKeyEvent> _CurrentKeysPool;
|
||||
private readonly Object _KeyCopyLock = new Object();
|
||||
|
||||
private List<SMouseEvent> _MousePool;
|
||||
private List<SMouseEvent> _CurrentMousePool;
|
||||
private readonly Object _MouseCopyLock = new Object();
|
||||
|
||||
private bool[] _ButtonStates;
|
||||
private Point _OldPosition;
|
||||
private bool _Connected;
|
||||
|
||||
private Thread _HandlerThread;
|
||||
private AutoResetEvent _EvTerminate;
|
||||
private Object _Sync;
|
||||
private bool _Active;
|
||||
private CRumbleTimer _RumbleTimer;
|
||||
|
||||
private CGesture _Gesture;
|
||||
|
||||
public void Init()
|
||||
public override string GetName()
|
||||
{
|
||||
return "WiiMote";
|
||||
}
|
||||
|
||||
public override bool Init()
|
||||
{
|
||||
if (!base.Init())
|
||||
return false;
|
||||
_Sync = new Object();
|
||||
_RumbleTimer = new CRumbleTimer();
|
||||
_Gesture = new CGesture();
|
||||
|
||||
_HandlerThread = new Thread(_MainLoop) {Name = "WiiMote", Priority = ThreadPriority.BelowNormal};
|
||||
|
||||
_KeysPool = new List<SKeyEvent>();
|
||||
_CurrentKeysPool = new List<SKeyEvent>();
|
||||
|
||||
_MousePool = new List<SMouseEvent>();
|
||||
_CurrentMousePool = new List<SMouseEvent>();
|
||||
_EvTerminate = new AutoResetEvent(false);
|
||||
|
||||
_ButtonStates = new bool[11];
|
||||
_OldPosition = new Point();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
public override void Close()
|
||||
{
|
||||
_Active = false;
|
||||
_WiiMote.Disconnect();
|
||||
if (_HandlerThread != null)
|
||||
{
|
||||
//Join before freeing stuff
|
||||
//This also ensures, that no other thread is created till the current one is terminated
|
||||
_EvTerminate.Set();
|
||||
_HandlerThread.Join();
|
||||
_HandlerThread = null;
|
||||
_EvTerminate.Dispose();
|
||||
_EvTerminate = null;
|
||||
}
|
||||
base.Close();
|
||||
}
|
||||
|
||||
public void Connect()
|
||||
public override void Connect()
|
||||
{
|
||||
if (_Active)
|
||||
if (_Active || _HandlerThread == null)
|
||||
return;
|
||||
_Active = true;
|
||||
_HandlerThread.Start();
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
public override void Disconnect()
|
||||
{
|
||||
_Active = false;
|
||||
//TODO: Allow reconnect
|
||||
Close();
|
||||
}
|
||||
|
||||
public bool IsConnected()
|
||||
public override bool IsConnected()
|
||||
{
|
||||
return _Connected;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
_CopyEvents();
|
||||
}
|
||||
|
||||
public bool PollKeyEvent(ref SKeyEvent keyEvent)
|
||||
{
|
||||
if (_CurrentKeysPool.Count > 0)
|
||||
{
|
||||
keyEvent = _CurrentKeysPool[0];
|
||||
_CurrentKeysPool.RemoveAt(0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool PollMouseEvent(ref SMouseEvent mouseEvent)
|
||||
{
|
||||
if (_CurrentMousePool.Count > 0)
|
||||
{
|
||||
mouseEvent = _CurrentMousePool[0];
|
||||
_CurrentMousePool.RemoveAt(0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SetRumble(float duration)
|
||||
public override void SetRumble(float duration)
|
||||
{
|
||||
lock (_Sync)
|
||||
{
|
||||
@@ -137,7 +116,7 @@ namespace Vocaluxe.Lib.Input.WiiMote
|
||||
if (!_WiiMote.Connected)
|
||||
{
|
||||
if (!_DoConnect())
|
||||
Thread.Sleep(1000);
|
||||
_EvTerminate.WaitOne(1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -243,14 +222,7 @@ namespace Vocaluxe.Lib.Input.WiiMote
|
||||
|
||||
|
||||
if (key != Keys.None)
|
||||
{
|
||||
var pool = new SKeyEvent(ESender.WiiMote, false, false, false, false, char.MinValue, key);
|
||||
|
||||
lock (_KeyCopyLock)
|
||||
{
|
||||
_KeysPool.Add(pool);
|
||||
}
|
||||
}
|
||||
AddKeyEvent(new SKeyEvent(ESender.WiiMote, false, false, false, false, char.MinValue, key));
|
||||
|
||||
//mouse events
|
||||
const float reducing = 0.15f;
|
||||
@@ -270,48 +242,13 @@ namespace Vocaluxe.Lib.Input.WiiMote
|
||||
if (gesture == EGesture.ScrollDown)
|
||||
wheel = 1;
|
||||
|
||||
var mpool = new SMouseEvent();
|
||||
bool trigger = false;
|
||||
|
||||
if (!lb && !rb && (p.X != _OldPosition.X || p.Y != _OldPosition.Y))
|
||||
{
|
||||
mpool = new SMouseEvent(ESender.WiiMote, EModifier.None, x, y, false, false, false, wheel, lbh, false, false, false);
|
||||
trigger = true;
|
||||
}
|
||||
AddMouseEvent(new SMouseEvent(ESender.WiiMote, EModifier.None, x, y, false, false, false, wheel, lbh, false, false, false));
|
||||
else if (lb || rb)
|
||||
{
|
||||
mpool = new SMouseEvent(ESender.WiiMote, EModifier.None, x, y, lb, false, rb, wheel, false, false, false, false);
|
||||
trigger = true;
|
||||
}
|
||||
|
||||
|
||||
if (trigger)
|
||||
{
|
||||
lock (_MouseCopyLock)
|
||||
{
|
||||
_MousePool.Add(mpool);
|
||||
}
|
||||
}
|
||||
AddMouseEvent(new SMouseEvent(ESender.WiiMote, EModifier.None, x, y, lb, false, rb, wheel, false, false, false, false));
|
||||
|
||||
_OldPosition.X = p.X;
|
||||
_OldPosition.Y = p.Y;
|
||||
}
|
||||
|
||||
private void _CopyEvents()
|
||||
{
|
||||
lock (_KeyCopyLock)
|
||||
{
|
||||
foreach (SKeyEvent e in _KeysPool)
|
||||
_CurrentKeysPool.Add(e);
|
||||
_KeysPool.Clear();
|
||||
}
|
||||
|
||||
lock (_MouseCopyLock)
|
||||
{
|
||||
foreach (SMouseEvent e in _MousePool)
|
||||
_CurrentMousePool.Add(e);
|
||||
_MousePool.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,14 +63,14 @@ namespace Vocaluxe.Lib.Playlist
|
||||
filename = "1";
|
||||
|
||||
int i = 0;
|
||||
while (File.Exists(Path.Combine(CSettings.DataPath, CSettings.FolderPlaylists, filename + ".xml")))
|
||||
while (File.Exists(Path.Combine(CSettings.DataPath, CConfig.FolderPlaylists, filename + ".xml")))
|
||||
{
|
||||
i++;
|
||||
if (!File.Exists(Path.Combine(CSettings.DataPath, CSettings.FolderPlaylists, filename + i + ".xml")))
|
||||
if (!File.Exists(Path.Combine(CSettings.DataPath, CConfig.FolderPlaylists, filename + i + ".xml")))
|
||||
filename += i;
|
||||
}
|
||||
|
||||
PlaylistFile = Path.Combine(CSettings.DataPath, CSettings.FolderPlaylists, filename + ".xml");
|
||||
PlaylistFile = Path.Combine(CSettings.DataPath, CConfig.FolderPlaylists, filename + ".xml");
|
||||
}
|
||||
|
||||
XmlWriter writer;
|
||||
|
||||
@@ -1,209 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound
|
||||
{
|
||||
class CBuffer : IDisposable
|
||||
{
|
||||
private const double _BaseToneFreq = 65.4064;
|
||||
public const int NumHalfTones = 17;
|
||||
|
||||
private const int _AnalysisBufLen = 4096;
|
||||
private const int _AnalysisByteBufLen = _AnalysisBufLen * 2;
|
||||
|
||||
private readonly Int16[] _AnalysisBuffer = new Int16[_AnalysisBufLen];
|
||||
private readonly byte[] _AnalysisByteBuffer = new byte[_AnalysisByteBufLen]; //tmp buffer (stream->tmpBuffer->(Int16)AnalysisBuffer)
|
||||
|
||||
private static readonly int[] _SamplesPerPeriodPerTone = new int[NumHalfTones]; //Precalculated table holding sample# per period for each tone
|
||||
private readonly float[] _TmpWeight = new float[NumHalfTones]; //tmp buffer for weights (gets copied to ToneWeight when checked)
|
||||
|
||||
private double _MaxVolume;
|
||||
private bool _NewSamples;
|
||||
|
||||
private MemoryStream _Stream = new MemoryStream(); // full buffer
|
||||
|
||||
public CBuffer()
|
||||
{
|
||||
ToneWeigth = new float[NumHalfTones];
|
||||
for (int i = 0; i < ToneWeigth.Length; i++)
|
||||
ToneWeigth[i] = 0.99f;
|
||||
}
|
||||
|
||||
static CBuffer()
|
||||
{
|
||||
const double halftoneBase = 1.05946309436; // 2^(1/12) -> HalftoneBase^12 = 2 (one octave)
|
||||
//Init Array to avoid costly calculations
|
||||
for (int toneIndex = 0; toneIndex < NumHalfTones; toneIndex++)
|
||||
{
|
||||
double freq = _BaseToneFreq * Math.Pow(halftoneBase, toneIndex);
|
||||
_SamplesPerPeriodPerTone[toneIndex] = (int)Math.Round(44100.0 / freq); // samples in one period
|
||||
}
|
||||
}
|
||||
|
||||
public int ToneAbs { get; private set; }
|
||||
|
||||
public int Tone { get; set; }
|
||||
|
||||
public float MaxVolume
|
||||
{
|
||||
get { return (float)_MaxVolume; }
|
||||
}
|
||||
|
||||
public bool ToneValid { get; private set; }
|
||||
|
||||
public float[] ToneWeigth { get; private set; }
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
lock (_Stream)
|
||||
{
|
||||
_Stream.SetLength(0L);
|
||||
_NewSamples = false;
|
||||
}
|
||||
ToneValid = false;
|
||||
ToneAbs = 0;
|
||||
Tone = 0;
|
||||
}
|
||||
|
||||
private void _Add(byte[] bytes)
|
||||
{
|
||||
_Stream.Write(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
public void ProcessNewBuffer(byte[] buffer)
|
||||
{
|
||||
// apply software boost
|
||||
//BoostBuffer(Buffer, BufferSize);
|
||||
|
||||
// voice passthrough (send data to playback-device)
|
||||
//if (assigned(fVoiceStream)) then
|
||||
//fVoiceStream.WriteData(Buffer, BufferSize);
|
||||
lock (_Stream)
|
||||
{
|
||||
_Add(buffer);
|
||||
_NewSamples = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void AnalyzeBuffer()
|
||||
{
|
||||
if (!_NewSamples)
|
||||
return;
|
||||
|
||||
lock (_Stream)
|
||||
{
|
||||
if (_Stream.Length >= _AnalysisByteBufLen)
|
||||
{
|
||||
_Stream.Position -= _AnalysisByteBufLen;
|
||||
_Stream.Read(_AnalysisByteBuffer, 0, _AnalysisByteBufLen);
|
||||
}
|
||||
_NewSamples = false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _AnalysisBufLen; i++)
|
||||
_AnalysisBuffer[i] = BitConverter.ToInt16(_AnalysisByteBuffer, i * 2);
|
||||
|
||||
try
|
||||
{
|
||||
// find maximum volume
|
||||
_FindMaxVolume();
|
||||
|
||||
if (MaxVolume >= 0.02)
|
||||
_AnalyzeByAutocorrelation();
|
||||
else
|
||||
ToneValid = false;
|
||||
}
|
||||
catch (Exception) {}
|
||||
}
|
||||
|
||||
private void _FindMaxVolume()
|
||||
{
|
||||
short maxVolume = 0;
|
||||
for (int i = 0; i < _AnalysisBuffer.Length / 4; i++)
|
||||
{
|
||||
short volume = _AnalysisBuffer[i];
|
||||
if (volume < 0)
|
||||
{
|
||||
if (volume == Int16.MinValue)
|
||||
{
|
||||
maxVolume = Int16.MaxValue;
|
||||
break;
|
||||
}
|
||||
volume = (short)-volume;
|
||||
}
|
||||
if (volume > maxVolume)
|
||||
maxVolume = volume;
|
||||
}
|
||||
_MaxVolume = (double)maxVolume / Int16.MaxValue;
|
||||
}
|
||||
|
||||
private void _AnalyzeByAutocorrelation()
|
||||
{
|
||||
// prepare to analyze
|
||||
double maxWeight = -1.0;
|
||||
double minWeight = 1.0;
|
||||
int maxTone = -1;
|
||||
|
||||
// analyze halftones
|
||||
// Note: at the lowest tone (~65Hz) and a buffer-size of 4096
|
||||
// at 44.1 (or 48kHz) only 6 (or 5) samples are compared, this might be
|
||||
// too few samples -> use a bigger buffer-size
|
||||
for (int toneIndex = 0; toneIndex < NumHalfTones; toneIndex++)
|
||||
{
|
||||
double curWeight = _AnalyzeAutocorrelationTone(toneIndex);
|
||||
|
||||
if (curWeight > maxWeight)
|
||||
{
|
||||
maxWeight = curWeight;
|
||||
maxTone = toneIndex;
|
||||
}
|
||||
|
||||
if (curWeight < minWeight)
|
||||
minWeight = curWeight;
|
||||
|
||||
_TmpWeight[toneIndex] = (float)curWeight;
|
||||
}
|
||||
|
||||
if (maxWeight - minWeight > 0.01)
|
||||
{
|
||||
Array.Copy(_TmpWeight, ToneWeigth, NumHalfTones);
|
||||
|
||||
ToneAbs = maxTone;
|
||||
Tone = maxTone % 12;
|
||||
ToneValid = true;
|
||||
}
|
||||
else
|
||||
ToneValid = false;
|
||||
}
|
||||
|
||||
private double _AnalyzeAutocorrelationTone(int tone)
|
||||
{
|
||||
int samplesPerPeriod = _SamplesPerPeriodPerTone[tone]; // samples in one period
|
||||
|
||||
int accumDist = 0; // accumulated distances
|
||||
|
||||
// compare correlating samples
|
||||
int sampleIndex = 0; // index of sample to analyze
|
||||
// Start value= index of sample one period ahead
|
||||
for (int correlatingSampleIndex = sampleIndex + samplesPerPeriod; correlatingSampleIndex < _AnalysisBufLen; correlatingSampleIndex++, sampleIndex++)
|
||||
{
|
||||
// calc distance (correlation: 1-dist/IntMax) to corresponding sample in next period
|
||||
// distance (0=equal .. IntMax=totally different) between correlated samples
|
||||
int dist = Math.Abs(_AnalysisBuffer[sampleIndex] - _AnalysisBuffer[correlatingSampleIndex]);
|
||||
accumDist += dist;
|
||||
}
|
||||
|
||||
return 1.0 - (double)accumDist / Int16.MaxValue / _AnalysisBufLen;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_Stream != null)
|
||||
{
|
||||
_Stream.Dispose();
|
||||
_Stream = null;
|
||||
}
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,415 +0,0 @@
|
||||
#region license
|
||||
// This file is part of Vocaluxe.
|
||||
//
|
||||
// Vocaluxe 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Vocaluxe 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 Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
using SlimDX.DirectSound;
|
||||
using SlimDX.Multimedia;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Vocaluxe.Base;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound
|
||||
{
|
||||
class CDirectSoundRecord : IRecord
|
||||
{
|
||||
private bool _Initialized;
|
||||
private List<CRecordDevice> _Devices;
|
||||
private List<CSoundCardSource> _Sources;
|
||||
|
||||
private readonly CBuffer[] _Buffer;
|
||||
|
||||
public CDirectSoundRecord()
|
||||
{
|
||||
_Buffer = new CBuffer[CSettings.MaxNumPlayer];
|
||||
for (int i = 0; i < _Buffer.Length; i++)
|
||||
_Buffer[i] = new CBuffer();
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
public bool Init()
|
||||
{
|
||||
DeviceCollection devices = DirectSoundCapture.GetDevices();
|
||||
_Devices = new List<CRecordDevice>();
|
||||
_Sources = new List<CSoundCardSource>();
|
||||
|
||||
int id = 0;
|
||||
foreach (DeviceInformation dev in devices)
|
||||
{
|
||||
using (var ds = new DirectSoundCapture(dev.DriverGuid))
|
||||
{
|
||||
var device = new CRecordDevice {Driver = dev.DriverGuid.ToString(), ID = id, Name = dev.Description, Channels = ds.Capabilities.Channels};
|
||||
|
||||
if (device.Channels > 2)
|
||||
device.Channels = 2; //more are not supported in vocaluxe
|
||||
|
||||
_Devices.Add(device);
|
||||
|
||||
id++;
|
||||
}
|
||||
}
|
||||
|
||||
_Initialized = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void CloseAll()
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
Stop();
|
||||
_Initialized = false;
|
||||
}
|
||||
//System.IO.File.WriteAllBytes("test0.raw", _Buffer[0].Buffer);
|
||||
}
|
||||
|
||||
public bool Start()
|
||||
{
|
||||
if (!_Initialized)
|
||||
return false;
|
||||
|
||||
foreach (CBuffer buffer in _Buffer)
|
||||
buffer.Reset();
|
||||
|
||||
var active = new bool[_Devices.Count];
|
||||
var guid = new Guid[_Devices.Count];
|
||||
var channels = new short[_Devices.Count];
|
||||
for (int dev = 0; dev < _Devices.Count; dev++)
|
||||
{
|
||||
active[dev] = false;
|
||||
if (_Devices[dev].PlayerChannel1 > 0 || _Devices[dev].PlayerChannel2 > 0)
|
||||
active[dev] = true;
|
||||
guid[dev] = new Guid(_Devices[dev].Driver);
|
||||
channels[dev] = (short)_Devices[dev].Channels;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _Devices.Count; i++)
|
||||
{
|
||||
if (active[i])
|
||||
{
|
||||
var source = new CSoundCardSource(guid[i], channels[i]) {SampleRateKhz = 44.1};
|
||||
source.SampleDataReady += _OnDataReady;
|
||||
source.Start();
|
||||
|
||||
_Sources.Add(source);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Stop()
|
||||
{
|
||||
if (!_Initialized)
|
||||
return false;
|
||||
|
||||
foreach (CSoundCardSource source in _Sources)
|
||||
{
|
||||
source.Stop();
|
||||
source.Dispose();
|
||||
}
|
||||
_Sources.Clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void AnalyzeBuffer(int player)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
|
||||
_Buffer[player].AnalyzeBuffer();
|
||||
}
|
||||
|
||||
public int GetToneAbs(int player)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return 0;
|
||||
|
||||
return _Buffer[player].ToneAbs;
|
||||
}
|
||||
|
||||
public int GetTone(int player)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return 0;
|
||||
|
||||
return _Buffer[player].Tone;
|
||||
}
|
||||
|
||||
public void SetTone(int player, int tone)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
|
||||
_Buffer[player].Tone = tone;
|
||||
}
|
||||
|
||||
public float GetMaxVolume(int player)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return 0f;
|
||||
|
||||
return _Buffer[player].MaxVolume;
|
||||
}
|
||||
|
||||
public bool ToneValid(int player)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return false;
|
||||
|
||||
return _Buffer[player].ToneValid;
|
||||
}
|
||||
|
||||
public ReadOnlyCollection<CRecordDevice> RecordDevices()
|
||||
{
|
||||
if (!_Initialized)
|
||||
return null;
|
||||
|
||||
if (_Devices.Count == 0)
|
||||
return null;
|
||||
|
||||
return _Devices.AsReadOnly();
|
||||
}
|
||||
|
||||
public int NumHalfTones(int player)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return 0;
|
||||
|
||||
return CBuffer.NumHalfTones;
|
||||
}
|
||||
|
||||
public float[] ToneWeigth(int player)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return null;
|
||||
|
||||
return _Buffer[player].ToneWeigth;
|
||||
}
|
||||
|
||||
private void _OnDataReady(object sender, CSampleDataEventArgs e)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
var leftBuffer = new byte[e.Data.Length / 2];
|
||||
var rightBuffer = new byte[e.Data.Length / 2];
|
||||
|
||||
//[]: Sample, L: Left channel R: Right channel
|
||||
//[LR][LR][LR][LR][LR][LR]
|
||||
//The data is interleaved and needs to be demultiplexed
|
||||
for (int i = 0; i < e.Data.Length / 2; i++)
|
||||
{
|
||||
leftBuffer[i] = e.Data[i * 2 - (i % 2)];
|
||||
rightBuffer[i] = e.Data[i * 2 - (i % 2) + 2];
|
||||
}
|
||||
|
||||
foreach (CRecordDevice device in _Devices)
|
||||
{
|
||||
if (device.Driver == e.Guid.ToString())
|
||||
{
|
||||
if (device.PlayerChannel1 > 0)
|
||||
_Buffer[device.PlayerChannel1 - 1].ProcessNewBuffer(leftBuffer);
|
||||
|
||||
if (device.PlayerChannel2 > 0)
|
||||
_Buffer[device.PlayerChannel2 - 1].ProcessNewBuffer(rightBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class CSampleDataEventArgs : EventArgs
|
||||
{
|
||||
public CSampleDataEventArgs(byte[] data, Guid guid)
|
||||
{
|
||||
Data = data;
|
||||
Guid = guid;
|
||||
}
|
||||
|
||||
public byte[] Data { get; private set; }
|
||||
public Guid Guid { get; private set; }
|
||||
}
|
||||
|
||||
private class CSoundCardSource : IDisposable
|
||||
{
|
||||
private volatile bool _Running;
|
||||
private readonly int _BufferSize;
|
||||
private CaptureBuffer _CaptureBuffer;
|
||||
private CaptureBufferDescription _BufferDescription;
|
||||
private DirectSoundCapture _CaptureDevice;
|
||||
private readonly WaveFormat _WaveFormat;
|
||||
private Thread _CaptureThread;
|
||||
private List<NotificationPosition> _Notifications;
|
||||
private int _BufferPortionCount;
|
||||
private int _BufferPortionSize;
|
||||
private WaitHandle[] _WaitHandles;
|
||||
private double _SampleRate;
|
||||
private readonly Guid _Guid;
|
||||
private readonly short _Channels;
|
||||
|
||||
public CSoundCardSource(Guid guid, short channels)
|
||||
{
|
||||
_Guid = guid;
|
||||
_Channels = channels;
|
||||
_WaveFormat = new WaveFormat();
|
||||
SampleRateKhz = 44.1;
|
||||
_BufferSize = 2048;
|
||||
}
|
||||
|
||||
public event EventHandler<CSampleDataEventArgs> SampleDataReady = delegate { };
|
||||
|
||||
public double SampleRateKhz
|
||||
{
|
||||
get { return _SampleRate; }
|
||||
|
||||
set
|
||||
{
|
||||
_SampleRate = value;
|
||||
|
||||
if (_Running)
|
||||
Restart();
|
||||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (_Running)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
if (_CaptureDevice == null)
|
||||
_CaptureDevice = new DirectSoundCapture(_Guid);
|
||||
|
||||
_WaveFormat.FormatTag = WaveFormatTag.Pcm; // Change to WaveFormatTag.IeeeFloat for float
|
||||
_WaveFormat.BitsPerSample = 16; // Set this to 32 for float
|
||||
_WaveFormat.BlockAlignment = (short)(_Channels * (_WaveFormat.BitsPerSample / 8));
|
||||
_WaveFormat.Channels = _Channels;
|
||||
_WaveFormat.SamplesPerSecond = (int)(SampleRateKhz * 1000D);
|
||||
_WaveFormat.AverageBytesPerSecond =
|
||||
_WaveFormat.SamplesPerSecond *
|
||||
_WaveFormat.BlockAlignment;
|
||||
|
||||
_BufferPortionCount = 2;
|
||||
|
||||
_BufferDescription.BufferBytes = _BufferSize * sizeof(short) * _BufferPortionCount * _Channels;
|
||||
_BufferDescription.Format = _WaveFormat;
|
||||
_BufferDescription.WaveMapped = false;
|
||||
|
||||
_CaptureBuffer = new CaptureBuffer(_CaptureDevice, _BufferDescription);
|
||||
|
||||
_BufferPortionSize = _CaptureBuffer.SizeInBytes / _BufferPortionCount;
|
||||
_Notifications = new List<NotificationPosition>();
|
||||
|
||||
for (int i = 0; i < _BufferPortionCount; i++)
|
||||
{
|
||||
var notification = new NotificationPosition {Offset = _BufferPortionCount - 1 + (_BufferPortionSize * i), Event = new AutoResetEvent(false)};
|
||||
_Notifications.Add(notification);
|
||||
}
|
||||
|
||||
_CaptureBuffer.SetNotificationPositions(_Notifications.ToArray());
|
||||
_WaitHandles = new WaitHandle[_Notifications.Count];
|
||||
|
||||
for (int i = 0; i < _Notifications.Count; i++)
|
||||
_WaitHandles[i] = _Notifications[i].Event;
|
||||
|
||||
_CaptureThread = new Thread(_DoCapture) {Name = "DirectSoundCapture", IsBackground = true};
|
||||
|
||||
_Running = true;
|
||||
_CaptureThread.Start();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_Running = false;
|
||||
|
||||
if (_CaptureThread != null)
|
||||
{
|
||||
_CaptureThread.Join();
|
||||
_CaptureThread = null;
|
||||
}
|
||||
|
||||
if (_CaptureBuffer != null)
|
||||
{
|
||||
_CaptureBuffer.Dispose();
|
||||
_CaptureBuffer = null;
|
||||
}
|
||||
|
||||
if (_Notifications != null)
|
||||
{
|
||||
foreach (NotificationPosition notification in _Notifications)
|
||||
notification.Event.Close();
|
||||
|
||||
_Notifications.Clear();
|
||||
_Notifications = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Restart()
|
||||
{
|
||||
Stop();
|
||||
Start();
|
||||
}
|
||||
|
||||
private void _DoCapture()
|
||||
{
|
||||
int bufferPortionSamples = _BufferPortionSize / sizeof(byte);
|
||||
|
||||
// Buffer type must match this.waveFormat.FormatTag and this.waveFormat.BitsPerSample
|
||||
var bufferPortion = new byte[bufferPortionSamples];
|
||||
|
||||
_CaptureBuffer.Start(true);
|
||||
|
||||
while (_Running)
|
||||
{
|
||||
int bufferPortionIndex = WaitHandle.WaitAny(_WaitHandles);
|
||||
|
||||
_CaptureBuffer.Read(
|
||||
bufferPortion,
|
||||
0,
|
||||
bufferPortionSamples,
|
||||
_BufferPortionSize * Math.Abs((bufferPortionIndex - 1) % _BufferPortionCount));
|
||||
|
||||
SampleDataReady(this, new CSampleDataEventArgs(bufferPortion, _Guid));
|
||||
}
|
||||
|
||||
_CaptureBuffer.Stop();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
protected void Dispose(bool disposing)
|
||||
// ReSharper restore InconsistentNaming
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Stop();
|
||||
|
||||
if (_CaptureDevice != null)
|
||||
{
|
||||
_CaptureDevice.Dispose();
|
||||
_CaptureDevice = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,494 +0,0 @@
|
||||
#region license
|
||||
// This file is part of Vocaluxe.
|
||||
//
|
||||
// Vocaluxe 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Vocaluxe 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 Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Gst;
|
||||
using GLib;
|
||||
using System.Runtime.InteropServices;
|
||||
using Vocaluxe.Base;
|
||||
using VocaluxeLib;
|
||||
using Thread = System.Threading.Thread;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound
|
||||
{
|
||||
public class CGstreamerSharpAudio : IPlayback
|
||||
{
|
||||
private readonly Dictionary<int, CGstreamerSharpAudioStream> _Streams = new Dictionary<int, CGstreamerSharpAudioStream>();
|
||||
private static int _IDCount;
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
|
||||
private static extern void SetDllDirectory(string lpPathName);
|
||||
|
||||
public bool Init()
|
||||
{
|
||||
#if ARCH_X86
|
||||
const string path = ".\\x86\\gstreamer";
|
||||
#endif
|
||||
#if ARCH_X64
|
||||
const string path = ".\\x64\\gstreamer";
|
||||
#endif
|
||||
SetDllDirectory(path);
|
||||
Application.Init();
|
||||
Registry reg = Registry.Get();
|
||||
reg.ScanPath(path);
|
||||
|
||||
return Application.IsInitialized;
|
||||
}
|
||||
|
||||
public void SetGlobalVolume(float volume)
|
||||
{
|
||||
foreach (CGstreamerSharpAudioStream stream in _Streams.Values)
|
||||
stream.Volume = volume;
|
||||
}
|
||||
|
||||
public int GetStreamCount()
|
||||
{
|
||||
return _Streams.Count;
|
||||
}
|
||||
|
||||
public void CloseAll()
|
||||
{
|
||||
foreach (CGstreamerSharpAudioStream stream in _Streams.Values)
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
public int Load(string media)
|
||||
{
|
||||
return Load(media, false);
|
||||
}
|
||||
|
||||
public int Load(string media, bool prescan)
|
||||
{
|
||||
var stream = new CGstreamerSharpAudioStream(media, prescan);
|
||||
_Streams[_IDCount] = stream;
|
||||
return _IDCount++;
|
||||
}
|
||||
|
||||
public void Close(int stream)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
_Streams[stream].Close();
|
||||
}
|
||||
|
||||
public void Play(int stream)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
_Streams[stream].Playing = true;
|
||||
}
|
||||
|
||||
public void Play(int stream, bool loop)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
Play(stream, loop);
|
||||
}
|
||||
|
||||
public void Pause(int stream)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
_Streams[stream].Paused = true;
|
||||
}
|
||||
|
||||
public void Stop(int stream)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
_Streams[stream].Stop();
|
||||
}
|
||||
|
||||
public void Fade(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
_Streams[stream].Fade(targetVolume, seconds);
|
||||
}
|
||||
|
||||
public void FadeAndPause(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
_Streams[stream].FadeAndPause(targetVolume, seconds);
|
||||
}
|
||||
|
||||
public void FadeAndClose(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
_Streams[stream].FadeAndClose(targetVolume, seconds);
|
||||
}
|
||||
|
||||
public void FadeAndStop(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
_Streams[stream].FadeAndStop(targetVolume, seconds);
|
||||
}
|
||||
|
||||
public void SetStreamVolume(int stream, float volume)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
_Streams[stream].Volume = volume;
|
||||
}
|
||||
|
||||
public void SetStreamVolumeMax(int stream, float volume)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
_Streams[stream].MaxVolume = volume;
|
||||
}
|
||||
|
||||
public float GetLength(int stream)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return -1;
|
||||
return _Streams[stream].Length;
|
||||
}
|
||||
|
||||
public float GetPosition(int stream)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return -1f;
|
||||
return _Streams[stream].Position;
|
||||
}
|
||||
|
||||
public bool IsPlaying(int stream)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return false;
|
||||
return _Streams[stream].Playing;
|
||||
}
|
||||
|
||||
public bool IsPaused(int stream)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return false;
|
||||
return _Streams[stream].Paused;
|
||||
}
|
||||
|
||||
public bool IsFinished(int stream)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return true;
|
||||
return _Streams[stream].Finished;
|
||||
}
|
||||
|
||||
public void SetPosition(int stream, float position)
|
||||
{
|
||||
if (!_Streams.ContainsKey(stream))
|
||||
return;
|
||||
_Streams[stream].Position = position;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
var streamsToDelete = new List<int>();
|
||||
foreach (KeyValuePair<int, CGstreamerSharpAudioStream> stream in _Streams)
|
||||
{
|
||||
if (stream.Value.Closed)
|
||||
streamsToDelete.Add(stream.Key);
|
||||
stream.Value.Update();
|
||||
}
|
||||
foreach (int key in streamsToDelete)
|
||||
_Streams.Remove(key);
|
||||
}
|
||||
|
||||
private class CGstreamerSharpAudioStream
|
||||
{
|
||||
private Element _Element;
|
||||
private bool _Loop;
|
||||
public volatile bool Closed = true;
|
||||
public volatile bool Finished;
|
||||
|
||||
private CFading _Fading;
|
||||
private EStreamAction _AfterFadeAction;
|
||||
private float _Volume = 1f;
|
||||
|
||||
private float _MaxVolume = 1f;
|
||||
|
||||
private volatile float _Duration = -1f;
|
||||
private volatile float _Position;
|
||||
private volatile bool _QueryingDuration;
|
||||
|
||||
public CGstreamerSharpAudioStream(string media, bool prescan)
|
||||
{
|
||||
Element convert = ElementFactory.Make("audioconvert", "convert");
|
||||
Element audiosink = ElementFactory.Make("directsoundsink", "audiosink");
|
||||
var audioSinkBin = new Bin("Audiosink");
|
||||
|
||||
if (convert == null || audiosink == null)
|
||||
{
|
||||
CLog.LogError("Could not create pipeline");
|
||||
return;
|
||||
}
|
||||
|
||||
audioSinkBin.Add(convert);
|
||||
audioSinkBin.Add(audiosink);
|
||||
convert.Link(audiosink);
|
||||
Pad pad = convert.GetStaticPad("sink");
|
||||
GhostPad ghostpad = new GhostPad("sink", pad);
|
||||
|
||||
if (pad == null)
|
||||
{
|
||||
CLog.LogError("Could not create pads");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ghostpad.SetActive(true))
|
||||
{
|
||||
CLog.LogError("Could not link pads");
|
||||
return;
|
||||
}
|
||||
if (!audioSinkBin.AddPad(ghostpad))
|
||||
{
|
||||
CLog.LogError("Could not add pad");
|
||||
return;
|
||||
}
|
||||
|
||||
_Element = ElementFactory.Make("playbin", "playbin");
|
||||
if (_Element == null)
|
||||
{
|
||||
CLog.LogError("Could not create playbin");
|
||||
return;
|
||||
}
|
||||
_Element["audio-sink"] = audioSinkBin;
|
||||
_Element["flags"] = 1 << 1;
|
||||
_Element["uri"] = new Uri(media).AbsoluteUri;
|
||||
_Element.SetState(State.Paused);
|
||||
|
||||
Closed = false; //Enable stream
|
||||
|
||||
// Passing CLOCK_TIME_NONE here causes the pipeline to block for a long time so with
|
||||
// prescan enabled the pipeline will wait 500ms for stream to initialize and then continue
|
||||
// if it takes more than 500ms, duration queries will be performed asynchronously
|
||||
if (prescan)
|
||||
{
|
||||
Message msg = _Element.Bus.TimedPopFiltered(0xffffffffffffffff, MessageType.AsyncDone);
|
||||
if (msg.Handle != IntPtr.Zero)
|
||||
_UpdateDuration();
|
||||
}
|
||||
}
|
||||
|
||||
private void _OnMessage(Message msg)
|
||||
{
|
||||
if (msg.Handle == IntPtr.Zero)
|
||||
return;
|
||||
switch (msg.Type)
|
||||
{
|
||||
case MessageType.Eos:
|
||||
if (_Loop)
|
||||
Position = 0;
|
||||
else
|
||||
{
|
||||
Finished = true;
|
||||
Close();
|
||||
}
|
||||
break;
|
||||
case MessageType.Error:
|
||||
GException error;
|
||||
string debug;
|
||||
msg.ParseError(out error, out debug);
|
||||
CLog.LogError("Gstreamer error: message" + error.Message + ", code" + error.Code + " ,debug information" + debug);
|
||||
break;
|
||||
case MessageType.DurationChanged:
|
||||
if (!_QueryingDuration)
|
||||
_UpdateDuration();
|
||||
break;
|
||||
}
|
||||
msg.Unref();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (!Closed)
|
||||
{
|
||||
Closed = true;
|
||||
Finished = true;
|
||||
var t = new Thread(_TerminateStream) {Name = "GSt Terminate"};
|
||||
t.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private void _TerminateStream()
|
||||
{
|
||||
if (_Element != null)
|
||||
{
|
||||
_Element.SetState(State.Null);
|
||||
_Element.Dispose();
|
||||
_Element = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Play(bool loop = false)
|
||||
{
|
||||
if (_Element != null)
|
||||
_Element.SetState(State.Playing);
|
||||
_Loop = loop;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (_Element != null)
|
||||
_Element.SetState(State.Ready);
|
||||
Position = 0;
|
||||
}
|
||||
|
||||
public void Fade(float targetVolume, float seconds)
|
||||
{
|
||||
_Fading = new CFading(_Volume, targetVolume, seconds);
|
||||
_AfterFadeAction = EStreamAction.Nothing;
|
||||
}
|
||||
|
||||
public void FadeAndPause(float targetVolume, float seconds)
|
||||
{
|
||||
Fade(targetVolume, seconds);
|
||||
_AfterFadeAction = EStreamAction.Pause;
|
||||
}
|
||||
|
||||
public void FadeAndClose(float targetVolume, float seconds)
|
||||
{
|
||||
Fade(targetVolume, seconds);
|
||||
_AfterFadeAction = EStreamAction.Close;
|
||||
}
|
||||
|
||||
public void FadeAndStop(float targetVolume, float seconds)
|
||||
{
|
||||
Fade(targetVolume, seconds);
|
||||
_AfterFadeAction = EStreamAction.Stop;
|
||||
}
|
||||
|
||||
public float Volume
|
||||
{
|
||||
get { return _Volume * 100f; }
|
||||
set
|
||||
{
|
||||
_Volume = value / 100f;
|
||||
if (_Element != null)
|
||||
_Element["volume"] = (_Volume * _MaxVolume);
|
||||
}
|
||||
}
|
||||
|
||||
public float MaxVolume
|
||||
{
|
||||
get { return _MaxVolume * 100f; }
|
||||
set
|
||||
{
|
||||
_MaxVolume = value / 100f;
|
||||
Volume = _Volume;
|
||||
}
|
||||
}
|
||||
|
||||
public float Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Duration < 0 && !_QueryingDuration)
|
||||
{
|
||||
_QueryingDuration = true; // Set this to avoid race conditions
|
||||
var t = new Thread(_UpdateDuration) {Name = "GSt Update Duration"};
|
||||
t.Start();
|
||||
}
|
||||
return _Duration > 0 ? _Duration : -1;
|
||||
}
|
||||
}
|
||||
|
||||
public float Position
|
||||
{
|
||||
get
|
||||
{
|
||||
long position;
|
||||
if (!_Element.QueryPosition(Format.Time, out position))
|
||||
CLog.LogError("Could not query position");
|
||||
else
|
||||
_Position = (float)(position / (double)Constants.SECOND);
|
||||
return _Position;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_Element != null)
|
||||
_Element.SeekSimple(Format.Time, SeekFlags.Accurate | SeekFlags.Flush, (long)(value * (double)Constants.SECOND));
|
||||
}
|
||||
}
|
||||
|
||||
public bool Paused
|
||||
{
|
||||
get { return _Element == null || _Element.TargetState == State.Paused; }
|
||||
set
|
||||
{
|
||||
if (value && _Element != null)
|
||||
_Element.SetState(State.Paused);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Playing
|
||||
{
|
||||
get { return _Element != null && (_Element.TargetState == State.Playing && !Finished); }
|
||||
set
|
||||
{
|
||||
if (value && _Element != null)
|
||||
_Element.SetState(State.Playing);
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
while (_Element != null && _Element.Bus != null && _Element.Bus.HavePending())
|
||||
_OnMessage(_Element.Bus.Pop());
|
||||
|
||||
if (_Fading != null)
|
||||
{
|
||||
bool finished;
|
||||
Volume = _Fading.GetValue(out finished);
|
||||
if (finished)
|
||||
{
|
||||
switch (_AfterFadeAction)
|
||||
{
|
||||
case EStreamAction.Pause:
|
||||
Paused = true;
|
||||
break;
|
||||
case EStreamAction.Stop:
|
||||
Stop();
|
||||
break;
|
||||
case EStreamAction.Close:
|
||||
Close();
|
||||
break;
|
||||
}
|
||||
_Fading = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void _UpdateDuration()
|
||||
{
|
||||
_QueryingDuration = true;
|
||||
long duration = -1;
|
||||
while (duration < 0 && !Closed && !Finished && _Element != null)
|
||||
{
|
||||
if (_Element.QueryDuration(Format.Time, out duration))
|
||||
{
|
||||
_Duration = duration / (float)Constants.SECOND;
|
||||
_QueryingDuration = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using PortAudioSharp;
|
||||
using Vocaluxe.Base;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound
|
||||
{
|
||||
/// <summary>
|
||||
/// PortAudio can be used for record and playback
|
||||
/// So do some common stuff here and make sure those 2 do not interfere
|
||||
/// Basic lifetime: On Init() get a new handle, close/dispose it in your close/dispose
|
||||
///
|
||||
/// DO NEVER use following Pa_* functions other than the ones from this class:
|
||||
/// Initialize, Terminate, OpenStream, CloseStream
|
||||
/// </summary>
|
||||
class CPortAudioHandle : IDisposable
|
||||
{
|
||||
private static int _RefCount;
|
||||
private static readonly object _Mutex = new object();
|
||||
|
||||
private bool _Disposed;
|
||||
private readonly List<IntPtr> _Streams = new List<IntPtr>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes PortAudio library (if required)
|
||||
/// </summary>
|
||||
public CPortAudioHandle()
|
||||
{
|
||||
lock (_Mutex)
|
||||
{
|
||||
if (_RefCount == 0)
|
||||
{
|
||||
if (CheckError("Initialize", PortAudio.Pa_Initialize()))
|
||||
throw new Exception();
|
||||
}
|
||||
_RefCount++;
|
||||
}
|
||||
}
|
||||
|
||||
~CPortAudioHandle()
|
||||
{
|
||||
_Dispose(false);
|
||||
}
|
||||
|
||||
private void _Dispose(bool disposing)
|
||||
{
|
||||
if (_Disposed)
|
||||
return;
|
||||
if (!disposing)
|
||||
CLog.LogDebug("Did not close CPortAudioHandle");
|
||||
//Make sure we do not leek any streams as we may keep PA open
|
||||
if (_Streams.Count > 0)
|
||||
{
|
||||
CLog.LogDebug("Did not close " + _Streams.Count + "PortAudio-Stream(s)");
|
||||
while (_Streams.Count > 0)
|
||||
CloseStream(_Streams[0]);
|
||||
}
|
||||
lock (_Mutex)
|
||||
{
|
||||
Debug.Assert(_RefCount > 0);
|
||||
_RefCount--;
|
||||
if (_RefCount == 0)
|
||||
PortAudio.Pa_Terminate();
|
||||
}
|
||||
_Disposed = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close the PortAudio handle once you are done
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public PortAudio.PaError OpenStream(out IntPtr stream, ref PortAudio.PaStreamParameters? inputParameters, ref PortAudio.PaStreamParameters? outputParameters,
|
||||
double sampleRate, uint framesPerBuffer, PortAudio.PaStreamFlags streamFlags,
|
||||
PortAudio.PaStreamCallbackDelegate streamCallback, IntPtr userData)
|
||||
{
|
||||
if (_Disposed)
|
||||
throw new ObjectDisposedException("PortAudioHandle already disposed");
|
||||
|
||||
lock (_Mutex)
|
||||
{
|
||||
PortAudio.PaError res = PortAudio.Pa_OpenStream(out stream, ref inputParameters, ref outputParameters, sampleRate, framesPerBuffer, streamFlags, streamCallback,
|
||||
userData);
|
||||
if (res == PortAudio.PaError.paNoError)
|
||||
_Streams.Add(stream);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convenience method to safely open an input stream and log potential error
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="inputParameters"></param>
|
||||
/// <param name="sampleRate"></param>
|
||||
/// <param name="framesPerBuffer"></param>
|
||||
/// <param name="streamFlags"></param>
|
||||
/// <param name="streamCallback"></param>
|
||||
/// <param name="userData"></param>
|
||||
/// <returns>True on success</returns>
|
||||
public bool OpenInputStream(out IntPtr stream, ref PortAudio.PaStreamParameters? inputParameters,
|
||||
double sampleRate, uint framesPerBuffer, PortAudio.PaStreamFlags streamFlags,
|
||||
PortAudio.PaStreamCallbackDelegate streamCallback, IntPtr userData)
|
||||
{
|
||||
PortAudio.PaStreamParameters? outputParameters = null;
|
||||
return
|
||||
!CheckError("OpenInputStream",
|
||||
OpenStream(out stream, ref inputParameters, ref outputParameters, sampleRate, framesPerBuffer, streamFlags, streamCallback, userData));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convenience method to safely open an output stream and log potential error
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="outputParameters"></param>
|
||||
/// <param name="sampleRate"></param>
|
||||
/// <param name="framesPerBuffer"></param>
|
||||
/// <param name="streamFlags"></param>
|
||||
/// <param name="streamCallback"></param>
|
||||
/// <param name="userData"></param>
|
||||
/// <returns>True on success</returns>
|
||||
public bool OpenOutputStream(out IntPtr stream, ref PortAudio.PaStreamParameters? outputParameters,
|
||||
double sampleRate, uint framesPerBuffer, PortAudio.PaStreamFlags streamFlags,
|
||||
PortAudio.PaStreamCallbackDelegate streamCallback, IntPtr userData)
|
||||
{
|
||||
PortAudio.PaStreamParameters? inputParameters = null;
|
||||
return
|
||||
!CheckError("OpenOutputStream",
|
||||
OpenStream(out stream, ref inputParameters, ref outputParameters, sampleRate, framesPerBuffer, streamFlags, streamCallback, userData));
|
||||
}
|
||||
|
||||
public void CloseStream(IntPtr stream)
|
||||
{
|
||||
if (_Disposed)
|
||||
throw new ObjectDisposedException("PortAudioHandle already disposed");
|
||||
|
||||
lock (_Mutex)
|
||||
{
|
||||
PortAudio.Pa_CloseStream(stream);
|
||||
_Streams.Remove(stream);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if PA returned an error and logs it
|
||||
/// Returns true on error
|
||||
/// </summary>
|
||||
/// <param name="action">Action identifier (E.g. openStream)</param>
|
||||
/// <param name="errorCode">Result returned by Pa_* call</param>
|
||||
/// <returns>True on error</returns>
|
||||
public bool CheckError(String action, PortAudio.PaError errorCode)
|
||||
{
|
||||
if (_Disposed)
|
||||
throw new ObjectDisposedException("PortAudioHandle already disposed");
|
||||
|
||||
if (errorCode != PortAudio.PaError.paNoError)
|
||||
{
|
||||
CLog.LogError(action + " error: " + PortAudio.Pa_GetErrorText(errorCode));
|
||||
if (errorCode == PortAudio.PaError.paUnanticipatedHostError)
|
||||
{
|
||||
PortAudio.PaHostErrorInfo errorInfo = PortAudio.Pa_GetLastHostErrorInfo();
|
||||
CLog.LogError("- Host error API type: " + errorInfo.hostApiType);
|
||||
CLog.LogError("- Host error code: " + errorInfo.errorCode);
|
||||
CLog.LogError("- Host error text: " + errorInfo.errorText);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects the most appropriate host api
|
||||
/// </summary>
|
||||
/// <returns>The most appropriate host api</returns>
|
||||
public int GetHostApi()
|
||||
{
|
||||
if (_Disposed)
|
||||
throw new ObjectDisposedException("PortAudioHandle already disposed");
|
||||
|
||||
int selectedHostApi = PortAudio.Pa_GetDefaultHostApi();
|
||||
int apiCount = PortAudio.Pa_GetHostApiCount();
|
||||
for (int i = 0; i < apiCount; i++)
|
||||
{
|
||||
PortAudio.PaHostApiInfo apiInfo = PortAudio.Pa_GetHostApiInfo(i);
|
||||
if ((apiInfo.type == PortAudio.PaHostApiTypeId.paDirectSound)
|
||||
|| (apiInfo.type == PortAudio.PaHostApiTypeId.paALSA))
|
||||
selectedHostApi = i;
|
||||
}
|
||||
return selectedHostApi;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,946 +0,0 @@
|
||||
#region license
|
||||
// This file is part of Vocaluxe.
|
||||
//
|
||||
// Vocaluxe 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Vocaluxe 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 Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using Vocaluxe.Base;
|
||||
using Vocaluxe.Lib.Sound.Decoder;
|
||||
using Vocaluxe.Lib.Sound.PortAudio;
|
||||
using VocaluxeLib;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound
|
||||
{
|
||||
class CPortAudioPlay : IPlayback
|
||||
{
|
||||
private bool _Initialized;
|
||||
private readonly List<CPortAudioStream> _Decoder = new List<CPortAudioStream>();
|
||||
private Closeproc _Closeproc;
|
||||
private int _Count = 1;
|
||||
|
||||
private readonly Object _MutexDecoder = new Object();
|
||||
|
||||
private List<SAudioStreams> _Streams;
|
||||
|
||||
public bool Init()
|
||||
{
|
||||
if (_Initialized)
|
||||
CloseAll();
|
||||
|
||||
_Closeproc = _CloseProc;
|
||||
_Initialized = true;
|
||||
|
||||
_Streams = new List<SAudioStreams>();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void CloseAll()
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
for (int i = 0; i < _Decoder.Count; i++)
|
||||
_Decoder[i].Free(_Closeproc, i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetGlobalVolume(float volume)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
//Bass.BASS_SetVolume(Volume / 100f);
|
||||
}
|
||||
}
|
||||
|
||||
public int GetStreamCount()
|
||||
{
|
||||
if (!_Initialized)
|
||||
return 0;
|
||||
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
return _Streams.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public void Update() {}
|
||||
|
||||
#region Stream Handling
|
||||
public int Load(string media)
|
||||
{
|
||||
return Load(media, false);
|
||||
}
|
||||
|
||||
public int Load(string media, bool prescan)
|
||||
{
|
||||
var stream = new SAudioStreams(0);
|
||||
var decoder = new CPortAudioStream();
|
||||
|
||||
if (decoder.Open(media) > -1)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
_Decoder.Add(decoder);
|
||||
stream.Handle = _Count++;
|
||||
_Streams.Add(stream);
|
||||
return stream.Handle;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void Close(int stream)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Decoder[_GetStreamIndex(stream)].Free(_Closeproc, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Play(int stream)
|
||||
{
|
||||
Play(stream, false);
|
||||
}
|
||||
|
||||
public void Play(int stream, bool loop)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
{
|
||||
_Decoder[_GetStreamIndex(stream)].Loop = loop;
|
||||
_Decoder[_GetStreamIndex(stream)].Play();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Pause(int stream)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Decoder[_GetStreamIndex(stream)].Paused = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop(int stream)
|
||||
{
|
||||
Pause(stream);
|
||||
}
|
||||
|
||||
public void Fade(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Decoder[_GetStreamIndex(stream)].Fade(targetVolume, seconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void FadeAndPause(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Decoder[_GetStreamIndex(stream)].FadeAndPause(targetVolume, seconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void FadeAndClose(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Decoder[_GetStreamIndex(stream)].FadeAndClose(targetVolume, seconds, _Closeproc, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void FadeAndStop(int stream, float targetVolume, float seconds)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Decoder[_GetStreamIndex(stream)].FadeAndStop(targetVolume, seconds, _Closeproc, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetStreamVolume(int stream, float volume)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Decoder[_GetStreamIndex(stream)].Volume = volume;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetStreamVolumeMax(int stream, float volume)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Decoder[_GetStreamIndex(stream)].VolumeMax = volume;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float GetLength(int stream)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
return _Decoder[_GetStreamIndex(stream)].Length;
|
||||
}
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public float GetPosition(int stream)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
return _Decoder[_GetStreamIndex(stream)].Position;
|
||||
}
|
||||
|
||||
return 0f;
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public bool IsPlaying(int stream)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
return !_Decoder[_GetStreamIndex(stream)].Paused && !_Decoder[_GetStreamIndex(stream)].Finished;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsPaused(int stream)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
return _Decoder[_GetStreamIndex(stream)].Paused;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsFinished(int stream)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
return _Decoder[_GetStreamIndex(stream)].Finished;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetPosition(int stream, float position)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(stream))
|
||||
_Decoder[_GetStreamIndex(stream)].Skip(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion Stream Handling
|
||||
|
||||
private bool _AlreadyAdded(int stream)
|
||||
{
|
||||
return _Streams.Any(st => st.Handle == stream);
|
||||
}
|
||||
|
||||
private int _GetStreamIndex(int stream)
|
||||
{
|
||||
for (int i = 0; i < _Streams.Count; i++)
|
||||
{
|
||||
if (_Streams[i].Handle == stream)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void _CloseProc(int streamID)
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
lock (_MutexDecoder)
|
||||
{
|
||||
if (_AlreadyAdded(streamID))
|
||||
{
|
||||
int index = _GetStreamIndex(streamID);
|
||||
_Decoder.RemoveAt(index);
|
||||
_Streams.RemoveAt(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CPortAudioStream : IDisposable
|
||||
{
|
||||
private const long _Bufsize = 1000000L;
|
||||
private const long _Beginrefill = 800000L;
|
||||
|
||||
private readonly CSyncTimer _SyncTimer;
|
||||
private static bool _Initialized;
|
||||
private static int _NumStreams;
|
||||
private static readonly Object _Mutex = new object();
|
||||
private int _ByteCount = 4;
|
||||
private float _Volume = 1f;
|
||||
private float _VolumeMax = 1f;
|
||||
|
||||
private EStreamAction _AfterFadeAction;
|
||||
private CFading _Fading;
|
||||
|
||||
private static CPortAudio.SPaHostApiInfo _ApiInfo;
|
||||
private static CPortAudio.SPaDeviceInfo _OutputDeviceInfo;
|
||||
private IntPtr _Ptr = new IntPtr(0);
|
||||
|
||||
private Closeproc _Closeproc;
|
||||
private CPortAudio.PaStreamCallbackDelegate _PaStreamCallback;
|
||||
private int _StreamID;
|
||||
private IAudioDecoder _Decoder;
|
||||
private float _BytesPerSecond;
|
||||
private bool _NoMoreData;
|
||||
|
||||
private bool _FileOpened;
|
||||
|
||||
private bool _Waiting;
|
||||
private bool _Skip;
|
||||
|
||||
private bool _Loop;
|
||||
private float _Duration;
|
||||
private float _CurrentTime;
|
||||
private float _TimeCode;
|
||||
|
||||
private bool _Paused;
|
||||
|
||||
private CRingBuffer _Data;
|
||||
private float _SetStart;
|
||||
private float _Start;
|
||||
private bool _SetLoop;
|
||||
private bool _SetSkip;
|
||||
private bool _Terminated;
|
||||
|
||||
private readonly Thread _DecoderThread;
|
||||
|
||||
private AutoResetEvent _EventDecode = new AutoResetEvent(false);
|
||||
|
||||
private readonly Object _LockData = new Object();
|
||||
private readonly Object _LockSyncSignals = new Object();
|
||||
|
||||
public CPortAudioStream()
|
||||
{
|
||||
_SyncTimer = new CSyncTimer(0f, 1f, 0.02f);
|
||||
_DecoderThread = new Thread(_Execute) {Name = "PortAudio Decode"};
|
||||
}
|
||||
|
||||
~CPortAudioStream()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void Free(Closeproc closeProc, int streamID)
|
||||
{
|
||||
_Closeproc = closeProc;
|
||||
_StreamID = streamID;
|
||||
_Terminated = true;
|
||||
}
|
||||
|
||||
public float Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_FileOpened)
|
||||
return _Duration;
|
||||
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Finished
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_LockData)
|
||||
{
|
||||
return _NoMoreData && _Data.BytesNotRead == 0L && _SyncTimer.Time >= _Duration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float Volume
|
||||
{
|
||||
get { return _Volume * 100f; }
|
||||
set
|
||||
{
|
||||
lock (_LockData)
|
||||
{
|
||||
_Volume = value / 100f;
|
||||
if (_Volume < 0f)
|
||||
_Volume = 0f;
|
||||
|
||||
if (_Volume > 1f)
|
||||
_Volume = 1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float VolumeMax
|
||||
{
|
||||
get { return _VolumeMax * 100f; }
|
||||
set
|
||||
{
|
||||
lock (_LockData)
|
||||
{
|
||||
_VolumeMax = value / 100f;
|
||||
if (_VolumeMax < 0f)
|
||||
_VolumeMax = 0f;
|
||||
|
||||
if (_VolumeMax > 1f)
|
||||
_VolumeMax = 1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float Position
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_LockData)
|
||||
{
|
||||
if (Finished)
|
||||
_SyncTimer.Pause();
|
||||
//TODO: Why not use Decoder.GetPosition()?
|
||||
return _SyncTimer.Time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Paused
|
||||
{
|
||||
get { return _Paused; }
|
||||
set
|
||||
{
|
||||
_Paused = value;
|
||||
lock (_LockData)
|
||||
{
|
||||
if (_Paused)
|
||||
_SyncTimer.Pause();
|
||||
else
|
||||
{
|
||||
_SyncTimer.Resume();
|
||||
_EventDecode.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Fade(float targetVolume, float fadeTime)
|
||||
{
|
||||
_Fading = new CFading(_Volume, targetVolume, fadeTime);
|
||||
_AfterFadeAction = EStreamAction.Nothing;
|
||||
}
|
||||
|
||||
public void FadeAndPause(float targetVolume, float fadeTime)
|
||||
{
|
||||
Fade(targetVolume, fadeTime);
|
||||
}
|
||||
|
||||
public void FadeAndClose(float targetVolume, float fadeTime, Closeproc closeProc, int streamID)
|
||||
{
|
||||
_Closeproc = closeProc;
|
||||
_StreamID = streamID;
|
||||
Fade(targetVolume, fadeTime);
|
||||
_AfterFadeAction = EStreamAction.Close;
|
||||
}
|
||||
|
||||
public void FadeAndStop(float targetVolume, float fadeTime, Closeproc closeProc, int streamID)
|
||||
{
|
||||
_Closeproc = closeProc;
|
||||
_StreamID = streamID;
|
||||
Fade(targetVolume, fadeTime);
|
||||
_AfterFadeAction = EStreamAction.Stop;
|
||||
}
|
||||
|
||||
public void Play()
|
||||
{
|
||||
Paused = false;
|
||||
_AfterFadeAction = EStreamAction.Nothing;
|
||||
lock (_Mutex)
|
||||
{
|
||||
_ErrorCheck("StartStream", CPortAudio.Pa_StartStream(_Ptr));
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
lock (_Mutex)
|
||||
{
|
||||
_ErrorCheck("StopStream (playback)", CPortAudio.Pa_StopStream(_Ptr));
|
||||
}
|
||||
Skip(0f);
|
||||
}
|
||||
|
||||
public bool Loop
|
||||
{
|
||||
get { return _SetLoop; }
|
||||
set { _SetLoop = value; }
|
||||
}
|
||||
|
||||
public int Open(string fileName)
|
||||
{
|
||||
if (_FileOpened)
|
||||
return -1;
|
||||
|
||||
if (!File.Exists(fileName))
|
||||
return -1;
|
||||
|
||||
if (_FileOpened)
|
||||
return -1;
|
||||
|
||||
_Decoder = new CAudioDecoderFFmpeg();
|
||||
_Decoder.Init();
|
||||
|
||||
try
|
||||
{
|
||||
lock (_Mutex)
|
||||
{
|
||||
if (!_Initialized)
|
||||
{
|
||||
if (_ErrorCheck("Initialize", CPortAudio.Pa_Initialize()))
|
||||
return -1;
|
||||
_Initialized = true;
|
||||
|
||||
int hostApi = _ApiSelect();
|
||||
_ApiInfo = CPortAudio.PaGetHostApiInfo(hostApi);
|
||||
_OutputDeviceInfo = CPortAudio.PaGetDeviceInfo(_ApiInfo.DefaultOutputDevice);
|
||||
if (_OutputDeviceInfo.DefaultLowOutputLatency < 0.1)
|
||||
_OutputDeviceInfo.DefaultLowOutputLatency = 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
_PaStreamCallback = _ProcessNewData;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_Initialized = false;
|
||||
CLog.LogError("Error Init PortAudio Playback");
|
||||
return -1;
|
||||
}
|
||||
|
||||
_Decoder.Open(fileName);
|
||||
_Duration = _Decoder.GetLength();
|
||||
|
||||
SFormatInfo format = _Decoder.GetFormatInfo();
|
||||
if (format.SamplesPerSecond == 0)
|
||||
return -1;
|
||||
|
||||
_ByteCount = 2 * format.ChannelCount;
|
||||
_BytesPerSecond = format.SamplesPerSecond * _ByteCount;
|
||||
_CurrentTime = 0f;
|
||||
_SyncTimer.Time = _CurrentTime;
|
||||
|
||||
var stream = new SAudioStreams(0);
|
||||
|
||||
var data = new IntPtr(0);
|
||||
|
||||
var outputParams = new CPortAudio.SPaStreamParameters
|
||||
{
|
||||
ChannelCount = format.ChannelCount,
|
||||
Device = _ApiInfo.DefaultOutputDevice,
|
||||
SampleFormat = CPortAudio.EPaSampleFormat.PaInt16,
|
||||
SuggestedLatency = _OutputDeviceInfo.DefaultLowOutputLatency
|
||||
};
|
||||
|
||||
var bufsize = (uint)CConfig.AudioBufferSize;
|
||||
lock (_Mutex)
|
||||
{
|
||||
_ErrorCheck("OpenDefaultStream (playback)", CPortAudio.Pa_OpenStream(
|
||||
out _Ptr,
|
||||
IntPtr.Zero,
|
||||
ref outputParams,
|
||||
format.SamplesPerSecond,
|
||||
bufsize,
|
||||
CPortAudio.EPaStreamFlags.PaNoFlag,
|
||||
_PaStreamCallback,
|
||||
data));
|
||||
}
|
||||
|
||||
stream.Handle = _Ptr.ToInt32();
|
||||
|
||||
if (stream.Handle != 0)
|
||||
{
|
||||
_NumStreams++;
|
||||
_Paused = true;
|
||||
_Waiting = true;
|
||||
_FileOpened = true;
|
||||
_Data = new CRingBuffer(_Bufsize);
|
||||
_NoMoreData = false;
|
||||
_DecoderThread.Priority = ThreadPriority.Normal;
|
||||
_DecoderThread.Name = Path.GetFileName(fileName);
|
||||
_DecoderThread.Start();
|
||||
|
||||
return stream.Handle;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void Skip(float time)
|
||||
{
|
||||
lock (_LockSyncSignals)
|
||||
{
|
||||
_SetStart = time;
|
||||
_SetSkip = true;
|
||||
_Waiting = true;
|
||||
}
|
||||
}
|
||||
|
||||
#region Threading
|
||||
private void _DoSkip()
|
||||
{
|
||||
lock (_LockData)
|
||||
{
|
||||
_Decoder.SetPosition(_Start);
|
||||
_CurrentTime = _Start;
|
||||
_TimeCode = _Start;
|
||||
_Data = new CRingBuffer(_Bufsize);
|
||||
_NoMoreData = false;
|
||||
_EventDecode.Set();
|
||||
_Waiting = false;
|
||||
_SyncTimer.Time = _Start;
|
||||
}
|
||||
}
|
||||
|
||||
private void _Execute()
|
||||
{
|
||||
while (!_Terminated)
|
||||
{
|
||||
if (_EventDecode.WaitOne(10))
|
||||
{
|
||||
lock (_LockSyncSignals)
|
||||
{
|
||||
if (_SetSkip)
|
||||
{
|
||||
_Skip = true;
|
||||
_EventDecode.Set();
|
||||
_Waiting = true;
|
||||
}
|
||||
|
||||
_SetSkip = false;
|
||||
|
||||
_Start = _SetStart;
|
||||
_Loop = _SetLoop;
|
||||
}
|
||||
|
||||
if (_Skip)
|
||||
{
|
||||
_DoSkip();
|
||||
_Skip = false;
|
||||
}
|
||||
|
||||
if (!_Waiting)
|
||||
{
|
||||
_DoDecode();
|
||||
_Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_DoFree();
|
||||
}
|
||||
|
||||
private void _DoDecode()
|
||||
{
|
||||
if (!_FileOpened)
|
||||
return;
|
||||
|
||||
if (_Paused)
|
||||
return;
|
||||
|
||||
if (_Terminated)
|
||||
return;
|
||||
|
||||
float timecode;
|
||||
byte[] buffer;
|
||||
|
||||
bool doIt = false;
|
||||
lock (_LockData)
|
||||
{
|
||||
if (!_Skip && _Beginrefill > _Data.BytesNotRead)
|
||||
doIt = true;
|
||||
}
|
||||
|
||||
if (!doIt)
|
||||
return;
|
||||
|
||||
_Decoder.Decode(out buffer, out timecode);
|
||||
|
||||
if (buffer == null)
|
||||
{
|
||||
if (_Loop)
|
||||
{
|
||||
lock (_LockSyncSignals)
|
||||
{
|
||||
_CurrentTime = 0f;
|
||||
_Start = 0f;
|
||||
}
|
||||
|
||||
_DoSkip();
|
||||
}
|
||||
else
|
||||
_NoMoreData = true;
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_LockData)
|
||||
{
|
||||
_Data.Write(buffer);
|
||||
_TimeCode = timecode;
|
||||
if (_Data.BytesNotRead < _Beginrefill)
|
||||
{
|
||||
_Waiting = false;
|
||||
_EventDecode.Set();
|
||||
}
|
||||
else
|
||||
_Waiting = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void _DoFree()
|
||||
{
|
||||
if (_Initialized)
|
||||
{
|
||||
Stop();
|
||||
_NumStreams--;
|
||||
if (_NumStreams == 0)
|
||||
{
|
||||
lock (_Mutex)
|
||||
{
|
||||
CPortAudio.Pa_Terminate();
|
||||
_Initialized = false;
|
||||
}
|
||||
}
|
||||
_Decoder.Close();
|
||||
}
|
||||
|
||||
_Closeproc(_StreamID);
|
||||
}
|
||||
#endregion Threading
|
||||
|
||||
#region Callbacks
|
||||
private CPortAudio.EPaStreamCallbackResult _ProcessNewData(
|
||||
IntPtr input,
|
||||
IntPtr output,
|
||||
uint frameCount,
|
||||
ref CPortAudio.SPaStreamCallbackTimeInfo timeInfo,
|
||||
CPortAudio.EPaStreamCallbackFlags statusFlags,
|
||||
IntPtr userData)
|
||||
{
|
||||
var buf = new byte[frameCount * _ByteCount];
|
||||
|
||||
if (_Paused)
|
||||
{
|
||||
try
|
||||
{
|
||||
Marshal.Copy(buf, 0, output, (int)frameCount * _ByteCount);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e.ToString());
|
||||
}
|
||||
return CPortAudio.EPaStreamCallbackResult.PaContinue;
|
||||
}
|
||||
|
||||
lock (_LockData)
|
||||
{
|
||||
if (_NoMoreData || _Data.BytesNotRead >= buf.Length)
|
||||
{
|
||||
_Data.Read(buf);
|
||||
|
||||
var b = new byte[2];
|
||||
for (int i = 0; i < buf.Length; i += _ByteCount)
|
||||
{
|
||||
b[0] = buf[i];
|
||||
b[1] = buf[i + 1];
|
||||
|
||||
b = BitConverter.GetBytes((Int16)(BitConverter.ToInt16(b, 0) * _Volume * _VolumeMax));
|
||||
buf[i] = b[0];
|
||||
buf[i + 1] = b[1];
|
||||
|
||||
if (_ByteCount == 4)
|
||||
{
|
||||
b[0] = buf[i + 2];
|
||||
b[1] = buf[i + 3];
|
||||
|
||||
b = BitConverter.GetBytes((Int16)(BitConverter.ToInt16(b, 0) * _Volume * _VolumeMax));
|
||||
buf[i + 2] = b[0];
|
||||
buf[i + 3] = b[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_Data.BytesNotRead < _Beginrefill)
|
||||
{
|
||||
_EventDecode.Set();
|
||||
_Waiting = false;
|
||||
}
|
||||
else
|
||||
_Waiting = true;
|
||||
|
||||
float latency = buf.Length / _BytesPerSecond + CConfig.AudioLatency / 1000f;
|
||||
float time = _TimeCode - _Data.BytesNotRead / _BytesPerSecond - latency;
|
||||
|
||||
if (!_NoMoreData)
|
||||
_CurrentTime = _SyncTimer.Update(time);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Marshal.Copy(buf, 0, output, (int)frameCount * _ByteCount);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CLog.LogError("Error PortAudio.StreamCallback: " + e.Message);
|
||||
}
|
||||
|
||||
return CPortAudio.EPaStreamCallbackResult.PaContinue;
|
||||
}
|
||||
#endregion Callbacks
|
||||
|
||||
private bool _ErrorCheck(String action, CPortAudio.EPaError errorCode)
|
||||
{
|
||||
if (errorCode != CPortAudio.EPaError.PaNoError)
|
||||
{
|
||||
if (errorCode == CPortAudio.EPaError.PaStreamIsNotStopped)
|
||||
return false;
|
||||
|
||||
CLog.LogError(action + " error (playback): " + CPortAudio.PaGetErrorText(errorCode));
|
||||
if (errorCode == CPortAudio.EPaError.PaUnanticipatedHostError)
|
||||
{
|
||||
CPortAudio.SPaHostErrorInfo errorInfo = CPortAudio.PaGetLastHostErrorInfo();
|
||||
CLog.LogError("- Host error API type: " + errorInfo.HostApiType);
|
||||
CLog.LogError("- Host error code: " + errorInfo.ErrorCode);
|
||||
CLog.LogError("- Host error text: " + errorInfo.ErrorText);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private int _ApiSelect()
|
||||
{
|
||||
if (!_Initialized)
|
||||
return 0;
|
||||
|
||||
int selectedHostApi = CPortAudio.Pa_GetDefaultHostApi();
|
||||
int apiCount = CPortAudio.Pa_GetHostApiCount();
|
||||
for (int i = 0; i < apiCount; i++)
|
||||
{
|
||||
CPortAudio.SPaHostApiInfo apiInfo = CPortAudio.PaGetHostApiInfo(i);
|
||||
if ((apiInfo.Type == CPortAudio.EPaHostApiTypeId.PaDirectSound)
|
||||
|| (apiInfo.Type == CPortAudio.EPaHostApiTypeId.PaALSA))
|
||||
selectedHostApi = i;
|
||||
}
|
||||
return selectedHostApi;
|
||||
}
|
||||
|
||||
private void _Update()
|
||||
{
|
||||
if (_Fading != null)
|
||||
{
|
||||
bool finished;
|
||||
_Volume = _Fading.GetValue(out finished);
|
||||
if (finished)
|
||||
{
|
||||
switch (_AfterFadeAction)
|
||||
{
|
||||
case EStreamAction.Close:
|
||||
_Terminated = true;
|
||||
break;
|
||||
case EStreamAction.Stop:
|
||||
Stop();
|
||||
break;
|
||||
case EStreamAction.Pause:
|
||||
Paused = true;
|
||||
break;
|
||||
}
|
||||
_Fading = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_EventDecode != null)
|
||||
{
|
||||
_EventDecode.Close();
|
||||
_EventDecode = null;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,356 +0,0 @@
|
||||
#region license
|
||||
// This file is part of Vocaluxe.
|
||||
//
|
||||
// Vocaluxe 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Vocaluxe 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 Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using Vocaluxe.Base;
|
||||
using Vocaluxe.Lib.Sound.PortAudio;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound
|
||||
{
|
||||
class CPortAudioRecord : IRecord
|
||||
{
|
||||
private bool _Initialized;
|
||||
private List<CRecordDevice> _Devices;
|
||||
|
||||
private CPortAudio.PaStreamCallbackDelegate _MyRecProc;
|
||||
private IntPtr[] _RecHandle;
|
||||
|
||||
private readonly CBuffer[] _Buffer;
|
||||
|
||||
public CPortAudioRecord()
|
||||
{
|
||||
_Devices = null;
|
||||
|
||||
_Buffer = new CBuffer[CSettings.MaxNumPlayer];
|
||||
for (int i = 0; i < _Buffer.Length; i++)
|
||||
_Buffer[i] = new CBuffer();
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Init PortAudio and list record devices
|
||||
/// </summary>
|
||||
/// <returns>true if success</returns>
|
||||
public bool Init()
|
||||
{
|
||||
_Devices = new List<CRecordDevice>();
|
||||
|
||||
try
|
||||
{
|
||||
if (_Initialized)
|
||||
CloseAll();
|
||||
|
||||
if (_ErrorCheck("Initialize", CPortAudio.Pa_Initialize()))
|
||||
return false;
|
||||
|
||||
_Initialized = true;
|
||||
int hostAPI = _ApiSelect();
|
||||
|
||||
int numDevices = CPortAudio.Pa_GetDeviceCount();
|
||||
for (int i = 0; i < numDevices; i++)
|
||||
{
|
||||
CPortAudio.SPaDeviceInfo info = CPortAudio.PaGetDeviceInfo(i);
|
||||
if (info.HostApi == hostAPI && info.MaxInputChannels > 0)
|
||||
{
|
||||
var dev = new CRecordDevice {ID = i, Name = info.Name, Driver = info.Name + i, Channels = info.MaxInputChannels};
|
||||
|
||||
if (dev.Channels > 2)
|
||||
dev.Channels = 2; //more are not supported in vocaluxe
|
||||
|
||||
_Devices.Add(dev);
|
||||
}
|
||||
}
|
||||
|
||||
_RecHandle = new IntPtr[_Devices.Count];
|
||||
_MyRecProc = _MyPaStreamCallback;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_Initialized = false;
|
||||
CLog.LogError("Error initializing PortAudio: " + e.Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start Voice Capturing
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool Start()
|
||||
{
|
||||
if (!_Initialized)
|
||||
return false;
|
||||
|
||||
if (_RecHandle == null || _RecHandle.Length == 0)
|
||||
return false;
|
||||
|
||||
foreach (CBuffer buffer in _Buffer)
|
||||
buffer.Reset();
|
||||
|
||||
foreach (IntPtr handle in _RecHandle)
|
||||
{
|
||||
int waitcount = 0;
|
||||
while (waitcount < 5 && CPortAudio.Pa_IsStreamStopped(handle) == CPortAudio.EPaError.PaStreamIsNotStopped)
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
waitcount++;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _RecHandle.Length; i++)
|
||||
_RecHandle[i] = IntPtr.Zero;
|
||||
|
||||
var active = new bool[_Devices.Count];
|
||||
for (int dev = 0; dev < _Devices.Count; dev++)
|
||||
{
|
||||
active[dev] = false;
|
||||
if (_Devices[dev].PlayerChannel1 > 0 || _Devices[dev].PlayerChannel2 > 0)
|
||||
active[dev] = true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _RecHandle.Length; i++)
|
||||
{
|
||||
if (active[i])
|
||||
{
|
||||
var inputParams = new CPortAudio.SPaStreamParameters
|
||||
{
|
||||
ChannelCount = _Devices[i].Channels,
|
||||
Device = _Devices[i].ID,
|
||||
SampleFormat = CPortAudio.EPaSampleFormat.PaInt16,
|
||||
SuggestedLatency = CPortAudio.PaGetDeviceInfo(_Devices[i].ID).DefaultLowInputLatency
|
||||
};
|
||||
|
||||
if (_ErrorCheck("OpenStream (rec)", CPortAudio.Pa_OpenStream(
|
||||
out _RecHandle[i],
|
||||
ref inputParams,
|
||||
IntPtr.Zero,
|
||||
44100,
|
||||
882,
|
||||
CPortAudio.EPaStreamFlags.PaNoFlag,
|
||||
_MyRecProc,
|
||||
new IntPtr(i))))
|
||||
return false;
|
||||
|
||||
if (_ErrorCheck("Start Stream (rec)", CPortAudio.Pa_StartStream(_RecHandle[i])))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop Voice Capturing
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool Stop()
|
||||
{
|
||||
if (!_Initialized)
|
||||
return false;
|
||||
|
||||
foreach (IntPtr handle in _RecHandle)
|
||||
CPortAudio.Pa_StopStream(handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop all voice capturing streams and terminate PortAudio
|
||||
/// </summary>
|
||||
public void CloseAll()
|
||||
{
|
||||
Stop();
|
||||
|
||||
if (_Initialized)
|
||||
{
|
||||
CPortAudio.Pa_Terminate();
|
||||
_Initialized = false;
|
||||
}
|
||||
|
||||
//System.IO.File.WriteAllBytes("test0.raw", _Buffer[0].Buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detect Pitch and Volume of the newest voice buffer
|
||||
/// </summary>
|
||||
/// <param name="player"></param>
|
||||
public void AnalyzeBuffer(int player)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
|
||||
_Buffer[player].AnalyzeBuffer();
|
||||
}
|
||||
|
||||
public int GetToneAbs(int player)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return 0;
|
||||
|
||||
return _Buffer[player].ToneAbs;
|
||||
}
|
||||
|
||||
public int GetTone(int player)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return 0;
|
||||
|
||||
return _Buffer[player].Tone;
|
||||
}
|
||||
|
||||
public void SetTone(int player, int tone)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return;
|
||||
|
||||
_Buffer[player].Tone = tone;
|
||||
}
|
||||
|
||||
public float GetMaxVolume(int player)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return 0f;
|
||||
|
||||
return _Buffer[player].MaxVolume;
|
||||
}
|
||||
|
||||
public bool ToneValid(int player)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return false;
|
||||
|
||||
return _Buffer[player].ToneValid;
|
||||
}
|
||||
|
||||
public int NumHalfTones(int player)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return 0;
|
||||
|
||||
return CBuffer.NumHalfTones;
|
||||
}
|
||||
|
||||
public float[] ToneWeigth(int player)
|
||||
{
|
||||
if (!_Initialized)
|
||||
return null;
|
||||
|
||||
return _Buffer[player].ToneWeigth;
|
||||
}
|
||||
|
||||
public ReadOnlyCollection<CRecordDevice> RecordDevices()
|
||||
{
|
||||
if (!_Initialized)
|
||||
return null;
|
||||
|
||||
if (_Devices.Count == 0)
|
||||
return null;
|
||||
|
||||
return _Devices.AsReadOnly();
|
||||
}
|
||||
|
||||
private CPortAudio.EPaStreamCallbackResult _MyPaStreamCallback(
|
||||
IntPtr input,
|
||||
IntPtr output,
|
||||
uint frameCount,
|
||||
ref CPortAudio.SPaStreamCallbackTimeInfo timeInfo,
|
||||
CPortAudio.EPaStreamCallbackFlags statusFlags,
|
||||
IntPtr userData)
|
||||
{
|
||||
try
|
||||
{
|
||||
frameCount *= 4;
|
||||
if (frameCount > 0 && input != IntPtr.Zero)
|
||||
{
|
||||
var recbuffer = new byte[frameCount];
|
||||
var leftBuffer = new byte[frameCount / 2];
|
||||
var rightBuffer = new byte[frameCount / 2];
|
||||
|
||||
// copy from managed to unmanaged memory
|
||||
Marshal.Copy(input, recbuffer, 0, (int)frameCount);
|
||||
|
||||
// copy into left/right Buffer
|
||||
for (int i = 0; i < frameCount / 2; i++)
|
||||
{
|
||||
leftBuffer[i] = recbuffer[i * 2 - (i % 2)];
|
||||
rightBuffer[i] = recbuffer[i * 2 - (i % 2) + 2];
|
||||
}
|
||||
|
||||
for (int i = 0; i < _RecHandle.Length; i++)
|
||||
{
|
||||
if (new IntPtr(i) == userData)
|
||||
{
|
||||
if (_Devices[i].PlayerChannel1 > 0)
|
||||
_Buffer[_Devices[i].PlayerChannel1 - 1].ProcessNewBuffer(leftBuffer);
|
||||
|
||||
if (_Devices[i].PlayerChannel2 > 0)
|
||||
_Buffer[_Devices[i].PlayerChannel2 - 1].ProcessNewBuffer(rightBuffer);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CLog.LogError("Error on Stream Callback (rec): " + e);
|
||||
}
|
||||
|
||||
return CPortAudio.EPaStreamCallbackResult.PaContinue;
|
||||
}
|
||||
|
||||
private bool _ErrorCheck(String action, CPortAudio.EPaError errorCode)
|
||||
{
|
||||
if (errorCode != CPortAudio.EPaError.PaNoError)
|
||||
{
|
||||
CLog.LogError(action + " error (rec): " + CPortAudio.PaGetErrorText(errorCode));
|
||||
if (errorCode == CPortAudio.EPaError.PaUnanticipatedHostError)
|
||||
{
|
||||
CPortAudio.SPaHostErrorInfo errorInfo = CPortAudio.PaGetLastHostErrorInfo();
|
||||
CLog.LogError("- Host error API type: " + errorInfo.HostApiType);
|
||||
CLog.LogError("- Host error code: " + errorInfo.ErrorCode);
|
||||
CLog.LogError("- Host error text: " + errorInfo.ErrorText);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private int _ApiSelect()
|
||||
{
|
||||
if (!_Initialized)
|
||||
return 0;
|
||||
|
||||
int selectedHostApi = CPortAudio.Pa_GetDefaultHostApi();
|
||||
int apiCount = CPortAudio.Pa_GetHostApiCount();
|
||||
for (int i = 0; i < apiCount; i++)
|
||||
{
|
||||
CPortAudio.SPaHostApiInfo apiInfo = CPortAudio.PaGetHostApiInfo(i);
|
||||
if ((apiInfo.Type == CPortAudio.EPaHostApiTypeId.PaDirectSound)
|
||||
|| (apiInfo.Type == CPortAudio.EPaHostApiTypeId.PaALSA))
|
||||
selectedHostApi = i;
|
||||
}
|
||||
return selectedHostApi;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
#region license
|
||||
// This file is part of Vocaluxe.
|
||||
//
|
||||
// Vocaluxe 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Vocaluxe 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 Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound
|
||||
{
|
||||
class CRecordDevice
|
||||
{
|
||||
public int ID;
|
||||
public string Name;
|
||||
public string Driver;
|
||||
|
||||
public int Channels;
|
||||
public int PlayerChannel1;
|
||||
public int PlayerChannel2;
|
||||
}
|
||||
|
||||
struct SMicConfig
|
||||
{
|
||||
public string DeviceName;
|
||||
public string DeviceDriver;
|
||||
public int Channel;
|
||||
|
||||
// ReSharper disable UnusedParameter.Local
|
||||
public SMicConfig(int dummy)
|
||||
// ReSharper restore UnusedParameter.Local
|
||||
{
|
||||
DeviceName = String.Empty;
|
||||
DeviceDriver = String.Empty;
|
||||
Channel = 0;
|
||||
}
|
||||
}
|
||||
|
||||
interface IRecord
|
||||
{
|
||||
bool Init();
|
||||
void CloseAll();
|
||||
|
||||
bool Start();
|
||||
bool Stop();
|
||||
void AnalyzeBuffer(int player);
|
||||
|
||||
int GetToneAbs(int player);
|
||||
int GetTone(int player);
|
||||
void SetTone(int player, int tone);
|
||||
float GetMaxVolume(int player);
|
||||
bool ToneValid(int player);
|
||||
int NumHalfTones(int player);
|
||||
float[] ToneWeigth(int player);
|
||||
|
||||
ReadOnlyCollection<CRecordDevice> RecordDevices();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -20,7 +20,7 @@ using System.Runtime.InteropServices;
|
||||
using Vocaluxe.Base;
|
||||
using Vocaluxe.Lib.Video.Acinerella;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound.Decoder
|
||||
namespace Vocaluxe.Lib.Sound.Playback.Decoder
|
||||
{
|
||||
class CAudioDecoderFFmpeg : IAudioDecoder, IDisposable
|
||||
{
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
namespace Vocaluxe.Lib.Sound.Decoder
|
||||
namespace Vocaluxe.Lib.Sound.Playback.Decoder
|
||||
{
|
||||
struct SFormatInfo
|
||||
{
|
||||
+1
-2
@@ -17,9 +17,8 @@
|
||||
|
||||
using System;
|
||||
using Vocaluxe.Base;
|
||||
using Vocaluxe.Lib.Sound.Gstreamer;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound
|
||||
namespace Vocaluxe.Lib.Sound.Playback.Gstreamer
|
||||
{
|
||||
class CGstreamerAudio : IPlayback
|
||||
{
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Vocaluxe.Lib.Sound.Gstreamer
|
||||
namespace Vocaluxe.Lib.Sound.Playback.Gstreamer
|
||||
{
|
||||
public static class CGstreamerAudioWrapper
|
||||
{
|
||||
Loaded 100 of 149 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user