diff --git a/.gitignore b/.gitignore index 6413e521..a12662d6 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/ClientServerLib/ClientServerLib/CDiscover.cs b/ClientServerLib/ClientServerLib/CDiscover.cs index 17beab61..d78bc19b 100644 --- a/ClientServerLib/ClientServerLib/CDiscover.cs +++ b/ClientServerLib/ClientServerLib/CDiscover.cs @@ -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; diff --git a/ClientServerLib/ClientServerLib/CServer.cs b/ClientServerLib/ClientServerLib/CServer.cs index 51a6d2b7..3403a092 100644 --- a/ClientServerLib/ClientServerLib/CServer.cs +++ b/ClientServerLib/ClientServerLib/CServer.cs @@ -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? diff --git a/ClientServerLib/ClientServerLib/Properties/AssemblyInfo.cs b/ClientServerLib/ClientServerLib/Properties/AssemblyInfo.cs index fe3863d2..2acbaa9a 100644 --- a/ClientServerLib/ClientServerLib/Properties/AssemblyInfo.cs +++ b/ClientServerLib/ClientServerLib/Properties/AssemblyInfo.cs @@ -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 diff --git a/GstreamerHelper/GstreamerHelper.vcxproj b/GstreamerHelper/GstreamerHelper.vcxproj index 21d99413..cf79bf57 100644 --- a/GstreamerHelper/GstreamerHelper.vcxproj +++ b/GstreamerHelper/GstreamerHelper.vcxproj @@ -95,8 +95,7 @@ - - + false bin\$(Configuration).$(Platform)\ $(MSBuildProjectDirectory)\x86\lib\glib-2.0\include;$(MSBuildProjectDirectory)\x86\include\gstreamer-1.0;$(MSBuildProjectDirectory)\x86\include\glib-2.0;$(MSBuildProjectDirectory)\x86\include;$(IncludePath) $(MSBuildProjectDirectory)\x86\lib;$(LibraryPath) @@ -104,12 +103,12 @@ obj\$(Configuration).$(Platform)\ - true bin\$(Configuration).$(Platform)\ $(MSBuildProjectDirectory)\x64\lib\glib-2.0\include;$(MSBuildProjectDirectory)\x64\include\gstreamer-1.0;$(MSBuildProjectDirectory)\x64\include\glib-2.0;$(MSBuildProjectDirectory)\x64\include;$(IncludePath) $(MSBuildProjectDirectory)\x64\lib;$(LibraryPath) ..\CheckingRules.ruleset obj\$(Configuration).$(Platform)\ + false false diff --git a/Output/AForge.Video.DirectShow.dll b/Output/AForge.Video.DirectShow.dll index 2d14556a..e3f806f3 100644 Binary files a/Output/AForge.Video.DirectShow.dll and b/Output/AForge.Video.DirectShow.dll differ diff --git a/Output/AForge.Video.dll b/Output/AForge.Video.dll index 7ff735b4..dc67243e 100644 Binary files a/Output/AForge.Video.dll and b/Output/AForge.Video.dll differ diff --git a/Output/LICENSE.txt b/Output/LICENSE.txt index 20d40b6b..e587591e 100644 --- a/Output/LICENSE.txt +++ b/Output/LICENSE.txt @@ -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. - - - Copyright (C) - - 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 . - -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: - - Copyright (C) - 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 -. - - 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 -. \ No newline at end of file + END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/Output/Licenses/AForge/License.txt b/Output/Licenses/AForge/License.txt new file mode 100644 index 00000000..39482789 --- /dev/null +++ b/Output/Licenses/AForge/License.txt @@ -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 \ No newline at end of file diff --git a/Output/Licenses/AForge/SourceAndVersion.txt b/Output/Licenses/AForge/SourceAndVersion.txt new file mode 100644 index 00000000..24332f3d --- /dev/null +++ b/Output/Licenses/AForge/SourceAndVersion.txt @@ -0,0 +1,7 @@ +http://code.google.com/p/aforge + +Version: 2.2.5 + +Files: +AForge.Video.dll +AForge.Video.DirectShow.dll \ No newline at end of file diff --git a/Output/Licenses/AForge/gpl-3.0.txt b/Output/Licenses/AForge/gpl-3.0.txt new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/Output/Licenses/AForge/gpl-3.0.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. diff --git a/Output/Licenses/AForge/lgpl-3.0.txt b/Output/Licenses/AForge/lgpl-3.0.txt new file mode 100644 index 00000000..5e5be416 --- /dev/null +++ b/Output/Licenses/AForge/lgpl-3.0.txt @@ -0,0 +1,166 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. diff --git a/Output/Licenses/PortAudio-Sharp/SourceAndVersion.txt b/Output/Licenses/PortAudio-Sharp/SourceAndVersion.txt new file mode 100644 index 00000000..d9759e5b --- /dev/null +++ b/Output/Licenses/PortAudio-Sharp/SourceAndVersion.txt @@ -0,0 +1,7 @@ +https://code.google.com/p/portaudiosharp/ + +Version: rev44 (Feb 26, 2012) + +Files: +PortAudioSharp.dll +PortAudioSharp.dll.config \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/Portaudio/copyright.txt b/Output/Licenses/PortAudio-Sharp/copyright.txt similarity index 100% rename from Vocaluxe/Lib/Sound/Portaudio/copyright.txt rename to Output/Licenses/PortAudio-Sharp/copyright.txt diff --git a/Vocaluxe/Lib/Sound/Portaudio/license.txt b/Output/Licenses/PortAudio-Sharp/license.txt similarity index 100% rename from Vocaluxe/Lib/Sound/Portaudio/license.txt rename to Output/Licenses/PortAudio-Sharp/license.txt diff --git a/Vocaluxe/Lib/Sound/Portaudio/readme.txt b/Output/Licenses/PortAudio-Sharp/readme.txt similarity index 100% rename from Vocaluxe/Lib/Sound/Portaudio/readme.txt rename to Output/Licenses/PortAudio-Sharp/readme.txt diff --git a/Output/Licenses/PortAudio/LICENSE.txt b/Output/Licenses/PortAudio/LICENSE.txt new file mode 100644 index 00000000..e0ac4e8a --- /dev/null +++ b/Output/Licenses/PortAudio/LICENSE.txt @@ -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 + * Implementation + * Copyright (c) 1999-2000 + * + * 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. + */ +<<<<< \ No newline at end of file diff --git a/Output/Licenses/PortAudio/README.txt b/Output/Licenses/PortAudio/README.txt new file mode 100644 index 00000000..21138278 --- /dev/null +++ b/Output/Licenses/PortAudio/README.txt @@ -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 diff --git a/Output/Licenses/PortAudio/SourceAndVersion.txt b/Output/Licenses/PortAudio/SourceAndVersion.txt new file mode 100644 index 00000000..0042f210 --- /dev/null +++ b/Output/Licenses/PortAudio/SourceAndVersion.txt @@ -0,0 +1,7 @@ +http://www.portaudio.com/ + +Version: January 30, 2014, SVN rev 1919 + +Files: +x86/PortAudio.dll +x64/PortAudio.dll \ No newline at end of file diff --git a/Output/LICENSE QRCoder.txt b/Output/Licenses/QR Coder/QRCoder.txt similarity index 94% rename from Output/LICENSE QRCoder.txt rename to Output/Licenses/QR Coder/QRCoder.txt index 5624f5cf..e331740c 100644 --- a/Output/LICENSE QRCoder.txt +++ b/Output/Licenses/QR Coder/QRCoder.txt @@ -1,5 +1,3 @@ -// QRCode.dll from https://github.com/codebude/QRCoder/ - The MIT License (MIT) Copyright (c) 2013 Raffael Herrmann diff --git a/Output/Licenses/QR Coder/SourceAndVersion.txt b/Output/Licenses/QR Coder/SourceAndVersion.txt new file mode 100644 index 00000000..21625974 --- /dev/null +++ b/Output/Licenses/QR Coder/SourceAndVersion.txt @@ -0,0 +1,6 @@ +https://github.com/codebude/QRCoder/ + +Version: master 19.01.2014 + +Files: +QRCoder.dll \ No newline at end of file diff --git a/Output/Licenses/SlimDx/License.txt b/Output/Licenses/SlimDx/License.txt new file mode 100644 index 00000000..a976ec46 --- /dev/null +++ b/Output/Licenses/SlimDx/License.txt @@ -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. \ No newline at end of file diff --git a/Output/Licenses/SlimDx/SourceAndVersion.txt b/Output/Licenses/SlimDx/SourceAndVersion.txt new file mode 100644 index 00000000..7ad52997 --- /dev/null +++ b/Output/Licenses/SlimDx/SourceAndVersion.txt @@ -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 \ No newline at end of file diff --git a/Output/PortAudioSharp.dll b/Output/PortAudioSharp.dll new file mode 100644 index 00000000..f48cfc2a Binary files /dev/null and b/Output/PortAudioSharp.dll differ diff --git a/Output/PortAudioSharp.dll.config b/Output/PortAudioSharp.dll.config new file mode 100644 index 00000000..9147bca7 --- /dev/null +++ b/Output/PortAudioSharp.dll.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Output/Security.dll b/Output/Security.dll deleted file mode 100644 index f0c2c513..00000000 Binary files a/Output/Security.dll and /dev/null differ diff --git a/Output/x64/PortAudio.dll b/Output/x64/PortAudio.dll new file mode 100644 index 00000000..033672a2 Binary files /dev/null and b/Output/x64/PortAudio.dll differ diff --git a/Output/x64/SlimDX.dll b/Output/x64/SlimDX.dll index e2304d42..36d058d1 100644 Binary files a/Output/x64/SlimDX.dll and b/Output/x64/SlimDX.dll differ diff --git a/Output/x64/portaudio_x64.dll b/Output/x64/portaudio_x64.dll deleted file mode 100644 index 106610f8..00000000 Binary files a/Output/x64/portaudio_x64.dll and /dev/null differ diff --git a/Output/x86/PortAudio.dll b/Output/x86/PortAudio.dll new file mode 100644 index 00000000..0367aa1e Binary files /dev/null and b/Output/x86/PortAudio.dll differ diff --git a/Output/x86/SlimDX.dll b/Output/x86/SlimDX.dll index 3ff12733..1593c2a6 100644 Binary files a/Output/x86/SlimDX.dll and b/Output/x86/SlimDX.dll differ diff --git a/Output/x86/portaudio_x86.dll b/Output/x86/portaudio_x86.dll deleted file mode 100644 index 61617743..00000000 Binary files a/Output/x86/portaudio_x86.dll and /dev/null differ diff --git a/PartyModes/PartyModeChallenge/CPartyScreenChallengeNames.cs b/PartyModes/PartyModeChallenge/CPartyScreenChallengeNames.cs index 46d00571..14b5102c 100644 --- a/PartyModes/PartyModeChallenge/CPartyScreenChallengeNames.cs +++ b/PartyModes/PartyModeChallenge/CPartyScreenChallengeNames.cs @@ -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 { diff --git a/PartyModes/PartyModeChallengeMedley/CPartyScreenChallengeMedleyNames.cs b/PartyModes/PartyModeChallengeMedley/CPartyScreenChallengeMedleyNames.cs index 3858de80..9c3521af 100644 --- a/PartyModes/PartyModeChallengeMedley/CPartyScreenChallengeMedleyNames.cs +++ b/PartyModes/PartyModeChallengeMedley/CPartyScreenChallengeMedleyNames.cs @@ -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 { diff --git a/PartyModes/PartyModeTicTacToe/CPartyScreenTicTacToeNames.cs b/PartyModes/PartyModeTicTacToe/CPartyScreenTicTacToeNames.cs index 00f99acc..ebc7eddf 100644 --- a/PartyModes/PartyModeTicTacToe/CPartyScreenTicTacToeNames.cs +++ b/PartyModes/PartyModeTicTacToe/CPartyScreenTicTacToeNames.cs @@ -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 { diff --git a/PitchTracker/Helper.h b/PitchTracker/Helper.h new file mode 100644 index 00000000..ae13e309 --- /dev/null +++ b/PitchTracker/Helper.h @@ -0,0 +1,61 @@ +#pragma once +#include "PitchWrapper.h" +#include +#include + +float* short2FloatArray(short* in, size_t len){ + const float maxShort = 32768.0; //maximum abs value of a short + + float* result = static_cast(malloc(len * sizeof(float))); + float* curOut = result; + for(size_t i=0; i(malloc(len * sizeof(double))); + double* curOut = result; + for(size_t i=0; iFreq = tone->freq; + result->DB = tone->db; + result->Stabledb = tone->stabledb; + result->Age = static_cast(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(std::round(result->NoteExact)); + return result; + } + } +} +*/ \ No newline at end of file diff --git a/PitchTracker/PitchTracker.vcxproj b/PitchTracker/PitchTracker.vcxproj new file mode 100644 index 00000000..47be7dd7 --- /dev/null +++ b/PitchTracker/PitchTracker.vcxproj @@ -0,0 +1,192 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {54A48AF7-515C-4EDD-B395-2BFFA096050A} + Win32Proj + PitchTracker + PitchTracker + + + + DynamicLibrary + true + v110_xp + Unicode + + + DynamicLibrary + true + v110_xp + Unicode + + + DynamicLibrary + false + v110_xp + true + Unicode + + + DynamicLibrary + false + v110_xp + true + Unicode + + + + + + + + + + + + + + + + + + + true + bin\$(Configuration).$(Platform)\ + obj\$(Configuration).$(Platform)\ + + + true + bin\$(Configuration).$(Platform)\ + obj\$(Configuration).$(Platform)\ + + + false + bin\$(Configuration).$(Platform)\ + obj\$(Configuration).$(Platform)\ + + + false + bin\$(Configuration).$(Platform)\ + obj\$(Configuration).$(Platform)\ + + + + NotUsing + Level3 + Disabled + WIN64;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Fast + false + + + Windows + true + + + xcopy "$(OutDir)*.dll" "$(SolutionDir)Output\x64\" /I /Y + + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Fast + false + + + Windows + true + + + xcopy "$(OutDir)*.dll" "$(SolutionDir)Output\x86\" /I /Y + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN64;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Speed + Fast + false + + + Windows + false + true + true + + + xcopy "$(OutDir)*.dll" "$(SolutionDir)Output\x64\" /I /Y + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Speed + Fast + + + Windows + false + true + true + + + xcopy "$(OutDir)*.dll" "$(SolutionDir)Output\x86\" /I /Y + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PitchTracker/PitchTracker.vcxproj.filters b/PitchTracker/PitchTracker.vcxproj.filters new file mode 100644 index 00000000..efd0974f --- /dev/null +++ b/PitchTracker/PitchTracker.vcxproj.filters @@ -0,0 +1,68 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {a47c9d33-78d2-453d-bb59-e5724cd85f67} + + + {4c72400a-ff2c-4842-8c20-54deb5df606d} + + + {d71782a0-47fc-4fe1-8569-0a0ce8fe9b45} + + + + + Quelldateien + + + Quelldateien + + + Quelldateien\performous + + + Quelldateien + + + + + Headerdateien + + + Headerdateien + + + Headerdateien + + + Headerdateien\performous\libda + + + Headerdateien\performous\libda + + + Headerdateien + + + + + Headerdateien\performous + + + Headerdateien\performous + + + \ No newline at end of file diff --git a/PitchTracker/PitchWrapper.cpp b/PitchTracker/PitchWrapper.cpp new file mode 100644 index 00000000..4fc47b4c --- /dev/null +++ b/PitchTracker/PitchWrapper.cpp @@ -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(static_cast(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(static_cast(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^ data){ + if(data->Length == 0) + return; + pin_ptr dataPtr = &data[0]; + float* dataPtr2 = dataPtr; + m_analyzer->input(dataPtr2, dataPtr2 + data->Length); + } + + void CAnalyzer::Input(array^ data){ + if(data->Length == 0) + return; + pin_ptr dataPtr = &data[0]; + float* dataFloat = short2FloatArray(dataPtr, data->Length); + m_analyzer->input(dataFloat, dataFloat + data->Length); + freeFloatArray(dataFloat); + } + + void CAnalyzer::Input(array^ data){ + if(data->Length == 0) + return; + pin_ptr dataPtr = &data[0]; + float* dataFloat = short2FloatArray(static_cast(static_cast(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^ CAnalyzer::GetTones(){ + Analyzer::tones_t umTones = m_analyzer->getTones(); + array^ tones = gcnew array(static_cast(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^ data, float rate){ + pin_ptr 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^ samples, array^ weights){ + if(samples->Length == 0) + return -1; + pin_ptr dataPtr = &samples[0]; + pin_ptr weightsPtr = &weights[0]; + double* dataDouble = short2DoubleArray(dataPtr, samples->Length, false); + int result = PitchTracker::GetTone(dataDouble, samples->Length, weightsPtr, true); + freeDoubleArray(dataDouble); + return result; + } + } +} +*/ \ No newline at end of file diff --git a/PitchTracker/PitchWrapper.h b/PitchTracker/PitchWrapper.h new file mode 100644 index 00000000..d3039bc9 --- /dev/null +++ b/PitchTracker/PitchWrapper.h @@ -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^ data); + void Input(array^ data); + void Input(array^ data); + void Process(); + double GetPeak(); + array^ GetTones(); + CTone^ FindTone(); + CTone^ FindTone(double minFreq); + CTone^ FindTone(double minFreq, double maxFreq); + void Output(array^ 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^ samples, array^ weights); +}; +*/ \ No newline at end of file diff --git a/PitchTracker/compatibility.cpp b/PitchTracker/compatibility.cpp new file mode 100644 index 00000000..207b750d --- /dev/null +++ b/PitchTracker/compatibility.cpp @@ -0,0 +1,30 @@ +#include "compatibility.h" + +#include + +#ifdef _MSC_VER +#if(_MSC_VER < 1800) // < VS2013 +namespace std +{ + float round(float d) + { + return static_cast(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 \ No newline at end of file diff --git a/PitchTracker/compatibility.h b/PitchTracker/compatibility.h new file mode 100644 index 00000000..ebdf8a88 --- /dev/null +++ b/PitchTracker/compatibility.h @@ -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 \ No newline at end of file diff --git a/PitchTracker/performous/License.txt b/PitchTracker/performous/License.txt new file mode 100644 index 00000000..9a031336 --- /dev/null +++ b/PitchTracker/performous/License.txt @@ -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 \ No newline at end of file diff --git a/PitchTracker/performous/Performous.txt b/PitchTracker/performous/Performous.txt new file mode 100644 index 00000000..5861375b --- /dev/null +++ b/PitchTracker/performous/Performous.txt @@ -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. + + + Copyright (C) + + 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. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + diff --git a/PitchTracker/performous/libda/fft.hpp b/PitchTracker/performous/libda/fft.hpp new file mode 100644 index 00000000..1611c2fb --- /dev/null +++ b/PitchTracker/performous/libda/fft.hpp @@ -0,0 +1,84 @@ +#pragma once + +/** + * @file fft.hpp FFT and related facilities. + */ + +#include "sample.hpp" +#include +#include +#include + +#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 struct DanielsonLanczos { + static void apply(std::complex* data) { + constexpr std::size_t N = 1 << P; + constexpr std::size_t M = N / 2; + // Compute even and odd halves + DanielsonLanczos

().apply(data); + DanielsonLanczos

().apply(data + M); + // Combine the results + std::complex w(1.0); + for (std::size_t i = 0; i < M; ++i) { + const std::complex temp = data[i + M] * w; + data[M + i] = data[i] - temp; + data[i] += temp; + w *= std::polar(1.0, -M_TAU / N); + } + } + }; + + template struct DanielsonLanczos<0, T> { static void apply(std::complex*) {} }; + + /** Perform FFT on data. **/ + template void fft(std::complex* 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::apply(data); + } + + /** Perform FFT on data from floating point iterator, windowing the input. **/ + template std::vector > fft(InIt begin, Window window) { + std::vector > 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::apply(&data[0]); + return data; + } + + template void ifft(std::complex* 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

(data); + constexpr std::complex 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 + } + +} diff --git a/PitchTracker/performous/libda/sample.hpp b/PitchTracker/performous/libda/sample.hpp new file mode 100644 index 00000000..a09bbcb6 --- /dev/null +++ b/PitchTracker/performous/libda/sample.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include +#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 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 static inline Float msinc(Float x) { return std::abs(x) < Float(eps) ? Float(1) : std::sin(x) / x; } + + /// Normalized (signal processing) sinc function + template static inline Float sinc(Float x) { return msinc(Float(pi) * x); } + + /// Lanczos kernel of size A + template 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(round(s * max_s16)), static_cast(min_s16), static_cast(max_s16)); } + static inline int conv_to_s24(sample_t s) { return clamp(static_cast(round(s * max_s24)), static_cast(min_s24), static_cast(max_s24)); } + static inline int conv_to_s32(sample_t s) { return static_cast(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(s * max_s16); } + static inline int conv_to_s24_fast(sample_t s) { return static_cast(s * max_s24); } + static inline int conv_to_s32_fast(sample_t s) { return static_cast(s * max_s32); } + + template class step_iterator: public std::iterator { + 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_iterator; + typedef step_iterator sample_const_iterator; +} diff --git a/PitchTracker/performous/pitch.cc b/PitchTracker/performous/pitch.cc new file mode 100644 index 00000000..760dc9dc --- /dev/null +++ b/PitchTracker/performous/pitch.cc @@ -0,0 +1,235 @@ +#include "pitch.hh" + +#include "util.hh" +#include "libda/fft.hpp" +#include +#include +#include + +// 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(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((end - begin) / 2 /* stereo */); + if (out == 0) return false; + const unsigned in = static_cast(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(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(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(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& 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(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 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(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(); +} + + diff --git a/PitchTracker/performous/pitch.hh b/PitchTracker/performous/pitch.hh new file mode 100644 index 00000000..1108e0b9 --- /dev/null +++ b/PitchTracker/performous/pitch.hh @@ -0,0 +1,131 @@ +#pragma once + +#include +#include +#include +#include +#include +#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 class RingBuffer { +public: + constexpr static size_t capacity = SIZE; + RingBuffer(): m_read(), m_write() {} ///< Initialize empty buffer + template 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(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 bool read(OutIt begin, OutIt end) { + size_t r = m_read; + if (size() < static_cast(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 > fft_t; + /// list of tones + typedef std::list 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 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 m_window; + fft_t m_fft; + std::vector m_fftLastPhase; + double m_peak; + tones_t m_tones; + mutable double m_oldfreq; + bool calcFFT(); + void calcTones(); + void mergeWithOld(tones_t& tones) const; +}; diff --git a/PitchTracker/performous/util.hh b/PitchTracker/performous/util.hh new file mode 100644 index 00000000..f07753d4 --- /dev/null +++ b/PitchTracker/performous/util.hh @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include +#include + +/** Limit val to range [min, max] **/ +template 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 struct MinMax { + Numeric min, max; + explicit MinMax(Numeric min = std::numeric_limits::min(), Numeric max = std::numeric_limits::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::quiet_NaN(); } + +/** A convenient way for getting infs **/ +static inline double getInf() { return std::numeric_limits::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; +} + diff --git a/PitchTracker/ptAKF.cpp b/PitchTracker/ptAKF.cpp new file mode 100644 index 00000000..fd272a6f --- /dev/null +++ b/PitchTracker/ptAKF.cpp @@ -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 . + +#include "ptAKF.h" +#include + +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(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(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 + 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(samplesPerPeriodD); + T fHigh = static_cast(samplesPerPeriodD - samplesPerPeriod); + T fLow = static_cast(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 + 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(samples, sampleCt, toneIndex, scale); + + if (curWeight > maxWeight) + { + maxWeight = curWeight; + maxTone = toneIndex; + } + + if (curWeight < minWeight) + minWeight = curWeight; + + weights[toneIndex - MinHalfTone] = static_cast(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); + +} +*/ \ No newline at end of file diff --git a/PitchTracker/ptAKF.h b/PitchTracker/ptAKF.h new file mode 100644 index 00000000..240e7aa7 --- /dev/null +++ b/PitchTracker/ptAKF.h @@ -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); +}; \ No newline at end of file diff --git a/Vocaluxe.sln b/Vocaluxe.sln index 5d225110..0298c529 100644 --- a/Vocaluxe.sln +++ b/Vocaluxe.sln @@ -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 diff --git a/Vocaluxe/Base/CBackgroundMusic.cs b/Vocaluxe/Base/CBackgroundMusic.cs index 9afc32dd..c06c0b2c 100644 --- a/Vocaluxe/Base/CBackgroundMusic.cs +++ b/Vocaluxe/Base/CBackgroundMusic.cs @@ -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 _AllFileNames = new List(); - private static readonly List _NotPlayedFileNames = new List(); - private static readonly List _BGMusicFileNames = new List(); - private static readonly List _PreviousFileNames = new List(); + 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 _BGMusicFiles = new List(); //Background music files + private static readonly List _NotPlayedFiles = new List(); //Not played files + private static readonly List _PreviousFiles = new List(); //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(); + } + } + + ///

+ /// Initializes the background music with values from config + /// Has to be called before any other method is used + /// public static void Init() { - var templist = new List(); + if (_Initialized) + return; - foreach (string ending in CSettings.MusicFileTypes) - templist.AddRange(CHelper.ListFiles(CSettings.FolderBackgroundMusic, ending, true, true)); + List 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); + } + + /// + /// (Re-)Starts the _CurrentPlaylistElement + /// + 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; - } - } } \ No newline at end of file diff --git a/Vocaluxe/Base/CConfig.cs b/Vocaluxe/Base/CConfig.cs index 2b5d8b3d..6eb0be6b 100644 --- a/Vocaluxe/Base/CConfig.cs +++ b/Vocaluxe/Base/CConfig.cs @@ -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 SongFolders = new List() + { + Path.Combine(Directory.GetCurrentDirectory(), _FolderSongs) + +#if INSTALLER + ,Path.Combine(CSettings.DataPath, CSettings.FolderSongs); +#endif + }; + /// + /// Folders with profiles + /// First one is used for new profiles + /// + public static readonly List ProfileFolders = new List + { +#if INSTALLER + Path.Combine(Environment.CurrentDirectory, CSettings.FolderProfiles), +#endif + Path.Combine(CSettings.DataPath, _FolderProfiles) + }; + // Game - public static readonly List SongFolder = new List(); 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 _Params = new List(); private static readonly List _Values = new List(); - //Variables to save old values for commandline-parameters - private static readonly List _SongFolderOld = new List(); 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 /// public static bool IsMicConfig() { - ReadOnlyCollection devices = CSound.RecordGetDevices(); + ReadOnlyCollection devices = CRecord.GetDevices(); if (devices == null) return false; @@ -640,7 +649,7 @@ namespace Vocaluxe.Base /// public static bool IsMicConfig(int player) { - ReadOnlyCollection devices = CSound.RecordGetDevices(); + ReadOnlyCollection 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 devices = CSound.RecordGetDevices(); + //SRecordDevice[] Devices = new SRecordDevice[CRecord.RecordGetDevices().Length]; + ReadOnlyCollection 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 /// 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; } } diff --git a/Vocaluxe/Base/CController.cs b/Vocaluxe/Base/CController.cs index ce70f459..604b17c0 100644 --- a/Vocaluxe/Base/CController.cs +++ b/Vocaluxe/Base/CController.cs @@ -36,19 +36,26 @@ namespace Vocaluxe.Base _KeysPool = new List(); _MousePool = new List(); - _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(); + if (_Controller != null) + { + foreach (IController controller in _Controller) + controller.Close(); + _Controller = null; + _KeysPool = null; + _MousePool = null; + } } public static void Connect() diff --git a/Vocaluxe/Base/CCover.cs b/Vocaluxe/Base/CCover.cs index a156fa8b..fddf06d2 100644 --- a/Vocaluxe/Base/CCover.cs +++ b/Vocaluxe/Base/CCover.cs @@ -36,11 +36,9 @@ namespace Vocaluxe.Base static class CCover { private static readonly XmlWriterSettings _Settings = new XmlWriterSettings(); - private static readonly Dictionary _Cover = new Dictionary(); + private static readonly Dictionary _Covers = new Dictionary(); private static readonly List _CoverThemes = new List(); - 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 /// public static CTexture Cover(string name) { - lock (_MutexCover) + lock (_Covers) { if (!_CoverExists(name)) return NoCover; - return _Cover[name]; + return _Covers[name]; } } /// /// Returns true if a cover with the given name exists. + /// MUST HOLD _Covers lock at this point /// private static bool _CoverExists(string name) { - lock (_MutexCover) - { - return _Cover.ContainsKey(name); - } + return _Covers.ContainsKey(name); } /// @@ -112,14 +108,15 @@ namespace Vocaluxe.Base /// public static void ReloadCover() { - List 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 coverPath = Path.Combine(CSettings.FolderCover, coverTheme.Folder); + List 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 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 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); + } } } + /// + /// Ads a cover with the given name if it does not exist yet + /// MUST HOLD _Covers lock at this point + /// + /// Name of the cover + /// Filename of image file 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; } } } diff --git a/Vocaluxe/Base/CCursor.cs b/Vocaluxe/Base/CCursor.cs index 94a1e495..ecb0738c 100644 --- a/Vocaluxe/Base/CCursor.cs +++ b/Vocaluxe/Base/CCursor.cs @@ -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); } diff --git a/Vocaluxe/Base/CDataBase.cs b/Vocaluxe/Base/CDataBase.cs index 888ea52d..3a512891 100644 --- a/Vocaluxe/Base/CDataBase.cs +++ b/Vocaluxe/Base/CDataBase.cs @@ -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() + /// + /// You have to hold the CoverMutex when calling this! + /// + 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; + } } } diff --git a/Vocaluxe/Base/CDraw.cs b/Vocaluxe/Base/CDraw.cs index e888cad3..f43d8542 100644 --- a/Vocaluxe/Base/CDraw.cs +++ b/Vocaluxe/Base/CDraw.cs @@ -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() diff --git a/Vocaluxe/Base/CGame.cs b/Vocaluxe/Base/CGame.cs index 009e585f..46df686f 100644 --- a/Vocaluxe/Base/CGame.cs +++ b/Vocaluxe/Base/CGame.cs @@ -15,6 +15,9 @@ // along with Vocaluxe. If not, see . #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) diff --git a/Vocaluxe/Base/CGraphics.cs b/Vocaluxe/Base/CGraphics.cs index 0966424a..06a28e45 100644 --- a/Vocaluxe/Base/CGraphics.cs +++ b/Vocaluxe/Base/CGraphics.cs @@ -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) { diff --git a/Vocaluxe/Base/CLog.cs b/Vocaluxe/Base/CLog.cs index 8afe6cd4..a0484670 100644 --- a/Vocaluxe/Base/CLog.cs +++ b/Vocaluxe/Base/CLog.cs @@ -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 } diff --git a/Vocaluxe/Base/CMain.cs b/Vocaluxe/Base/CMain.cs index e0f02c3d..a66a960d 100644 --- a/Vocaluxe/Base/CMain.cs +++ b/Vocaluxe/Base/CMain.cs @@ -118,7 +118,7 @@ namespace Vocaluxe.Base public IEnumerable 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); } diff --git a/Vocaluxe/Base/CParty.cs b/Vocaluxe/Base/CParty.cs index 5735786f..a52c0c60 100644 --- a/Vocaluxe/Base/CParty.cs +++ b/Vocaluxe/Base/CParty.cs @@ -223,6 +223,7 @@ namespace Vocaluxe.Base foreach (string file in files) { SPartyMode pm = _LoadPartyMode(file); + if(pm.NoErrors) _PartyModes.Add(pm.PartyModeID, pm); } } diff --git a/Vocaluxe/Base/CPlaylistElement.cs b/Vocaluxe/Base/CPlaylistElement.cs new file mode 100644 index 00000000..195be8e7 --- /dev/null +++ b/Vocaluxe/Base/CPlaylistElement.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Vocaluxe/Base/CPlaylists.cs b/Vocaluxe/Base/CPlaylists.cs index e73dbd2b..5d928c7c 100644 --- a/Vocaluxe/Base/CPlaylists.cs +++ b/Vocaluxe/Base/CPlaylists.cs @@ -58,7 +58,7 @@ namespace Vocaluxe.Base private static void _ConvertUSDXPlaylists() { var files = new List(); - 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(); var files = new List(); - 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) { diff --git a/Vocaluxe/Base/CProfiles.cs b/Vocaluxe/Base/CProfiles.cs index b4f4ae50..8c5c9887 100644 --- a/Vocaluxe/Base/CProfiles.cs +++ b/Vocaluxe/Base/CProfiles.cs @@ -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(); - 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(); - 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 } diff --git a/Vocaluxe/Base/CRecord.cs b/Vocaluxe/Base/CRecord.cs new file mode 100644 index 00000000..5af44fd1 --- /dev/null +++ b/Vocaluxe/Base/CRecord.cs @@ -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 GetDevices() + { + ReadOnlyCollection 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; + } + } +} \ No newline at end of file diff --git a/Vocaluxe/Base/CSettings.cs b/Vocaluxe/Base/CSettings.cs index e978691d..7c57abf1 100644 --- a/Vocaluxe/Base/CSettings.cs +++ b/Vocaluxe/Base/CSettings.cs @@ -33,6 +33,10 @@ namespace Vocaluxe.Base // ReSharper restore UnusedMember.Global } + /// + /// This class contains settings for the program + /// These are not editable, everything that can be changed by the user is in CConfig + /// 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 FoldersProfiles = new List - { -#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 MusicFileTypes = new List - { - "*.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 { - 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); + } + } } } } \ No newline at end of file diff --git a/Vocaluxe/Base/CSongCategorizer.cs b/Vocaluxe/Base/CSongCategorizer.cs index 74784cc2..8c68e615 100644 --- a/Vocaluxe/Base/CSongCategorizer.cs +++ b/Vocaluxe/Base/CSongCategorizer.cs @@ -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() + /// + /// Sets the SortStrings of the songpointers to the actual category names + /// + 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) diff --git a/Vocaluxe/Base/CSongSorter.cs b/Vocaluxe/Base/CSongSorter.cs index eefb796f..f1703224 100644 --- a/Vocaluxe/Base/CSongSorter.cs +++ b/Vocaluxe/Base/CSongSorter.cs @@ -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 _CreateSortList(string fieldName) + private void _AddSongToList(CSong song, List list) { - var sortList = new List(); - if (fieldName == "") - CSongs.Filter.FilteredSongs.ForEach(song => sortList.Add(new CSongPointer(song.ID, ""))); + string value = null; + List 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)) - 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)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 sortList = _CreateSortList(fieldName); + + List sortList = new List(); + foreach (CSong song in CSongs.Filter.FilteredSongs) + _AddSongToList(song, sortList); switch (_SongSorting) { case ESongSorting.TR_CONFIG_ARTIST_LETTER: diff --git a/Vocaluxe/Base/CSongs.cs b/Vocaluxe/Base/CSongs.cs index 14b2100e..36d0d6f2 100644 --- a/Vocaluxe/Base/CSongs.cs +++ b/Vocaluxe/Base/CSongs.cs @@ -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(); - 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(); } diff --git a/Vocaluxe/Base/CSound.cs b/Vocaluxe/Base/CSound.cs index d6b55cbe..a09ba20a 100644 --- a/Vocaluxe/Base/CSound.cs +++ b/Vocaluxe/Base/CSound.cs @@ -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 RecordGetDevices() - { - ReadOnlyCollection 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 } } \ No newline at end of file diff --git a/Vocaluxe/Base/CTheme.cs b/Vocaluxe/Base/CTheme.cs index 35180bcb..4f9dd431 100644 --- a/Vocaluxe/Base/CTheme.cs +++ b/Vocaluxe/Base/CTheme.cs @@ -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) diff --git a/Vocaluxe/Base/CVideo.cs b/Vocaluxe/Base/CVideo.cs index 76a7ce5e..3dee6082 100644 --- a/Vocaluxe/Base/CVideo.cs +++ b/Vocaluxe/Base/CVideo.cs @@ -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() diff --git a/Vocaluxe/Base/CWebcam.cs b/Vocaluxe/Base/CWebcam.cs index dac871aa..0b7dcb46 100644 --- a/Vocaluxe/Base/CWebcam.cs +++ b/Vocaluxe/Base/CWebcam.cs @@ -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() diff --git a/Vocaluxe/Base/Fonts/CFonts.cs b/Vocaluxe/Base/Fonts/CFonts.cs index 8b561072..1c904e98 100644 --- a/Vocaluxe/Base/Fonts/CFonts.cs +++ b/Vocaluxe/Base/Fonts/CFonts.cs @@ -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"); } /// @@ -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) diff --git a/Vocaluxe/Base/Fonts/CGlyph.cs b/Vocaluxe/Base/Fonts/CGlyph.cs index cab5d9b6..c694c84d 100644 --- a/Vocaluxe/Base/Fonts/CGlyph.cs +++ b/Vocaluxe/Base/Fonts/CGlyph.cs @@ -15,6 +15,9 @@ // along with Vocaluxe. If not, see . #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 } } } diff --git a/Vocaluxe/Base/Server/CVocaluxeServer.cs b/Vocaluxe/Base/Server/CVocaluxeServer.cs index 154dd1db..19f91e62 100644 --- a/Vocaluxe/Base/Server/CVocaluxeServer.cs +++ b/Vocaluxe/Base/Server/CVocaluxeServer.cs @@ -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; diff --git a/Vocaluxe/COSFunctions.cs b/Vocaluxe/COSFunctions.cs new file mode 100644 index 00000000..472956c6 --- /dev/null +++ b/Vocaluxe/COSFunctions.cs @@ -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 + } + } +} \ No newline at end of file diff --git a/Vocaluxe/CProgrammHelper.cs b/Vocaluxe/CProgrammHelper.cs index f4425776..800b907a 100644 --- a/Vocaluxe/CProgrammHelper.cs +++ b/Vocaluxe/CProgrammHelper.cs @@ -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; + } + /// /// Checks if a given program is installed by checking the Uninstall registry key. /// @@ -56,10 +67,11 @@ namespace Vocaluxe /// 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"); + } } } \ No newline at end of file diff --git a/Vocaluxe/GlobalSuppressions.cs b/Vocaluxe/GlobalSuppressions.cs index da284174..8a667a11 100644 --- a/Vocaluxe/GlobalSuppressions.cs +++ b/Vocaluxe/GlobalSuppressions.cs @@ -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)")] \ No newline at end of file + Target = "Vocaluxe.Lib.Input.CHIDAPI.#Read(System.IntPtr,System.Byte[]&,System.Int32)")] \ No newline at end of file diff --git a/Vocaluxe/Lib/Draw/CDirect3D.cs b/Vocaluxe/Lib/Draw/CDirect3D.cs index 11b11c21..032d952b 100644 --- a/Vocaluxe/Lib/Draw/CDirect3D.cs +++ b/Vocaluxe/Lib/Draw/CDirect3D.cs @@ -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(); + ; } /// @@ -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(); diff --git a/Vocaluxe/Lib/Draw/CDrawWinForm.cs b/Vocaluxe/Lib/Draw/CDrawWinForm.cs index d5309d25..c1fd96b9 100644 --- a/Vocaluxe/Lib/Draw/CDrawWinForm.cs +++ b/Vocaluxe/Lib/Draw/CDrawWinForm.cs @@ -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()) diff --git a/Vocaluxe/Lib/Draw/COpenGL.cs b/Vocaluxe/Lib/Draw/COpenGL.cs index f80147d5..5b7e19ae 100644 --- a/Vocaluxe/Lib/Draw/COpenGL.cs +++ b/Vocaluxe/Lib/Draw/COpenGL.cs @@ -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(); diff --git a/Vocaluxe/Lib/Input/CControllerFramework.cs b/Vocaluxe/Lib/Input/CControllerFramework.cs index 265c0858..2ac282d1 100644 --- a/Vocaluxe/Lib/Input/CControllerFramework.cs +++ b/Vocaluxe/Lib/Input/CControllerFramework.cs @@ -15,51 +15,63 @@ // along with Vocaluxe. If not, see . #endregion -using System; using System.Collections.Generic; using VocaluxeLib; namespace Vocaluxe.Lib.Input { - public class CControllerFramework : IController + public abstract class CControllerFramework : IController { - private List _KeysPool; + protected bool _Initialized; + private readonly List _KeysPool = new List(); private List _CurrentKeysPool; - private readonly Object _KeyCopyLock = new Object(); - private List _MousePool; + private readonly List _MousePool = new List(); private List _CurrentMousePool; - private readonly Object _MouseCopyLock = new Object(); - public virtual void Init() + public abstract string GetName(); + + public virtual bool Init() { - _KeysPool = new List(); + if (_Initialized) + return false; _CurrentKeysPool = new List(); - _MousePool = new List(); _CurrentMousePool = new List(); - } - - 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); diff --git a/Vocaluxe/Lib/Input/IController.cs b/Vocaluxe/Lib/Input/IController.cs index 7ad6c39b..cd6bec01 100644 --- a/Vocaluxe/Lib/Input/IController.cs +++ b/Vocaluxe/Lib/Input/IController.cs @@ -32,7 +32,8 @@ namespace Vocaluxe.Lib.Input interface IController { - void Init(); + string GetName(); + bool Init(); void Close(); void Connect(); void Disconnect(); diff --git a/Vocaluxe/Lib/Input/WiiMote/CWiiMote.cs b/Vocaluxe/Lib/Input/WiiMote/CWiiMote.cs index 6aa70841..fc29b7dc 100644 --- a/Vocaluxe/Lib/Input/WiiMote/CWiiMote.cs +++ b/Vocaluxe/Lib/Input/WiiMote/CWiiMote.cs @@ -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 _KeysPool; - private List _CurrentKeysPool; - private readonly Object _KeyCopyLock = new Object(); - - private List _MousePool; - private List _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(); - _CurrentKeysPool = new List(); - - _MousePool = new List(); - _CurrentMousePool = new List(); + _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(); - } - } } } \ No newline at end of file diff --git a/Vocaluxe/Lib/Playlist/CPlaylistFile.cs b/Vocaluxe/Lib/Playlist/CPlaylistFile.cs index 4ab37b0f..ef439692 100644 --- a/Vocaluxe/Lib/Playlist/CPlaylistFile.cs +++ b/Vocaluxe/Lib/Playlist/CPlaylistFile.cs @@ -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; diff --git a/Vocaluxe/Lib/Sound/CBuffer.cs b/Vocaluxe/Lib/Sound/CBuffer.cs deleted file mode 100644 index 34831ccd..00000000 --- a/Vocaluxe/Lib/Sound/CBuffer.cs +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/CDirectSoundRecord.cs b/Vocaluxe/Lib/Sound/CDirectSoundRecord.cs deleted file mode 100644 index eabf0d73..00000000 --- a/Vocaluxe/Lib/Sound/CDirectSoundRecord.cs +++ /dev/null @@ -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 . -#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 _Devices; - private List _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(); - _Sources = new List(); - - 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 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 _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 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(); - - 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; - } - } - } - } - } -} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/CGstreamerSharpAudio.cs b/Vocaluxe/Lib/Sound/CGstreamerSharpAudio.cs deleted file mode 100644 index dc856e98..00000000 --- a/Vocaluxe/Lib/Sound/CGstreamerSharpAudio.cs +++ /dev/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 . -#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 _Streams = new Dictionary(); - 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(); - foreach (KeyValuePair 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; - } - } - } - } - } -} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/CPortAudioHandle.cs b/Vocaluxe/Lib/Sound/CPortAudioHandle.cs new file mode 100644 index 00000000..0406055e --- /dev/null +++ b/Vocaluxe/Lib/Sound/CPortAudioHandle.cs @@ -0,0 +1,203 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using PortAudioSharp; +using Vocaluxe.Base; + +namespace Vocaluxe.Lib.Sound +{ + /// + /// 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 + /// + class CPortAudioHandle : IDisposable + { + private static int _RefCount; + private static readonly object _Mutex = new object(); + + private bool _Disposed; + private readonly List _Streams = new List(); + + /// + /// Initializes PortAudio library (if required) + /// + 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); + } + + /// + /// Close the PortAudio handle once you are done + /// + 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; + } + } + + /// + /// Convenience method to safely open an input stream and log potential error + /// + /// + /// + /// + /// + /// + /// + /// + /// True on success + 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)); + } + + /// + /// Convenience method to safely open an output stream and log potential error + /// + /// + /// + /// + /// + /// + /// + /// + /// True on success + 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); + } + } + + /// + /// Checks if PA returned an error and logs it + /// Returns true on error + /// + /// Action identifier (E.g. openStream) + /// Result returned by Pa_* call + /// True on error + 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; + } + + /// + /// Selects the most appropriate host api + /// + /// The most appropriate host api + 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; + } + } +} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/CPortAudioPlay.cs b/Vocaluxe/Lib/Sound/CPortAudioPlay.cs deleted file mode 100644 index 2be82c2c..00000000 --- a/Vocaluxe/Lib/Sound/CPortAudioPlay.cs +++ /dev/null @@ -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 . -#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 _Decoder = new List(); - private Closeproc _Closeproc; - private int _Count = 1; - - private readonly Object _MutexDecoder = new Object(); - - private List _Streams; - - public bool Init() - { - if (_Initialized) - CloseAll(); - - _Closeproc = _CloseProc; - _Initialized = true; - - _Streams = new List(); - 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); - } - } - } -} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/CPortAudioRecord.cs b/Vocaluxe/Lib/Sound/CPortAudioRecord.cs deleted file mode 100644 index 37d6c808..00000000 --- a/Vocaluxe/Lib/Sound/CPortAudioRecord.cs +++ /dev/null @@ -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 . -#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 _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(); - } - - /// - /// Init PortAudio and list record devices - /// - /// true if success - public bool Init() - { - _Devices = new List(); - - 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; - } - - /// - /// Start Voice Capturing - /// - /// - 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; - } - - /// - /// Stop Voice Capturing - /// - /// - public bool Stop() - { - if (!_Initialized) - return false; - - foreach (IntPtr handle in _RecHandle) - CPortAudio.Pa_StopStream(handle); - return true; - } - - /// - /// Stop all voice capturing streams and terminate PortAudio - /// - public void CloseAll() - { - Stop(); - - if (_Initialized) - { - CPortAudio.Pa_Terminate(); - _Initialized = false; - } - - //System.IO.File.WriteAllBytes("test0.raw", _Buffer[0].Buffer); - } - - /// - /// Detect Pitch and Volume of the newest voice buffer - /// - /// - 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 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; - } - } -} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/IRecord.cs b/Vocaluxe/Lib/Sound/IRecord.cs deleted file mode 100644 index f4568a93..00000000 --- a/Vocaluxe/Lib/Sound/IRecord.cs +++ /dev/null @@ -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 . -#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 RecordDevices(); - } -} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/Decoder/CAudioDecoderFFmpeg.cs b/Vocaluxe/Lib/Sound/Playback/Decoder/CAudioDecoderFFmpeg.cs similarity index 99% rename from Vocaluxe/Lib/Sound/Decoder/CAudioDecoderFFmpeg.cs rename to Vocaluxe/Lib/Sound/Playback/Decoder/CAudioDecoderFFmpeg.cs index d1503dfc..1668deb0 100644 --- a/Vocaluxe/Lib/Sound/Decoder/CAudioDecoderFFmpeg.cs +++ b/Vocaluxe/Lib/Sound/Playback/Decoder/CAudioDecoderFFmpeg.cs @@ -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 { diff --git a/Vocaluxe/Lib/Sound/Decoder/IAudioDecoder.cs b/Vocaluxe/Lib/Sound/Playback/Decoder/IAudioDecoder.cs similarity index 96% rename from Vocaluxe/Lib/Sound/Decoder/IAudioDecoder.cs rename to Vocaluxe/Lib/Sound/Playback/Decoder/IAudioDecoder.cs index 7308ba09..6187c279 100644 --- a/Vocaluxe/Lib/Sound/Decoder/IAudioDecoder.cs +++ b/Vocaluxe/Lib/Sound/Playback/Decoder/IAudioDecoder.cs @@ -15,7 +15,7 @@ // along with Vocaluxe. If not, see . #endregion -namespace Vocaluxe.Lib.Sound.Decoder +namespace Vocaluxe.Lib.Sound.Playback.Decoder { struct SFormatInfo { diff --git a/Vocaluxe/Lib/Sound/CGstreamerAudio.cs b/Vocaluxe/Lib/Sound/Playback/Gstreamer/CGstreamerAudio.cs similarity index 98% rename from Vocaluxe/Lib/Sound/CGstreamerAudio.cs rename to Vocaluxe/Lib/Sound/Playback/Gstreamer/CGstreamerAudio.cs index e29937de..66a30301 100644 --- a/Vocaluxe/Lib/Sound/CGstreamerAudio.cs +++ b/Vocaluxe/Lib/Sound/Playback/Gstreamer/CGstreamerAudio.cs @@ -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 { diff --git a/Vocaluxe/Lib/Sound/Gstreamer/CGstreamerAudioWrapper.cs b/Vocaluxe/Lib/Sound/Playback/Gstreamer/CGstreamerAudioWrapper.cs similarity index 99% rename from Vocaluxe/Lib/Sound/Gstreamer/CGstreamerAudioWrapper.cs rename to Vocaluxe/Lib/Sound/Playback/Gstreamer/CGstreamerAudioWrapper.cs index e4db4114..bf600899 100644 --- a/Vocaluxe/Lib/Sound/Gstreamer/CGstreamerAudioWrapper.cs +++ b/Vocaluxe/Lib/Sound/Playback/Gstreamer/CGstreamerAudioWrapper.cs @@ -17,7 +17,7 @@ using System.Runtime.InteropServices; -namespace Vocaluxe.Lib.Sound.Gstreamer +namespace Vocaluxe.Lib.Sound.Playback.Gstreamer { public static class CGstreamerAudioWrapper { diff --git a/Vocaluxe/Lib/Sound/Playback/GstreamerSharp/CGstreamerSharpAudio.cs b/Vocaluxe/Lib/Sound/Playback/GstreamerSharp/CGstreamerSharpAudio.cs new file mode 100644 index 00000000..640b0c8d --- /dev/null +++ b/Vocaluxe/Lib/Sound/Playback/GstreamerSharp/CGstreamerSharpAudio.cs @@ -0,0 +1,205 @@ +#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 . +#endregion + +using System.Collections.Generic; +using Gst; + +namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp +{ + public class CGstreamerSharpAudio : IPlayback + { + private readonly Dictionary _Streams = new Dictionary(); + private static int _IDCount; + + 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(); + foreach (KeyValuePair stream in _Streams) + { + if (stream.Value.Closed) + streamsToDelete.Add(stream.Key); + stream.Value.Update(); + } + foreach (int key in streamsToDelete) + _Streams.Remove(key); + } + } +} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/Playback/GstreamerSharp/CGstreamerSharpAudioStream.cs b/Vocaluxe/Lib/Sound/Playback/GstreamerSharp/CGstreamerSharpAudioStream.cs new file mode 100644 index 00000000..bbcbd725 --- /dev/null +++ b/Vocaluxe/Lib/Sound/Playback/GstreamerSharp/CGstreamerSharpAudioStream.cs @@ -0,0 +1,297 @@ +using System; +using GLib; +using Gst; +using Vocaluxe.Base; +using VocaluxeLib; + +namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp +{ + public 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 System.Threading.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) + { + targetVolume.Clamp(0f, 100f); + _Fading = new CFading(_Volume, targetVolume / 100f, 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 + { + value.Clamp(0f, 100f); + _Volume = value / 100f; + _SetElementVolume(); + } + } + + private void _SetElementVolume() + { + if (_Element != null) + _Element["volume"] = (_Volume * _MaxVolume); + } + + public float MaxVolume + { + get { return _MaxVolume * 100f; } + set + { + value.Clamp(0f, 100f); + _MaxVolume = value / 100f; + _SetElementVolume(); + } + } + + public float Length + { + get + { + if (_Duration < 0 && !_QueryingDuration) + { + _QueryingDuration = true; // Set this to avoid race conditions + var t = new System.Threading.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); + _SetElementVolume(); + 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; + } + } + } + } +} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/IPlayback.cs b/Vocaluxe/Lib/Sound/Playback/IPlayback.cs similarity index 97% rename from Vocaluxe/Lib/Sound/IPlayback.cs rename to Vocaluxe/Lib/Sound/Playback/IPlayback.cs index 22d7d7b7..13ce9857 100644 --- a/Vocaluxe/Lib/Sound/IPlayback.cs +++ b/Vocaluxe/Lib/Sound/Playback/IPlayback.cs @@ -15,7 +15,7 @@ // along with Vocaluxe. If not, see . #endregion -namespace Vocaluxe.Lib.Sound +namespace Vocaluxe.Lib.Sound.Playback { public enum EStreamAction { @@ -35,7 +35,7 @@ namespace Vocaluxe.Lib.Sound } } - delegate void Closeproc(int streamID); + delegate void Closeproc(int stream); interface IPlayback { diff --git a/Vocaluxe/Lib/Sound/Playback/OpenAL/COpenALPlay.cs b/Vocaluxe/Lib/Sound/Playback/OpenAL/COpenALPlay.cs new file mode 100644 index 00000000..ac7b533b --- /dev/null +++ b/Vocaluxe/Lib/Sound/Playback/OpenAL/COpenALPlay.cs @@ -0,0 +1,349 @@ +#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 . +#endregion + +using System.Linq; +using OpenTK.Audio; +using System; +using System.Collections.Generic; + +namespace Vocaluxe.Lib.Sound.Playback.OpenAL +{ + class COpenALPlay : IPlayback, IDisposable + { + private bool _Initialized; + private readonly List _Decoder = new List(); + private Closeproc _Closeproc; + private int _Count = 1; + private AudioContext _Context; + + private readonly Object _MutexDecoder = new Object(); + + private List _Streams; + + public bool Init() + { + if (_Initialized) + CloseAll(); + + _Context = new AudioContext(); + + _Context.MakeCurrent(); + + + _Closeproc = _CloseProc; + _Initialized = true; + + _Streams = new List(); + return true; + } + + public void CloseAll() + { + lock (_MutexDecoder) + { + for (int i = 0; i < _Decoder.Count; i++) + _Decoder[i].Free(_Closeproc, i + 1, _MutexDecoder); + } + } + + 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() + { + if (_Initialized) + { + lock (_MutexDecoder) + { + foreach (COpenAlStream stream in _Decoder) + stream.UploadData(); + } + } + } + + #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 COpenAlStream(); + + 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, _MutexDecoder); + } + } + } + + 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)].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); + } + } + } + } + + public void Dispose() + { + _Context.Dispose(); + _Context = null; + } + + public void FadeAndStop(int stream, float targetVolume, float seconds) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/COpenALPlay.cs b/Vocaluxe/Lib/Sound/Playback/OpenAL/COpenAlStream.cs similarity index 51% rename from Vocaluxe/Lib/Sound/COpenALPlay.cs rename to Vocaluxe/Lib/Sound/Playback/OpenAL/COpenAlStream.cs index 6ab9f2b4..f3742b7d 100644 --- a/Vocaluxe/Lib/Sound/COpenALPlay.cs +++ b/Vocaluxe/Lib/Sound/Playback/OpenAL/COpenAlStream.cs @@ -1,358 +1,14 @@ -#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 . -#endregion - -using System.Linq; -using OpenTK.Audio; -using System; -using System.Collections.Generic; +using System; using System.Diagnostics; using System.IO; using System.Threading; +using OpenTK.Audio; using Vocaluxe.Base; -using Vocaluxe.Lib.Sound.Decoder; +using Vocaluxe.Lib.Sound.Playback.Decoder; using VocaluxeLib; -namespace Vocaluxe.Lib.Sound +namespace Vocaluxe.Lib.Sound.Playback.OpenAL { - class COpenALPlay : IPlayback, IDisposable - { - private bool _Initialized; - private readonly List _Decoder = new List(); - private Closeproc _Closeproc; - private int _Count = 1; - private AudioContext _Context; - - private readonly Object _MutexDecoder = new Object(); - - private List _Streams; - - public bool Init() - { - if (_Initialized) - CloseAll(); - - _Context = new AudioContext(); - - _Context.MakeCurrent(); - - - _Closeproc = _CloseProc; - _Initialized = true; - - _Streams = new List(); - return true; - } - - public void CloseAll() - { - lock (_MutexDecoder) - { - for (int i = 0; i < _Decoder.Count; i++) - _Decoder[i].Free(_Closeproc, i + 1, _MutexDecoder); - } - } - - 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() - { - if (_Initialized) - { - lock (_MutexDecoder) - { - foreach (COpenAlStream stream in _Decoder) - stream.UploadData(); - } - } - } - - #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 COpenAlStream(); - - 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, _MutexDecoder); - } - } - } - - 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)].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); - } - } - } - } - - public void Dispose() - { - _Context.Dispose(); - _Context = null; - } - - public void FadeAndStop(int stream, float targetVolume, float seconds) - { - throw new NotImplementedException(); - } - } - class COpenAlStream : IDisposable { private const int _BufferSize = 2048; @@ -401,19 +57,13 @@ namespace Vocaluxe.Lib.Sound private bool _SetSkip; private bool _Terminated; - private readonly Thread _DecoderThread; + private Thread _DecoderThread; private AutoResetEvent _EventDecode = new AutoResetEvent(false); private readonly Object _MutexData = new Object(); private readonly Object _MutexSyncSignals = new Object(); - public COpenAlStream() - { - _Initialized = false; - _DecoderThread = new Thread(_Execute) {Name = "OpenAL Decode"}; - } - public void Free(Closeproc closeProc, int streamID, Object closeMutex) { _Closeproc = closeProc; @@ -451,12 +101,8 @@ namespace Vocaluxe.Lib.Sound { lock (_MutexData) { + value.Clamp(0f, 100f); _Volume = value / 100f; - if (_Volume < 0f) - _Volume = 0f; - - if (_Volume > 1f) - _Volume = 1f; } } } @@ -468,12 +114,8 @@ namespace Vocaluxe.Lib.Sound { lock (_MutexData) { + value.Clamp(0f, 100f); _VolumeMax = value / 100f; - if (_VolumeMax < 0f) - _VolumeMax = 0f; - - if (_VolumeMax > 1f) - _VolumeMax = 1f; } } } @@ -514,7 +156,8 @@ namespace Vocaluxe.Lib.Sound public void Fade(float targetVolume, float fadeTime) { - _Fading = new CFading(_Volume, targetVolume, fadeTime); + targetVolume.Clamp(0f, 100f); + _Fading = new CFading(_Volume, targetVolume / 100f, fadeTime); _AfterFadeAction = EStreamAction.Nothing; } @@ -595,8 +238,7 @@ namespace Vocaluxe.Lib.Sound _FileOpened = true; _Data = new CRingBuffer(_Bufsize); _NoMoreData = false; - _DecoderThread.Priority = ThreadPriority.Normal; - _DecoderThread.Name = Path.GetFileName(fileName); + _DecoderThread = new Thread(_Execute) {Priority = ThreadPriority.Normal, Name = Path.GetFileName(fileName)}; _DecoderThread.Start(); return stream.Handle; @@ -772,7 +414,7 @@ namespace Vocaluxe.Lib.Sound { AL.GetSource(_Source, ALGetSourcei.BuffersProcessed, out processedCount); doit = false; - Console.WriteLine("Buffers Processed on Stream " + _Source + " = " + processedCount); + //Console.WriteLine("Buffers Processed on Stream " + _Source + " = " + processedCount); if (processedCount < 1) return; } @@ -787,29 +429,15 @@ namespace Vocaluxe.Lib.Sound { _Data.Read(buf); - - var b = new byte[2]; - for (int i = 0; i < buf.Length; i += _ByteCount) + //We want to scale all values. No matter how many channels we have (_ByteCount=2 or 4) we have short values + //So just process 2 bytes a time + for (int i = 0; i < buf.Length; i += 2) { - b[0] = buf[i]; - b[1] = buf[i + 1]; - - b = BitConverter.GetBytes((Int16)(BitConverter.ToInt16(b, 0) * _Volume * _VolumeMax)); + byte[] b = BitConverter.GetBytes((Int16)(BitConverter.ToInt16(buf, i) * _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]; - } } - int buffer; if (!doit) buffer = AL.SourceUnqueueBuffer(_Source); @@ -823,7 +451,6 @@ namespace Vocaluxe.Lib.Sound { ALFormat alFormat = (_Format.ChannelCount == 2) ? ALFormat.Stereo16 : ALFormat.Mono16; AL.BufferData(buffer, alFormat, buf, buf.Length, _Format.SamplesPerSecond); - Console.WriteLine("Write to Buffer: " + buffer); AL.SourceQueueBuffer(_Source, buffer); } } @@ -835,8 +462,7 @@ namespace Vocaluxe.Lib.Sound } _CurrentTime = _TimeCode - _Data.BytesNotRead / _BytesPerSecond - 0.1f; - _Timer.Reset(); - _Timer.Start(); + _Timer.Restart(); } public void Dispose() diff --git a/Vocaluxe/Lib/Sound/Playback/PortAudio/CPortAudioPlay.cs b/Vocaluxe/Lib/Sound/Playback/PortAudio/CPortAudioPlay.cs new file mode 100644 index 00000000..86f62f4d --- /dev/null +++ b/Vocaluxe/Lib/Sound/Playback/PortAudio/CPortAudioPlay.cs @@ -0,0 +1,331 @@ +#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 . +#endregion + +using System.Collections.Generic; + +namespace Vocaluxe.Lib.Sound.Playback.PortAudio +{ + class CPortAudioPlay : IPlayback + { + private bool _Initialized; + private readonly List _Streams = new List(); + private int _NextID; + + public bool Init() + { + if (_Initialized) + CloseAll(); + + _Initialized = true; + + return true; + } + + public void CloseAll() + { + lock (_Streams) + { + foreach (CPortAudioStream stream in _Streams) + stream.Free(); + _Streams.Clear(); + } + _Initialized = false; + } + + public void SetGlobalVolume(float volume) + { + if (_Initialized) + { + //Bass.BASS_SetVolume(Volume / 100f); + } + } + + public int GetStreamCount() + { + if (!_Initialized) + return 0; + + lock (_Streams) + { + 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) + { + //Note: We ignore prescan here as the stream does it always + + var stream = new CPortAudioStream(_NextID++, _CloseProc); + + if (stream.Open(media)) + { + lock (_Streams) + { + _Streams.Add(stream); + return stream.ID; + } + } + return 0; + } + + public void Close(int stream) + { + if (!_Initialized) + return; + lock (_Streams) + { + int index = _GetStreamIndex(stream); + if (index >= 0) + { + _Streams[index].Free(); + _Streams.RemoveAt(index); + } + } + } + + public void Play(int stream) + { + Play(stream, false); + } + + public void Play(int stream, bool loop) + { + if (!_Initialized) + return; + lock (_Streams) + { + int index = _GetStreamIndex(stream); + if (index >= 0) + { + _Streams[index].Loop = loop; + _Streams[index].Play(); + } + } + } + + public void Pause(int stream) + { + if (!_Initialized) + return; + lock (_Streams) + { + if (_AlreadyAdded(stream)) + _Streams[_GetStreamIndex(stream)].Paused = true; + } + } + + public void Stop(int stream) + { + if (!_Initialized) + return; + lock (_Streams) + { + if (_AlreadyAdded(stream)) + _Streams[_GetStreamIndex(stream)].Stop(); + } + } + + public void Fade(int stream, float targetVolume, float seconds) + { + if (!_Initialized) + return; + lock (_Streams) + { + if (_AlreadyAdded(stream)) + _Streams[_GetStreamIndex(stream)].Fade(targetVolume, seconds); + } + } + + public void FadeAndPause(int stream, float targetVolume, float seconds) + { + if (!_Initialized) + return; + lock (_Streams) + { + if (_AlreadyAdded(stream)) + _Streams[_GetStreamIndex(stream)].FadeAndPause(targetVolume, seconds); + } + } + + public void FadeAndClose(int stream, float targetVolume, float seconds) + { + if (!_Initialized) + return; + lock (_Streams) + { + if (_AlreadyAdded(stream)) + _Streams[_GetStreamIndex(stream)].FadeAndClose(targetVolume, seconds); + } + } + + public void FadeAndStop(int stream, float targetVolume, float seconds) + { + if (!_Initialized) + return; + lock (_Streams) + { + if (_AlreadyAdded(stream)) + _Streams[_GetStreamIndex(stream)].FadeAndStop(targetVolume, seconds); + } + } + + public void SetStreamVolume(int stream, float volume) + { + if (!_Initialized) + return; + lock (_Streams) + { + if (_AlreadyAdded(stream)) + _Streams[_GetStreamIndex(stream)].Volume = volume; + } + } + + public void SetStreamVolumeMax(int stream, float volume) + { + if (!_Initialized) + return; + lock (_Streams) + { + if (_AlreadyAdded(stream)) + _Streams[_GetStreamIndex(stream)].VolumeMax = volume; + } + } + + public float GetLength(int stream) + { + if (!_Initialized) + return 0f; + lock (_Streams) + { + if (_AlreadyAdded(stream)) + return _Streams[_GetStreamIndex(stream)].Length; + } + return 0f; + } + + public float GetPosition(int stream) + { + if (!_Initialized) + return 0f; + lock (_Streams) + { + if (_AlreadyAdded(stream)) + return _Streams[_GetStreamIndex(stream)].Position; + } + + return 0f; + } + + public bool IsPlaying(int stream) + { + if (!_Initialized) + return false; + lock (_Streams) + { + if (_AlreadyAdded(stream)) + return !_Streams[_GetStreamIndex(stream)].Paused && !_Streams[_GetStreamIndex(stream)].Finished; + } + return false; + } + + public bool IsPaused(int stream) + { + if (!_Initialized) + return false; + lock (_Streams) + { + if (_AlreadyAdded(stream)) + return _Streams[_GetStreamIndex(stream)].Paused; + } + return false; + } + + public bool IsFinished(int stream) + { + if (!_Initialized) + return true; + lock (_Streams) + { + if (_AlreadyAdded(stream)) + return _Streams[_GetStreamIndex(stream)].Finished; + } + return true; + } + + public void SetPosition(int stream, float position) + { + if (!_Initialized) + return; + lock (_Streams) + { + if (_AlreadyAdded(stream)) + _Streams[_GetStreamIndex(stream)].Skip(position); + } + } + #endregion Stream Handling + + /// + /// Returns true if strem with given id is found + /// MUST hold _Stream-lock + /// + /// Stream id + /// + private bool _AlreadyAdded(int stream) + { + return _GetStreamIndex(stream) >= 0; + } + + /// + /// Returns the index of the stream with the given id + /// MUST hold _Stream-lock + /// + /// Stream id + /// + private int _GetStreamIndex(int stream) + { + for (int i = 0; i < _Streams.Count; i++) + { + if (_Streams[i].ID == stream) + return i; + } + return -1; + } + + /// + /// Removes the stream with the given handle + /// + /// Stream handle + private void _CloseProc(int stream) + { + if (!_Initialized) + return; + lock (_Streams) + { + if (_AlreadyAdded(stream)) + _Streams.RemoveAt(_GetStreamIndex(stream)); + } + } + } +} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/Playback/PortAudio/CPortAudioStream.cs b/Vocaluxe/Lib/Sound/Playback/PortAudio/CPortAudioStream.cs new file mode 100644 index 00000000..cd2328e2 --- /dev/null +++ b/Vocaluxe/Lib/Sound/Playback/PortAudio/CPortAudioStream.cs @@ -0,0 +1,528 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; +using Vocaluxe.Base; +using Vocaluxe.Lib.Sound.Playback.Decoder; +using VocaluxeLib; + +namespace Vocaluxe.Lib.Sound.Playback.PortAudio +{ + class CPortAudioStream : IDisposable + { + private const long _Bufsize = 1000000L; + private const long _Beginrefill = 800000L; + + public readonly int ID; + private readonly Closeproc _Closeproc; + private CPortAudioHandle _PaHandle; + + private readonly CSyncTimer _SyncTimer; + private int _ByteCount = 4; + private float _Volume = 1f; + private float _VolumeMax = 1f; + + private EStreamAction _AfterFadeAction; + private CFading _Fading; + + private static PortAudioSharp.PortAudio.PaHostApiInfo _ApiInfo; + private static PortAudioSharp.PortAudio.PaDeviceInfo _OutputDeviceInfo; + private IntPtr _Stream = IntPtr.Zero; + + private PortAudioSharp.PortAudio.PaStreamCallbackDelegate _PaStreamCallback; + 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 Thread _DecoderThread; + + private AutoResetEvent _EventDecode = new AutoResetEvent(false); + + private readonly Object _LockData = new Object(); + private readonly Object _LockSyncSignals = new Object(); + + public CPortAudioStream(int id, Closeproc closeproc) + { + ID = id; + _Closeproc = closeproc; + _SyncTimer = new CSyncTimer(0f, 1f, 0.02f); + } + + ~CPortAudioStream() + { + Dispose(); + } + + public void Free() + { + _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) + { + value.Clamp(0f, 100f); + _Volume = value / 100f; + } + } + } + + public float VolumeMax + { + get { return _VolumeMax * 100f; } + set + { + lock (_LockData) + { + value.Clamp(0f, 100f); + _VolumeMax = value / 100f; + } + } + } + + public float Position + { + get + { + lock (_LockData) + { + if (Finished) + _SyncTimer.Pause(); + //TODO: Why not use Decoder.GetPosition()? + //Decoder may return wrong timestamps. If you change this to exessive testing for monoton timestamps espacially for ogg files + 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) + { + targetVolume = targetVolume.Clamp(0f, 100f); + _Fading = new CFading(_Volume, targetVolume / 100f, fadeTime); + _AfterFadeAction = EStreamAction.Nothing; + } + + public void FadeAndPause(float targetVolume, float fadeTime) + { + Fade(targetVolume, fadeTime); + _AfterFadeAction = EStreamAction.Pause; + } + + public void FadeAndClose(float targetVolume, float fadeTime) + { + Fade(targetVolume, fadeTime); + _AfterFadeAction = EStreamAction.Close; + } + + public void FadeAndStop(float targetVolume, float fadeTime) + { + Fade(targetVolume, fadeTime); + _AfterFadeAction = EStreamAction.Stop; + } + + public void Play() + { + Paused = false; + _AfterFadeAction = EStreamAction.Nothing; + _PaHandle.CheckError("StartStream", PortAudioSharp.PortAudio.Pa_StartStream(_Stream)); + } + + public void Stop() + { + _PaHandle.CheckError("StopStream (playback)", PortAudioSharp.PortAudio.Pa_StopStream(_Stream)); + Skip(0f); + } + + public bool Loop + { + get { return _SetLoop; } + set { _SetLoop = value; } + } + + public bool Open(string fileName) + { + if (_FileOpened) + return false; + + if (!File.Exists(fileName)) + return false; + + try + { + _PaHandle = new CPortAudioHandle(); + + int hostApi = _PaHandle.GetHostApi(); + _ApiInfo = PortAudioSharp.PortAudio.Pa_GetHostApiInfo(hostApi); + _OutputDeviceInfo = PortAudioSharp.PortAudio.Pa_GetDeviceInfo(_ApiInfo.defaultOutputDevice); + if (_OutputDeviceInfo.defaultLowOutputLatency < 0.1) + _OutputDeviceInfo.defaultLowOutputLatency = 0.1; + + _PaStreamCallback = _ProcessNewData; + } + catch (Exception) + { + if (_PaHandle != null) + { + _PaHandle.Close(); + _PaHandle = null; + } + CLog.LogError("Error Init PortAudio Playback"); + return false; + } + + _Decoder = new CAudioDecoderFFmpeg(); + _Decoder.Init(); + _Decoder.Open(fileName); + + SFormatInfo format = _Decoder.GetFormatInfo(); + if (format.SamplesPerSecond == 0) + { + _PaHandle.Close(); + _PaHandle = null; + _Decoder.Close(); + _Decoder = null; + CLog.LogError("Error Init PortAudio Playback (samples=0)"); + return false; + } + + _Duration = _Decoder.GetLength(); + _ByteCount = 2 * format.ChannelCount; + _BytesPerSecond = format.SamplesPerSecond * _ByteCount; + _CurrentTime = 0f; + _SyncTimer.Time = _CurrentTime; + + PortAudioSharp.PortAudio.PaStreamParameters? outputParams = new PortAudioSharp.PortAudio.PaStreamParameters + { + channelCount = format.ChannelCount, + device = _ApiInfo.defaultOutputDevice, + sampleFormat = PortAudioSharp.PortAudio.PaSampleFormat.paInt16, + suggestedLatency = _OutputDeviceInfo.defaultLowOutputLatency, + hostApiSpecificStreamInfo = IntPtr.Zero + }; + + if (!_PaHandle.OpenOutputStream( + out _Stream, + ref outputParams, + format.SamplesPerSecond, + (uint)CConfig.AudioBufferSize, + PortAudioSharp.PortAudio.PaStreamFlags.paNoFlag, + _PaStreamCallback, + IntPtr.Zero) || _Stream == IntPtr.Zero) + { + _PaHandle.Close(); + _PaHandle = null; + _Decoder.Close(); + _Decoder = null; + return false; + } + + //From now on closing the driver and the decoder is handled by the thread ONLY! + + _Paused = true; + _Waiting = true; + _FileOpened = true; + _Data = new CRingBuffer(_Bufsize); + _NoMoreData = false; + _DecoderThread = new Thread(_Execute) {Priority = ThreadPriority.Normal, Name = Path.GetFileName(fileName)}; + _DecoderThread.Start(); + + return true; + } + + 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 (_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() + { + _PaHandle.CloseStream(_Stream); + _PaHandle.Close(); + _PaHandle = null; + _Decoder.Close(); + _Decoder = null; + + _Closeproc(ID); + } + #endregion Threading + + #region Callbacks + private PortAudioSharp.PortAudio.PaStreamCallbackResult _ProcessNewData( + IntPtr input, + IntPtr output, + uint frameCount, + ref PortAudioSharp.PortAudio.PaStreamCallbackTimeInfo timeInfo, + PortAudioSharp.PortAudio.PaStreamCallbackFlags 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 PortAudioSharp.PortAudio.PaStreamCallbackResult.paContinue; + } + + lock (_LockData) + { + if (_NoMoreData || _Data.BytesNotRead >= buf.Length) + { + _Data.Read(buf); + //We want to scale all values. No matter how many channels we have (_ByteCount=2 or 4) we have short values + //So just process 2 bytes a time + for (int i = 0; i < buf.Length; i += 2) + { + byte[] b = BitConverter.GetBytes((Int16)(BitConverter.ToInt16(buf, i) * _Volume * _VolumeMax)); + buf[i] = b[0]; + buf[i + 1] = 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.treamCallback: " + e.Message); + } + + return PortAudioSharp.PortAudio.PaStreamCallbackResult.paContinue; + } + #endregion Callbacks + + 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); + } + } + } +} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/CPt1.cs b/Vocaluxe/Lib/Sound/Playback/PortAudio/CPt1.cs similarity index 97% rename from Vocaluxe/Lib/Sound/CPt1.cs rename to Vocaluxe/Lib/Sound/Playback/PortAudio/CPt1.cs index 4ca4b951..17b92f45 100644 --- a/Vocaluxe/Lib/Sound/CPt1.cs +++ b/Vocaluxe/Lib/Sound/Playback/PortAudio/CPt1.cs @@ -1,6 +1,6 @@ using System.Diagnostics; -namespace Vocaluxe.Lib.Sound +namespace Vocaluxe.Lib.Sound.Playback.PortAudio { class CPt1 { diff --git a/Vocaluxe/Lib/Sound/CSyncTimer.cs b/Vocaluxe/Lib/Sound/Playback/PortAudio/CSyncTimer.cs similarity index 97% rename from Vocaluxe/Lib/Sound/CSyncTimer.cs rename to Vocaluxe/Lib/Sound/Playback/PortAudio/CSyncTimer.cs index 6ccd380c..2b405c9c 100644 --- a/Vocaluxe/Lib/Sound/CSyncTimer.cs +++ b/Vocaluxe/Lib/Sound/Playback/PortAudio/CSyncTimer.cs @@ -1,7 +1,7 @@ using System; using System.Diagnostics; -namespace Vocaluxe.Lib.Sound +namespace Vocaluxe.Lib.Sound.Playback.PortAudio { class CSyncTimer { diff --git a/Vocaluxe/Lib/Sound/Portaudio/PortAudio.cs b/Vocaluxe/Lib/Sound/Portaudio/PortAudio.cs deleted file mode 100644 index d8c6c9f5..00000000 --- a/Vocaluxe/Lib/Sound/Portaudio/PortAudio.cs +++ /dev/null @@ -1,558 +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 . -#endregion - -using System; -using System.Runtime.InteropServices; - -namespace Vocaluxe.Lib.Sound.PortAudio -{ - /** - - PortAudio v.19 bindings for .NET - - */ - // ReSharper disable UnusedMember.Global - // ReSharper disable FieldCanBeMadeReadOnly.Global - // ReSharper disable MemberCanBePrivate.Global - - public static class CPortAudio - { - #region **** PORTAUDIO CALLBACKS **** - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate EPaStreamCallbackResult PaStreamCallbackDelegate( - IntPtr input, - IntPtr output, - uint frameCount, - ref SPaStreamCallbackTimeInfo timeInfo, - EPaStreamCallbackFlags statusFlags, - IntPtr userData); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void PaStreamFinishedCallbackDelegate(IntPtr userData); - #endregion - - #region **** PORTAUDIO DATA STRUCTURES **** - [StructLayout(LayoutKind.Sequential)] - public struct SPaDeviceInfo - { - public int StructVersion; - [MarshalAs(UnmanagedType.LPStr)] public string Name; - public int HostApi; - public int MaxInputChannels; - public int MaxOutputChannels; - public double DefaultLowInputLatency; - public double DefaultLowOutputLatency; - public double DefaultHighInputLatency; - public double DefaultHighOutputLatency; - public double DefaultSampleRate; - - public override string ToString() - { - return "[" + GetType().Name + "]" + "\n" - + "name: " + Name + "\n" - + "hostApi: " + HostApi + "\n" - + "maxInputChannels: " + MaxInputChannels + "\n" - + "maxOutputChannels: " + MaxOutputChannels + "\n" - + "defaultLowInputLatency: " + DefaultLowInputLatency + "\n" - + "defaultLowOutputLatency: " + DefaultLowOutputLatency + "\n" - + "defaultHighInputLatency: " + DefaultHighInputLatency + "\n" - + "defaultHighOutputLatency: " + DefaultHighOutputLatency + "\n" - + "defaultSampleRate: " + DefaultSampleRate; - } - } - - [StructLayout(LayoutKind.Sequential)] - public struct SPaHostApiInfo - { - public int StructVersion; - public EPaHostApiTypeId Type; - [MarshalAs(UnmanagedType.LPStr)] public string Name; - public int DeviceCount; - public int DefaultInputDevice; - public int DefaultOutputDevice; - - public override string ToString() - { - return "[" + GetType().Name + "]" + "\n" - + "structVersion: " + StructVersion + "\n" - + "type: " + Type + "\n" - + "name: " + Name + "\n" - + "deviceCount: " + DeviceCount + "\n" - + "defaultInputDevice: " + DefaultInputDevice + "\n" - + "defaultOutputDevice: " + DefaultOutputDevice; - } - } - - [StructLayout(LayoutKind.Sequential)] - public struct SPaHostErrorInfo - { - public EPaHostApiTypeId HostApiType; - public int ErrorCode; - [MarshalAs(UnmanagedType.LPStr)] public string ErrorText; - - public override string ToString() - { - return "[" + GetType().Name + "]" + "\n" - + "hostApiType: " + HostApiType + "\n" - + "errorCode: " + ErrorCode + "\n" - + "errorText: " + ErrorText; - } - } - - [StructLayout(LayoutKind.Sequential)] - public struct SPaStreamCallbackTimeInfo - { - public double InputBufferAdcTime; - public double CurrentTime; - public double OutputBufferDacTime; - - public override string ToString() - { - return "[" + GetType().Name + "]" + "\n" - + "currentTime: " + CurrentTime + "\n" - + "inputBufferAdcTime: " + InputBufferAdcTime + "\n" - + "outputBufferDacTime: " + OutputBufferDacTime; - } - } - - [StructLayout(LayoutKind.Sequential)] - public struct SPaStreamInfo - { - public int StructVersion; - public double InputLatency; - public double OutputLatency; - public double SampleRate; - - public override string ToString() - { - return "[" + GetType().Name + "]" + "\n" - + "structVersion: " + StructVersion + "\n" - + "inputLatency: " + InputLatency + "\n" - + "outputLatency: " + OutputLatency + "\n" - + "sampleRate: " + SampleRate; - } - } - - [StructLayout(LayoutKind.Sequential)] - public struct SPaStreamParameters - { - public int Device; - public int ChannelCount; - public EPaSampleFormat SampleFormat; - public double SuggestedLatency; - internal IntPtr HostApiSpecificStreamInfo; - - public override string ToString() - { - return "[" + GetType().Name + "]" + "\n" - + "device: " + Device + "\n" - + "channelCount: " + ChannelCount + "\n" - + "sampleFormat: " + SampleFormat + "\n" - + "suggestedLatency: " + SuggestedLatency; - } - } - #endregion - - #region **** PORTAUDIO DEFINES **** - public enum EPaDeviceIndex - { - PaNoDevice = -1, - PaUseHostApiSpecificDeviceSpecification = -2 - } - - public enum EPaSampleFormat : uint - { - PaFloat32 = 0x00000001, - PaInt32 = 0x00000002, - PaInt24 = 0x00000004, - PaInt16 = 0x00000008, - PaInt8 = 0x00000010, - PaUInt8 = 0x00000020, - PaCustomFormat = 0x00010000, - PaNonInterleaved = 0x80000000, - } - - public const int PaFormatIsSupported = 0; - public const int PaFramesPerBufferUnspecified = 0; - - public enum EPaStreamFlags : uint - { - PaNoFlag = 0, - PaClipOff = 0x00000001, - PaDitherOff = 0x00000002, - PaNeverDropInput = 0x00000004, - PaPrimeOutputBuffersUsingStreamCallback = 0x00000008, - PaPlatformSpecificFlags = 0xFFFF0000 - } - - public enum EPaStreamCallbackFlags : uint - { - PaInputUnderflow = 0x00000001, - PaInputOverflow = 0x00000002, - PaOutputUnderflow = 0x00000004, - PaOutputOverflow = 0x00000008, - PaPrimingOutput = 0x00000010 - } - #endregion - - #region **** PORTAUDIO ENUMERATIONS **** - public enum EPaError - { - PaNoError = 0, - PaNotInitialized = -10000, - PaUnanticipatedHostError, - PaInvalidChannelCount, - PaInvalidSampleRate, - PaInvalidDevice, - PaInvalidFlag, - PaSampleFormatNotSupported, - PaBadIODeviceCombination, - PaInsufficientMemory, - PaBufferTooBig, - PaBufferTooSmall, - PaNullCallback, - PaBadStreamPtr, - PaTimedOut, - PaInternalError, - PaDeviceUnavailable, - PaIncompatibleHostApiSpecificStreamInfo, - PaStreamIsStopped, - PaStreamIsNotStopped, - PaInputOverflowed, - PaOutputUnderflowed, - PaHostApiNotFound, - PaInvalidHostApi, - PaCanNotReadFromCallbackStream, - PaCanNotWriteToACallbackStream, - PaCanNotReadFromAnOutputOnlyStream, - PaCanNotWriteToAnInputOnlyStream, - PaIncompatibleStreamHostApi, - PaBadBufferPtr - } - - public enum EPaHostApiTypeId : uint - { - PaInDevelopment = 0, - PaDirectSound = 1, - PaMME = 2, - PaASIO = 3, - PaSoundManager = 4, - PaCoreAudio = 5, - PaOSS = 7, - PaALSA = 8, - PaAL = 9, - PaBeOS = 10, - PaWDMKS = 11, - PaJACK = 12, - PaWASAPI = 13, - PaAudioScienceHPI = 14 - } - - public enum EPaStreamCallbackResult : uint - { - PaContinue = 0, - PaComplete = 1, - PaAbort = 2 - } - #endregion - - #region **** PORTAUDIO FUNCTIONS **** -#if ARCH_X86 -#if WIN - private const string _PaDll = "x86\\portaudio_x86.dll"; -#endif - -#if LINUX - private const string _PaDll = "libportaudio.so.2.0.0"; -#endif -#endif - -#if ARCH_X64 -#if WIN - private const string _PaDll = "x64\\portaudio_x64.dll"; -#endif - -#if LINUX - private const string _PaDll = "libportaudio.so.2.0.0"; -#endif -#endif - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern int Pa_GetVersion(); - - [DllImport(_PaDll, EntryPoint = "Pa_GetVersionText", CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr IntPtr_Pa_GetVersionText(); - - public static string PaGetVersionText() - { - IntPtr strptr = IntPtr_Pa_GetVersionText(); - return Marshal.PtrToStringAnsi(strptr); - } - - [DllImport(_PaDll, EntryPoint = "Pa_GetErrorText", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr IntPtr_Pa_GetErrorText(EPaError errorCode); - - public static string PaGetErrorText(EPaError errorCode) - { - IntPtr strptr = IntPtr_Pa_GetErrorText(errorCode); - return Marshal.PtrToStringAnsi(strptr); - } - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_Initialize(); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_Terminate(); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern int Pa_GetHostApiCount(); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern int Pa_GetDefaultHostApi(); - - [DllImport(_PaDll, EntryPoint = "Pa_GetHostApiInfo", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr IntPtr_Pa_GetHostApiInfo(int hostApi); - - public static SPaHostApiInfo PaGetHostApiInfo(int hostApi) - { - IntPtr structptr = IntPtr_Pa_GetHostApiInfo(hostApi); - return (SPaHostApiInfo)Marshal.PtrToStructure(structptr, typeof(SPaHostApiInfo)); - } - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern int Pa_HostApiTypeIdToHostApiIndex(EPaHostApiTypeId type); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern int Pa_HostApiDeviceIndexToDeviceIndex(int hostApi, int hostApiDeviceIndex); - - [DllImport(_PaDll, EntryPoint = "Pa_GetLastHostErrorInfo", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr IntPtr_Pa_GetLastHostErrorInfo(); - - public static SPaHostErrorInfo PaGetLastHostErrorInfo() - { - IntPtr structptr = IntPtr_Pa_GetLastHostErrorInfo(); - return (SPaHostErrorInfo)Marshal.PtrToStructure(structptr, typeof(SPaHostErrorInfo)); - } - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern int Pa_GetDeviceCount(); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern int Pa_GetDefaultInputDevice(); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern int Pa_GetDefaultOutputDevice(); - - [DllImport(_PaDll, EntryPoint = "Pa_GetDeviceInfo", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr IntPtr_Pa_GetDeviceInfo(int device); - - public static SPaDeviceInfo PaGetDeviceInfo(int device) - { - IntPtr structptr = IntPtr_Pa_GetDeviceInfo(device); - return (SPaDeviceInfo)Marshal.PtrToStructure(structptr, typeof(SPaDeviceInfo)); - } - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_IsFormatSupported( - ref SPaStreamParameters inputParameters, - ref SPaStreamParameters outputParameters, - double sampleRate); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_OpenStream( - out IntPtr stream, - ref SPaStreamParameters inputParameters, - ref SPaStreamParameters outputParameters, - double sampleRate, - uint framesPerBuffer, - EPaStreamFlags streamFlags, - PaStreamCallbackDelegate streamCallback, - IntPtr userData); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_OpenStream( - out IntPtr stream, - ref SPaStreamParameters inputParameters, - IntPtr outputParameters, - double sampleRate, - uint framesPerBuffer, - EPaStreamFlags streamFlags, - PaStreamCallbackDelegate streamCallback, - IntPtr userData); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_OpenStream( - out IntPtr stream, - IntPtr inputParameters, - ref SPaStreamParameters outputParameters, - double sampleRate, - uint framesPerBuffer, - EPaStreamFlags streamFlags, - PaStreamCallbackDelegate streamCallback, - IntPtr userData); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_OpenDefaultStream( - out IntPtr stream, - int numInputChannels, - int numOutputChannels, - uint sampleFormat, - double sampleRate, - uint framesPerBuffer, - PaStreamCallbackDelegate streamCallback, - IntPtr userData); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_CloseStream(IntPtr stream); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_SetStreamFinishedCallback( - ref IntPtr stream, - [MarshalAs(UnmanagedType.FunctionPtr)] PaStreamFinishedCallbackDelegate streamFinishedCallback); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_StartStream(IntPtr stream); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_StopStream(IntPtr stream); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_AbortStream(IntPtr stream); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_IsStreamStopped(IntPtr stream); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_IsStreamActive(IntPtr stream); - - [DllImport(_PaDll, EntryPoint = "Pa_GetStreamInfo", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr IntPtr_Pa_GetStreamInfo(IntPtr stream); - - public static SPaStreamInfo PaGetStreamInfo(IntPtr stream) - { - IntPtr structptr = IntPtr_Pa_GetStreamInfo(stream); - return (SPaStreamInfo)Marshal.PtrToStructure(structptr, typeof(SPaStreamInfo)); - } - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern double Pa_GetStreamTime(IntPtr stream); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern double Pa_GetStreamCpuLoad(IntPtr stream); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_ReadStream( - IntPtr stream, - [Out] float[] buffer, - uint frames); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_ReadStream( - IntPtr stream, - [Out] byte[] buffer, - uint frames); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_ReadStream( - IntPtr stream, - [Out] sbyte[] buffer, - uint frames); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_ReadStream( - IntPtr stream, - [Out] ushort[] buffer, - uint frames); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_ReadStream( - IntPtr stream, - [Out] short[] buffer, - uint frames); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_ReadStream( - IntPtr stream, - [Out] uint[] buffer, - uint frames); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_ReadStream( - IntPtr stream, - [Out] int[] buffer, - uint frames); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_WriteStream( - IntPtr stream, - [In] float[] buffer, - uint frames); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_WriteStream( - IntPtr stream, - [In] byte[] buffer, - uint frames); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_WriteStream( - IntPtr stream, - [In] sbyte[] buffer, - uint frames); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_WriteStream( - IntPtr stream, - [In] ushort[] buffer, - uint frames); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_WriteStream( - IntPtr stream, - [In] short[] buffer, - uint frames); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_WriteStream( - IntPtr stream, - [In] uint[] buffer, - uint frames); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_WriteStream( - IntPtr stream, - [In] int[] buffer, - uint frames); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern int Pa_GetStreamReadAvailable(IntPtr stream); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern int Pa_GetStreamWriteAvailable(IntPtr stream); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern EPaError Pa_GetSampleSize(EPaSampleFormat format); - - [DllImport(_PaDll, CallingConvention = CallingConvention.Cdecl)] - public static extern void Pa_Sleep(int msec); - #endregion - } - - // ReSharper restore UnusedMember.Global - // ReSharper restore FieldCanBeMadeReadOnly.Global - // ReSharper restore MemberCanBePrivate.Global -} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/Record/CAnalyzer.cs b/Vocaluxe/Lib/Sound/Record/CAnalyzer.cs new file mode 100644 index 00000000..39d79f00 --- /dev/null +++ b/Vocaluxe/Lib/Sound/Record/CAnalyzer.cs @@ -0,0 +1,162 @@ +using System; +using System.Runtime.InteropServices; + +namespace Vocaluxe.Lib.Sound.Record +{ + /// + /// Analyzer from Performous + /// Should be very reliable but needs some proof (artificial tests show some errors) + /// + class CAnalyzer : IDisposable + { + #region Imports + [DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr Analyzer_Create(uint step = 200); + + [DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)] + private static extern void Analyzer_Free(IntPtr analyzer); + + [DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)] + private static extern void Analyzer_InputFloat(IntPtr analyzer, [In] float[] data, int sampleCt); + + [DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)] + private static extern void Analyzer_InputShort(IntPtr analyzer, [In] short[] data, int sampleCt); + + [DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)] + private static extern void Analyzer_InputByte(IntPtr analyzer, [In] byte[] data, int sampleCt); + + [DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)] + private static extern void Analyzer_Process(IntPtr analyzer); + + [DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)] + private static extern double Analyzer_GetPeak(IntPtr analyzer); + + [DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)] + private static extern double Analyzer_FindNote(IntPtr analyzer, double minFreq, double maxFreq); + + [DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)] + private static extern bool Analyzer_OutputFloat(IntPtr analyzer, [Out] float[] data, int sampleCt, float rate); + #endregion + + private IntPtr _Instance; + + public CAnalyzer(uint step = 200) + { + _Instance = Analyzer_Create(step); + } + + ~CAnalyzer() + { + _Dispose(false); + } + + public void Input(float[] data) + { + Analyzer_InputFloat(_Instance, data, data.Length); + } + + public void Input(short[] data) + { + Analyzer_InputShort(_Instance, data, data.Length); + } + + public void Input(byte[] data) + { + Analyzer_InputByte(_Instance, data, data.Length / 2); + } + + public void Process() + { + Analyzer_Process(_Instance); + } + + public double GetPeak() + { + return Analyzer_GetPeak(_Instance); + } + + public double FindNote(double minFreq = 35.0, double maxFreq = 1000.0) + { + return Analyzer_FindNote(_Instance, minFreq, maxFreq); + } + + public bool Output(float[] data, float rate) + { + return Analyzer_OutputFloat(_Instance, data, data.Length, rate); + } + + private void _Dispose(bool disposing) + { + if (_Instance == IntPtr.Zero) + throw new ObjectDisposedException(GetType().Name); + Analyzer_Free(_Instance); + _Instance = IntPtr.Zero; + } + + public void Dispose() + { + _Dispose(true); + GC.SuppressFinalize(this); + } + } + + /// + /// Pitchtracker (Pt) that uses autocorrelation (AKF) and AMDF + /// Quite fast and perfect in artificial tests, but may fail in real world scenarios (e.g. missing fundamental) + /// + class CPtAKF : IDisposable + { + #region Imports + [DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr PtAKF_Create(); + + [DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)] + private static extern void PtAKF_Free(IntPtr analyzer); + + [DllImport("PitchTracker.dll", EntryPoint = "PtAKF_GetNumHalfTones", CallingConvention = CallingConvention.Cdecl)] + public static extern int GetNumHalfTones(); + + [DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)] + private static extern void PtAKF_InputByte(IntPtr analyzer, [In] byte[] data, int sampleCt); + + [DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)] + private static extern int PtAKF_GetNote(IntPtr analyzer, [Out] out double maxVolume, [Out] float[] weights); + #endregion + + private IntPtr _Instance; + + public CPtAKF() + { + _Instance = PtAKF_Create(); + } + + ~CPtAKF() + { + _Dispose(false); + } + + public void Input(byte[] data) + { + PtAKF_InputByte(_Instance, data, data.Length / 2); + } + + public int GetNote(out double maxVolume, float[] weights) + { + return PtAKF_GetNote(_Instance, out maxVolume, weights); + } + + private void _Dispose(bool disposing) + { + if (_Instance == IntPtr.Zero) + throw new ObjectDisposedException(GetType().Name); + PtAKF_Free(_Instance); + _Instance = IntPtr.Zero; + } + + public void Dispose() + { + _Dispose(true); + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/Record/CBuffer.cs b/Vocaluxe/Lib/Sound/Record/CBuffer.cs new file mode 100644 index 00000000..f2114109 --- /dev/null +++ b/Vocaluxe/Lib/Sound/Record/CBuffer.cs @@ -0,0 +1,278 @@ +#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 . +#endregion + +#define TEST_PITCH + +using System; +#if TEST_PITCH +using System.Windows.Forms; +using System.Diagnostics; +using VocaluxeLib; + +#endif + +namespace Vocaluxe.Lib.Sound.Record +{ + class CBuffer : IDisposable + { +#if TEST_PITCH + //Half tones: C C♯ D D# E F F♯ G G♯ A A# B + private const double _BaseToneFreq = 65.4064; // lowest (half-)tone to analyze (C2 = 65.4064 Hz) + private const double _HalftoneBase = 1.05946309436; // 2^(1/12) -> HalftoneBase^12 = 2 (one octave) +#endif + private CPtAKF _Analyzer = new CPtAKF(); + + private double _MaxVolume; + + public CBuffer() + { + MinVolume = 0.02f; + ToneWeigths = new float[GetNumHalfTones()]; + Reset(); +#if TEST_PITCH + _TestPitchDetection(); +#endif + } + + ~CBuffer() + { + _Dispose(); + } + + public static int GetNumHalfTones() + { + return CPtAKF.GetNumHalfTones(); + } + + 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[] ToneWeigths { get; private set; } + + /// + /// Minimum volume for a tone to be valid + /// + public float MinVolume { get; set; } + + public void Reset() + { + ToneValid = false; + ToneAbs = 0; + Tone = 0; + for (int i = 0; i < ToneWeigths.Length; i++) + ToneWeigths[i] = 0f; + } + + 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); + _Analyzer.Input(buffer); + } + + public void AnalyzeBuffer() + { + //_Analyzer.Process(); + //_MaxVolume = _Analyzer.GetPeak() / 43 + 1; + int tone = _Analyzer.GetNote(out _MaxVolume, ToneWeigths); //(int)Math.Round(_Analyzer.FindNote()); // + if (tone >= 0 && _MaxVolume >= MinVolume) + { + ToneAbs = tone; + Tone = ToneAbs % 12; + ToneValid = true; + } + else + ToneValid = false; + } + + public void Dispose() + { + _Dispose(); + GC.SuppressFinalize(this); + } + + private void _Dispose() + { + if (_Analyzer != null) + { + _Analyzer.Dispose(); + _Analyzer = null; + } + } + +#if TEST_PITCH + private static bool _PitchTestRun; + + private static string _ToneToNote(int tone, bool withOctave = true) + { + tone += 24; + string[] notes = {"C", "C♯", "D", "D#", "E", "F", "F♯", "G", "G♯", "A", "A#", "B"}; + string result = notes[tone % 12]; + if (withOctave) + result += (tone / 12); + return result; + } + + private void _TestPitchDetection() + { + if (_PitchTestRun) + return; + _PitchTestRun = true; + int toneFrom = 0; + const int toneTo = 47; //B5 + Console.WriteLine("Testing notes " + _ToneToNote(toneFrom) + " - " + _ToneToNote(toneTo)); + byte[] data; + byte[] data2 = new byte[2048]; + int ok = 0; + double angle = 0; + for (int distort = 0; distort < 10; distort++) + { + for (int tone = toneFrom; tone <= toneTo; tone++) + { + _GetSineWave(_BaseToneFreq * Math.Pow(_HalftoneBase, tone), 44100, ref angle, out data); + _Distort(data, tone, distort); + + for (int i = 0; i < 4; i++) + { + Buffer.BlockCopy(data, i * 2048, data2, 0, 2048); + ProcessNewBuffer(data2); + AnalyzeBuffer(); + } + if (!ToneValid) + CBase.Log.LogDebug("Note " + _ToneToNote(tone) + " not detected (Distortion: " + distort + ")"); + else if (Tone != tone % 12) + CBase.Log.LogDebug("Note " + _ToneToNote(tone) + " wrongly detected as " + _ToneToNote(Tone, false) + " (Distortion: " + distort + ")"); + else + ok++; + } + } + _GetSineWave(_BaseToneFreq * Math.Pow(_HalftoneBase, 5), 44100, ref angle, out data); + Stopwatch sw = new Stopwatch(); + sw.Start(); + const int repeats = 100; + for (int i = 0; i < repeats; i++) + { + ProcessNewBuffer(data); + AnalyzeBuffer(); + } + sw.Stop(); + string msg = "Analyser: Errors: " + ((toneTo - toneFrom + 1) * 10 - ok) + "; Speed: " + + (int)(repeats / (sw.ElapsedMilliseconds / 1000.0)) + " buffers/s"; + Console.WriteLine(msg); + MessageBox.Show(msg); + Reset(); + } + + private static short[] _GetDistort(int len, int tone, int type) + { + short[] sdata = new short[len]; + if (type < 9) + { + int newTone = 1; + switch (type) + { + case 0: + newTone = 2; + break; + case 1: + newTone = 3; + break; + case 2: + newTone = 6; + break; + case 3: + newTone = 8; + break; + case 4: + newTone = 10; + break; + case 5: + newTone = 15; + break; + case 6: + newTone = 25; + break; + case 7: + newTone = 26; + break; + case 8: + newTone = 29; + break; + } + byte[] data2; + double angle = 0; + _GetSineWave(_BaseToneFreq * Math.Pow(_HalftoneBase, tone + newTone), 44100, ref angle, out data2); + Buffer.BlockCopy(data2, 0, sdata, 0, len * 2); + } + else + { + Random r = new Random(0xBEEF); + for (int i = 0; i < sdata.Length; i++) + sdata[i] = (short)(r.Next() - Int16.MinValue); + } + return sdata; + } + + private static void _Distort(byte[] data, int tone, int distortCt) + { + if (distortCt < 1) + return; + short[] sdata = new short[data.Length / 2]; + Buffer.BlockCopy(data, 0, sdata, 0, data.Length); + + short[][] distortions = new short[distortCt][]; + for (int i = 0; i < distortCt; i++) + distortions[i] = _GetDistort(data.Length / 2, tone, i); + + for (int i = 0; i < sdata.Length; i++) + { + double distortion = 0; + for (int j = 0; j < distortCt; j++) + distortion += distortions[j][i]; + distortion /= distortCt; + sdata[i] = (short)(sdata[i] * 4.0 / 5.0 + distortion / 5.0); + } + Buffer.BlockCopy(sdata, 0, data, 0, data.Length); + } + + private static void _GetSineWave(double freq, int sampleRate, ref double angle, out byte[] data) + { + const short max = short.MaxValue; + const int len = 4096; //sampleRate * durationMs / 1000; + short[] data16Bit = new short[len]; + for (int i = 0; i < len; i++) + data16Bit[i] = (short)(Math.Sin(2 * Math.PI * i / sampleRate * freq + angle) * max); + angle = 2 * Math.PI * len / sampleRate * freq + angle; + angle = angle % (2 * Math.PI); + data = new byte[data16Bit.Length * 2]; + Buffer.BlockCopy(data16Bit, 0, data, 0, data.Length); + } +#endif + } +} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/CDelayTest.cs b/Vocaluxe/Lib/Sound/Record/CDelayTest.cs similarity index 94% rename from Vocaluxe/Lib/Sound/CDelayTest.cs rename to Vocaluxe/Lib/Sound/Record/CDelayTest.cs index 047b3999..a9974c40 100644 --- a/Vocaluxe/Lib/Sound/CDelayTest.cs +++ b/Vocaluxe/Lib/Sound/Record/CDelayTest.cs @@ -1,7 +1,7 @@ using System.Diagnostics; using Vocaluxe.Base; -namespace Vocaluxe.Lib.Sound +namespace Vocaluxe.Lib.Sound.Record { class CDelayTest { @@ -82,7 +82,7 @@ namespace Vocaluxe.Lib.Sound { if (_DelaysChannel[i].Channel < 0 || _DelaysChannel[i].Finished) continue; - if (CSound.RecordGetMaxVolume(_DelaysChannel[i].Channel) > 0.1f && CSound.RecordGetTone(_DelaysChannel[i].Channel) == 9) + if (CRecord.GetMaxVolume(_DelaysChannel[i].Channel) > 0.1f && CRecord.GetTone(_DelaysChannel[i].Channel) == 9) { Delays[i] = (int)_Timer.ElapsedMilliseconds; _DelaysChannel[i].Finished = true; diff --git a/Vocaluxe/Lib/Sound/Record/CRecordBase.cs b/Vocaluxe/Lib/Sound/Record/CRecordBase.cs new file mode 100644 index 00000000..f68fc268 --- /dev/null +++ b/Vocaluxe/Lib/Sound/Record/CRecordBase.cs @@ -0,0 +1,148 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Vocaluxe.Base; + +namespace Vocaluxe.Lib.Sound.Record +{ + /// + /// Base class to implement common functionality for Record classes + /// + abstract class CRecordBase + { + private bool _Initialized; + protected List _Devices; + protected CBuffer[] _Buffer; + + /// + /// Init recording + /// Child class should overwrite this to list devices + /// + /// true if success + public virtual bool Init() + { + if (_Initialized) + return false; + + _Devices = new List(); + _Buffer = new CBuffer[CSettings.MaxNumPlayer]; + for (int i = 0; i < _Buffer.Length; i++) + _Buffer[i] = new CBuffer(); + + _Initialized = true; + + return true; + } + + /// + /// Stop all voice capturing streams and terminates recording freeing resources + /// + public virtual void Close() + { + if (!_Initialized) + return; + + _Devices = null; + if (_Buffer != null) + { + foreach (CBuffer buffer in _Buffer) + buffer.Dispose(); + _Buffer = null; + } + + _Initialized = false; + } + + /// + /// Procedure to be called by descendent classes with data that got recorded + /// + /// Device from which the data was recorded + /// Recorded samples, assume Int16 and interleaved for multi-channels + protected void _HandleData(CRecordDevice device, byte[] data) + { + byte[] leftBuffer; + byte[] rightBuffer; + + if (device.Channels == 2) + { + leftBuffer = new byte[data.Length / 2]; + rightBuffer = new byte[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 < data.Length / 4; i++) + { + leftBuffer[i * 2] = data[i * 4]; + leftBuffer[i * 2 + 1] = data[i * 4 + 1]; + rightBuffer[i * 2] = data[i * 4 + 2]; + rightBuffer[i * 2 + 1] = data[i * 4 + 3]; + } + } + else + leftBuffer = rightBuffer = data; + + if (device.PlayerChannel1 > 0) + _Buffer[device.PlayerChannel1 - 1].ProcessNewBuffer(leftBuffer); + + if (device.PlayerChannel2 > 0) + _Buffer[device.PlayerChannel2 - 1].ProcessNewBuffer(rightBuffer); + } + + /// + /// Detect Pitch and Volume of the newest voice buffer + /// + /// + 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) + { + return _Initialized ? _Buffer[player].Tone : 0; + } + + public void SetTone(int player, int tone) + { + if (!_Initialized) + return; + + _Buffer[player].Tone = tone; + } + + public float GetMaxVolume(int player) + { + return _Initialized ? _Buffer[player].MaxVolume : 0f; + } + + public bool ToneValid(int player) + { + return _Initialized && _Buffer[player].ToneValid; + } + + public int NumHalfTones() + { + return CBuffer.GetNumHalfTones(); + } + + public float[] ToneWeigth(int player) + { + return _Initialized ? _Buffer[player].ToneWeigths : null; + } + + public ReadOnlyCollection RecordDevices() + { + return (_Initialized && _Devices.Count > 0) ? _Devices.AsReadOnly() : null; + } + } +} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/Record/DirectSound/CDirectSoundRecord.cs b/Vocaluxe/Lib/Sound/Record/DirectSound/CDirectSoundRecord.cs new file mode 100644 index 00000000..61b54b23 --- /dev/null +++ b/Vocaluxe/Lib/Sound/Record/DirectSound/CDirectSoundRecord.cs @@ -0,0 +1,111 @@ +#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 . +#endregion + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using SlimDX.DirectSound; +using Vocaluxe.Base; + +namespace Vocaluxe.Lib.Sound.Record.DirectSound +{ + class CDirectSoundRecord : CRecordBase, IRecord + { + private bool _Initialized; + + private List _Sources; + + public override bool Init() + { + if (!base.Init()) + return false; + + _Sources = new List(); + + DeviceCollection devices = DirectSoundCapture.GetDevices(); + + foreach (DeviceInformation dev in devices) + { + using (var ds = new DirectSoundCapture(dev.DriverGuid)) + { + var device = new CRecordDevice(_Devices.Count, dev.DriverGuid.ToString(), dev.Description, ds.Capabilities.Channels); + + _Devices.Add(device); + } + } + + _Initialized = true; + + return true; + } + + public override void Close() + { + Stop(); + _Initialized = false; + base.Close(); + } + + public bool Start() + { + if (!_Initialized) + return false; + + foreach (CBuffer buffer in _Buffer) + buffer.Reset(); + + foreach (CRecordDevice device in _Devices) + { + if (device.PlayerChannel1 > 0 || device.PlayerChannel2 > 0) + { + var source = new CSoundCardSource(device.Driver, (short)device.Channels) {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; + } + + private void _OnDataReady(object sender, CSampleDataEventArgs e) + { + if (!_Initialized) + return; + CRecordDevice dev = _Devices.FirstOrDefault(device => device.Driver == e.Guid); + if (dev == null) + return; + _HandleData(dev, e.Data); + } + } +} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/Record/DirectSound/CSampleDataEventArgs.cs b/Vocaluxe/Lib/Sound/Record/DirectSound/CSampleDataEventArgs.cs new file mode 100644 index 00000000..22a9fcd1 --- /dev/null +++ b/Vocaluxe/Lib/Sound/Record/DirectSound/CSampleDataEventArgs.cs @@ -0,0 +1,16 @@ +using System; + +namespace Vocaluxe.Lib.Sound.Record.DirectSound +{ + public class CSampleDataEventArgs : EventArgs + { + public readonly byte[] Data; + public readonly string Guid; + + public CSampleDataEventArgs(byte[] data, string guid) + { + Data = data; + Guid = guid; + } + } +} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/Record/DirectSound/CSoundCardSource.cs b/Vocaluxe/Lib/Sound/Record/DirectSound/CSoundCardSource.cs new file mode 100644 index 00000000..b3a7d5c4 --- /dev/null +++ b/Vocaluxe/Lib/Sound/Record/DirectSound/CSoundCardSource.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using SlimDX.DirectSound; +using SlimDX.Multimedia; + +namespace Vocaluxe.Lib.Sound.Record.DirectSound +{ + public 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 _Notifications; + private int _BufferPortionCount; + private int _BufferPortionSize; + private WaitHandle[] _WaitHandles; + private double _SampleRate; + private readonly string _Guid; + private readonly short _Channels; + + public CSoundCardSource(string guid, short channels) + { + _Guid = guid; + _Channels = channels; + _WaveFormat = new WaveFormat(); + SampleRateKhz = 44.1; + _BufferSize = 2048; + } + + public event EventHandler 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(new Guid(_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(); + + 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; + } + } + } + } +} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/Record/IRecord.cs b/Vocaluxe/Lib/Sound/Record/IRecord.cs new file mode 100644 index 00000000..04899008 --- /dev/null +++ b/Vocaluxe/Lib/Sound/Record/IRecord.cs @@ -0,0 +1,147 @@ +#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 . +#endregion + +using System; +using System.Collections.ObjectModel; + +namespace Vocaluxe.Lib.Sound.Record +{ + class CRecordDevice + { + public readonly int ID; + public readonly string Name; + public readonly string Driver; + + public readonly int Channels; + public int PlayerChannel1; + public int PlayerChannel2; + + public CRecordDevice(int id, string name, string driver, int channels) + { + ID = id; + Name = name; + Driver = driver; + + if (channels > 2) + channels = 2; //more are not supported in vocaluxe + + Channels = channels; + } + } + + 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 + { + /// + /// Initializes recording + /// + /// True on success + bool Init(); + + /// + /// Closes recording. You must not use it before calling Init again + /// + void Close(); + + /// + /// Starts recording on all enabled devices (PlayerChannelX set) + /// + /// True on success, false on error or not initialized + bool Start(); + + /// + /// Stops recording on all devices + /// + /// True on success, false on error or not initialized + bool Stop(); + + /// + /// Analyzes the buffer for the given player + /// + /// + void AnalyzeBuffer(int player); + + /// + /// Get the current raw tone value for a player + /// + /// 0-based player index + /// Tone between 0 and NumHalftones-1 + int GetToneAbs(int player); + + /// + /// Get the current tone value for a player + /// + /// 0-based player index + /// Tone between 0 and 11 (one octave) + int GetTone(int player); + + /// + /// DEBUG ONLY: Sets a tone for a player + /// + /// 0-based player index + /// Raw tone value between 0 and NumHalfTones-1 + void SetTone(int player, int tone); + + /// + /// Get the current maximum input volume (normalized) for a player + /// + /// 0-based player index + /// Maximum volume between 0 and 1 + float GetMaxVolume(int player); + + /// + /// Returns whether the current tone is valid. That includes, if maximum volume is higher than the threshold + /// + /// 0-based player index + /// True if tone is detected and volume higher than threshold + bool ToneValid(int player); + + /// + /// Returns the number of half tones used for the raw tone detection + /// + /// number of half tones used for the raw tone detection + int NumHalfTones(); + + /// + /// Returns the tone weights for a player as an array of normalized values for each halftone + /// + /// 0-based player index + /// Array of values (0-1) with one entry per halftone + float[] ToneWeigth(int player); + + /// + /// Readonly list of all detected record devices. Modifying the devices should not be done when record is running. + /// + /// Readonly list of all detected record devices + ReadOnlyCollection RecordDevices(); + } +} \ No newline at end of file diff --git a/Vocaluxe/Lib/Sound/Record/PortAudio/CPortAudioRecord.cs b/Vocaluxe/Lib/Sound/Record/PortAudio/CPortAudioRecord.cs new file mode 100644 index 00000000..76b952da --- /dev/null +++ b/Vocaluxe/Lib/Sound/Record/PortAudio/CPortAudioRecord.cs @@ -0,0 +1,201 @@ +#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 . +#endregion + +using System; +using System.Runtime.InteropServices; +using System.Threading; +using Vocaluxe.Base; + +namespace Vocaluxe.Lib.Sound.Record.PortAudio +{ + class CPortAudioRecord : CRecordBase, IRecord + { + private bool _Initialized; + private CPortAudioHandle _PaHandle; + private PortAudioSharp.PortAudio.PaStreamCallbackDelegate _MyRecProc; + private IntPtr[] _RecHandle; + + /// + /// Init PortAudio and list record devices + /// + /// true if success + public override bool Init() + { + if (!base.Init()) + return false; + + try + { + _PaHandle = new CPortAudioHandle(); + + int hostAPI = _PaHandle.GetHostApi(); + int numDevices = PortAudioSharp.PortAudio.Pa_GetDeviceCount(); + for (int i = 0; i < numDevices; i++) + { + PortAudioSharp.PortAudio.PaDeviceInfo info = PortAudioSharp.PortAudio.Pa_GetDeviceInfo(i); + if (info.hostApi == hostAPI && info.maxInputChannels > 0) + { + var dev = new CRecordDevice(i, info.name, info.name + i, info.maxInputChannels); + + _Devices.Add(dev); + } + } + + _RecHandle = new IntPtr[_Devices.Count]; + _MyRecProc = _MyPaStreamCallback; + _Initialized = true; + } + catch (Exception e) + { + _Initialized = false; + CLog.LogError("Error initializing PortAudio: " + e.Message); + Close(); + return false; + } + + return true; + } + + /// + /// Start Voice Capturing + /// + /// + public bool Start() + { + if (!_Initialized) + return false; + + Stop(); + + foreach (IntPtr handle in _RecHandle) + { + int waitcount = 0; + while (waitcount < 5 && PortAudioSharp.PortAudio.Pa_IsStreamStopped(handle) == PortAudioSharp.PortAudio.PaError.paStreamIsNotStopped) + { + Thread.Sleep(1); + waitcount++; + } + } + + foreach (CBuffer buffer in _Buffer) + buffer.Reset(); + + for (int i = 0; i < _RecHandle.Length; i++) + _RecHandle[i] = IntPtr.Zero; + + for (int dev = 0; dev < _Devices.Count; dev++) + { + if (_Devices[dev].PlayerChannel1 > 0 || _Devices[dev].PlayerChannel2 > 0) + { + PortAudioSharp.PortAudio.PaStreamParameters? inputParams = new PortAudioSharp.PortAudio.PaStreamParameters + { + channelCount = _Devices[dev].Channels, + device = _Devices[dev].ID, + sampleFormat = PortAudioSharp.PortAudio.PaSampleFormat.paInt16, + suggestedLatency = PortAudioSharp.PortAudio.Pa_GetDeviceInfo(_Devices[dev].ID).defaultLowInputLatency, + hostApiSpecificStreamInfo = IntPtr.Zero + }; + if (!_PaHandle.OpenInputStream( + out _RecHandle[dev], + ref inputParams, + 44100, + 882, + PortAudioSharp.PortAudio.PaStreamFlags.paNoFlag, + _MyRecProc, + new IntPtr(dev))) + return false; + + if (_PaHandle.CheckError("Start Stream (rec)", PortAudioSharp.PortAudio.Pa_StartStream(_RecHandle[dev]))) + return false; + } + } + return true; + } + + /// + /// Stop Voice Capturing + /// + /// + public bool Stop() + { + if (!_Initialized) + return false; + + foreach (IntPtr handle in _RecHandle) + PortAudioSharp.PortAudio.Pa_StopStream(handle); + return true; + } + + /// + /// Stop all voice capturing streams and terminate PortAudio + /// + public override void Close() + { + if (_RecHandle != null) + { + foreach (IntPtr handle in _RecHandle) + { + if (handle != IntPtr.Zero) + _PaHandle.CloseStream(handle); + } + } + if (_PaHandle != null) + { + _PaHandle.Close(); + _PaHandle = null; + } + + _Initialized = false; + + base.Close(); + } + + private PortAudioSharp.PortAudio.PaStreamCallbackResult _MyPaStreamCallback( + IntPtr input, + IntPtr output, + uint frameCount, + ref PortAudioSharp.PortAudio.PaStreamCallbackTimeInfo timeInfo, + PortAudioSharp.PortAudio.PaStreamCallbackFlags statusFlags, + IntPtr userData) + { + try + { + if (frameCount > 0 && input != IntPtr.Zero) + { + CRecordDevice dev = _Devices[userData.ToInt32()]; + uint numBytes; + if (dev.Channels == 2) + numBytes = frameCount * 4; + else + numBytes = frameCount * 2; + + byte[] recbuffer = new byte[numBytes]; + + // copy from managed to unmanaged memory + Marshal.Copy(input, recbuffer, 0, (int)numBytes); + _HandleData(dev, recbuffer); + } + } + catch (Exception e) + { + CLog.LogError("Error on Stream Callback (rec): " + e); + } + + return PortAudioSharp.PortAudio.PaStreamCallbackResult.paContinue; + } + } +} \ No newline at end of file diff --git a/Vocaluxe/Lib/Video/Acinerella/CDecoder.cs b/Vocaluxe/Lib/Video/Acinerella/CDecoder.cs index 743d5747..9203b118 100644 --- a/Vocaluxe/Lib/Video/Acinerella/CDecoder.cs +++ b/Vocaluxe/Lib/Video/Acinerella/CDecoder.cs @@ -15,7 +15,6 @@ // along with Vocaluxe. If not, see . #endregion -using System; using System.Diagnostics; using System.IO; using VocaluxeLib.Draw; diff --git a/Vocaluxe/Program.cs b/Vocaluxe/Program.cs index b59d4f9e..74464be9 100644 --- a/Vocaluxe/Program.cs +++ b/Vocaluxe/Program.cs @@ -20,24 +20,23 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; -using System.Runtime.InteropServices; +using System.Runtime.ExceptionServices; using System.Threading; using System.Windows.Forms; using Vocaluxe.Base; -using System.Runtime.ExceptionServices; using Vocaluxe.Base.Fonts; using Vocaluxe.Base.Server; namespace Vocaluxe { + class CLoadingException : Exception + { + public CLoadingException(string component) + : base("Failed to load " + component) {} + } + static class CMainProgram { -#if WIN - [DllImport("user32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool SetForegroundWindow(IntPtr hWnd); -#endif - private static CSplashScreen _SplashScreen; [STAThread, HandleProcessCorruptedStateExceptions] @@ -85,156 +84,172 @@ namespace Vocaluxe if (!CProgrammHelper.CheckRequirements()) return; + CProgrammHelper.Init(); CMain.Init(); - CSettings.CreateFolders(); Application.DoEvents(); // Init Language - CLog.StartBenchmark(0, "Init Language"); + CLog.StartBenchmark("Init Language"); CLanguage.Init(); - CLog.StopBenchmark(0, "Init Language"); + CLog.StopBenchmark("Init Language"); Application.DoEvents(); // load config - CLog.StartBenchmark(0, "Init Config"); + CLog.StartBenchmark("Init Config"); CConfig.LoadCommandLineParams(args); CConfig.UseCommandLineParamsBefore(); CConfig.Init(); CConfig.UseCommandLineParamsAfter(); - CLog.StopBenchmark(0, "Init Config"); + CLog.StopBenchmark("Init Config"); + + // Create folders + CSettings.CreateFolders(); Application.DoEvents(); _SplashScreen = new CSplashScreen(); Application.DoEvents(); // Init Draw - CLog.StartBenchmark(0, "Init Draw"); - CDraw.InitDraw(); - CLog.StopBenchmark(0, "Init Draw"); + CLog.StartBenchmark("Init Draw"); + if (!CDraw.InitDraw()) + throw new CLoadingException("drawing"); + CLog.StopBenchmark("Init Draw"); Application.DoEvents(); // Init Database - CLog.StartBenchmark(0, "Init Database"); - CDataBase.Init(); - CLog.StopBenchmark(0, "Init Database"); + CLog.StartBenchmark("Init Database"); + if (!CDataBase.Init()) + throw new CLoadingException("database"); + CLog.StopBenchmark("Init Database"); Application.DoEvents(); // Init Playback - CLog.StartBenchmark(0, "Init Playback"); - CSound.Init(); - CLog.StopBenchmark(0, "Init Playback"); + CLog.StartBenchmark("Init Playback"); + if (!CSound.Init()) + throw new CLoadingException("playback"); + CLog.StopBenchmark("Init Playback"); Application.DoEvents(); // Init Record - CLog.StartBenchmark(0, "Init Record"); - CSound.RecordInit(); - CLog.StopBenchmark(0, "Init Record"); + CLog.StartBenchmark("Init Record"); + if (!CRecord.Init()) + throw new CLoadingException("record"); + CLog.StopBenchmark("Init Record"); Application.DoEvents(); //Init Webcam - CLog.StartBenchmark(0, "Init Webcam"); - CWebcam.Init(); - CLog.StopBenchmark(0, "Init Webcam"); + CLog.StartBenchmark("Init Webcam"); + if (!CWebcam.Init()) + throw new CLoadingException("webcam"); + CLog.StopBenchmark("Init Webcam"); Application.DoEvents(); // Init Background Music - CLog.StartBenchmark(0, "Init Background Music"); + CLog.StartBenchmark("Init Background Music"); CBackgroundMusic.Init(); - CLog.StopBenchmark(0, "Init Background Music"); + CLog.StopBenchmark("Init Background Music"); Application.DoEvents(); // Init Profiles - CLog.StartBenchmark(0, "Init Profiles"); + CLog.StartBenchmark("Init Profiles"); CProfiles.Init(); - CLog.StopBenchmark(0, "Init Profiles"); + CLog.StopBenchmark("Init Profiles"); Application.DoEvents(); // Init Font - CLog.StartBenchmark(0, "Init Font"); + CLog.StartBenchmark("Init Font"); CFonts.Init(); - CLog.StopBenchmark(0, "Init Font"); + CLog.StopBenchmark("Init Font"); Application.DoEvents(); // Init VideoDecoder - CLog.StartBenchmark(0, "Init Videodecoder"); - CVideo.Init(); - CLog.StopBenchmark(0, "Init Videodecoder"); + CLog.StartBenchmark("Init Videodecoder"); + if (!CVideo.Init()) + throw new CLoadingException("video"); + CLog.StopBenchmark("Init Videodecoder"); Application.DoEvents(); // Load Cover - CLog.StartBenchmark(0, "Init Cover"); + CLog.StartBenchmark("Init Cover"); CCover.Init(); - CLog.StopBenchmark(0, "Init Cover"); + CLog.StopBenchmark("Init Cover"); Application.DoEvents(); // Theme System - CLog.StartBenchmark(0, "Init Theme"); - CTheme.InitTheme(); - CLog.StopBenchmark(0, "Init Theme"); + CLog.StartBenchmark("Init Theme"); + if (!CTheme.Init()) + throw new CLoadingException("theme"); + CLog.StopBenchmark("Init Theme"); Application.DoEvents(); // Init Screens - CLog.StartBenchmark(0, "Init Screens"); - CGraphics.InitGraphics(); - CLog.StopBenchmark(0, "Init Screens"); + CLog.StartBenchmark("Init Screens"); + CGraphics.Init(); + CLog.StopBenchmark("Init Screens"); Application.DoEvents(); // Init Server - CLog.StartBenchmark(0, "Init Server"); + CLog.StartBenchmark("Init Server"); CVocaluxeServer.Init(); - CLog.StopBenchmark(0, "Init Server"); + CLog.StopBenchmark("Init Server"); Application.DoEvents(); // Init Input - CLog.StartBenchmark(0, "Init Input"); + CLog.StartBenchmark("Init Input"); CController.Init(); CController.Connect(); - CLog.StopBenchmark(0, "Init Input"); + CLog.StopBenchmark("Init Input"); Application.DoEvents(); // Init Game; - CLog.StartBenchmark(0, "Init Game"); + CLog.StartBenchmark("Init Game"); CGame.Init(); CProfiles.Update(); CConfig.UsePlayers(); - CLog.StopBenchmark(0, "Init Game"); + CLog.StopBenchmark("Init Game"); Application.DoEvents(); // Init Party Modes; - CLog.StartBenchmark(0, "Init Party Modes"); + CLog.StartBenchmark("Init Party Modes"); CParty.Init(); - CLog.StopBenchmark(0, "Init Party Modes"); + CLog.StopBenchmark("Init Party Modes"); Application.DoEvents(); + //Only reasonable point to call GC.Collect() because initialization may cause lots of garbage + //Rely on GC doing its job afterwards and call Dispose methods where appropriate + GC.Collect(); } catch (Exception e) { - MessageBox.Show("Error on start up: " + e.Message + e.StackTrace); + MessageBox.Show("Error on start up: " + e.Message); CLog.LogError("Error on start up: " + e); - _SplashScreen.Close(); + if (_SplashScreen != null) + _SplashScreen.Close(); _CloseProgram(); + return; } Application.DoEvents(); // Start Main Loop - _SplashScreen.Close(); + if (_SplashScreen != null) + _SplashScreen.Close(); CVocaluxeServer.Start(); CDraw.MainLoop(); @@ -247,11 +262,11 @@ namespace Vocaluxe { CVocaluxeServer.Close(); CController.Close(); - CSound.RecordCloseAll(); - CSound.CloseAllStreams(); - CVideo.CloseAll(); + CRecord.Close(); + CSound.Close(); + CVideo.Close(); CDraw.Unload(); - CDataBase.CloseConnections(); + CDataBase.Close(); CWebcam.Close(); CLog.CloseAll(); } @@ -332,7 +347,7 @@ namespace Vocaluxe { IntPtr wnd = process.MainWindowHandle; if (wnd != IntPtr.Zero) - SetForegroundWindow(wnd); + COSFunctions.SetForegroundWindow(wnd); } #endif return false; diff --git a/Vocaluxe/Properties/AssemblyInfo.cs b/Vocaluxe/Properties/AssemblyInfo.cs index 861326f6..7a725ec9 100644 --- a/Vocaluxe/Properties/AssemblyInfo.cs +++ b/Vocaluxe/Properties/AssemblyInfo.cs @@ -49,5 +49,5 @@ using System.Runtime.InteropServices; // Revision // -[assembly: AssemblyVersion("0.3.0.83")] -[assembly: AssemblyFileVersion("0.3.0.83")] \ No newline at end of file +[assembly: AssemblyVersion("0.3.0.84")] +[assembly: AssemblyFileVersion("0.3.0.84")] \ No newline at end of file diff --git a/Vocaluxe/Screens/CPopupScreenVolumeControl.cs b/Vocaluxe/Screens/CPopupScreenVolumeControl.cs index f95f651a..42b132bb 100644 --- a/Vocaluxe/Screens/CPopupScreenVolumeControl.cs +++ b/Vocaluxe/Screens/CPopupScreenVolumeControl.cs @@ -115,7 +115,7 @@ namespace Vocaluxe.Screens else { CConfig.BackgroundMusicVolume = _SelectSlides[_SelectSlideVolume].Selection * 5; - CBackgroundMusic.ApplyVolume(); + CBackgroundMusic.SetVolume(CConfig.BackgroundMusicVolume); } break; @@ -125,7 +125,7 @@ namespace Vocaluxe.Screens default: CConfig.BackgroundMusicVolume = _SelectSlides[_SelectSlideVolume].Selection * 5; - CBackgroundMusic.ApplyVolume(); + CBackgroundMusic.SetVolume(CConfig.BackgroundMusicVolume); break; } CConfig.SaveConfig(); diff --git a/Vocaluxe/Screens/CScreenLoad.cs b/Vocaluxe/Screens/CScreenLoad.cs index 4b6cf767..5bcc054d 100644 --- a/Vocaluxe/Screens/CScreenLoad.cs +++ b/Vocaluxe/Screens/CScreenLoad.cs @@ -42,8 +42,6 @@ namespace Vocaluxe.Screens private bool _IntroOutPlayed; private CVideoPlayer[] _Intros; - private bool _BGMusicStarted; - public override void Init() { base.Init(); @@ -99,8 +97,6 @@ namespace Vocaluxe.Screens } } - _SongLoaderThread = new Thread(CSongs.LoadSongs) {Name = "SongLoader"}; - _Texts[_TextStatus].Text = CLanguage.Translate("TR_SCREENLOAD_TOTAL") + ": 0 " + CLanguage.Translate("TR_SCREENLOAD_SONGS") + " (0 " + CLanguage.Translate("TR_SCREENLOAD_LOADED") + ")"; @@ -115,8 +111,6 @@ namespace Vocaluxe.Screens _Intros[i].Load(_IntroVideo[i]); _Texts[_TextProgramName].Visible = false; } - - _BGMusicStarted = false; } public override void OnShowFinish() @@ -129,22 +123,12 @@ namespace Vocaluxe.Screens videoPlayer.PreLoad(); } - CLog.StartBenchmark(0, "Load Songs Full"); - _SongLoaderThread.IsBackground = true; + CLog.StartBenchmark("Load Songs Full"); + _SongLoaderThread = new Thread(CSongs.LoadSongs) {Name = "SongLoader", IsBackground = true}; _SongLoaderThread.Start(); + CBackgroundMusic.OwnSongsAvailable = false; - if (CConfig.BackgroundMusic == EOffOn.TR_CONFIG_ON && - CConfig.BackgroundMusicSource == EBackgroundMusicSource.TR_CONFIG_NO_OWN_MUSIC && !_BGMusicStarted) - { - CBackgroundMusic.AddOwnMusic(); - - if (!CBackgroundMusic.IsPlaying) - CBackgroundMusic.Next(); - - _BGMusicStarted = true; - } - - CBackgroundMusic.CanSing = false; + CBackgroundMusic.Play(); } public override bool UpdateGame() @@ -153,10 +137,10 @@ namespace Vocaluxe.Screens bool next = CConfig.CoverLoading != ECoverLoading.TR_CONFIG_COVERLOADING_ATSTART || CSongs.CoverLoaded; - if ((_IntroOutPlayed || _SkipIntro) && next && CSettings.GameState != EGameState.EditTheme && CSongs.SongsLoaded) - CSettings.GameState = EGameState.Normal; + if ((_IntroOutPlayed || _SkipIntro) && next && CSettings.ProgramState != EProgramState.EditTheme && CSongs.SongsLoaded) + CSettings.ProgramState = EProgramState.Normal; - if (CSettings.GameState == EGameState.Normal) + if (CSettings.ProgramState == EProgramState.Normal) CGraphics.FadeTo(EScreens.ScreenMain); _Texts[_TextStatus].Text = @@ -164,15 +148,11 @@ namespace Vocaluxe.Screens CLanguage.Translate("TR_SCREENLOAD_SONGS") + " (" + CSongs.NumSongsWithCoverLoaded + " " + CLanguage.Translate("TR_SCREENLOAD_LOADED") + ")"; - if (CSongs.SongsLoaded && CConfig.BackgroundMusic == EOffOn.TR_CONFIG_ON && - CConfig.BackgroundMusicSource != EBackgroundMusicSource.TR_CONFIG_NO_OWN_MUSIC && !_BGMusicStarted) + if (CSongs.SongsLoaded && !CBackgroundMusic.OwnSongsAvailable) { - CBackgroundMusic.AddOwnMusic(); + CBackgroundMusic.OwnSongsAvailable = true; - if (!CBackgroundMusic.IsPlaying) - CBackgroundMusic.Next(); - - _BGMusicStarted = true; + CBackgroundMusic.Play(); } return true; @@ -196,14 +176,12 @@ namespace Vocaluxe.Screens foreach (CVideoPlayer videoPlayer in _Intros) videoPlayer.Close(); - CBackgroundMusic.CanSing = true; - - CLog.StopBenchmark(0, "Load Songs Full"); + CLog.StopBenchmark("Load Songs Full"); //Init Playlists - CLog.StartBenchmark(0, "Init Playlists"); + CLog.StartBenchmark("Init Playlists"); CPlaylists.Init(); - CLog.StopBenchmark(0, "Init Playlists"); + CLog.StopBenchmark("Init Playlists"); } private void _CheckStartIntroVideos() @@ -224,6 +202,7 @@ namespace Vocaluxe.Screens } else if (_CurrentIntroVideo == 0 && _Intros[0].IsFinished && CConfig.CoverLoading == ECoverLoading.TR_CONFIG_COVERLOADING_ATSTART) { + _Intros[_CurrentIntroVideo].Close(); _CurrentIntroVideo = 1; _Intros[1].Loop = true; _Intros[1].Start(); @@ -231,6 +210,7 @@ namespace Vocaluxe.Screens else if ((_CurrentIntroVideo == 1 && CSongs.CoverLoaded) || (_CurrentIntroVideo == 0 && _Intros[0].IsFinished)) { + _Intros[_CurrentIntroVideo].Close(); _CurrentIntroVideo = 2; _Intros[2].Start(); } diff --git a/Vocaluxe/Screens/CScreenNames.cs b/Vocaluxe/Screens/CScreenNames.cs index be65f5cc..f3789e5c 100644 --- a/Vocaluxe/Screens/CScreenNames.cs +++ b/Vocaluxe/Screens/CScreenNames.cs @@ -546,8 +546,8 @@ namespace Vocaluxe.Screens for (int i = 1; i <= CGame.NumPlayer; i++) { - CSound.AnalyzeBuffer(i - 1); - _Equalizers["EqualizerPlayer" + i].Update(CSound.ToneWeigth(i - 1)); + CRecord.AnalyzeBuffer(i - 1); + _Equalizers["EqualizerPlayer" + i].Update(CRecord.ToneWeigth(i - 1)); } return true; } @@ -555,7 +555,7 @@ namespace Vocaluxe.Screens public override void OnShow() { base.OnShow(); - CSound.RecordStart(); + CRecord.Start(); _NameSelections[_NameSelection].Init(); _LoadProfiles(); @@ -565,7 +565,7 @@ namespace Vocaluxe.Screens public override void OnClose() { base.OnClose(); - CSound.RecordStop(); + CRecord.Stop(); } public override bool Draw() diff --git a/Vocaluxe/Screens/CScreenOptionsRecord.cs b/Vocaluxe/Screens/CScreenOptionsRecord.cs index 979809c6..cd41ebaf 100644 --- a/Vocaluxe/Screens/CScreenOptionsRecord.cs +++ b/Vocaluxe/Screens/CScreenOptionsRecord.cs @@ -19,7 +19,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Windows.Forms; using Vocaluxe.Base; -using Vocaluxe.Lib.Sound; +using Vocaluxe.Lib.Sound.Record; using VocaluxeLib; using VocaluxeLib.Menu; @@ -212,7 +212,7 @@ namespace Vocaluxe.Screens public override bool UpdateGame() { for (int i = 0; i < CSettings.MaxNumPlayer; i++) - CSound.AnalyzeBuffer(i); + CRecord.AnalyzeBuffer(i); if (_DelayTest.Running) { @@ -227,8 +227,8 @@ namespace Vocaluxe.Screens int player = _SelectSlides[_SelectSlideRecordChannel1].Selection; if (player > 0) { - _ChannelEnergy[0] = CSound.RecordGetMaxVolume(player - 1); - _Equalizers[_EqualizerChannel1].Update(CSound.ToneWeigth(player - 1)); + _ChannelEnergy[0] = CRecord.GetMaxVolume(player - 1); + _Equalizers[_EqualizerChannel1].Update(CRecord.ToneWeigth(player - 1)); } else _Equalizers[_EqualizerChannel1].Reset(); @@ -237,8 +237,8 @@ namespace Vocaluxe.Screens player = _SelectSlides[_SelectSlideRecordChannel2].Selection; if (player > 0) { - _ChannelEnergy[1] = CSound.RecordGetMaxVolume(player - 1); - _Equalizers[_EqualizerChannel2].Update(CSound.ToneWeigth(player - 1)); + _ChannelEnergy[1] = CRecord.GetMaxVolume(player - 1); + _Equalizers[_EqualizerChannel2].Update(CRecord.ToneWeigth(player - 1)); } else _Equalizers[_EqualizerChannel2].Reset(); @@ -265,7 +265,7 @@ namespace Vocaluxe.Screens _DeviceNr = -1; - _Devices = CSound.RecordGetDevices(); + _Devices = CRecord.GetDevices(); if (_Devices != null) { _DeviceNr = 0; @@ -336,7 +336,7 @@ namespace Vocaluxe.Screens public override void OnClose() { base.OnClose(); - CSound.RecordStop(); + CRecord.Stop(); _DelayTest.Reset(); CBackgroundMusic.Disabled = false; @@ -362,7 +362,7 @@ namespace Vocaluxe.Screens if (_Devices == null) return; - CSound.RecordStop(); + CRecord.Stop(); _SetMicConfig(); if (_CheckMicConfig()) @@ -388,7 +388,7 @@ namespace Vocaluxe.Screens } CConfig.SaveConfig(); } - CSound.RecordStart(); + CRecord.Start(); } private void _SetPlayerColors() diff --git a/Vocaluxe/Screens/CScreenOptionsSound.cs b/Vocaluxe/Screens/CScreenOptionsSound.cs index 847586f8..8e417a2c 100644 --- a/Vocaluxe/Screens/CScreenOptionsSound.cs +++ b/Vocaluxe/Screens/CScreenOptionsSound.cs @@ -150,13 +150,16 @@ namespace Vocaluxe.Screens { CConfig.GameMusicVolume = _SelectSlides[_SelectSlideGameMusicVolume].Selection * 5; CConfig.PreviewMusicVolume = _SelectSlides[_SelectSlidePreviewMusicVolume].Selection * 5; + CConfig.BackgroundMusic=(EOffOn)_SelectSlides[_SelectSlideBackgroundMusic].Selection; + CConfig.BackgroundMusicSource = (EBackgroundMusicSource)_SelectSlides[_SelectSlideBackgroundMusicSource].Selection; + CConfig.BackgroundMusicVolume= _SelectSlides[_SelectSlideBackgroundMusicVolume].Selection * 5; CConfig.SaveConfig(); - var newOffOn = (EOffOn)_SelectSlides[_SelectSlideBackgroundMusic].Selection; - var newSource = (EBackgroundMusicSource)_SelectSlides[_SelectSlideBackgroundMusicSource].Selection; - float newVolume = _SelectSlides[_SelectSlideBackgroundMusicVolume].Selection * 5; - - CBackgroundMusic.CheckAndApplyConfig(newOffOn, newSource, newVolume); + CBackgroundMusic.SetMusicSource(CConfig.BackgroundMusicSource); + CBackgroundMusic.SetVolume(CConfig.BackgroundMusicVolume); + if (CConfig.BackgroundMusic == EOffOn.TR_CONFIG_ON) + CBackgroundMusic.Play(); + else CBackgroundMusic.Pause(); } } } \ No newline at end of file diff --git a/Vocaluxe/Screens/CScreenProfiles.cs b/Vocaluxe/Screens/CScreenProfiles.cs index d3b06914..9039f293 100644 --- a/Vocaluxe/Screens/CScreenProfiles.cs +++ b/Vocaluxe/Screens/CScreenProfiles.cs @@ -330,7 +330,7 @@ namespace Vocaluxe.Screens private void _OnSaveSnapshot() { - string filename = Path.Combine(CSettings.DataPath, CSettings.FolderProfiles, "snapshot"); + string filename = Path.Combine(CSettings.DataPath, CConfig.ProfileFolders[0], "snapshot"); int i = 0; while (File.Exists(filename + i + ".png")) i++; diff --git a/Vocaluxe/Screens/CScreenSing.cs b/Vocaluxe/Screens/CScreenSing.cs index 4eb21fbd..32e0442e 100644 --- a/Vocaluxe/Screens/CScreenSing.cs +++ b/Vocaluxe/Screens/CScreenSing.cs @@ -556,7 +556,7 @@ namespace Vocaluxe.Screens { if (_CurrentStream > -1) CSound.FadeAndClose(_CurrentStream, 0f, 0.5f); - CSound.RecordStop(); + CRecord.Stop(); if (_CurrentVideo != -1) { CVideo.Close(_CurrentVideo); @@ -577,7 +577,6 @@ namespace Vocaluxe.Screens _Texts[_TextSongName].Text = String.Empty; _Texts[_TextDuetName1].Text = String.Empty; _Texts[_TextDuetName2].Text = String.Empty; - GC.Collect(); } private void _LoadNextSong() @@ -639,7 +638,6 @@ namespace Vocaluxe.Screens _SlideShow = GetNewBackground(); foreach (string bgFile in song.BackgroundFileNames) _SlideShow.AddSlideShowTexture(Path.Combine(song.Folder, bgFile)); - _Background = null; } else if (song.BackgroundFileNames.Count == 1) { @@ -738,7 +736,7 @@ namespace Vocaluxe.Screens { _PrepareTimeLine(); CSound.Play(_CurrentStream); - CSound.RecordStart(); + CRecord.Start(); if (_Webcam) CWebcam.Start(); } diff --git a/Vocaluxe/Vocaluxe.csproj b/Vocaluxe/Vocaluxe.csproj index 1b5d5b87..d0c8f9ba 100644 --- a/Vocaluxe/Vocaluxe.csproj +++ b/Vocaluxe/Vocaluxe.csproj @@ -199,6 +199,10 @@ ..\Output\OpenTK.GLControl.dll + + False + ..\Output\PortAudioSharp.dll + False ..\Output\QRCoder.dll @@ -212,14 +216,22 @@ - + + + + + + + + + - - + + @@ -252,11 +264,14 @@ - + + + + - + @@ -278,17 +293,17 @@ - - - + + + - - - - - - - + + + + + + + @@ -322,7 +337,6 @@ Designer - PreserveNewest diff --git a/VocaluxeLib/CHelper.cs b/VocaluxeLib/CHelper.cs index a03b1ad9..77c8f729 100644 --- a/VocaluxeLib/CHelper.cs +++ b/VocaluxeLib/CHelper.cs @@ -26,6 +26,22 @@ namespace VocaluxeLib { public static class CHelper { + private static readonly List _SoundFileTypes = new List + { + "*.mp3", + "*.wma", + "*.ogg", + "*.wav" + }; + + private static readonly List _ImageFileTypes = new List + { + "*.jpg", + "*.jpeg", + "*.png", + "*.gif" + }; + public static int CombinationCount(int n, int k) { if (k > n) @@ -61,7 +77,7 @@ namespace VocaluxeLib /// public static string ListStrings(string[] str) { - string result = string.Empty; + string result = String.Empty; for (int i = 0; i < str.Length; i++) { result += str[i]; @@ -95,7 +111,7 @@ namespace VocaluxeLib return 0; } int result; - return int.TryParse(value, out result) ? result : 0; + return Int32.TryParse(value, out result) ? result : 0; } public static void SetRect(RectangleF bounds, out RectangleF rect, float rectAspect, EAspect aspect) @@ -145,7 +161,15 @@ namespace VocaluxeLib rect = new RectangleF(left, upper, scaledWidth, scaledHeight); } - public static List ListFiles(string path, string cast, bool recursive = false, bool fullpath = false) + /// + /// Returns a list with all files in the given path that match a given pattern + /// + /// Path to search for + /// Pattern to match (e.g. "*.jpg") + /// Search directories recursively + /// False for just file names, True for full path + /// List of file names + public static List ListFiles(string path, string searchPattern, bool recursive = false, bool fullpath = false) { var files = new List(); var dir = new DirectoryInfo(path); @@ -155,14 +179,14 @@ namespace VocaluxeLib try { // ReSharper disable LoopCanBeConvertedToQuery - foreach (FileInfo file in dir.GetFiles(cast)) + foreach (FileInfo file in dir.GetFiles(searchPattern)) // ReSharper restore LoopCanBeConvertedToQuery files.Add(!fullpath ? file.Name : file.FullName); if (recursive) { foreach (DirectoryInfo di in dir.GetDirectories()) - files.AddRange(ListFiles(di.FullName, cast, true, fullpath)); + files.AddRange(ListFiles(di.FullName, searchPattern, true, fullpath)); } } catch (Exception) {} @@ -170,6 +194,47 @@ namespace VocaluxeLib return files; } + /// + /// Returns a list with all files in the given path that matches at least one of the given patterns + /// + /// Path to search for + /// List of patterns to match + /// Search directories recursively + /// False for just file names, True for full path + /// List of file names + public static List ListFiles(string path, IEnumerable searchPatterns, bool recursive = false, bool fullpath = false) + { + var files = new List(); + foreach (string pattern in searchPatterns) + files.AddRange(ListFiles(path, pattern, recursive, fullpath)); + + return files; + } + + /// + /// Returns a list with all image files in the given path + /// + /// Path to search for + /// Search directories recursively + /// False for just file names, True for full path + /// List of image file names + public static List ListImageFiles(string path, bool recursive = false, bool fullpath = false) + { + return ListFiles(path, _ImageFileTypes, recursive, fullpath); + } + + /// + /// Returns a list with all image files in the given path + /// + /// Path to search for + /// Search directories recursively + /// False for just file names, True for full path + /// List of image file names + public static List ListSoundFiles(string path, bool recursive = false, bool fullpath = false) + { + return ListFiles(path, _SoundFileTypes, recursive, fullpath); + } + public static bool TryParse(string value, out T result, bool ignoreCase = false) where T : struct { @@ -187,7 +252,7 @@ namespace VocaluxeLib public static bool TryParse(string value, out float result) { value = value.Replace(',', '.'); - return float.TryParse(value, NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, NumberFormatInfo.InvariantInfo, out result); + return Single.TryParse(value, NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, NumberFormatInfo.InvariantInfo, out result); } public static bool IsInBounds(SRectF bounds, SMouseEvent mouseEvent) diff --git a/VocaluxeLib/Enums.cs b/VocaluxeLib/Enums.cs index 5ac45bd3..2f647bd1 100644 --- a/VocaluxeLib/Enums.cs +++ b/VocaluxeLib/Enums.cs @@ -189,7 +189,6 @@ namespace VocaluxeLib { // ReSharper disable InconsistentNaming TR_CONFIG_NONE, - //TR_CONFIG_RANDOM, TR_CONFIG_FOLDER, TR_CONFIG_ARTIST, TR_CONFIG_ARTIST_LETTER, @@ -291,7 +290,7 @@ namespace VocaluxeLib } #endregion Config - public enum EGameState + public enum EProgramState { Start, Normal, diff --git a/VocaluxeLib/IBasic.cs b/VocaluxeLib/IBasic.cs index 334221a0..433e5fc8 100644 --- a/VocaluxeLib/IBasic.cs +++ b/VocaluxeLib/IBasic.cs @@ -71,7 +71,7 @@ namespace VocaluxeLib float GetZFar(); float GetZNear(); - EGameState GetGameState(); + EProgramState GetProgramState(); int GetToneMin(); int GetToneMax(); @@ -124,8 +124,6 @@ namespace VocaluxeLib void Pause(); void Play(); - void ApplyVolume(); - CTexture GetVideoTexture(); } @@ -211,6 +209,7 @@ namespace VocaluxeLib EGameDifficulty GetDifficulty(int profileID); string GetPlayerName(int profileID, int playerNum = 0); CTexture GetAvatar(int profileID); + CAvatar GetAvatarByFilename(string fileName); bool IsProfileIDValid(int profileID); bool IsGuest(int profileID); void AddProfileChangedCallback(ProfileChangedCallback notification); @@ -279,7 +278,7 @@ namespace VocaluxeLib public interface IDataBase { bool GetCover(string fileName, ref CTexture texture, int coverSize); - bool GetDataBaseSongInfos(string artist, string title, out int numPlayed, out string dateAdded, out int highscoreID); + bool GetDataBaseSongInfos(string artist, string title, out int numPlayed, out DateTime dateAdded, out int highscoreID); } public interface IControllers diff --git a/VocaluxeLib/Menu/CButton.cs b/VocaluxeLib/Menu/CButton.cs index 7a9726ec..1a9a8a49 100644 --- a/VocaluxeLib/Menu/CButton.cs +++ b/VocaluxeLib/Menu/CButton.cs @@ -276,7 +276,7 @@ namespace VocaluxeLib.Menu public void Draw(bool forceDraw = false) { - if (!Visible && CBase.Settings.GetGameState() != EGameState.EditTheme && !forceDraw) + if (!Visible && CBase.Settings.GetProgramState() != EProgramState.EditTheme && !forceDraw) return; CTexture texture; diff --git a/VocaluxeLib/Menu/CLyric.cs b/VocaluxeLib/Menu/CLyric.cs index a1c72daa..68b26136 100644 --- a/VocaluxeLib/Menu/CLyric.cs +++ b/VocaluxeLib/Menu/CLyric.cs @@ -224,7 +224,7 @@ namespace VocaluxeLib.Menu #region draw public void Draw(float actualBeat) { - if (Visible || CBase.Settings.GetGameState() == EGameState.EditTheme) + if (Visible || CBase.Settings.GetProgramState() == EProgramState.EditTheme) { switch (LyricStyle) { diff --git a/VocaluxeLib/Menu/CMenu.cs b/VocaluxeLib/Menu/CMenu.cs index c64c9400..af2e5443 100644 --- a/VocaluxeLib/Menu/CMenu.cs +++ b/VocaluxeLib/Menu/CMenu.cs @@ -968,7 +968,7 @@ namespace VocaluxeLib.Menu float z = CBase.Settings.GetZFar(); for (int i = 0; i < _Interactions.Count; i++) { - if ((CBase.Settings.GetGameState() != EGameState.EditTheme) && (_Interactions[i].ThemeEditorOnly || !_IsVisible(i) || !_IsEnabled(i))) + if ((CBase.Settings.GetProgramState() != EProgramState.EditTheme) && (_Interactions[i].ThemeEditorOnly || !_IsVisible(i) || !_IsEnabled(i))) continue; if (!_IsMouseOver(x, y, _Interactions[i])) continue; @@ -1043,7 +1043,7 @@ namespace VocaluxeLib.Menu private void _NextInteraction() { _UnsetSelected(); - if (CBase.Settings.GetGameState() != EGameState.EditTheme) + if (CBase.Settings.GetProgramState() != EProgramState.EditTheme) { bool found = false; int start = _Selection; @@ -1070,7 +1070,7 @@ namespace VocaluxeLib.Menu private void _PrevInteraction() { _UnsetSelected(); - if (CBase.Settings.GetGameState() != EGameState.EditTheme) + if (CBase.Settings.GetProgramState() != EProgramState.EditTheme) { bool found = false; int start = _Selection; diff --git a/VocaluxeLib/Menu/CObjectInteractions.cs b/VocaluxeLib/Menu/CObjectInteractions.cs index 57d86b00..791412d8 100644 --- a/VocaluxeLib/Menu/CObjectInteractions.cs +++ b/VocaluxeLib/Menu/CObjectInteractions.cs @@ -470,7 +470,7 @@ namespace VocaluxeLib.Menu float z = CBase.Settings.GetZFar(); for (int i = 0; i < _Interactions.Count; i++) { - if ((CBase.Settings.GetGameState() == EGameState.EditTheme) || (!_Interactions[i].ThemeEditorOnly && _IsVisible(i) && _IsEnabled(i))) + if ((CBase.Settings.GetProgramState() == EProgramState.EditTheme) || (!_Interactions[i].ThemeEditorOnly && _IsVisible(i) && _IsEnabled(i))) { if (_IsMouseOver(x, y, _Interactions[i])) { @@ -553,7 +553,7 @@ namespace VocaluxeLib.Menu private void _NextInteraction() { _UnsetSelected(); - if (CBase.Settings.GetGameState() != EGameState.EditTheme) + if (CBase.Settings.GetProgramState() != EProgramState.EditTheme) { bool found = false; int start = _Selection; @@ -580,7 +580,7 @@ namespace VocaluxeLib.Menu private void _PrevInteraction() { _UnsetSelected(); - if (CBase.Settings.GetGameState() != EGameState.EditTheme) + if (CBase.Settings.GetProgramState() != EProgramState.EditTheme) { bool found = false; int start = _Selection; diff --git a/VocaluxeLib/Menu/CPlaylist.cs b/VocaluxeLib/Menu/CPlaylist.cs index 1610fd52..1e71a262 100644 --- a/VocaluxeLib/Menu/CPlaylist.cs +++ b/VocaluxeLib/Menu/CPlaylist.cs @@ -416,7 +416,7 @@ namespace VocaluxeLib.Menu { if (_PlaylistElements.Count <= 0) LoadPlaylist(0); - if (!Visible && CBase.Settings.GetGameState() != EGameState.EditTheme && !forceDraw) + if (!Visible && CBase.Settings.GetProgramState() != EProgramState.EditTheme && !forceDraw) return; for (int i = 0; i < _PlaylistElements.Count; i++) diff --git a/VocaluxeLib/Menu/CSelectSlide.cs b/VocaluxeLib/Menu/CSelectSlide.cs index 93d0bea3..039533f4 100644 --- a/VocaluxeLib/Menu/CSelectSlide.cs +++ b/VocaluxeLib/Menu/CSelectSlide.cs @@ -710,7 +710,7 @@ namespace VocaluxeLib.Menu public void Draw() { - if (!Visible && CBase.Settings.GetGameState() != EGameState.EditTheme) + if (!Visible && CBase.Settings.GetProgramState() != EProgramState.EditTheme) return; CTexture texture = CBase.Theme.GetSkinTexture(_Theme.TextureName, _PartyModeID); @@ -733,7 +733,7 @@ namespace VocaluxeLib.Menu else CBase.Drawing.DrawTexture(texture, Rect, Color); - if (_Selection > 0 || CBase.Settings.GetGameState() == EGameState.EditTheme) + if (_Selection > 0 || CBase.Settings.GetProgramState() == EProgramState.EditTheme) { if (_ArrowLeftSelected) CBase.Drawing.DrawTexture(selTextureArrowLeft, RectArrowLeft, SelColorArrow); @@ -741,7 +741,7 @@ namespace VocaluxeLib.Menu CBase.Drawing.DrawTexture(textureArrowLeft, RectArrowLeft, ColorArrow); } - if (_Selection < _ValueNames.Count - 1 || CBase.Settings.GetGameState() == EGameState.EditTheme) + if (_Selection < _ValueNames.Count - 1 || CBase.Settings.GetProgramState() == EProgramState.EditTheme) { if (_ArrowRightSelected) CBase.Drawing.DrawTexture(selTextureArrowRight, RectArrowRight, SelColorArrow); diff --git a/VocaluxeLib/Menu/CStatic.cs b/VocaluxeLib/Menu/CStatic.cs index 5051abe7..24b9b56c 100644 --- a/VocaluxeLib/Menu/CStatic.cs +++ b/VocaluxeLib/Menu/CStatic.cs @@ -227,14 +227,14 @@ namespace VocaluxeLib.Menu } var color = new SColorF(Color.R, Color.G, Color.B, Color.A * Alpha); - if (Visible || forceDraw || (CBase.Settings.GetGameState() == EGameState.EditTheme)) + if (Visible || forceDraw || (CBase.Settings.GetProgramState() == EProgramState.EditTheme)) { CBase.Drawing.DrawTexture(texture, rect, color, bounds); if (Reflection) CBase.Drawing.DrawTextureReflection(texture, rect, color, bounds, ReflectionSpace, ReflectionHeight); } - if (Selected && (CBase.Settings.GetGameState() == EGameState.EditTheme)) + if (Selected && (CBase.Settings.GetProgramState() == EProgramState.EditTheme)) CBase.Drawing.DrawColor(new SColorF(1f, 1f, 1f, 0.5f), rect); } diff --git a/VocaluxeLib/Menu/CText.cs b/VocaluxeLib/Menu/CText.cs index 94c2c01c..711cc930 100644 --- a/VocaluxeLib/Menu/CText.cs +++ b/VocaluxeLib/Menu/CText.cs @@ -457,7 +457,7 @@ namespace VocaluxeLib.Menu public void Draw(bool forceDraw = false) { - if (!forceDraw && !Visible && CBase.Settings.GetGameState() != EGameState.EditTheme) + if (!forceDraw && !Visible && CBase.Settings.GetProgramState() != EProgramState.EditTheme) return; // Update Text @@ -474,7 +474,7 @@ namespace VocaluxeLib.Menu if (ReflectionHeight > 0) CBase.Fonts.DrawTextReflection(_Text, Rect.H, Rect.X, Rect.Y, Z, color, ReflectionSpace, ReflectionHeight); - if (Selected && (CBase.Settings.GetGameState() == EGameState.EditTheme)) + if (Selected && (CBase.Settings.GetProgramState() == EProgramState.EditTheme)) CBase.Drawing.DrawColor(new SColorF(0.5f, 1f, 0.5f, 0.5f), new SRectF(Rect.X, Rect.Y, Rect.W, Rect.H, Z)); } @@ -493,7 +493,7 @@ namespace VocaluxeLib.Menu // TODO } - if (Selected && (CBase.Settings.GetGameState() == EGameState.EditTheme)) + if (Selected && (CBase.Settings.GetProgramState() == EProgramState.EditTheme)) CBase.Drawing.DrawColor(new SColorF(0.5f, 1f, 0.5f, 0.5f), new SRectF(X, Y, Rect.W, Rect.H, Z)); } diff --git a/VocaluxeLib/Profile/CAvatar.cs b/VocaluxeLib/Profile/CAvatar.cs index 1928c915..9418a034 100644 --- a/VocaluxeLib/Profile/CAvatar.cs +++ b/VocaluxeLib/Profile/CAvatar.cs @@ -22,36 +22,46 @@ namespace VocaluxeLib.Profile public class CAvatar { public int ID; - public string FileName = ""; - public CTexture Texture; + private string _FileName = ""; + private CTexture _Texture; - public CAvatar(int id) + public CTexture Texture + { + get { return _Texture; } + } + public string FileName + { + get { return _FileName; } + } + + public static CAvatar GetAvatar(string fileName) + { + CTexture texture = CBase.Drawing.AddTexture(fileName); + return texture == null ? null : new CAvatar(texture, fileName); + } + + private CAvatar(CTexture texture, string fileName, int id = -1) { ID = id; } public bool LoadFromFile(string fileName) { - FileName = fileName; + _FileName = fileName; return Reload(); } public bool Reload() { - CBase.Drawing.RemoveTexture(ref Texture); - Texture = CBase.Drawing.AddTexture(FileName); + Unload(); + _Texture = CBase.Drawing.AddTexture(_FileName); - return Texture != null; + return _Texture != null; } public void Unload() { - CBase.Drawing.RemoveTexture(ref Texture); - } - - public string GetFileName() - { - return FileName; + CBase.Drawing.RemoveTexture(ref _Texture); } } } \ No newline at end of file diff --git a/VocaluxeLib/Profile/CProfile.cs b/VocaluxeLib/Profile/CProfile.cs index 4e27b4bd..ca8de4d6 100644 --- a/VocaluxeLib/Profile/CProfile.cs +++ b/VocaluxeLib/Profile/CProfile.cs @@ -38,22 +38,12 @@ namespace VocaluxeLib.Profile public string PlayerName; public string FilePath; public string FileName; - public string AvatarFileName; public byte[] PasswordHash; public byte[] PasswordSalt; public EGameDifficulty Difficulty; - private CAvatar _Avatar; - public CAvatar Avatar - { - get { return _Avatar; } - set - { - _Avatar = value; - AvatarFileName = _Avatar.FileName; - } - } + public CAvatar Avatar { get; set; } public EUserRole UserRole; public EOffOn Active; @@ -62,11 +52,9 @@ namespace VocaluxeLib.Profile { PlayerName = String.Empty; Difficulty = EGameDifficulty.TR_CONFIG_EASY; - Avatar = new CAvatar(-1); UserRole = EUserRole.TR_USERROLE_NORMAL; Active = EOffOn.TR_CONFIG_ON; - AvatarFileName = String.Empty; FileName = String.Empty; } @@ -95,9 +83,10 @@ namespace VocaluxeLib.Profile if (xmlReader.GetValue("//root/Info/PlayerName", out value, value)) { PlayerName = value; - + string avatarFileName; xmlReader.TryGetEnumValue("//root/Info/Difficulty", ref Difficulty); - xmlReader.GetValue("//root/Info/Avatar", out AvatarFileName, String.Empty); + xmlReader.GetValue("//root/Info/Avatar", out avatarFileName, String.Empty); + Avatar = CBase.Profiles.GetAvatarByFilename(avatarFileName); xmlReader.TryGetEnumValue("//root/Info/UserRole", ref UserRole); xmlReader.TryGetEnumValue("//root/Info/Active", ref Active); string passwordHash; diff --git a/VocaluxeLib/Songs/CSong.cs b/VocaluxeLib/Songs/CSong.cs index c16c1874..26d1d6fd 100644 --- a/VocaluxeLib/Songs/CSong.cs +++ b/VocaluxeLib/Songs/CSong.cs @@ -130,6 +130,7 @@ namespace VocaluxeLib.Songs public string Creator = ""; public string Version = ""; + public string Length = ""; //Length set in song file, SHOULD match actual song length but is more a hint public string Source = ""; public readonly List UnknownTags = new List(); @@ -172,7 +173,7 @@ namespace VocaluxeLib.Songs public readonly List Languages = new List(); public int DataBaseSongID = -1; - public string DateAdded = ""; + public DateTime DateAdded = DateTime.Today; public int NumPlayed; public int NumPlayedSession; @@ -240,6 +241,7 @@ namespace VocaluxeLib.Songs Creator = song.Creator; Version = song.Version; + Length = song.Length; Source = song.Source; UnknownTags = new List(song.UnknownTags); @@ -318,10 +320,7 @@ namespace VocaluxeLib.Songs { if (CoverFileName == "") { - List files = CHelper.ListFiles(Folder, "*.jpg"); - files.AddRange(CHelper.ListFiles(Folder, "*.jpeg")); - files.AddRange(CHelper.ListFiles(Folder, "*.png")); - files.AddRange(CHelper.ListFiles(Folder, "*.gif")); + List files = CHelper.ListImageFiles(Folder); foreach (String file in files) { if (file.ContainsIgnoreCase("[CO]") && @@ -332,10 +331,7 @@ namespace VocaluxeLib.Songs if (BackgroundFileNames.Count == 0) { - List files = CHelper.ListFiles(Folder, "*.jpg"); - files.AddRange(CHelper.ListFiles(Folder, "*.jpeg")); - files.AddRange(CHelper.ListFiles(Folder, "*.png")); - files.AddRange(CHelper.ListFiles(Folder, "*.gif")); + List files = CHelper.ListImageFiles(Folder); foreach (String file in files) { if (file.ContainsIgnoreCase("[BG]") && diff --git a/VocaluxeLib/Songs/CSongLoader.cs b/VocaluxeLib/Songs/CSongLoader.cs index 8c959583..7c18a981 100644 --- a/VocaluxeLib/Songs/CSongLoader.cs +++ b/VocaluxeLib/Songs/CSongLoader.cs @@ -192,6 +192,9 @@ namespace VocaluxeLib.Songs case "YOUTUBE": _Song.Source = value; break; + case "LENGTH": + _Song.Length = value; + break; case "MP3": if (File.Exists(Path.Combine(_Song.Folder, value))) { diff --git a/VocaluxeLib/Songs/CSongWriter.cs b/VocaluxeLib/Songs/CSongWriter.cs index 1e3221d8..a36e3fe6 100644 --- a/VocaluxeLib/Songs/CSongWriter.cs +++ b/VocaluxeLib/Songs/CSongWriter.cs @@ -71,6 +71,7 @@ namespace VocaluxeLib.Songs _WriteHeaderEntry("ENCODING", _Song.Encoding.GetEncodingName()); _WriteHeaderEntry("CREATOR", _Song.Creator); _WriteHeaderEntry("VERSION", _Song.Version); + _WriteHeaderEntry("LENGTH", _Song.Length); _WriteHeaderEntry("SOURCE", _Song.Source); if (!String.IsNullOrEmpty(_Song._Comment)) { diff --git a/WebserverInitalConfig/CConfigHttpApi.cs b/WebserverInitalConfig/CConfigHttpApi.cs index 38002df3..e16f3a44 100644 --- a/WebserverInitalConfig/CConfigHttpApi.cs +++ b/WebserverInitalConfig/CConfigHttpApi.cs @@ -17,6 +17,7 @@ using System.Security.Authentication; using System.Security.Principal; +using System.Windows.Forms; using Security.Cryptography; using Security.Cryptography.X509Certificates; using System; @@ -124,7 +125,22 @@ namespace WebserverInitalConfig keyParam.Parameters.Add(new CngProperty("Length", BitConverter.GetBytes(2048), CngPropertyOptions.None)); - CngKey key = CngKey.Create(CngAlgorithm2.Rsa, Guid.NewGuid().ToString(), keyParam); + CngKey key; + try + { + key= CngKey.Create(CngAlgorithm2.Rsa, Guid.NewGuid().ToString(), keyParam); + } + catch (PlatformNotSupportedException) + { + try + { + key = CngKey.Create(CngAlgorithm2.Aes, Guid.NewGuid().ToString(), keyParam); + } + catch (PlatformNotSupportedException) + { + return null; + } + } X509CertificateCreationParameters param = new X509CertificateCreationParameters(new X500DistinguishedName(subjectName)) { @@ -156,6 +172,11 @@ namespace WebserverInitalConfig if (!IsAdministrator()) throw new AuthenticationException(); cert = _GetSelfSignedCert(certName); + if (cert == null) + { + MessageBox.Show("Could not create certificate. Secure server connection may not work. Disable it if required."); + return; + } _AddCertToStore(cert); } _BindCert(cert); diff --git a/WebserverInitalConfig/Program.cs b/WebserverInitalConfig/Program.cs index fc09dac1..60e3ce4a 100644 --- a/WebserverInitalConfig/Program.cs +++ b/WebserverInitalConfig/Program.cs @@ -20,6 +20,7 @@ using System.Diagnostics; using System.IO; using System.Net; using System.Security.Authentication; +using System.Windows.Forms; namespace WebserverInitalConfig { @@ -29,49 +30,56 @@ namespace WebserverInitalConfig private static void Main(string[] args) // ReSharper restore InconsistentNaming { - int port; - if (args.Length < 2 || !int.TryParse(args[0], out port)) + try { - Console.WriteLine("Usage: VocaluxeServerConfig port isHttps"); - Environment.Exit(-1); - } - else - { - CConfigHttpApi config = new CConfigHttpApi("Vocaluxe", Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Vocaluxe.exe"), "0.0.0.0", port, true); - try + int port; + if (args.Length < 2 || !int.TryParse(args[0], out port)) { - config.AddFirewallRule(); - if (args[1].ToLower() == "true") - config.CreateAndAddCert(Dns.GetHostName()); - - if ((args.Length >= 3 && args[2] == "reserve") || CConfigHttpApi.IsAdministrator()) - config.ReserveUrl(); + Console.WriteLine("Usage: VocaluxeServerConfig port isHttps"); + Environment.Exit(-1); } - catch (AuthenticationException) + else { - ProcessStartInfo proc = new ProcessStartInfo - { - UseShellExecute = true, - WorkingDirectory = Environment.CurrentDirectory, - FileName = Process.GetCurrentProcess().MainModule.FileName, - Arguments = args[0] + " " + args[1], - Verb = "runas", - CreateNoWindow = true, - WindowStyle = ProcessWindowStyle.Hidden, - }; - + CConfigHttpApi config = new CConfigHttpApi("Vocaluxe", Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Vocaluxe.exe"), "0.0.0.0", port, true); try { - Process.Start(proc); + config.AddFirewallRule(); + if (args[1].ToLower() == "true") + config.CreateAndAddCert(Dns.GetHostName()); + + if ((args.Length >= 3 && args[2] == "reserve") || CConfigHttpApi.IsAdministrator()) + config.ReserveUrl(); } - catch + catch (AuthenticationException) { - // The user refused the elevation. - // Do nothing and return directly ... - Environment.Exit(-1); + ProcessStartInfo proc = new ProcessStartInfo + { + UseShellExecute = true, + WorkingDirectory = Environment.CurrentDirectory, + FileName = Process.GetCurrentProcess().MainModule.FileName, + Arguments = args[0] + " " + args[1], + Verb = "runas", + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + }; + + try + { + Process.Start(proc); + } + catch + { + // The user refused the elevation. + // Do nothing and return directly ... + Environment.Exit(-1); + } } } } + catch (Exception e) + { + MessageBox.Show("Error while installing server: " + e.Message); + } } } } \ No newline at end of file diff --git a/WebserverInitalConfig/WebserverInitalConfig.csproj b/WebserverInitalConfig/WebserverInitalConfig.csproj index 2f48f927..5405cbe2 100644 --- a/WebserverInitalConfig/WebserverInitalConfig.csproj +++ b/WebserverInitalConfig/WebserverInitalConfig.csproj @@ -41,16 +41,13 @@ True ..\Output\Interop.NetFwTypeLib.dll - - False - ..\Output\Security.dll - False ..\Output\Security.Cryptography.dll +