pax_global_header00006660000000000000000000000064123376670130014522gustar00rootroot0000000000000052 comment=2694488f5c35a77aea8140f76abb7366697873e2 karlyriceditor-1.11/000077500000000000000000000000001233766701300144735ustar00rootroot00000000000000karlyriceditor-1.11/Changelog000066400000000000000000000112651233766701300163120ustar00rootroot000000000000002013-11-10 gyunaev - Released version 1.9 - Fixed jittering sound output when a newer version of ffmped is installed; - Fixed reversed Artist and Title fields in the CD+G export dialog; - Fixed the registration dialog which didn't pop up; - Fixed the "missing LRC header" error while importing KFN lyrics via "Import lyrics" menu - When a new project is created the editor now will try to read the meta tags and fill up the Artist, Title and Album automatically. 2013-10-05 gyunaev - Released version 1.8 - Fixed the registration dialog entry lockup with recent Qt versions 2013-08-08 gyunaev - Released version 1.7 - Fixed the CD+G output which now uses two color gradations. The output looks better, and the "Color table out of colors" error is gone forever. 2013-05-30 gyunaev - Released version 1.6 - Added support for importing Doblon PowerKaraoke lyrics and Del MP3 KOK lyrics; - Fixed "Test CD+G" option sometime showing a black screen; - Fixed KFN parsing and Ultrastar output; - Fixed compilation on x86_64 2012-12-22 gyunaev - Released version 1.4 - Added partial KaraFun (KFN) format support. The KaraFun files could be imported during the project creation (only music and lyrics). Also the lyrics could be imported via Project -> Import lyrics. - Added the time adjustment dialog which allows modifying all the timestamps in the file simultaneously. - Added the option to automatically create end-of-line timing marks (useful with import of LRCv2) - Exceeding the 16 colors during CD+G output does not result in crash anymore. - The code does not use deprecated FFMpeg functions anymore, should fix builds on some new distributions. - Various video and CD+G output fixes. 2012-02-01 gyunaev - Released versio 1.3 - Audio player switched to FFMpeg/SDL instead of incredibly buggy and unreliable Phonon. No more “cannot play this file” error on Win32. - Added improved export in CD+G format with a preview dialog and easily configurable options. - Improved the CD+G tester which allows playing any CD+G file, not only the one generated internally. Therefore it is now possible to do several exports, test them all and see which one would suit you the best. - Added support for the video and image backgrounds (Edit/Insert picture and Edit/Insert video). Any lyrics can use multiple backgrounds, which will be switched at specified times. Note that all existing lyrics formats are based on text and do not support backgrounds (it will be ignored when exporting to, for example, LRC format). Therefore it is only relevant for CD+G and video export. - Added support for video export. You can now export your Karaoke song together with music, lyrics and the background into a video file! - Numerous improvements and bugfixes. 2011-01-13 gyunaev - Released version 1.1 - Fixed several major bugs, including CD+G rendering; - Removed UltraStar pitch editor as it seems like nobody was using it; - Switched the editor to use regular text instead of annoying drawn timing marks. - Text editor is now not locked if the music file is not loaded. 2010-07-14 gyunaev - Released version 1.0 - Added support for CD+G format; - Fixed several minor bugs; 2009-12-13 gyunaev - Released version 0.7 - Added piano roll for choosing pitch for UltraStar format lyrics; - Added feature to check whether a new version is available; - Fixed a lot of UltraStar export issues; - Fixed issue when project loaded with an empty editor window; - If lyrics type changed, project settings now change accordingly. - Multiple UI changes. 2009-12-13 gyunaev - Added "Show player" and "Show piano roll" actions to Settings menu. - Piano roll is now only shown for UltraStar projects. - Fixed timing mark tooltips; now show the note and pitch as well. - Piano roll now generates pitch; - CheckNewVersion now uses special type; - Fixed pitch applicator for editor; 2009-12-12 gyunaev - Fixed import into disabled editor window. - Added "check for new version" feature. - If lyrics type changed, project settings change accordingly. 2009-12-07 gyunaev - Fixed UltraStar export, import and other specific stuff. - Color button is now drawn better way. - Added new Ultrastar-specific settings - Timing mark editor dialog redesigned. - "Save project" button is disabled unless changes are made to the project. 2009-12-06 gyunaev - Initial release, version 0.6. karlyriceditor-1.11/LICENSE000066400000000000000000001045131233766701300155040ustar00rootroot00000000000000 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 . karlyriceditor-1.11/build-release.sh000077500000000000000000000057171233766701300175610ustar00rootroot00000000000000#!/bin/sh RELEASEDIR="releases" # Path to (cross-platform) mingw compiler MINGWPATH=/usr/toolchains/windows-x86-mingw-qtsdl/bin QMAKE=i686-pc-mingw32-qmake FILE_VERSION="src/version.h" RPM_ARCH="i586" RPM_OUTDIR="$HOME/rpmbuild/RPMS/" # Get current version VERSION_MAJOR=`sed -n 's/^\#define\s\+APP_VERSION_MAJOR\s\+\([0-9]\+\)/\1/p' $FILE_VERSION` VERSION_MINOR=`sed -n 's/^\#define\s\+APP_VERSION_MINOR\s\+\([0-9]\+\)/\1/p' $FILE_VERSION` CURRENTVER="$VERSION_MAJOR.$VERSION_MINOR" OUTDIR="$RELEASEDIR/$CURRENTVER" if [ ! -d "$OUTDIR" ]; then mkdir -p "$OUTDIR" || exit 1 fi BUILDDIR="karlyriceditor-$CURRENTVER" if [ -d "$BUILDDIR" ]; then rm -rf "$BUILDDIR" fi svn export . "$BUILDDIR/" || exit 1 # Example package tar zcf examples.tar.gz "$BUILDDIR/example" || exit 1 rm -rf "$BUILDDIR/example" # Source package without examples tar zcf "$OUTDIR/$BUILDDIR.tar.gz" $BUILDDIR || exit 1 # Making an RPM rm -rf "$BUILDDIR/buildroot" mkdir -p "$BUILDDIR/buildroot/usr/bin" mkdir -p "$BUILDDIR/buildroot/usr/share/applications" mkdir -p "$BUILDDIR/buildroot/usr/share/pixmaps" cp packages/karlyriceditor.desktop "$BUILDDIR/buildroot/usr/share/applications" cp packages/karlyriceditor.png "$BUILDDIR/buildroot/usr/share/pixmaps" # Prepare a spec file sed "s/^Version: [0-9.]\\+/Version: $CURRENTVER/" packages/rpm.spec > $BUILDDIR/rpm.spec # Build a 64-bit version (cd "$BUILDDIR" && qmake -r -spec linux-g++-64 && make -j4) || exit 1 strip --strip-all "$BUILDDIR/bin/karlyriceditor" -o "$BUILDDIR/buildroot/usr/bin/karlyriceditor" || exit 1 # Build a 64-bit RPM rpmbuild -bb --target=x86_64 --buildroot `pwd`"/$BUILDDIR/buildroot/" $BUILDDIR/rpm.spec || exit 1 mv $RPM_OUTDIR/x86_64/*.rpm "$OUTDIR/" || exit 1 # Clean up pushd "$BUILDDIR" make distclean for lib in crypto avformat avcodec swscale avresample avutil SDL QtGui QtCore; do libpath=`find /lib /usr/lib/ -name lib$lib\* | sort -r | head -n1` if [ -z "$libpath" ]; then echo "No library $lib found" exit fi ln -s $libpath "src/lib$lib.so" done popd # Build a 32-bit version rm -rf "$BUILDDIR/buildroot" mkdir -p "$BUILDDIR/buildroot/usr/bin" mkdir -p "$BUILDDIR/buildroot/usr/share/applications" mkdir -p "$BUILDDIR/buildroot/usr/share/pixmaps" cp packages/karlyriceditor.desktop "$BUILDDIR/buildroot/usr/share/applications" cp packages/karlyriceditor.png "$BUILDDIR/buildroot/usr/share/pixmaps" (cd "$BUILDDIR" && qmake -r -spec linux-g++-32 && make -j4) || exit 1 strip --strip-all "$BUILDDIR/bin/karlyriceditor" -o "$BUILDDIR/buildroot/usr/bin/karlyriceditor" || exit 1 # Build a 32-bit RPM rpmbuild -bb --target=i586 --buildroot `pwd`"/$BUILDDIR/buildroot/" $BUILDDIR/rpm.spec || exit 1 mv $RPM_OUTDIR/i586/*.rpm "$OUTDIR/" || exit 1 rm -rf "$BUILDDIR" # Win32 build svn export . "$BUILDDIR/" || exit 1 export PATH=$MINGWPATH:$PATH (cd $BUILDDIR && $QMAKE -r "CONFIG += release" && make -j4) || exit 1 # installer (cd $BUILDDIR/nsis && sh create_installer.sh "$CURRENTVER") || exit 1 mv $BUILDDIR/nsis/*.exe "$OUTDIR/" karlyriceditor-1.11/build-win32-mingw.sh000077500000000000000000000006131233766701300202100ustar00rootroot00000000000000#!/bin/sh # Path to (cross-platform) mingw compiler MINGWPATH=/usr/toolchains/windows-x86-mingw-qtsdl/bin QMAKE=i686-pc-mingw32-qmake BUILDDIR="build.win32" ################################## if [ -d "$BUILDDIR" ]; then rm -rf "$BUILDDIR" fi svn export . "$BUILDDIR/" || exit 1 cd "$BUILDDIR" # Compile it export PATH=$MINGWPATH:$PATH $QMAKE -r "CONFIG += release" && make -j4 || exit 1 karlyriceditor-1.11/karlyriceditor.pro000066400000000000000000000000421233766701300202400ustar00rootroot00000000000000SUBDIRS += src TEMPLATE = subdirs karlyriceditor-1.11/nsis/000077500000000000000000000000001233766701300154475ustar00rootroot00000000000000karlyriceditor-1.11/nsis/create_installer.sh000066400000000000000000000006031233766701300213220ustar00rootroot00000000000000#!/bin/sh # Version if [ -z "$1" ]; then echo "Usage: $0 " exit 1 fi # Generated binary BINARY=../src/bin/karlyriceditor.exe cp $BINARY karlyriceditor.exe export NSISDIR=/home/tim/bin/nsis INSTNAME="InstallKarLyricEditor-$1.exe" echo "Creating $INSTNAME" makensis installer.nsis || exit 1 # Remove stuff rm karlyriceditor.exe mv InstallKarLyricEditor.exe $INSTNAME karlyriceditor-1.11/nsis/installer.nsis000077500000000000000000000052741233766701300203550ustar00rootroot00000000000000; NSIS script file ;-------------------------------- ;Include Modern UI !include "MUI.nsh" ;-------------------------------- ;General ; The icon for the installer. ; MUI_ICON icon_file ; The icon for the uninstaller. ; MUI_UNICON icon_file Name "Karaoke Lyric Editor" OutFile "InstallKarLyricEditor.exe" InstallDir "$PROGRAMFILES\Ulduzsoft\KarLyricEditor" InstallDirRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Ulduzsoft\KarLyricEditor" "" SetCompressor lzma ;-------------------------------- ;Variables Var MUI_TEMP Var STARTMENU_FOLDER ;-------------------------------- ;Interface Settings !define MUI_ABORTWARNING ;-------------------------------- ;Pages !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_LICENSE "license.txt" !insertmacro MUI_PAGE_DIRECTORY ;Start Menu Folder Page Configuration !define MUI_STARTMENUPAGE_REGISTRY_ROOT HKEY_LOCAL_MACHINE !define MUI_STARTMENUPAGE_REGISTRY_KEY "SOFTWARE\Ulduzsoft\KarLyricEditor" !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_FINISH ; Uninstaller pages !insertmacro MUI_UNPAGE_WELCOME !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES !insertmacro MUI_UNPAGE_FINISH ;-------------------------------- ;Languages !insertmacro MUI_LANGUAGE "English" ;-------------------------------- ;Installer Sections Section "General" General SetOutPath "$INSTDIR" ; Our files File karlyriceditor.exe File karlyriceditor.exe.manifest ;Store installation folder WriteRegStr HKEY_LOCAL_MACHINE "SOFTWARE\Ulduzsoft\KarLyricEditor" "" "$INSTDIR" ;Create uninstaller WriteUninstaller "$INSTDIR\uninst.exe" !insertmacro MUI_STARTMENU_WRITE_BEGIN Application ;Create shortcuts CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER" CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\KarLyricEditor.lnk" "$INSTDIR\karlyriceditor.exe" CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Uninstall.lnk" "$INSTDIR\uninst.exe" !insertmacro MUI_STARTMENU_WRITE_END SectionEnd ;-------------------------------- ;Uninstaller Section Section "Uninstall" ; Our files Delete "$INSTDIR\karlyriceditor.exe" Delete "$INSTDIR\karlyriceditor.exe.manifest" Delete "$INSTDIR\uninst.exe" RMDir "$INSTDIR" !insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk" Delete "$SMPROGRAMS\$MUI_TEMP\KarLyricEditor.lnk" Delete "$SMPROGRAMS\$MUI_TEMP" DeleteRegKey /ifempty HKEY_LOCAL_MACHINE "SOFTWARE\Ulduzsoft\KarLyricEditor" DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\KarLyricEditor" SectionEnd karlyriceditor-1.11/nsis/karlyriceditor.exe.manifest000077500000000000000000000007321233766701300230130ustar00rootroot00000000000000 Windows Forms Common Control manifest karlyriceditor-1.11/nsis/license.txt000077500000000000000000001045131233766701300176410ustar00rootroot00000000000000 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 . karlyriceditor-1.11/packages/000077500000000000000000000000001233766701300162515ustar00rootroot00000000000000karlyriceditor-1.11/packages/karlyriceditor.desktop000066400000000000000000000002661233766701300226770ustar00rootroot00000000000000[Desktop Entry] Type=Application Terminal=false Categories=Multimedia;Karaoke Name=Karaoke Lyric Editor Encoding=UTF-8 Icon=/usr/share/pixmaps/karlyriceditor.png Exec=karlyriceditor karlyriceditor-1.11/packages/karlyriceditor.png000066400000000000000000000012171233766701300220070ustar00rootroot00000000000000PNG  IHDRw= pHYs fAIDATH͕?ha`EL:Pg(dQ$52! RT:Tt%C0`:QhEJ)A$DCDwir=3}=s;`.h `x|~#q,foա̱IENDB`karlyriceditor-1.11/packages/rpm.spec000066400000000000000000000013771233766701300177330ustar00rootroot00000000000000Summary: Karaoke Lyrics Editor Name: karlyriceditor Version: 0.7 Release: 1 License: GPLv3+ Group: Applications/Multimedia Packager: support@karlyriceditor.com URL: http://www.karlyriceditor.com %description Karaoke Lyrics Editor is a program which lets you edit and synchronize lyrics with karaoke songs in varions formats. Unlike just regular lyrics available everywhere, lyrics suitable for karaoke players need to have timing marks which tell the player when to show a specific line, and when and how highlight the words. Author: ------- George Yunaev Requires: libqt4 >= 4.5.0 %files %defattr(-,root,root) /usr/bin/karlyriceditor %defattr(-,root,root) /usr/share/applications/karlyriceditor.desktop %defattr(-,root,root) /usr/share/pixmaps/karlyriceditor.png karlyriceditor-1.11/src/000077500000000000000000000000001233766701300152625ustar00rootroot00000000000000karlyriceditor-1.11/src/audioplayer.cpp000066400000000000000000000050031233766701300203020ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include "audioplayer.h" #include "audioplayerprivate.h" AudioPlayer * pAudioPlayer; // // Audio player wrapper class // AudioPlayer::AudioPlayer() { d = new AudioPlayerPrivate(); } AudioPlayer::~AudioPlayer() { delete d; } bool AudioPlayer::init() { return d->init(); } bool AudioPlayer::open( const QString& filename ) { return d->open( filename ); } void AudioPlayer::play() { d->play(); } void AudioPlayer::reset() { d->reset(); } void AudioPlayer::stop() { d->stop(); } void AudioPlayer::seekTo( qint64 value ) { d->seekTo( value ); } bool AudioPlayer::isPlaying() const { return d->isPlaying(); } qint64 AudioPlayer::currentTime() const { return d->currentTime(); } qint64 AudioPlayer::totalTime() const { return d->totalTime(); } QString AudioPlayer::errorMsg() const { return d->errorMsg(); } void AudioPlayer::emitTickSignal( qint64 tickvalue ) { emit tick( tickvalue ); } void AudioPlayer::close() { d->close(); } AudioPlayerPrivate * AudioPlayer::impl() { return d; } QString AudioPlayer::metaTitle() const { return d->m_metaTitle; } QString AudioPlayer::metaArtist() const { return d->m_metaArtist; } QString AudioPlayer::metaAlbum() const { return d->m_metaAlbum; } karlyriceditor-1.11/src/audioplayer.h000066400000000000000000000054621233766701300177600ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef AUDIOPLAYER_H #define AUDIOPLAYER_H #include class AudioPlayerPrivate; // Must be a single instance class AudioPlayer : public QObject { Q_OBJECT public: AudioPlayer(); virtual ~AudioPlayer(); // If a function below returned an error, this function will // retrieve the error message QString errorMsg() const; // Initialize the player bool init(); // Open the audio file. Returns true on success, false otherwise bool open( const QString& filename ); // Closes the audio file. If another file is opened, the previous will // be closed automatically. void close(); // True if audio is playing bool isPlaying() const; // Current playing time qint64 currentTime() const; // The audio file length qint64 totalTime() const; // Metadata QString metaTitle() const; QString metaArtist() const; QString metaAlbum() const; // To access the implementation (for video encoding) AudioPlayerPrivate * impl(); signals: // Emitted every time the new audio packet is processed void tick( qint64 tickvalue ); public slots: // Start or continues playing void play(); // Pauses the playing void stop(); // Rewinds the music file back; to emulate real "stop" call "stop" and "reset" void reset(); // Rewinds to a specific position void seekTo( qint64 value ); private slots: friend class AudioPlayerPrivate; void emitTickSignal( qint64 tickvalue ); private: AudioPlayerPrivate * d; }; extern AudioPlayer * pAudioPlayer; #endif // AUDIOPLAYER_H karlyriceditor-1.11/src/audioplayerprivate.cpp000066400000000000000000000211611233766701300217000ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include "audioplayer.h" #include "audioplayerprivate.h" #include // SDL defines its own main() function in SDL_main. And so does Qt, so if we continue without // the #define below we'll end up with the following link error: // libqtmain.a(qtmain_win.o):qtmain_win.cpp:(.text+0x159): undefined reference to `qMain(int, char**)' #undef main #define SDL_AUDIO_BUFFER_SIZE 1024 // SDL audio callback void sdl_audio_callback( void *userdata, Uint8 *stream, int len) { AudioPlayerPrivate * player = (AudioPlayerPrivate*) userdata; player->SDL_audio_callback( stream, len ); } AudioPlayerPrivate::AudioPlayerPrivate() { pFormatCtx = 0; aCodecCtx = 0; pCodec = 0; m_audioOpened = false; m_playing = false; m_currentTime = 0; m_totalTime = 0; m_frame = 0; m_sample_buf_size = 0; m_sample_buf_idx = 0; } bool AudioPlayerPrivate::isPlaying() const { QMutexLocker m( &m_mutex ); return m_playing; } qint64 AudioPlayerPrivate::currentTime() const { QMutexLocker m( &m_mutex ); return m_currentTime; } qint64 AudioPlayerPrivate::totalTime() const { QMutexLocker m( &m_mutex ); return m_totalTime; } QString AudioPlayerPrivate::errorMsg() const { return m_errorMsg; } bool AudioPlayerPrivate::init() { // Init FFMpeg stuff ffmpeg_init_once(); // Init the SDL if ( SDL_Init (SDL_INIT_AUDIO ) ) { QMessageBox::critical( 0, QObject::tr("SDL init failed"), QObject::tr("Cannot initialize audio subsystem:\n\n%1").arg(SDL_GetError()) ); return false; } return true; } void AudioPlayerPrivate::close() { QMutexLocker m( &m_mutex ); SDL_PauseAudio( 1 ); // Close the codec if ( aCodecCtx ) avcodec_close( aCodecCtx ); // Close the video file if ( pFormatCtx ) avformat_close_input( &pFormatCtx ); if ( m_audioOpened ) { SDL_CloseAudio(); m_audioOpened = false; } if ( m_frame ) av_free( m_frame ); m_frame = 0; pFormatCtx = 0; aCodecCtx = 0; pCodec = 0; } static QString getMetaTag( AVDictionary* meta, const char * tagname ) { AVDictionaryEntry * ent = av_dict_get(meta, tagname, NULL, 0); if ( ent ) return QString::fromUtf8( ent->value ); else return ""; } bool AudioPlayerPrivate::open( const QString& filename ) { // Close if opened close(); QMutexLocker m( &m_mutex ); // Open the file if ( avformat_open_input( &pFormatCtx, FFMPEG_FILENAME( filename ), NULL, 0 ) != 0 ) { m_errorMsg = "Could not open the audio file"; return false; } // Retrieve stream information if ( avformat_find_stream_info( pFormatCtx, 0 ) < 0 ) { m_errorMsg = "Could not find stream information in the audio file"; return false; } // Find the first audio stream audioStream = -1; for ( unsigned i = 0; i < pFormatCtx->nb_streams; i++ ) { if ( pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO ) { audioStream = i; break; } } if ( audioStream == -1 ) { m_errorMsg = "This file does not contain any playable audio"; return false; } if ( pFormatCtx->streams[audioStream]->duration == (int64_t) AV_NOPTS_VALUE ) { m_errorMsg = "Cannot determine the total audio length"; return false; } // Extract some metadata AVDictionary* metadata = pFormatCtx->metadata; if ( metadata ) { m_metaTitle = getMetaTag( metadata, "title" ); m_metaArtist = getMetaTag( metadata, "artist" ); m_metaAlbum = getMetaTag( metadata, "album" ); } m_totalTime = av_rescale_q( pFormatCtx->streams[audioStream]->duration, pFormatCtx->streams[audioStream]->time_base, AV_TIME_BASE_Q ) / 1000; aCodecCtx = pFormatCtx->streams[audioStream]->codec; aCodecCtx->request_sample_fmt = AV_SAMPLE_FMT_S16; // Open audio codec AVCodec * aCodec = avcodec_find_decoder( aCodecCtx->codec_id ); if ( !aCodec ) { m_errorMsg = "Unsupported audio codec"; return false; } avcodec_open2( aCodecCtx, aCodec, 0 ); // Now initialize the SDL audio device SDL_AudioSpec wanted_spec, spec; wanted_spec.freq = aCodecCtx->sample_rate; wanted_spec.format = AUDIO_S16SYS; wanted_spec.channels = aCodecCtx->channels; wanted_spec.silence = 0; wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; wanted_spec.callback = sdl_audio_callback; wanted_spec.userdata = this; if ( SDL_OpenAudio( &wanted_spec, &spec ) < 0 ) { m_errorMsg = QObject::tr("Cannot initialize audio device: %1") .arg( SDL_GetError() ); return false; } // Allocate the buffer m_frame = avcodec_alloc_frame(); if ( !m_frame ) { m_errorMsg = QObject::tr("Cannot allocate frame memory buffer"); return false; } m_audioOpened = true; // Init the packet queue queueClear(); return true; } void AudioPlayerPrivate::queueClear() { m_sample_buf_idx = 0; m_sample_buf_size = 0; m_sample_buffer.clear(); } void AudioPlayerPrivate::play() { QMutexLocker m( &m_mutex ); m_playing = true; SDL_PauseAudio( 0 ); } void AudioPlayerPrivate::reset() { seekTo( 0 ); } void AudioPlayerPrivate::stop() { QMutexLocker m( &m_mutex ); m_playing = false; SDL_PauseAudio( 1 ); } void AudioPlayerPrivate::seekTo( qint64 value ) { QMutexLocker m( &m_mutex ); av_seek_frame( pFormatCtx, -1, value * 1000, 0 ); avcodec_flush_buffers( aCodecCtx ); queueClear(); } // Called from SDL thread - no GUI/Widget functions! void AudioPlayerPrivate::SDL_audio_callback( Uint8 *stream, int len) { QMutexLocker m( &m_mutex ); while ( len > 0 ) { if ( m_sample_buf_idx >= m_sample_buf_size ) { // We have already sent all our data; get more if ( !MoreAudio() ) { // If error, output silence memset( stream, 0, len ); return; } } int len1 = m_sample_buf_size - m_sample_buf_idx; if ( len1 > len ) len1 = len; memcpy( stream, (uint8_t *) m_sample_buffer.data() + m_sample_buf_idx, len1 ); len -= len1; stream += len1; m_sample_buf_idx += len1; } } // Called from the callback - no GUI/Widget functions! bool AudioPlayerPrivate::MoreAudio() { while ( m_playing ) { AVPacket packet; // Read a frame if ( av_read_frame( pFormatCtx, &packet ) < 0 ) return false; // Frame read failed (e.g. end of stream) if ( packet.stream_index != audioStream ) { av_free_packet( &packet ); continue; } m_sample_buf_idx = 0; m_sample_buf_size = 0; m_sample_buffer.clear(); m_currentTime = av_rescale_q( packet.pts, pFormatCtx->streams[audioStream]->time_base, AV_TIME_BASE_Q ) / 1000; QMetaObject::invokeMethod( pAudioPlayer, "emitTickSignal", Qt::QueuedConnection, Q_ARG( qint64, m_currentTime ) ); // Save the orig data so we can call av_free_packet() on it void * porigdata = packet.data; while ( packet.size > 0 ) { int got_frame_ptr; int len = avcodec_decode_audio4( aCodecCtx, m_frame, &got_frame_ptr, &packet ); if ( len < 0 ) { // if error, skip frame break; } packet.data += len; packet.size -= len; if ( !got_frame_ptr ) continue; void * samples = m_frame->data[0]; int decoded_data_size = av_samples_get_buffer_size( NULL, aCodecCtx->channels, m_frame->nb_samples, aCodecCtx->sample_fmt, 1 ); int cur = m_sample_buf_size; m_sample_buf_size += decoded_data_size; m_sample_buffer.resize( m_sample_buf_size ); memcpy( m_sample_buffer.data() + cur, samples, decoded_data_size ); } packet.data = (uint8_t*) porigdata; av_free_packet( &packet ); return true; } return false; } /* QByteArray AudioPlayerPrivate::nextAudioBuffer() { } */ karlyriceditor-1.11/src/audioplayerprivate.h000066400000000000000000000052271233766701300213520ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef AUDIOPLAYERPRIVATE_H #define AUDIOPLAYERPRIVATE_H #include #include #include "ffmpeg_headers.h" class FFMpegVideoEncoderPriv; typedef uint8_t Uint8; class AudioPlayerPrivate { public: AudioPlayerPrivate(); bool init(); bool open( const QString& filename ); void close(); void play(); void reset(); void stop(); void seekTo( qint64 value ); bool isPlaying() const; qint64 currentTime() const; qint64 totalTime() const; QString errorMsg() const; // Called from SDL in a different thread void SDL_audio_callback( Uint8 *stream, int len); // Meta tags QString m_metaTitle; QString m_metaArtist; QString m_metaAlbum; private: // Called from the callback bool MoreAudio(); void queueClear(); private: // Video encoder private class gets direct access to ffmpeg stuff friend class FFMpegVideoEncoderPriv; QString m_errorMsg; bool m_audioOpened; // Access to everything below is guarded by mutex mutable QMutex m_mutex; AVFormatContext *pFormatCtx; int audioStream; AVCodecContext *aCodecCtx; AVCodec *pCodec; bool m_playing; qint64 m_currentTime; qint64 m_totalTime; // Currently processed frame QByteArray m_sample_buffer; unsigned int m_sample_buf_size; unsigned int m_sample_buf_idx; AVFrame * m_frame; }; #endif // AUDIOPLAYERPRIVATE_H karlyriceditor-1.11/src/background.cpp000066400000000000000000000047261233766701300201160ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include "background.h" Background::Background() { } Background::~Background() { } void Background::reset() { } // // BackgroundImage // BackgroundImage::BackgroundImage( const QString& filename ) : Background() { if ( !m_image.load( filename ) ) qWarning("Cannot load image file %s", qPrintable(filename)); } bool BackgroundImage::isValid() const { return !m_image.isNull(); } qint64 BackgroundImage::doDraw( QImage& image, qint64 ) { // We don't care about timing image = m_image; // No updates return -1; } // // BackgroundVideo // BackgroundVideo::BackgroundVideo( const QString& filename ) : Background() { QRegExp videopathstart("^(.*);STARTFRAME=(\\d+)$"); if ( filename.indexOf( videopathstart ) != -1 ) { m_valid = m_videoDecoder.openFile( videopathstart.cap(1), videopathstart.cap(2).toUInt() ); } else m_valid = m_videoDecoder.openFile( filename, 0 ); } bool BackgroundVideo::isValid() const { return m_valid; } qint64 BackgroundVideo::doDraw( QImage& image, qint64 timing ) { QImage videoframe = m_videoDecoder.frame( timing ); if ( videoframe.isNull() ) return 0; image = videoframe; // We use our own cache return 0; } karlyriceditor-1.11/src/background.h000066400000000000000000000053701233766701300175570ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef BACKGROUND_H #define BACKGROUND_H #include #include "ffmpegvideodecoder.h" class Background { public: Background(); virtual ~Background(); // Actual draw function to implement. Should return time when the next // doDraw() should be called - for example, for videos it should only // be called when it is time to show the next frame; for static images // it should not be called at all. If 0 is returned, doDraw() will be called // again the next update. If -1 is returned, doDraw() will never be called // again, and the cached image will be used. virtual qint64 doDraw( QImage& image, qint64 timing ) = 0; // This function is called if timing went backward (user seek back), in which // case it will be called before doDraw() with a new time. Video players, for // example, may use it to seek back to zero. Default implementation does nothing. virtual void reset(); // Should return true if the event was created successfully virtual bool isValid() const = 0; }; class BackgroundImage : public Background { public: BackgroundImage( const QString& filename ); bool isValid() const; qint64 doDraw( QImage& image, qint64 timing ); private: QImage m_image; }; class BackgroundVideo : public Background { public: BackgroundVideo( const QString& arg ); bool isValid() const; qint64 doDraw( QImage& image, qint64 timing ); private: FFMpegVideoDecoder m_videoDecoder; bool m_valid; }; #endif // BACKGROUND_H karlyriceditor-1.11/src/cdg.h000066400000000000000000000066471233766701300162050ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef CDG_H #define CDG_H // CDG Command Code static const unsigned int CDG_COMMAND = 0x09; // CDG Instruction Codes static const unsigned int CDG_INST_MEMORY_PRESET = 1; static const unsigned int CDG_INST_BORDER_PRESET = 2; static const unsigned int CDG_INST_TILE_BLOCK = 6; static const unsigned int CDG_INST_SCROLL_PRESET = 20; static const unsigned int CDG_INST_SCROLL_COPY = 24; static const unsigned int CDG_INST_DEF_TRANSP_COL = 28; static const unsigned int CDG_INST_LOAD_COL_TBL_0_7 = 30; static const unsigned int CDG_INST_LOAD_COL_TBL_8_15 = 31; static const unsigned int CDG_INST_TILE_BLOCK_XOR = 38; // Bitmask for all CDG fields static const unsigned int CDG_MASK = 0x3F; // This is the size of the display as defined by the CDG specification. // The pixels in this region can be painted, and scrolling operations // rotate through this number of pixels. static const unsigned int CDG_FULL_WIDTH = 300; static const unsigned int CDG_FULL_HEIGHT = 216; static const unsigned int CDG_BORDER_WIDTH = 6; static const unsigned int CDG_BORDER_HEIGHT = 12; static const unsigned int CDG_DRAW_WIDTH = CDG_FULL_WIDTH - 2*CDG_BORDER_WIDTH; static const unsigned int CDG_DRAW_HEIGHT = CDG_FULL_HEIGHT - 2*CDG_BORDER_HEIGHT; typedef struct { char command; char instruction; char parityQ[2]; char data[16]; char parityP[4]; } SubCode; typedef struct { char color; // Only lower 4 bits are used, mask with 0x0F char repeat; // Only lower 4 bits are used, mask with 0x0F char filler[14]; } CDG_MemPreset; typedef struct { char color; // Only lower 4 bits are used, mask with 0x0F char filler[15]; } CDG_BorderPreset; typedef struct { char color0; // Only lower 4 bits are used, mask with 0x0F char color1; // Only lower 4 bits are used, mask with 0x0F char row; // Only lower 5 bits are used, mask with 0x1F char column; // Only lower 6 bits are used, mask with 0x3F char tilePixels[12]; // Only lower 6 bits of each byte are used } CDG_Tile; typedef struct { char colorSpec[16]; // AND with 0x3F3F to clear P and Q channel } CDG_LoadColorTable; #endif // CDG_H karlyriceditor-1.11/src/cdggenerator.cpp000066400000000000000000000315201233766701300204330ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include #include #include #include #include "editor.h" #include "cdggenerator.h" #include "export_params.h" #include "ui_dialog_encodingprogress.h" // Color code indexes static int COLOR_IDX_BACKGROUND = 0; // background CDGGenerator::CDGGenerator( Project * proj ) { m_project = proj; } void CDGGenerator::init() { // Initialize colors from m_project m_colorBackground = m_project->tag( Project::Tag_CDG_bgcolor, "black" ); m_colorInfo = m_project->tag( Project::Tag_CDG_infocolor, "white" ); m_colorInactive = m_project->tag( Project::Tag_CDG_inactivecolor, "blue" ); m_colorActive = m_project->tag( Project::Tag_CDG_activecolor, "green" ); initColors(); // Initialize the stream m_stream.clear(); addEmpty(); clearScreen(); } void CDGGenerator::addColorGradations( const QColor& color, unsigned int number ) { int r = color.red(); int g = color.green(); int b = color.blue(); int r_step = r / number; int g_step = g / number; int b_step = b / number; for ( unsigned int i = 0; i < number; i++ ) { QColor newcolor( r - r_step * i, g - g_step * i, b - b_step * i ); m_colors.push_back( newcolor ); } } int CDGGenerator::getColor( QRgb rgbcolor ) { // See http://stackoverflow.com/questions/4057475/rounding-colour-values-to-the-nearest-of-a-small-set-of-colours QColor targetcolor( rgbcolor ); int smallest_dist_idx = -1; double smallest_dist = 0.0; // Calculate the smallest color distance between this color and other colors in the array for ( int i = 0; i < m_colors.size(); i++ ) { const QColor& origcolor = m_colors.at( i ); double dist = sqrt( pow( origcolor.redF() - targetcolor.redF(), 2.0) + pow( origcolor.greenF() - targetcolor.greenF(), 2.0 ) + pow( origcolor.blueF() - targetcolor.blueF(), 2.0 ) ); if ( smallest_dist_idx == -1 || dist < smallest_dist ) { smallest_dist_idx = i; smallest_dist = dist; } } return smallest_dist_idx; } void CDGGenerator::initColors() { // Initialize the color map with the following: // - 1 entry for the background color // - 5 entries for the init color // - 5 entries for the sung color // - 5 entries for the unsung color m_colors.clear(); m_colors.push_back( m_colorBackground ); // We can't have more than two gradations here, CD+G format has too limited throughput addColorGradations( m_colorInfo, 2 ); addColorGradations( m_colorInactive, 2 ); addColorGradations( m_colorActive, 2 ); m_streamColorIndex = -1; } void CDGGenerator::addSubcode( const SubCode& sc ) { m_stream.append( sc ); } void CDGGenerator::addEmpty() { SubCode sc; memset( &sc, 0, sizeof( sc ) ); addSubcode( sc ); } void CDGGenerator::fillColor( char * buffer, const QColor& color ) { // Green char red = (color.red() / 17) & 0x0F; char green = (color.green() / 17) & 0x0F; char blue = (color.blue() / 17) & 0x0F; // Red and green buffer[0] = (red << 2) | (green >> 2); buffer[1] = ((green & 0x03) << 5 ) | blue; } void CDGGenerator::clearScreen() { SubCode sc; // First copy the colors if we got them (there is always one background color) if ( m_streamColorIndex != -1 && m_colors.size() > 1 ) { // Load first lower 8 colors SubCode sc; sc.command = CDG_COMMAND; sc.instruction = CDG_INST_LOAD_COL_TBL_0_7; memset( sc.data, 0, 16 ); for ( int i = 0; i < 7; i++ ) { if ( i >= m_colors.size() ) break; fillColor( sc.data + i * 2, m_colors[i] ); } m_stream[ m_streamColorIndex ] = sc; // Do we have more colors? if ( m_colors.size() > 8 ) { sc.instruction = CDG_INST_LOAD_COL_TBL_8_15; memset( sc.data, 0, 16 ); for ( int i = 8; i < 16; i++ ) { if ( i >= m_colors.size() ) break; fillColor( sc.data + i * 2, m_colors[i] ); } m_stream[ m_streamColorIndex + 1 ] = sc; } //initColors(); } // Now clear the screen for ( int i = 0; i < 16; i++ ) { sc.command = CDG_COMMAND; sc.instruction = CDG_INST_MEMORY_PRESET; CDG_MemPreset* preset = (CDG_MemPreset*) sc.data; preset->repeat = i & 0x0F; preset->color = COLOR_IDX_BACKGROUND; addSubcode( sc ); } sc.command = CDG_COMMAND; sc.instruction = CDG_INST_BORDER_PRESET; CDG_BorderPreset* preset = (CDG_BorderPreset*) sc.data; preset->color = COLOR_IDX_BACKGROUND; addSubcode( sc ); // Reserve space for two LoadColor commands m_streamColorIndex = m_stream.size(); addEmpty(); addEmpty(); } QByteArray CDGGenerator::stream() { return QByteArray( (const char*) m_stream.data(), m_stream.size() * sizeof( SubCode ) ); } void CDGGenerator::checkTile( int offset_x, int offset_y, const QImage& orig,const QImage& newimg ) { // The first loop checks if there are any colors to change, and enumerates them QMap< int, QList > color_changes; // Tiles are 6x12 for ( int y = 0; y < 12; y++ ) { // Since the offsets assume borders, but our image does not contain them, we // adjust in the calculations int image_offset_y = y + offset_y - CDG_BORDER_HEIGHT; const QRgb * orig_line = (const QRgb *) orig.scanLine( image_offset_y ); const QRgb * new_line = (const QRgb *) newimg.scanLine( image_offset_y ); for ( int x = 0; x < 6; x++ ) { int image_offset_x = offset_x + x - CDG_BORDER_WIDTH; if ( orig_line[ image_offset_x ] == new_line[ image_offset_x ] ) continue; int origcolor = getColor( orig_line[ image_offset_x ] ); int newcolor = getColor( new_line[ image_offset_x ] ); // Calculate the mask for the color change int mask = origcolor ^ newcolor; if ( (mask & 0xFFFFFF00) != 0 ) qFatal("error in mask calculation"); // Store the coordinates in lo/hi bytes int coord = x << 8 | y; color_changes[ mask ].push_back( coord ); } } // Anything to change? if ( color_changes.isEmpty() ) return; // Enumerate the map entries const QList& colors = color_changes.keys(); // Bitmasks quint8 bitmask[6] = { 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; for ( int i = 0; i < colors.size(); i++ ) { SubCode sc; sc.command = CDG_COMMAND; sc.instruction = CDG_INST_TILE_BLOCK_XOR; memset( sc.data, 0, 16 ); CDG_Tile* tile = (CDG_Tile*) sc.data; tile->column = offset_x / 6; tile->row = offset_y / 12; tile->color0 = 0; tile->color1 = colors[ i ]; const QList& coords = color_changes[ colors[ i ] ]; for ( int p = 0; p < coords.size(); p++ ) { int x = coords[p] >> 8; int y = coords[p] & 0x0F; tile->tilePixels[y] |= bitmask[x]; } addSubcode( sc ); } } void CDGGenerator::applyTileChanges( const QImage& orig,const QImage& newimg ) { /* static unsigned int i = 0; QString ofname = QString("/home/tim/1/%1-orig.bmp") .arg(i); QString nfname = QString("/home/tim/1/%1-new.bmp") .arg(i); qDebug("generating image %d", i ); if ( ++i > 90 ) i = 0; orig.save( ofname, "bmp" ); newimg.save( nfname, "bmp" ); */ // Tiles are 6x12, but we skip the border area for ( unsigned int offset_y = CDG_BORDER_HEIGHT; offset_y < CDG_FULL_HEIGHT - CDG_BORDER_HEIGHT; offset_y += 12 ) for ( unsigned int offset_x = CDG_BORDER_WIDTH; offset_x < CDG_FULL_WIDTH - CDG_BORDER_WIDTH; offset_x += 6 ) checkTile( offset_x, offset_y, orig, newimg ); } void CDGGenerator::generate( const Lyrics& lyrics, qint64 total_length ) { // Show the dialog with video options DialogExportOptions dlg( m_project, lyrics, false ); if ( dlg.exec() != QDialog::Accepted ) return; // Initialize the buffer and colors init(); // Prepare the renderer TextRenderer lyricrenderer( CDG_DRAW_WIDTH, CDG_DRAW_HEIGHT ); lyricrenderer.setLyrics( lyrics ); lyricrenderer.setRenderFont( QFont( m_project->tag(Project::Tag_CDG_font), m_project->tag(Project::Tag_CDG_fontsize).toInt()) ); lyricrenderer.setColorBackground( m_colorBackground ); lyricrenderer.setColorTitle( m_colorInfo ); lyricrenderer.setColorSang( m_colorInactive ); lyricrenderer.setColorToSing( m_colorActive ); // Title lyricrenderer.setTitlePageData( dlg.m_artist, dlg.m_title, dlg.m_createdBy, m_project->tag( Project::Tag_CDG_titletime, "5" ).toInt() * 1000 ); // Preamble lyricrenderer.setPreambleData( 4, 5000, 8 ); // CD+G prefetching lyricrenderer.setPrefetch( 1000 ); // CD+G fonts lyricrenderer.forceCDGmode(); // Prepare images QImage lastImage( CDG_DRAW_WIDTH, CDG_DRAW_HEIGHT, QImage::Format_ARGB32 ); lastImage.fill( m_colorBackground.rgb() ); // Pop up progress dialog QDialog progressDialog; Ui::DialogEncodingProgress progressUi; progressUi.setupUi( &progressDialog ); progressUi.groupBox->setTitle( "CD+G output statistics"); progressUi.txtFrames->setText("CD+G packets:"); progressUi.progressBar->setMaximum( 99 ); progressUi.progressBar->setMinimum( -1 ); progressUi.progressBar->setValue( -1 ); progressUi.lblFrames->setText( "0" ); progressUi.lblOutput->setText( "0 Mb" ); progressUi.lblTime->setText( "0:00.00" ); progressDialog.show(); qint64 dialog_step = total_length / 100; // Preallocate the array init(); m_stream.reserve( total_length * 300 / 1000 ); // Render try { while ( 1 ) { // There should be 300 packets in 1000ms of music // So each packet adds 1000 / 300 ms // Speed up time a little to compensate CD+G reader delay qint64 timing = m_stream.size() * 1000 / 300 + 250; // Should we show the next step? if ( timing / dialog_step > progressUi.progressBar->value() ) { progressUi.progressBar->setValue( timing / dialog_step ); progressUi.lblFrames->setText( QString::number( m_stream.size() ) ); progressUi.lblOutput->setText( QString( "%1 Kb" ) .arg( m_stream.size() * 24 / 1024 ) ); progressUi.lblTime->setText( markToTime( timing ) ); progressUi.image->setPixmap( QPixmap::fromImage( lastImage ) ); qApp->processEvents( QEventLoop::ExcludeUserInputEvents ); } if ( timing > total_length ) break; // qDebug("timing: %d packets, %dms (%d sec)", m_stream.size(), (int) timing, (int) (timing / 1000) ); int status = lyricrenderer.update( timing ); if ( status == LyricsRenderer::UPDATE_RESIZED ) { QImage errimg = lyricrenderer.image(); errimg.save( "error", "bmp" ); QMessageBox::critical( 0, "Invalid lyrics", QString("Lyrics out of boundary at %1, screen requested: %2x%3") .arg( markToTime( timing ) ) .arg( errimg.width() ) .arg( errimg.height() ) ); m_stream.clear(); return ; } if ( status == LyricsRenderer::UPDATE_NOCHANGE ) { addEmpty(); continue; } // Is change significant enough to warrant full redraw? if ( status == LyricsRenderer::UPDATE_FULL ) { clearScreen(); // Clear the old image too lastImage.fill( m_colorBackground.rgb() ); } int packets = m_stream.size(); const QImage& currImage = lyricrenderer.image(); applyTileChanges( lastImage, currImage ); lastImage = currImage; // Make sure we added at least some tiles if ( packets == m_stream.size() ) addEmpty(); } // Clean up the parity bits in the CD+G stream char *p = (char*) &m_stream[0]; for ( unsigned int i = 0; i < m_stream.size() * sizeof(SubCode); i++, p++ ) { if ( *p & 0xC0 ) *p &= 0x3F; } QFile file( dlg.m_outputVideo ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) { QMessageBox::critical( 0, QObject::tr("Cannot write CD+G file"), QObject::tr("Cannot write CD+G file %1: %2") .arg( dlg.m_outputVideo) .arg(file.errorString()) ); return; } file.write( stream() ); } catch ( QString& txt ) { QMessageBox::critical( 0, QObject::tr("Cannot write CD+G file"), QObject::tr("Cannot write CD+G file: %1") .arg( txt ) ); return; } } karlyriceditor-1.11/src/cdggenerator.h000066400000000000000000000051551233766701300201050ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef CDGGENERATOR_H #define CDGGENERATOR_H #include #include #include #include #include "cdg.h" #include "lyrics.h" #include "project.h" #include "textrenderer.h" class CDGGenerator { public: CDGGenerator( Project * project ); // Generate the CD+G lyrics void generate( const Lyrics& lyrics, qint64 total_length ); // Returns the CD+G stream QByteArray stream(); private: void init(); void initColors(); void addColorGradations( const QColor& color, unsigned int number ); void addSubcode( const SubCode& sc ); void addEmpty(); void addLoadColors( const QColor& bgcolor, const QColor& titlecolor, const QColor& actcolor, const QColor& inactcolor ); void clearScreen(); void applyTileChanges( const QImage& orig,const QImage& newimg ); void fillColor( char * buffer, const QColor& color ); int getColor( QRgb color ); void checkTile( int offset_x, int offset_y, const QImage& orig,const QImage& newimg ); private: QColor m_colorBackground; QColor m_colorInfo; QColor m_colorActive; QColor m_colorInactive; QVector< SubCode > m_stream; // CD+G stream QVector< QColor > m_colors; // 16 colors used in CD+G int m_streamColorIndex; // Reserved space for colors Project * m_project; }; #endif // CDGGENERATOR_H karlyriceditor-1.11/src/cdgrenderer.cpp000066400000000000000000000451201233766701300202540ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include #include "cdgrenderer.h" CDGRenderer::CDGRenderer() : LyricsRenderer() { m_streamIdx = -1; m_hOffset = 0; m_vOffset = 0; m_borderColor = 0; m_bgColor = 0; memset( m_cdgScreen, 0, sizeof(m_cdgScreen) ); for ( int i = 0; i < 16; i++ ) m_colorTable[i] = 0; } CDGRenderer::~CDGRenderer() { } quint8 CDGRenderer::getPixel( int x, int y ) { unsigned int offset = x + y * CDG_FULL_WIDTH; if ( x >= (int) CDG_FULL_WIDTH || y >= (int) CDG_FULL_HEIGHT ) return m_borderColor; if ( x < 0 || y < 0 || offset > CDG_FULL_HEIGHT * CDG_FULL_WIDTH ) { qWarning( "CDG renderer: requested pixel (%d,%d) is out of boundary", x, y ); return 0; } return m_cdgScreen[offset]; } void CDGRenderer::setPixel( int x, int y, quint8 color ) { unsigned int offset = x + y * CDG_FULL_WIDTH; if ( x < 0 || y < 0 || offset > CDG_FULL_HEIGHT * CDG_FULL_WIDTH ) { qWarning( "CDG renderer: set pixel (%d,%d) is out of boundary", x, y ); return; } m_cdgScreen[offset] = color; } void CDGRenderer::cmdMemoryPreset( const char * data ) { CDG_MemPreset* preset = (CDG_MemPreset*) data; if ( preset->repeat & 0x0F ) return; // No need for multiple clearings m_bgColor = preset->color & 0x0F; for ( unsigned int i = CDG_BORDER_WIDTH; i < CDG_FULL_WIDTH - CDG_BORDER_WIDTH; i++ ) for ( unsigned int j = CDG_BORDER_HEIGHT; j < CDG_FULL_HEIGHT - CDG_BORDER_HEIGHT; j++ ) setPixel( i, j, m_bgColor ); } void CDGRenderer::cmdBorderPreset( const char * data ) { CDG_BorderPreset* preset = (CDG_BorderPreset*) data; m_borderColor = preset->color & 0x0F; for ( unsigned int i = 0; i < CDG_BORDER_WIDTH; i++ ) { for ( unsigned int j = 0; j < CDG_FULL_HEIGHT; j++ ) { setPixel( i, j, m_borderColor ); setPixel( CDG_FULL_WIDTH - i - 1, j, m_borderColor ); } } for ( unsigned int i = 0; i < CDG_FULL_WIDTH; i++ ) { for ( unsigned int j = 0; j < CDG_BORDER_HEIGHT; j++ ) { setPixel( i, j, m_borderColor ); setPixel( i, CDG_FULL_HEIGHT - j - 1, m_borderColor ); } } //CLog::Log( LOGDEBUG, "CDG: border color set to %d", borderColor ); } void CDGRenderer::cmdTransparentColor( const char * data ) { int index = data[0] & 0x0F; m_colorTable[index] = 0xFFFFFFFF; } void CDGRenderer::cmdLoadColorTable( const char * data, int index ) { CDG_LoadColorTable* table = (CDG_LoadColorTable*) data; for ( int i = 0; i < 8; i++ ) { unsigned int colourEntry = ((table->colorSpec[2 * i] & CDG_MASK) << 8); colourEntry = colourEntry + (table->colorSpec[(2 * i) + 1] & CDG_MASK); colourEntry = ((colourEntry & 0x3F00) >> 2) | (colourEntry & 0x003F); quint8 red = ((colourEntry & 0x0F00) >> 8) * 17; quint8 green = ((colourEntry & 0x00F0) >> 4) * 17; quint8 blue = ((colourEntry & 0x000F)) * 17; m_colorTable[index+i] = (red << 16) | (green << 8) | blue; //CLog::Log( LOGDEBUG, "CDG: loadColors: color %d -> %02X %02X %02X (%08X)", index + i, red, green, blue, m_colorTable[index+i] ); } } void CDGRenderer::cmdTileBlock( const char * data ) { CDG_Tile* tile = (CDG_Tile*) data; unsigned int offset_y = (tile->row & 0x1F) * 12; unsigned int offset_x = (tile->column & 0x3F) * 6; //CLog::Log( LOGERROR, "TileBlockXor: %d, %d", offset_x, offset_y ); if ( offset_x + 6 >= CDG_FULL_WIDTH || offset_y + 12 >= CDG_FULL_HEIGHT ) return; // In the XOR variant, the color values are combined with the color values that are // already onscreen using the XOR operator. Since CD+G only allows a maximum of 16 // colors, we are XORing the pixel values (0-15) themselves, which correspond to // indexes into a color lookup table. We are not XORing the actual R,G,B values. quint8 color_0 = tile->color0 & 0x0F; quint8 color_1 = tile->color1 & 0x0F; quint8 mask[6] = { 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; for ( int i = 0; i < 12; i++ ) { quint8 bTemp = tile->tilePixels[i] & 0x3F; for ( int j = 0; j < 6; j++ ) { if ( bTemp & mask[j] ) setPixel( offset_x + j, offset_y + i, color_1 ); else setPixel( offset_x + j, offset_y + i, color_0 ); } } } void CDGRenderer::cmdTileBlockXor( const char * data ) { CDG_Tile* tile = (CDG_Tile*) data; unsigned int offset_y = (tile->row & 0x1F) * 12; unsigned int offset_x = (tile->column & 0x3F) * 6; //CLog::Log( LOGERROR, "TileBlockXor: %d, %d", offset_x, offset_y ); if ( offset_x + 6 >= CDG_FULL_WIDTH || offset_y + 12 >= CDG_FULL_HEIGHT ) return; // In the XOR variant, the color values are combined with the color values that are // already onscreen using the XOR operator. Since CD+G only allows a maximum of 16 // colors, we are XORing the pixel values (0-15) themselves, which correspond to // indexes into a color lookup table. We are not XORing the actual R,G,B values. quint8 color_0 = tile->color0 & 0x0F; quint8 color_1 = tile->color1 & 0x0F; quint8 mask[6] = { 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; for ( int i = 0; i < 12; i++ ) { quint8 bTemp = tile->tilePixels[i] & 0x3F; for ( int j = 0; j < 6; j++ ) { // Find the original color index quint8 origindex = getPixel( offset_x + j, offset_y + i ); if ( bTemp & mask[j] ) //pixel xored with color1 setPixel( offset_x + j, offset_y + i, origindex ^ color_1 ); else setPixel( offset_x + j, offset_y + i, origindex ^ color_0 ); } } } // Based on http://cdg2video.googlecode.com/svn/trunk/cdgfile.cpp void CDGRenderer::cmdScroll( const char * data, bool copy ) { int colour, hScroll, vScroll; int hSCmd, hOffset, vSCmd, vOffset; int vScrollPixels, hScrollPixels; // Decode the scroll command parameters colour = data[0] & 0x0F; hScroll = data[1] & 0x3F; vScroll = data[2] & 0x3F; hSCmd = (hScroll & 0x30) >> 4; hOffset = (hScroll & 0x07); vSCmd = (vScroll & 0x30) >> 4; vOffset = (vScroll & 0x0F); m_hOffset = hOffset < 5 ? hOffset : 5; m_vOffset = vOffset < 11 ? vOffset : 11; // Scroll Vertical - Calculate number of pixels vScrollPixels = 0; if (vSCmd == 2) { vScrollPixels = - 12; } else if (vSCmd == 1) { vScrollPixels = 12; } // Scroll Horizontal- Calculate number of pixels hScrollPixels = 0; if (hSCmd == 2) { hScrollPixels = - 6; } else if (hSCmd == 1) { hScrollPixels = 6; } if (hScrollPixels == 0 && vScrollPixels == 0) { return; } // Perform the actual scroll. unsigned char temp[CDG_FULL_HEIGHT][CDG_FULL_WIDTH]; int vInc = vScrollPixels + CDG_FULL_HEIGHT; int hInc = hScrollPixels + CDG_FULL_WIDTH; unsigned int ri; // row index unsigned int ci; // column index for (ri = 0; ri < CDG_FULL_HEIGHT; ++ri) { for (ci = 0; ci < CDG_FULL_WIDTH; ++ci) { temp[(ri + vInc) % CDG_FULL_HEIGHT][(ci + hInc) % CDG_FULL_WIDTH] = getPixel( ci, ri ); } } // if copy is false, we were supposed to fill in the new pixels // with a new colour. Go back and do that now. if (!copy) { if (vScrollPixels > 0) { for (ci = 0; ci < CDG_FULL_WIDTH; ++ci) { for (ri = 0; ri < (unsigned int)vScrollPixels; ++ri) { temp[ri][ci] = colour; } } } else if (vScrollPixels < 0) { for (ci = 0; ci < CDG_FULL_WIDTH; ++ci) { for (ri = CDG_FULL_HEIGHT + vScrollPixels; ri < CDG_FULL_HEIGHT; ++ri) { temp[ri][ci] = colour; } } } if (hScrollPixels > 0) { for (ci = 0; ci < (unsigned int)hScrollPixels; ++ci) { for (ri = 0; ri < CDG_FULL_HEIGHT; ++ri) { temp[ri][ci] = colour; } } } else if (hScrollPixels < 0) { for (ci = CDG_FULL_WIDTH + hScrollPixels; ci < CDG_FULL_WIDTH; ++ci) { for (ri = 0; ri < CDG_FULL_HEIGHT; ++ri) { temp[ri][ci] = colour; } } } } // Now copy the temporary buffer back to our array for (ri = 0; ri < CDG_FULL_HEIGHT; ++ri) { for (ci = 0; ci < CDG_FULL_WIDTH; ++ci) { setPixel( ci, ri, temp[ri][ci] ); } } } void CDGRenderer::setCDGdata( const QByteArray& cdgdata ) { // Parse the CD+G stream int buggy_commands = 0; m_cdgStream.clear(); m_cdgStream.reserve( cdgdata.size() / sizeof( SubCode ) ); for ( int offset = 0; offset < cdgdata.size(); offset += sizeof( SubCode ) ) { SubCode * sc = (SubCode *) (cdgdata.data() + offset); if ( ( sc->command & CDG_MASK) == CDG_COMMAND ) { CDGPacket packet; // Validate the command and instruction switch ( sc->instruction & CDG_MASK ) { case CDG_INST_MEMORY_PRESET: case CDG_INST_BORDER_PRESET: case CDG_INST_LOAD_COL_TBL_0_7: case CDG_INST_LOAD_COL_TBL_8_15: case CDG_INST_TILE_BLOCK_XOR: case CDG_INST_TILE_BLOCK: case CDG_INST_DEF_TRANSP_COL: case CDG_INST_SCROLL_PRESET: case CDG_INST_SCROLL_COPY: memcpy( &packet.subcode, sc, sizeof(SubCode) ); packet.packetnum = offset / sizeof( SubCode ); m_cdgStream.push_back( packet ); break; default: buggy_commands++; break; } } } // Init the screen memset( m_cdgScreen, 0, sizeof(m_cdgScreen) ); // Init color table for ( int i = 0; i < 16; i++ ) m_colorTable[i] = 0; m_streamIdx = 0; m_borderColor = 0; m_bgColor = 0; m_hOffset = 0; m_vOffset = 0; if ( buggy_commands > 0 ) qDebug( "CDG loader: CDG file was damaged, %d errors ignored", buggy_commands ); } void CDGRenderer::dumpPacket( CDGPacket * packet ) { SubCode& sc = packet->subcode; const char * data = sc.data; QString dumpstr = "Packet " + QString::number( packet->packetnum ) + ": "; switch ( sc.instruction & CDG_MASK ) { case CDG_INST_MEMORY_PRESET: { CDG_MemPreset* preset = (CDG_MemPreset*) data; dumpstr += "CDG_INST_MEMORY_PRESET, color " + QString::number( preset->color & 0x0F ) + ", repeat " + QString::number( preset->repeat & 0x0F ); break; } case CDG_INST_BORDER_PRESET: { CDG_BorderPreset* preset = (CDG_BorderPreset*) data; dumpstr += "CDG_INST_BORDER_PRESET, color " + QString::number( preset->color & 0x0F ); break; } case CDG_INST_LOAD_COL_TBL_0_7: { dumpstr += "CDG_INST_LOAD_COL_TBL_0_7\n"; CDG_LoadColorTable* table = (CDG_LoadColorTable*) data; for ( int i = 0; i < 8; i++ ) { unsigned int colourEntry = ((table->colorSpec[2 * i] & CDG_MASK) << 8); colourEntry = colourEntry + (table->colorSpec[(2 * i) + 1] & CDG_MASK); colourEntry = ((colourEntry & 0x3F00) >> 2) | (colourEntry & 0x003F); quint8 red = ((colourEntry & 0x0F00) >> 8) * 17; quint8 green = ((colourEntry & 0x00F0) >> 4) * 17; quint8 blue = ((colourEntry & 0x000F)) * 17; dumpstr += QString(" %1: %2 %3 %4\n") .arg( i ) .arg( red ) .arg( green ) .arg( blue ); } break; } case CDG_INST_LOAD_COL_TBL_8_15: { dumpstr += "CDG_INST_LOAD_COL_TBL_8_15\n"; CDG_LoadColorTable* table = (CDG_LoadColorTable*) data; for ( int i = 0; i < 8; i++ ) { unsigned int colourEntry = ((table->colorSpec[2 * i] & CDG_MASK) << 8); colourEntry = colourEntry + (table->colorSpec[(2 * i) + 1] & CDG_MASK); colourEntry = ((colourEntry & 0x3F00) >> 2) | (colourEntry & 0x003F); quint8 red = ((colourEntry & 0x0F00) >> 8) * 17; quint8 green = ((colourEntry & 0x00F0) >> 4) * 17; quint8 blue = ((colourEntry & 0x000F)) * 17; dumpstr += QString(" %1: %2 %3 %4\n") .arg( i + 8 ) .arg( red ) .arg( green ) .arg( blue ); } break; } case CDG_INST_DEF_TRANSP_COL: dumpstr += "CDG_INST_DEF_TRANSP_COL, color " + QString::number( data[0] & 0x0F ); break; case CDG_INST_TILE_BLOCK: { CDG_Tile* tile = (CDG_Tile*) data; unsigned int offset_y = (tile->row & 0x1F) * 12; unsigned int offset_x = (tile->column & 0x3F) * 6; dumpstr += "CDG_INST_TILE_BLOCK\n"; quint8 color_0 = tile->color0 & 0x0F; quint8 color_1 = tile->color1 & 0x0F; quint8 mask[6] = { 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; for ( int i = 0; i < 12; i++ ) { quint8 bTemp = tile->tilePixels[i] & 0x3F; for ( int j = 0; j < 6; j++ ) { if ( bTemp & mask[j] ) dumpstr += " pixel " + QString::number( offset_x + j ) + "," + QString::number(offset_y + i) + " -> " + QString::number(color_1) + "\n"; else dumpstr += " pixel " + QString::number( offset_x + j ) + ", " + QString::number(offset_y + i) + " -> " + QString::number(color_0) + "\n"; } } break; } case CDG_INST_TILE_BLOCK_XOR: { CDG_Tile* tile = (CDG_Tile*) data; unsigned int offset_y = (tile->row & 0x1F) * 12; unsigned int offset_x = (tile->column & 0x3F) * 6; dumpstr += "CDG_INST_TILE_BLOCK_XOR\n"; if ( offset_x + 6 >= CDG_FULL_WIDTH || offset_y + 12 >= CDG_FULL_HEIGHT ) return; // In the XOR variant, the color values are combined with the color values that are // already onscreen using the XOR operator. Since CD+G only allows a maximum of 16 // colors, we are XORing the pixel values (0-15) themselves, which correspond to // indexes into a color lookup table. We are not XORing the actual R,G,B values. quint8 color_0 = tile->color0 & 0x0F; quint8 color_1 = tile->color1 & 0x0F; quint8 mask[6] = { 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; for ( int i = 0; i < 12; i++ ) { quint8 bTemp = tile->tilePixels[i] & 0x3F; for ( int j = 0; j < 6; j++ ) { // Find the original color index quint8 origindex = getPixel( offset_x + j, offset_y + i ); if ( bTemp & mask[j] ) //pixel xored with color1 dumpstr += " " + QString::number( origindex ) + " pixel at " + QString::number( offset_x + j ) + "," + QString::number(offset_y + i) + " ^ " + QString::number(color_1) + "\n"; else dumpstr += " " + QString::number( origindex ) + " pixel at " + QString::number( offset_x + j ) + "," + QString::number(offset_y + i) + " ^ " + QString::number(color_0) + "\n"; } } break; } case CDG_INST_SCROLL_PRESET: case CDG_INST_SCROLL_COPY: { int colour = data[0] & 0x0F; int hScroll = data[1] & 0x3F; int vScroll = data[2] & 0x3F; int hSCmd = (hScroll & 0x30) >> 4; int hOffset = (hScroll & 0x07); int vSCmd = (vScroll & 0x30) >> 4; int vOffset = (vScroll & 0x0F); if ( (sc.instruction & CDG_MASK) == CDG_INST_SCROLL_COPY ) dumpstr += "CDG_INST_SCROLL_COPY"; else dumpstr += "CDG_INST_SCROLL_PRESET"; dumpstr += ": hSCmd " + QString::number( hSCmd ) + ", hoffset " + QString::number(hOffset) + "vSCmd " + QString::number( vSCmd ) + ", voffset " + QString::number(vOffset) + ", color " + QString::number(colour); break; } default: abort(); } QFile file( "cdg.dump" ); if ( !file.open( QIODevice::Append | QIODevice::WriteOnly ) ) abort(); dumpstr += "\n"; file.write( dumpstr.toLocal8Bit() ); file.close(); } int CDGRenderer::UpdateBuffer( unsigned int packets_due ) { int status = UPDATE_NOCHANGE; // Are we done? if ( m_streamIdx == -1 ) return status; // Was the stream position reversed? In this case we have to "replay" the whole stream // as the screen is a state machine, and "clear" may not be there. if ( m_streamIdx > 0 && m_cdgStream[ m_streamIdx-1 ].packetnum > packets_due ) { qDebug( "CDG renderer: packet number changed backward (%d played, %d asked", m_cdgStream[ m_streamIdx-1 ].packetnum, packets_due ); m_streamIdx = 0; } // Process all packets already due while ( m_cdgStream[ m_streamIdx ].packetnum <= packets_due ) { SubCode& sc = m_cdgStream[ m_streamIdx ].subcode; //dumpPacket( &m_cdgStream[ m_streamIdx ] ); // Execute the instruction switch ( sc.instruction & CDG_MASK ) { case CDG_INST_MEMORY_PRESET: cmdMemoryPreset( sc.data ); status = UPDATE_FULL; break; case CDG_INST_BORDER_PRESET: cmdBorderPreset( sc.data ); status = UPDATE_FULL; break; case CDG_INST_LOAD_COL_TBL_0_7: cmdLoadColorTable( sc.data, 0 ); break; case CDG_INST_LOAD_COL_TBL_8_15: cmdLoadColorTable( sc.data, 8 ); break; case CDG_INST_DEF_TRANSP_COL: cmdTransparentColor( sc.data ); break; case CDG_INST_TILE_BLOCK: cmdTileBlock( sc.data ); status = UPDATE_COLORCHANGE; break; case CDG_INST_TILE_BLOCK_XOR: cmdTileBlockXor( sc.data ); status = UPDATE_COLORCHANGE; break; case CDG_INST_SCROLL_PRESET: cmdScroll( sc.data, false ); status = UPDATE_COLORCHANGE; break; case CDG_INST_SCROLL_COPY: cmdScroll( sc.data, true ); status = UPDATE_COLORCHANGE; break; default: // this shouldn't happen as we validated the stream in Load() break; } m_streamIdx++; if ( m_streamIdx >= (int) m_cdgStream.size() ) { m_streamIdx = -1; break; } } return status;; } int CDGRenderer::update( qint64 songTime ) { int status; // Make the generic image twice larger if ( m_image.width() < (int) (2 * CDG_FULL_WIDTH) ) { m_image = QImage( 2 * CDG_FULL_WIDTH, 2 * CDG_FULL_HEIGHT, QImage::Format_ARGB32 ); status = UPDATE_RESIZED; } else { // Time to update? unsigned int packets_due = songTime * 300 / 1000; status = UpdateBuffer( packets_due ); } if ( status != UPDATE_NOCHANGE ) { QImage img( QSize( CDG_FULL_WIDTH, CDG_FULL_HEIGHT ), QImage::Format_ARGB32 ); // Update the image for ( unsigned int y = 0; y < CDG_FULL_HEIGHT; y++ ) { for ( unsigned int x = 0; x < CDG_FULL_WIDTH; x++ ) { quint8 colorindex = getPixel( x + m_hOffset, y + m_vOffset ); quint32 TexColor = m_colorTable[ colorindex ]; // Is it transparent color? if ( TexColor != 0xFFFFFFFF ) { // Uncomment this to make background colors transparent //if ( colorindex != m_bgColor ) TexColor |= 0xFF000000; // color table has 0x00 alpha } else TexColor = 0x00000000; img.setPixel( x, y, TexColor ); } } m_image = img.scaled( m_image.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); } return status; } karlyriceditor-1.11/src/cdgrenderer.h000066400000000000000000000064741233766701300177320ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef CDGRENDERER_H #define CDGRENDERER_H #include #include #include "lyricsrenderer.h" #include "cdg.h" class CDGRenderer : public LyricsRenderer { public: CDGRenderer(); ~CDGRenderer(); void setCDGdata( const QByteArray& cdgdata ); virtual int update( qint64 timing ); private: typedef struct { unsigned int packetnum; SubCode subcode; } CDGPacket; void dumpPacket( CDGPacket * packet ); int UpdateBuffer( unsigned int packets_due ); void RenderImage( QImage& imagepixels, unsigned int width, unsigned int height, unsigned int pitch ) const; quint8 getPixel( int x, int y ); void setPixel( int x, int y, quint8 color ); void cmdMemoryPreset( const char * data ); void cmdBorderPreset( const char * data ); void cmdLoadColorTable( const char * data, int index ); void cmdTileBlock( const char * data ); void cmdTileBlockXor( const char * data ); void cmdTransparentColor( const char * data ); void cmdScroll( const char * data, bool loop ); void scrollLeft( int color ); void scrollRight( int color ); void scrollUp( int color ); void scrollDown( int color ); QVector m_cdgStream; // Parsed CD+G stream storage int m_streamIdx; // packet offset which hasn't been processed yet // Rendering stuff quint32 m_colorTable[16];// CD+G color table; color format is A8R8G8B8 quint8 m_bgColor; // Background color index quint8 m_borderColor; // Border color index quint8 m_cdgScreen[CDG_FULL_WIDTH*CDG_FULL_HEIGHT]; // Image state for CD+G stream // These values are used to implement screen shifting. The CDG specification allows the entire // screen to be shifted, up to 5 pixels right and 11 pixels down. This shift is persistent // until it is reset to a different value. In practice, this is used in conjunction with // scrolling (which always jumps in integer blocks of 6x12 pixels) to perform // one-pixel-at-a-time scrolls. quint8 m_hOffset; quint8 m_vOffset; }; #endif // CDGRENDERER_H karlyriceditor-1.11/src/checknewversion.cpp000066400000000000000000000222071233766701300211660ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include #include #if !defined (WIN32) #include #include #include #include #include #include #else #include #endif #include "checknewversion.h" // Uncomment this to enable debugging messages //#define ENABLE_DEBUG_MESSAGES CheckNewVersion::CheckNewVersion() : QThread() { m_sockfd = -1; m_timeout = 180; m_inputOffset = 0; m_inputBuffer.resize( 8192 ); qRegisterMetaType< NewVersionMetaMap >("NewVersionMetaMap"); } void CheckNewVersion::setUrl( const QString& url ) { m_url = url; } void CheckNewVersion::setCurrentVersion( const QString& version ) { m_currentversion = version; } void CheckNewVersion::closeSocket() { if ( m_sockfd == -1 ) return; #if defined (WIN32) closesocket( m_sockfd ); #else close( m_sockfd ); #endif m_sockfd = -1; } void CheckNewVersion::fatalError( int code ) { #if defined (ENABLE_DEBUG_MESSAGES) #define CASE_PRINT(A) case A: qDebug("CheckNewVersion::fatalError( " #A " )"); break; switch ( code ) { CASE_PRINT( Error_URL_Invalid ); CASE_PRINT( Error_Name_Lookup ); CASE_PRINT( Error_System ); CASE_PRINT( Error_Connecting ); CASE_PRINT( Error_Sending ); CASE_PRINT( Error_Receiving ); CASE_PRINT( Error_HTTPerror ); CASE_PRINT( Error_InvalidFormat ); CASE_PRINT( Error_InvalidSignature ); } #undef CASE_PRINT #endif closeSocket(); emit error( code ); deleteLater(); } void CheckNewVersion::reportStatus( int status ) { #if defined (ENABLE_DEBUG_MESSAGES) #define CASE_PRINT(A) case A: qDebug("CheckNewVersion::reportStatus( " #A " )"); break; switch ( status ) { CASE_PRINT( Status_Resolving ); CASE_PRINT( Status_Connecting ); CASE_PRINT( Status_SendingRequest ); CASE_PRINT( Status_ReceivingResponse ); CASE_PRINT( Status_Proceeding ); CASE_PRINT( Status_Finished ); } #undef CASE_PRINT #endif emit statusChanged( status ); } void CheckNewVersion::run() { m_inputOffset = 0; // Validate the URL QUrl url( m_url ); if ( !url.isValid() || url.scheme() != "http" || url.host().isEmpty() ) { fatalError( Error_URL_Invalid ); return; } // Win32s-specific socket initialization #if defined (WIN32) WORD wVersionRequested = MAKEWORD (1, 1); WSADATA wsaData; if ( WSAStartup (wVersionRequested, &wsaData) != 0 ) { fatalError( Error_System ); return; } #endif // IPv4 address resolving struct sockaddr_in saddr; memset( &saddr, 0, sizeof(saddr) ); saddr.sin_family = AF_INET; saddr.sin_port = htons( url.port(80) ); saddr.sin_addr.s_addr = ::inet_addr ( qPrintable(url.host() ) ); if ( saddr.sin_addr.s_addr == INADDR_NONE ) { reportStatus( Status_Resolving ); struct hostent *hp; #if defined HAVE_GETHOSTBYNAME_R int tmp_errno; struct hostent tmp_hostent; char buf[2048]; if ( ::gethostbyname_r( qPrintable(url.host() ), &tmp_hostent, buf, sizeof(buf), &hp, &tmp_errno) ) hp = 0; #else hp = ::gethostbyname( qPrintable(url.host() ) ); #endif // HAVE_GETHOSTBYNAME_R if ( !hp ) { fatalError( Error_Name_Lookup ); return; } ::memcpy (&saddr.sin_addr, hp->h_addr, (size_t) hp->h_length); } // create the socket m_sockfd = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); if ( m_sockfd < 0 ) { fatalError( Error_System ); return; } reportStatus( Status_Connecting ); // Connect to the HTTP server if ( ::connect( m_sockfd, (struct sockaddr *) &saddr, sizeof(saddr)) ) { fatalError( Error_Connecting ); return; } // Prepare the HTTP request QString request = QString("GET %1 HTTP/1.1\r\n" "Host: %2\r\n" "User-Agent: Qt/New version checker (www.karlyriceditor.com)\r\nConnection: close\r\n\r\n") .arg( url.path() ) .arg( url.host() ); // Send the request reportStatus( Status_SendingRequest ); const char * reqmsg = qPrintable( request ); unsigned int offset = 0, length = strlen( reqmsg ); while ( offset < length ) { int sentamount = ::send( m_sockfd, reqmsg + offset, length - offset, 0 ); if ( sentamount <= 0 ) { fatalError( Error_Sending ); return; } offset += sentamount; } // Receive the response reportStatus( Status_ReceivingResponse ); // First, receive the HTTP header int contentlen = -1; QStringList header; while ( 1 ) { QString line = readLine(); // Has connection closed? if ( m_sockfd == -1 ) { fatalError( Error_Receiving ); return; } // Empty line separates header and body if ( line.isEmpty() ) break; header.push_back( line ); } // Make sure server didn't return error if ( header.isEmpty() || header[0].indexOf( QRegExp( "^http/1.\\d\\s+2\\d\\d", Qt::CaseInsensitive )) == -1 ) { #if defined (ENABLE_DEBUG_MESSAGES) if ( !header.isEmpty() ) qDebug("CheckNewVersion::run: server returned invalid header: %s", qPrintable( header[0]) ); #endif fatalError( Error_HTTPerror ); return; } // Find content-length QRegExp clr( "^content-length: (\\d+)$" ); clr.setCaseSensitivity( Qt::CaseInsensitive ); if ( header.indexOf( clr ) != -1 ) contentlen = clr.cap( 1 ).toInt(); // Read the rest of content until we have contentlen or connection closed while ( contentlen == -1 || contentlen < m_inputOffset ) { int amount = ::recv( m_sockfd, m_inputBuffer.data() + m_inputOffset, m_inputBuffer.size() - m_inputOffset, 0 ); // connection closed? if ( amount == 0 ) break; // read error if ( amount < 0 ) { fatalError( Error_Receiving ); return; } m_inputOffset += amount; } closeSocket(); m_inputBuffer[ m_inputOffset ] ='\0'; // Remove/replace line ends, and convert to a string reportStatus( Status_Proceeding ); m_inputBuffer.replace( '\r', '\n' ); QStringList content_list = QString::fromUtf8( m_inputBuffer ).split( '\n', QString::SkipEmptyParts ); QMap contentMap; // Validate the file, and parse it into map for ( int i = 0; i < content_list.size(); i++ ) { QRegExp reg( "^(\\w+)\\s*:(.*)$" ); if ( content_list[i].indexOf( reg ) == -1 ) { #if defined (ENABLE_DEBUG_MESSAGES) qDebug("CheckNewVersion::run: invalid line found: '%s'", qPrintable( content_list[i] ) ); #endif fatalError( Error_InvalidFormat ); return; } // Decode \n back to 0x0A QString value = reg.cap( 2 ).trimmed(); value.replace( "\\n", "\n" ); value.replace( "\\\\", "\\" ); contentMap[ reg.cap(1) ] = value; } // Validate signature if ( !contentMap.contains( "Signature" ) || !contentMap.contains( "Version" ) || contentMap["Signature"] != "CheckNewVersion1" ) { fatalError( Error_InvalidSignature ); return; } contentMap.remove( "Signature" ); // Do we need to call the callback? if ( m_currentversion.isEmpty() || contentMap["Version"].toDouble() > m_currentversion.toDouble() ) emit newVersionAvailable( contentMap ); reportStatus( Status_Finished ); deleteLater(); } QString CheckNewVersion::readLine() { while ( 1 ) { // First check if we have a line in buffer already if ( m_inputOffset > 0 ) { for ( int i = 0; i < m_inputOffset - 1; i++ ) { if ( m_inputBuffer[i] == '\r' && m_inputBuffer[i+1] == '\n' ) { // Null-terminate the buffer, and copy the string m_inputBuffer[i] = '\0'; QString line = QString::fromUtf8( m_inputBuffer ); // Now move the rest of the buffer if something left unsigned int amount = i + 2; // removing CRLF too) m_inputOffset -= amount; if ( m_inputOffset > 0 ) memmove( m_inputBuffer.data(), m_inputBuffer.data() + amount, m_inputOffset ); return line; } } } // No line in buffer yet if ( m_inputOffset + 1 > m_inputBuffer.size() ) return QString::null; int bytes = ::recv( m_sockfd, m_inputBuffer.data() + m_inputOffset, m_inputBuffer.size() - m_inputOffset, 0 ); // Error; restart on EINTR, abort on anything else if ( bytes < 0 ) { if ( errno == EINTR ) continue; break; } if ( bytes == 0 ) break; m_inputOffset += bytes; } return QString::null; } karlyriceditor-1.11/src/checknewversion.h000066400000000000000000000105751233766701300206400ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef CHECKNEWVERSION_H #define CHECKNEWVERSION_H // This class implements checking whether a new application version is available, // and reports its availability via signal. It does not use Qt networking stuff, // and therefore avoids linking with QtNetwork (savings of 1Mb!). // All processing is done in a separate thread, so it does not block the app. // It does not use GUI stuff, so should be safe. // // The proper way to use this class: // // CheckNewVersion * pN = new CheckNewVersion(); // connect( pN, SIGNAL( newVersionAvailable( const QMap&) ), this, SLOT( newVersionAvailable( const QMap&) ) ); // // pN->setUrl( "http://www.example.com/latestversion.txt" ); // pN->setCurrentVersion( "1.12" ); // pN->start(); // // The text file must have the following format: // : // Two field names are required (Signature and Version). Any other names are optional. Any name may be added. // A multiline value string should have all line feed characters replaced by \n, and all single backlashes replaced by two // Signature must be the first field, and must contain the "CheckNewVersion1" value // // An example file with extra fields "URL" and "Changes" added: // // Signature:CheckNewVersion1 // Version:1.12 // URL: http://example.com/latestversion.zip // Changes: new functionality added.\nA bar function added to package foo.\n\nZeta now works. // // #include #include #include typedef QMap NewVersionMetaMap; class CheckNewVersion : public QThread { Q_OBJECT public: enum { Status_Resolving, Status_Connecting, Status_SendingRequest, Status_ReceivingResponse, Status_Proceeding, Status_Finished, }; enum { Error_URL_Invalid, Error_Name_Lookup, Error_System, Error_Connecting, Error_Sending, Error_Receiving, Error_HTTPerror, Error_InvalidFormat, Error_InvalidSignature }; CheckNewVersion(); // Sets the full URL to get the latest version information from. void setUrl( const QString& url ); // Sets the current version. newVersionAvailable() will only be emitted // if current version does not match the version in the downloaded file. // If not called, newVersionAvailable() will be always emitted. void setCurrentVersion( const QString& version ); signals: void newVersionAvailable( const NewVersionMetaMap& metadata ); void statusChanged( int newstatus ); void error( int errorcode ); private: // Reimplemented void run(); // All those functions on error generate the event, and shut down the thread. void fatalError( int code ); // Read the line from socket (or m_inputBuffer). void reportStatus( int status ); // Read the data from socket up to length. May return less than length. QString readLine(); // Closing the socket void closeSocket(); private: QString m_url; QString m_currentversion; int m_sockfd; unsigned int m_timeout; int m_inputOffset; // in m_inputBuffer QByteArray m_inputBuffer; // for socket input }; Q_DECLARE_METATYPE(NewVersionMetaMap); #endif // CHECKNEWVERSION_H karlyriceditor-1.11/src/colorbutton.cpp000066400000000000000000000045301233766701300203420ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include #include "colorbutton.h" ColorButton::ColorButton( QWidget * parent ) : QPushButton( parent ) { connect( this, SIGNAL(clicked()), this, SLOT(btnClicked()) ); } void ColorButton::setColor( const QColor& color ) { // After the constructor is called, UIC-generated code calls setLabel, so we overwrite it here setText( tr("") ); m_selectedColor = color; update(); } QColor ColorButton::color() const { return m_selectedColor; } void ColorButton::btnClicked() { QColor newcolor = QColorDialog::getColor( color() ); if ( newcolor.isValid() ) setColor( newcolor ); } void ColorButton::paintEvent( QPaintEvent * event ) { QPushButton::paintEvent( event ); // Paint a rectangle QPainter painter (this); // Take 50% of height and 80% of width int rectwidth = width() * 0.8; int rectheight = height() * 0.5; QRect rect( (width() - rectwidth) / 2, (height() - rectheight) / 2, rectwidth, rectheight ); if ( isDown() ) rect.translate( 1, 1 ); painter.fillRect( rect, m_selectedColor ); painter.end(); } karlyriceditor-1.11/src/colorbutton.h000066400000000000000000000034441233766701300200120ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef COLORBUTTON_H #define COLORBUTTON_H #include // A button which changes its background to currently selected color, // and lets the user to select a color on click. class ColorButton : public QPushButton { Q_OBJECT public: ColorButton( QWidget * parent ); void setColor( const QColor& color ); QColor color() const; protected: void paintEvent( QPaintEvent * event ); private slots: void btnClicked(); private: QColor m_selectedColor; }; #endif // COLORBUTTON_H karlyriceditor-1.11/src/dialog_about.ui000066400000000000000000000150131233766701300202520ustar00rootroot00000000000000 DialogAbout 0 0 567 365 About Karaoke Lyric Editor 0 About application 0 0 :/images/application_icon.png true 2 2 TextLabel true true Third-party modules used <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Qt toolkit library</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This application is based on a portable GUI/core library available from <a href="http://qt.nokia.com"><span style=" text-decoration: underline; color:#0057ae;">http://qt.nokia.com</span></a>. This toolkit library is available and used under LGPL license.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Icons</span><br />This application uses icons developed by DryIcons, <a href="http://www.dryicons.com"><span style=" text-decoration: underline; color:#0057ae;">http://www.dryicons.com</span></a></p> <p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">FFMpeg</span><br />For playing music files, decoding video backgrounds and creating video output files this software uses code of <a href="http://ffmpeg.org"><span style=" text-decoration: underline; color:#0057ae;">FFmpeg</span></a> licensed under the <a href="http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html"><span style=" text-decoration: underline; color:#0057ae;">LGPLv2.1</span></a></p> <p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">SDL</span><br />For playing music files this software uses the <a href="http://www.libsdl.org"><span style=" text-decoration: underline; color:#0057ae;">Simple DirectMedia Layer library</span></a> licensed under the <a href="http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html"><span style=" text-decoration: underline; color:#0057ae;">LGPL license</span></a></p></body></html> true Qt::Horizontal QDialogButtonBox::Close buttonBox accepted() DialogAbout accept() 248 254 157 274 buttonBox rejected() DialogAbout reject() 316 260 286 274 karlyriceditor-1.11/src/dialog_encodingprogress.ui000066400000000000000000000102751233766701300225200ustar00rootroot00000000000000 DialogEncodingProgress 0 0 320 376 Generating video file Video output statistics Current time: 0 Frames processed: 0 Output file size: 0 Qt::Horizontal 40 20 Qt::Horizontal 40 20 24 QLayout::SetDefaultConstraint Qt::Horizontal 40 20 8 10 image Qt::Horizontal 40 20 karlyriceditor-1.11/src/dialog_export_params.ui000066400000000000000000000445651233766701300220420ustar00rootroot00000000000000 DialogExportParams 0 0 607 579 Specify video parameters 0 Options Video encoding parameters Quality Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Target medium Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 1 0 1 0 1 0 1 0 Subtarget Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Audio Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Video profile Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter encode according to profile copy original content no audio <html><head/><body><p><a href="yes"><span style=" text-decoration: underline; color:#006e28;">Click here to see the profile details</span></a></p></body></html> Qt::AlignBottom|Qt::AlignHCenter Lyrics rendering Font: Font family for preview window Size: Font size for preview window. Larger values are suggested (24 and up) 4 120 2 Detect size Test size Keep title at least 120 2 seconds Qt::Horizontal 40 20 Colors Not sung yet Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Preview window text color for the text which has been sung. PushButton Preview window text color for the text which has been sung. PushButton Sung Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Preview window text color for the text which is not being sung yet.. PushButton Information Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Preview window background color. PushButton Backround Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Draw squares showing where singing starts Title window parameters QFormLayout::ExpandingFieldsGrow Artist Title Created by: Output file Write video to file: Browse Qt::Vertical 20 0 Preview 0 5 199 Qt::Horizontal Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok ColorButton QPushButton
colorbutton.h
boxVideoMedium boxVideoTarget boxVideoProfile boxVideoQuality boxVideoAudio fontVideo fontVideoSize btnDetectSize btnTestSize titleVideoMin btnVideoColorBg btnVideoColorInactive btnVideoColorInfo btnVideoColorActive cbVideoPreamble leArtist leTitle leTitleCreatedBy leOutputFile btnBrowse buttonBox tabWidget seekSlider buttonBox accepted() DialogExportParams accept() 248 254 157 274 buttonBox rejected() DialogExportParams reject() 316 260 286 274
karlyriceditor-1.11/src/dialog_projectsettings.ui000066400000000000000000000254021233766701300223720ustar00rootroot00000000000000 DialogProjectSettings 0 0 479 535 Settings 0 General Selected music file: Music file: true Browse Project lyrics type LRC version 1 LRC version 2 true UltraStar lyrics Qt::Vertical 20 40 Required fields All those fields must be set Song title: Artist: Music file: BPM and GAP will be added by the editor Qt::Vertical 20 315 Optional fields LRC format optional fields Album Created by: Offset: Application: Version: Ultrastar format optional fields Video file: Video gap: Cover image: Background image: Genre: Edition: Language: QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogProjectSettings accept() 214 519 164 539 buttonBox rejected() DialogProjectSettings reject() 314 518 335 539 karlyriceditor-1.11/src/dialog_registration.ui000066400000000000000000000075251233766701300216630ustar00rootroot00000000000000 DialogRegistration 0 0 333 275 Registration information Enter the registration key <html><head/><body><p>The application is not registered. See the <a href="http://www.ulduzsoft.com/linux/karaoke-lyrics-editor/kleregistration/"><span style=" text-decoration: underline; color:#0057ae;">web site</span></a> for the registration information.</p><p>If you received the registration key, please enter it here:</p></body></html> true false Registration information Registered to: TextLabel Expires: TextLabel Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogRegistration accept() 248 254 157 274 buttonBox rejected() DialogRegistration reject() 316 260 286 274 karlyriceditor-1.11/src/dialog_selectencoding.cpp000066400000000000000000000100031233766701300222650ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include #include #include #include "dialog_selectencoding.h" static const char * ENCODED_SETTINGS = "general/savedstoredencoding"; DialogSelectEncoding::DialogSelectEncoding( const QByteArray& text, QWidget *parent ) : QDialog(parent), Ui::DialogSelectEncoding() { setupUi( this ); m_encodedText = text; connect( boxEncoding, SIGNAL(currentIndexChanged(int)), this, SLOT(encodingChanged(int)) ); // Add the supported encodings. QMap< QString, QString > encodings; encodings[ "CP1256" ] = "Arabic"; encodings[ "CP1257" ] = "Baltic"; encodings[ "CP1250" ] = "Central European"; encodings[ "GB18030" ] = "Chinese Simplified"; encodings[ "GBK" ] = "Chinese Simplified"; encodings[ "GB2313" ] = "Chinese Simplified"; encodings[ "Big5" ] = "Chinese Traditional"; encodings[ "Big5-HKSCS" ] = "Chinese Traditional"; encodings[ "CP1251" ] = "Cyrillic"; encodings[ "KOI8-R" ] = "Cyrillic"; encodings[ "CP1253" ] = "Greek"; encodings[ "CP1255" ] = "Hebrew"; encodings[ "eucJP" ] = "Japanese"; encodings[ "JIS7" ] = "Japanese"; encodings[ "Shift-JIS" ] = "Japanese"; encodings[ "eucKR" ] = "Korean"; encodings[ "TSCII" ] = "Tamil"; encodings[ "TIS-620" ] = "Thai"; encodings[ "KOI8-U" ] = "Ukrainian"; encodings[ "CP1254" ] = "Turkish"; encodings[ "CP1258" ] = "Vietnamese"; encodings[ "UTF-8" ] = "Unicode"; encodings[ "UTF-16" ] = "Unicode"; encodings[ "CP1252" ] = "Western"; // Fill the combo box for ( QMap::iterator it = encodings.begin(); it != encodings.end(); ++it ) { QString encname = it.key(); QString lang = it.value(); boxEncoding->addItem ( QString("%1 - %2") .arg(lang).arg(encname), encname ); } // Restore the last saved encoding if present QSettings settings; if ( settings.contains( ENCODED_SETTINGS ) ) { int index = boxEncoding->findData( settings.value( ENCODED_SETTINGS ).toString() ); if ( index != -1 ) boxEncoding->setCurrentIndex( index ); } } void DialogSelectEncoding::accept() { if ( boxEncoding->currentIndex() == -1 ) { QMessageBox::critical( 0, tr("Encoding not selected"), tr("Please select text encoding") ); return; } if ( cbSaveDefault->isChecked() ) { QSettings settings; settings.setValue( ENCODED_SETTINGS, m_selectedEncoding ); } QDialog::accept(); } void DialogSelectEncoding::encodingChanged( int index ) { if ( index == -1 ) return; m_selectedEncoding = boxEncoding->itemData ( index ).toString(); m_selectedCodec = QTextCodec::codecForName( qPrintable(m_selectedEncoding) ); if ( !m_selectedCodec ) qFatal("Failed to select Qt text codec for name '%s'", qPrintable(m_selectedEncoding) ); leSample->setText( m_selectedCodec->toUnicode( m_encodedText ) ); } karlyriceditor-1.11/src/dialog_selectencoding.h000066400000000000000000000037111233766701300217420ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef DIALOG_SELECTENCODING_H #define DIALOG_SELECTENCODING_H #include #include #include #include "ui_dialog_selectencoding.h" class DialogSelectEncoding : public QDialog, public Ui::DialogSelectEncoding { Q_OBJECT public: DialogSelectEncoding( const QByteArray& text, QWidget *parent = 0 ); QString encoding() const { return m_selectedEncoding; } QTextCodec* codec() const { return m_selectedCodec; } protected slots: void accept(); void encodingChanged( int index ); private: QTextCodec * m_selectedCodec; QString m_selectedEncoding; QByteArray m_encodedText; }; #endif // DIALOG_SELECTENCODING_H karlyriceditor-1.11/src/dialog_selectencoding.ui000066400000000000000000000061041233766701300221270ustar00rootroot00000000000000 DialogSelectEncoding 0 0 413 317 Select text encoding Select encoding Select the text encoding: 1 0 QComboBox::InsertAlphabetically Save this encoding as default choice for future Text example using the selected encoding true Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogSelectEncoding accept() 222 299 157 274 buttonBox rejected() DialogSelectEncoding reject() 290 305 286 274 karlyriceditor-1.11/src/dialog_settings.ui000066400000000000000000001005051233766701300210010ustar00rootroot00000000000000 DialogSettings 0 0 574 351 Application settings 0 Text editor General Editor font style: Font style for the main editor window where the text is being edited. size: Font size for main editor window 6 120 2 Qt::Horizontal 79 20 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">LRC format only.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">No karaoke player can show the whole lyrics on screen at the same time. Most players can only show 4-8 lines of text on screen. If the lyrics are continuous, the player decides itself where to split the text - usually applying simple logics, which may not necessary provide good results. However if the player supports blocks (XBMC does), you can add an empty line between verses, and the player will use those markers to separate verses when showing text on screen.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If enabled, the validator will allows block separators (single empty lines), and will check that the block size does not exceed a specific number of lines.</p></body></html> Support blocks; each block can have up to false If blocks are enabled, this parameter specifies the maximum number of lines allowed in block. Most players cannot show more than 8 lines, so before using a larger parameter, please make sure your player supports it. lines Qt::Horizontal 40 20 Automatic stop during tagging <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">LRC version 2 or UltraStar only. </span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">When tagging the song (using Insert Tag, which inserts current time tag at the cursor position), the cursor moves forward, typically to the beginning of next line. If this checkbox is set, the cursor will move to the end of current line, allowing you to put a time tag there (and after that it will move to the beginning of next line). This is important, because having a time tag at the end of line allows the player to calculate how much time the current line takes, and therefore provides more accurate character highlighting.</p></body></html> Stop at the end of line (to put a time tag there) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">LRC2 and UltraStar formats</span>.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Normally when inserting time tags, a placeholder is replaced by a single timing mark, and cursor moves forward. If this checkbox is set, the cursor will move one character ahead, skipping the new time mark, and will allow you to put two time tags in the place of placeholder. This is useful when there is a hearable pause between words on the line, and you want to put two timing tags, when the first would represent "first word end" and the second would represent "second word start". For example, in Hotel California refrain:</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">Welcome to the Hotel California</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A word "Welcome" is being sung approximately the same time as the words "to the Hotel". If you only put two time tags at the line beginning and ending, the player will assume all the words have equal length, and will speed up "welcome" and slow down "to the hotel". Instead you would put a placeholder between "welcome" and "to the hotel", press "Insert Tag" at the end of "welcome", and press it again at start "to the hotel", therefore providing better timings to the player.</p></body></html> Replace time placeholder by two time tags instead of one <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">LRC version 2 or UltraStar only. </span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">When tagging the song (using Insert Tag, which inserts current time tag at the cursor position), the cursor moves forward, typically to the beginning of next line or next placeholder. If this checkbox is set, the cursor will move to the beginning of next word. To skip prepositions and articles, it also skips the words which have less than specified number of characters.</p></body></html> Stop at the beginning of next word; a word must be longer than false chars Qt::Vertical 20 40 Timing marks Font Time mark font style: Font style for time placeholder. Since the font is usually smaller than editor font, do not chose serif fonts here. size: Placeholder font size. Should be at least 2px smaller than editor text font size. 4 120 Qt::Horizontal 79 20 Placeholder colors Background: Placeholder background color. PushButton Text: Placeholder text color. PushButton Timing mark colors Background: Timing mark background color. PushButton Text: Timing mark foreground color. PushButton Ultrastar-specific Background color for a timing mark which contains both time and pitch. Making it different from timing mark background color above would make it easier to detect timing marks which do not have pitch seet yet. Background color: 1 0 Background color for a timing mark which contains both time and pitch. Making it different from timing mark background color above would make it easier to detect timing marks which do not have pitch seet yet. PushButton Qt::Horizontal 26 20 If checked, the timing mark will also show the pitch. It takes more screen space, but also provides more information. The pitch will only be shown for lyric formats which suport it (only UltraStar so far). Show pitch in timing mark if available Qt::Vertical 20 90 Preview window Font Time mark font style: Font family for preview window size: Font size for preview window. Larger values are suggested (24 and up) 4 120 2 Qt::Horizontal 79 20 Colors Backround Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Preview window background color. PushButton Inactive Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Preview window text color for the text which is not being sung yet.. PushButton Active Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Preview window text color for the text which has been sung. PushButton Qt::Vertical 20 90 Advanced Phonon Audio delay introduced by Phonon: There is a delay between the Phonon (internal player) architecture plays the song, and before the tick() signal (which is used to synchronize lyrics) is played. This delay does not affect preview, since the delay is the same. However actual lyrics when tested with a karaoke player, might appear "shifted" (usually a little behind). This value(in milliseconds) represents the delay. It will be added to the exported lyrics automatically, and therefore the time might not match for UltraStar format. For LRC formats it is added via "offset" field. ms Qt::Horizontal 40 20 Check for updates If this option is enabled, the application will check for updates (when new version of application is released). If a new version is available, the program will show a messagebox dialog informing you about new version. This check will only performed once in 24 hours. No user information is requested or transmitted from your computer during this check. Check once a day, and warn me when a new version is available Last checked: Never Qt::AlignCenter Qt::Vertical 20 287 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok ColorButton QPushButton
colorbutton.h
buttonBox accepted() DialogSettings accept() 252 329 157 274 buttonBox rejected() DialogSettings reject() 320 329 286 274 cbEditorStopAtWords toggled(bool) leEditorWordCount setEnabled(bool) 62 258 484 256 cbEditorSupportBlocks toggled(bool) leEditorBlockLines setEnabled(bool) 114 118 345 132
karlyriceditor-1.11/src/dialog_testwindow.ui000066400000000000000000000032131233766701300213460ustar00rootroot00000000000000 DialogTestWindow 0 0 726 470 Dialog 0 2 QFrame::StyledPanel QFrame::Raised QLayout::SetMinimumSize 300 24 karlyriceditor-1.11/src/dialog_timeadjustment.cpp000066400000000000000000000054511233766701300223470ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include "dialog_timeadjustment.h" #include "editor.h" DialogTimeAdjustment::DialogTimeAdjustment(QWidget *parent) : QDialog(parent) { setupUi( this ); m_valueAdd = 0; m_valueMultiply = 1.0; connect( leAdd, SIGNAL(textChanged(QString)), this, SLOT(textChanged(QString)) ); connect( leMultiply, SIGNAL(textChanged(QString)), this, SLOT(textChanged(QString)) ); connect( leTestIn, SIGNAL(textChanged(QString)), this, SLOT(textChanged(QString)) ); leTestIn->setText( "0:20.11" ); leAdd->setText( QString::number( m_valueAdd ) ); leMultiply->setText( QString::number( m_valueMultiply ) ); } void DialogTimeAdjustment::textChanged ( const QString & ) { if ( !getAndValidate( false ) ) return; qint64 timing = timeToMark( leTestIn->text() ); timing *= m_valueMultiply; timing += m_valueAdd; leTestOut->setText( markToTime( timing )); } void DialogTimeAdjustment::accept() { if ( !getAndValidate( true ) ) return; QDialog::accept(); } bool DialogTimeAdjustment::getAndValidate( bool msgboxIfError ) { bool ok; QString errtxt; m_valueAdd = leAdd->text().toLongLong( &ok ); if ( ok ) { m_valueMultiply = leMultiply->text().toDouble( &ok ); if ( !ok ) errtxt = "Specified Multiply value is not valid"; } else errtxt = "Specified Add value is not valid"; if ( ok ) { txtError->setText(""); return true; } if ( msgboxIfError ) QMessageBox::critical( 0, "Invalid value", errtxt ); txtError->setText( "" + errtxt + "" ); return false; } karlyriceditor-1.11/src/dialog_timeadjustment.h000066400000000000000000000034401233766701300220100ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef DIALOG_TIMEADJUSTMENT_H #define DIALOG_TIMEADJUSTMENT_H #include #include "ui_dialog_timeadjustment.h" class DialogTimeAdjustment : public QDialog, Ui::DialogTimeAdjustment { Q_OBJECT public: explicit DialogTimeAdjustment( QWidget *parent = 0 ); private: bool getAndValidate( bool msgboxIfError ); protected slots: void textChanged ( const QString & text ); void accept(); public: qint64 m_valueAdd; double m_valueMultiply; }; #endif // DIALOG_TIMEADJUSTMENT_H karlyriceditor-1.11/src/dialog_timeadjustment.ui000066400000000000000000000070051233766701300221770ustar00rootroot00000000000000 DialogTimeAdjustment 0 0 351 207 Time adjustment Global timing adjustment Add milliseconds to each timing: 0 Multyply each timing (happens first): 1.0 Test the adjustment Time value of will become true Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogTimeAdjustment accept() 248 254 157 274 buttonBox rejected() DialogTimeAdjustment reject() 316 260 286 274 karlyriceditor-1.11/src/editor.cpp000066400000000000000000000605451233766701300172660ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include #include #include #include #include #include #include #include #include "mainwindow.h" #include "project.h" #include "editor.h" #include "settings.h" #include "editorhighlighting.h" #include "lyricsevents.h" #include "textrenderer.h" #include "dialog_timeadjustment.h" #include "cdg.h" const char * Editor::PLACEHOLDER = "[--:--]"; const char * Editor::PLACEHOLDER_VALUE = "--:--"; Editor::Editor( QWidget * parent ) : QTextEdit( parent ) { m_project = 0; m_timeId = 0; connect( this, SIGNAL( undoAvailable(bool)), pMainWindow, SLOT( editor_undoAvail(bool))); connect( this, SIGNAL( redoAvailable(bool)), pMainWindow, SLOT( editor_redoAvail(bool))); connect( this, SIGNAL(textChanged()), this, SLOT(textModified()) ); setAcceptRichText( false ); QFont font( pSettings->m_editorFontFamily, pSettings->m_editorFontSize ); setFont( font ); (void) new EditorHighlighting( this ); } void Editor::setProject( Project* proj ) { m_project = proj; } static inline bool isCRLF( QChar ch ) { return (ch == QChar::LineSeparator) || (ch == QChar::ParagraphSeparator ) || (ch == '\n' ); } void Editor::textModified() { if ( !m_project ) return; m_project->setModified(); } QString Editor::exportToString() { return toPlainText(); } bool Editor::importFromString( const QString& lyricstr ) { setPlainText( lyricstr ); return true; } bool Editor::importFromOldString( const QString& lyricstr ) { QString strlyrics; clear(); setEnabled( true ); // A simple state machine QString saved; for ( int i = 0; ; ++i ) { // Store the presaved text if ( i == lyricstr.length() || lyricstr[i] == '<' ) { // There is no qt::unescape saved.replace( "<", "<" ); saved.replace( ">", ">" ); saved.replace( "&", "&" ); strlyrics += saved; if ( i == lyricstr.length() ) break; saved.clear(); } else if ( lyricstr[i] == '>' ) { QString time; if ( saved.contains( '|' ) ) { QStringList values = saved.split( '|' ); time = "[" + markToTime( values[0].toLongLong() ) + "]"; } else time = "[" + markToTime( saved.toLongLong() ) + "]"; strlyrics += time; saved.clear(); } else saved.push_back( lyricstr[i] ); } setPlainText( strlyrics ); return true; } bool Editor::exportLyrics( Lyrics * lyrics ) { if ( !validate() ) return false; QString text = toPlainText(); QStringList lines = text.split( '\n' ); lyrics->beginLyrics(); foreach( QString line, lines ) { if ( line.trimmed().isEmpty() ) { // end of paragraph lyrics->curLyricAddEndOfLine(); continue; } // Skip comments if ( line.trimmed().startsWith( '#') ) continue; QString lyrictext, timing, special; bool in_time_tag = false; bool in_special_tag = false; int added_lyrics = 0; for ( int col = 0; col < line.size(); col++ ) { if ( in_special_tag ) { // Special tag ends? if ( line[col] == '}' ) in_special_tag = false; else special.append( line[col] ); } else if ( in_time_tag ) { // Time tag ends? if ( line[col] == ']' ) in_time_tag = false; else timing.append( line[col] ); } else if ( line[col] == '{' ) { in_special_tag = true; } else if ( line[col] == '[' ) { // If we already have the text, add the lyric if ( !timing.isEmpty() ) { if ( !special.isEmpty() ) lyrics->addBackgroundEvent( timeToMark( timing ), special ); // The first lyric should not be empty if ( added_lyrics > 0 || !lyrictext.isEmpty() ) { lyrics->curLyricSetTime( timeToMark( timing ) ); lyrics->curLyricAppendText( lyrictext ); lyrics->curLyricAdd(); added_lyrics++; } lyrictext.clear(); special.clear(); timing.clear(); } in_time_tag = true; } else lyrictext.append( line[col] ); } // There may be lyrics at the end of line if ( !timing.isEmpty() ) { if ( !special.isEmpty() ) lyrics->addBackgroundEvent( timeToMark( timing ), special ); lyrics->curLyricSetTime( timeToMark( timing ) ); lyrics->curLyricAppendText( lyrictext ); lyrics->curLyricAdd(); } lyrics->curLyricAddEndOfLine(); } lyrics->endLyrics(); return true; } void Editor::importLyrics( const Lyrics& lyrics ) { // clear the editor clear(); setEnabled( true ); QString strlyrics; // Fill the editor for ( int bl = 0; bl < lyrics.totalBlocks(); bl++ ) { const Lyrics::Block& block = lyrics.block( bl ); for ( int ln = 0; ln < block.size(); ln++ ) { const Lyrics::Line& line = block[ln]; for ( int pos = 0; pos < line.size(); pos++ ) { Lyrics::Syllable lentry = line[pos]; strlyrics += "[" + markToTime( lentry.timing ) + "]" + lentry.text; } strlyrics += "\n"; } strlyrics += "\n"; } setPlainText( strlyrics.trimmed() + "\n" ); } void Editor::cursorToLine( int line, int column ) { QTextCursor cur = textCursor(); cur.movePosition( QTextCursor::Start, QTextCursor::MoveAnchor ); cur.movePosition( QTextCursor::Down, QTextCursor::MoveAnchor, line - 1 ); if ( column ) { cur.movePosition( QTextCursor::Left, QTextCursor::MoveAnchor ); cur.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, column ); } setTextCursor( cur ); ensureCursorVisible(); } // validators bool Editor::validate( const QFont * font, const QSize * fitsize ) { QList errors; validate( errors, font, fitsize ); if ( !errors.isEmpty() ) { QMessageBox::critical( 0, tr("Validation error found"), tr("Error at line %1: ").arg( errors.front().line ) + errors.front().error ); cursorToLine( errors.front().line, errors.front().column ); return false; } return true; } void Editor::validate( QList& errors, const QFont * font, const QSize * fitsize ) { int linesinblock = 0; qint64 last_time = 0; QString paragraphtext; // Get the lyrics QString text = toPlainText(); QStringList lines = text.split( '\n' ); for ( int linenumber = 1; linenumber <= lines.size(); linenumber++ ) { const QString& line = lines[ linenumber - 1]; // Empty line is a paragraph separator. Handle it. if ( line.trimmed().isEmpty() ) { // Is it enabled? if ( !pSettings->m_editorSupportBlocks ) { errors.push_back( ValidatorError( linenumber, 0, tr("Empty line found.\n" "An empty line represents a block boundary, but blocks " "are currently disabled in settings") ) ); // recoverable goto cont_paragraph; } // Repeat? if ( paragraphtext.isEmpty() ) { // A new paragraph already started; duplicate empty line, not allowed errors.push_back( ValidatorError( linenumber, 0, tr("Double empty line found.\n" "A single empty line represents a block boundary; " "double lines are not supported.") ) ); // recoverable goto cont_paragraph; } // Paragraph-specific checks if ( font && fitsize ) { // Check if we exceed the screen height QFont nfont = *font; if ( !TextRenderer::checkFit( *fitsize, nfont, paragraphtext ) ) { errors.push_back( ValidatorError( linenumber - 1, 0, tr("Paragraph height exceed.\nThis paragraph cannot fit into the screen using the selected font" ) ) ); } } cont_paragraph: linesinblock = 0; paragraphtext = ""; continue; } // If we're here, this is not an empty line. linesinblock++; // Check if we're out of block line limit if ( pSettings->m_editorSupportBlocks && linesinblock > pSettings->m_editorMaxBlock ) { errors.push_back( ValidatorError( linenumber, 0, tr("Block size exceeded. The block contains more than %1 lines.\n" "Most karaoke players cannot show too large blocks because of " "limited screen space.\n\nPlease split the block by adding a " "block separator (an empty line).\n") .arg(pSettings->m_editorMaxBlock) ) ); } // Should not have lyrics before the first [ if ( line[0] != '[' ) { errors.push_back( ValidatorError( linenumber, 0, tr("Missing opening time tag. Every line must start with a [mm:ss.ms] time tag") ) ); } // LRCv2, UStar and CD+G must also end with ] if ( m_project->type() != Project::LyricType_LRC1 && !line.trimmed().endsWith( ']' ) ) { errors.push_back( ValidatorError( linenumber, 0, tr("Missing closing time tag. For this lyrics type every line must end with a [mm:ss.ms] time tag") ) ); } // Go through the line, and verify all time tags int time_tag_start = 0; bool in_time_tag = false; int special_tag_start = 0; bool in_special_tag = false; QString linetext; for ( int col = 0; col < line.size(); col++ ) { if ( in_special_tag ) { // Special tag ends? if ( line[col] == '}' ) { // Get the time and special part, if any QString special = line.mid( special_tag_start, col - special_tag_start ); if ( !special.isEmpty() ) { QString errmsg = LyricsEvents::validateEvent( special ); if ( !errmsg.isEmpty() ) { errors.push_back( ValidatorError( linenumber, time_tag_start, tr("Invalid special tag. %1") .arg( errmsg ) ) ); } } in_special_tag = false; continue; } } else if ( in_time_tag ) { // Time tag ends? if ( line[col] == ']' ) { // Get the time and special part, if any QString time = line.mid( time_tag_start, col - time_tag_start ); // Now validate the time if ( time == PLACEHOLDER_VALUE ) { errors.push_back( ValidatorError( linenumber, time_tag_start, tr("Placeholders should not be present in the production file.") ) ); } else { QRegExp rxtime( "^(\\d+):(\\d+)\\.(\\d+)$" ); if ( time.indexOf( rxtime ) != -1 ) { if ( rxtime.cap( 2 ).toInt() >= 60 ) { errors.push_back( ValidatorError( linenumber, time_tag_start, tr("Invalid time, number of seconds cannot exceed 59.") ) ); } qint64 timing = timeToMark( time ); if ( timing < last_time ) { errors.push_back( ValidatorError( linenumber, time_tag_start, tr("Time goes backward, previous time value is greater than current value.") ) ); } last_time = timing; } else { errors.push_back( ValidatorError( linenumber, time_tag_start, tr("Invalid time tag. Time tag must be in format [mm:ss.ms] where mm is minutes, ss is seconds and ms is milliseconds * 10") ) ); } } in_time_tag = false; continue; } // Only accept those characters if ( !line[col].isDigit() && line[col] != ':' && line[col] != '.' ) { errors.push_back( ValidatorError( linenumber, col, tr("Invalid character in the time tag. Time tag must be in format [mm:ss.ms] where mm is minutes, ss is seconds and ms is milliseconds * 10") ) ); in_time_tag = false; break; // done with this line } } else if ( line[col] == '[' ) { in_time_tag = true; time_tag_start = col + 1; continue; } else if ( line[col] == '{' ) { in_special_tag = true; special_tag_start = col + 1; continue; } else if ( line[col] == ']' ) { errors.push_back( ValidatorError( linenumber, col, tr("Invalid closing bracket usage outside the time block") ) ); } else if ( line[col] == '}' ) { errors.push_back( ValidatorError( linenumber, col, tr("Invalid closing bracket usage outside the special block") ) ); } else linetext += line[col]; } // Check if we exceed the screen width if ( font && fitsize ) { // Check if we exceed the screen height QFont nfont = *font; if ( !TextRenderer::checkFit( *fitsize, nfont, paragraphtext ) ) { errors.push_back( ValidatorError( linenumber, 0, tr("Line width exceed.\nThis line cannot fit into the screen using the selected font" ) ) ); } } paragraphtext += linetext + "\n"; // Verify opened time block if ( in_time_tag ) { errors.push_back( ValidatorError( linenumber, line.size() - 1, tr("Time tag is not closed properly") ) ); } } } void Editor::ensureCursorMiddle() { // Adjust for non-common cases and horizontally ensureCursorVisible(); // Now adjust vertically QScrollBar * vbar = verticalScrollBar(); QRect crect = cursorRect( textCursor() ); const int halfHeight = viewport()->height() / 2; const int curBottom = crect.y() + crect.height() + vbar->value(); if ( curBottom > vbar->value() + halfHeight ) vbar->setValue( qMax( 0, curBottom - halfHeight ) ); } bool Editor::canInsertFromMimeData ( const QMimeData * source ) const { return source->hasText() && !source->text().isEmpty(); } QMimeData * Editor::createMimeDataFromSelection () const { const QTextDocumentFragment fragment( textCursor() ); QString text = fragment.toPlainText(); QMimeData * m = new QMimeData(); m->setText( text ); return m; } void Editor::insertFromMimeData ( const QMimeData * source ) { QString text = source->text(); text.replace( QChar::LineSeparator, "\n" ); text.replace( QChar::ParagraphSeparator, "\n" ); text.remove( "\r" ); if ( !text.isNull() ) { QTextDocumentFragment fragment = QTextDocumentFragment::fromPlainText( text ); textCursor().insertFragment( fragment ); ensureCursorVisible(); } } void Editor::removeAllTimeTags() { // No need to do it in a more complex way than a simple regexp QString text = toPlainText(); // Placeholders text.remove( PLACEHOLDER ); text.remove( QRegExp( "\\[\\d+:\\d+\\.\\d+\\]" ) ); setPlainText( text ); } void Editor::removeExtraWhitespace() { // No need to do it in a more complex way than a simple regexp QStringList lyrics = toPlainText().split( '\n' ); for ( int i = 0; i < lyrics.size(); i++ ) { lyrics[i] = lyrics[i].trimmed(); } setPlainText( lyrics.join( "\n") ); } static bool isTimingMark( const QString& text, int * length = 0 ) { if ( text.startsWith( Editor::PLACEHOLDER ) ) { if ( length ) *length = strlen( Editor::PLACEHOLDER ); return true; } QRegExp rx( "^\\[\\d+:\\d+\\.\\d+\\]" ); if ( text.indexOf( rx ) != -1 ) { if ( length ) *length = rx.matchedLength(); return true; } return false; } void Editor::insertTimeTag( qint64 timing ) { // If we're replacing existing time tag, remove it first bool was_time_mark_deleted = false; QTextCursor cur = textCursor(); cur.beginEditBlock(); QString text = cur.block().text().mid( cur.position() - cur.block().position() ); int length; if ( timing > 0 && isTimingMark( text, &length ) )// only when playing { if ( text.startsWith( Editor::PLACEHOLDER ) ) was_time_mark_deleted = true; while ( length-- ) cur.deleteChar(); text = cur.block().text().mid( cur.position() - cur.block().position() ); } // Add the time if ( timing == 0 ) cur.insertText( PLACEHOLDER ); else cur.insertText( "[" + markToTime( timing ) + "]" ); cur.endEditBlock(); // Move the cursor according to policy if ( pSettings->m_editorDoubleTimeMark && was_time_mark_deleted ) return; // the cursor has been moved already int curPos = cur.position(); bool separator_found = false, tagged_word_ended = false; int word_start_offset = -1; while ( 1 ) { // Find the block QTextBlock block = document()->findBlock( curPos ); // Cursor position in the block int blockPos = curPos - block.position(); QChar ch; // If we're out of range, this is the end of block. if ( blockPos >= block.text().size() ) { // Text end? if ( !block.next().isValid() ) break; // Tell the rest of the code this is end of line ch = QChar::LineSeparator; } else ch = block.text().at( blockPos ); //qDebug("char: %s (%d), pos %d, blockpos %d", qPrintable( QString(ch)), ch.unicode(), curPos, blockPos ); // Time mark? if ( isTimingMark( block.text().mid( blockPos ) ) ) break; // We check for separator_found here because if the first character is timing mark, we want // it to be skipped too by a conditional check above if ( separator_found ) { if ( pSettings->m_editorSkipEmptyLines && isCRLF( ch ) ) { curPos++; continue; } break; } // New line is always a separator if ( isCRLF( ch ) ) { // If this is not the first character, stop on previous one if ( cur.position() != curPos ) { if ( pSettings->m_editorStopAtLineEnd ) break; } separator_found = true; } // Timing mark is always before a word, so if we find a space, this means the word is ended, // and a new word is about to start. So if we have multiple tags per line enabled, check it. if ( pSettings->m_editorStopNextWord ) { if ( ch.isSpace() ) { // If word_start_offset is not -1, this means this is the second word which is ended if ( word_start_offset != -1 ) { // Check the word length if ( curPos - word_start_offset > pSettings->m_editorWordChars ) { // Word size is more than needed, and there was no time mark. // Roll the cursor back abd break curPos = word_start_offset; break; } else { // The word is too small. Reset the word_start_offset and continue word_start_offset = -1; } } else tagged_word_ended = true; } else { if ( tagged_word_ended && word_start_offset == -1 ) word_start_offset = curPos; } } curPos++; } cur.setPosition( curPos, QTextCursor::MoveAnchor ); setTextCursor( cur ); ensureCursorMiddle(); } void Editor::removeLastTimeTag() { undo(); } QTextCursor Editor::cursorAtPoint( const QPoint& point ) { QAbstractTextDocumentLayout * layout = document()->documentLayout(); int pos; // Adjust for non-common cases and horizontally ensureCursorVisible(); // from QTextEditPrivate::mapToContents QPoint mapped = QPoint( point.x() + horizontalScrollBar()->value(), point.y() + verticalScrollBar()->value() ); if ( layout && (pos = layout->hitTest( mapped, Qt::ExactHit )) != -1 ) { QTextCursor cur = textCursor(); cur.setPosition( pos + 1 ); return cur; } return QTextCursor(); } qint64 Editor::timeForPosition( QTextCursor cur ) { QString line = cur.block().text(); int pos = cur.position() - cur.block().position(); QString left = line.left( pos ); QString right = line.mid( pos ); QRegExp markrx ( "\\[(\\d+:\\d+\\.\\d+)\\]" ); // Search left int offset = left.lastIndexOf( markrx ); if ( offset == -1 ) return -1; qint64 leftmark = timeToMark( markrx.cap( 1 ) ); left = left.mid( offset + markrx.matchedLength() ); left.remove( markrx ); // Search right offset = right.indexOf( markrx ); if ( offset == -1 ) return -1; qint64 rightmark = timeToMark( markrx.cap( 1 ) ); right = right.left( offset ); right.remove( markrx ); // We have time rightmark-leftmark for (left+right) characters int timediff = (int) (rightmark - leftmark); if ( timediff <= 0 ) return -1; qint64 timing = leftmark + left.length() * timediff / (left.length() + right.length() ); return timing; } void Editor::splitLine() { QTextCursor cur = textCursor(); qint64 timing = timeForPosition( cur ); if ( timing == -1 ) return; cur.beginEditBlock(); cur.insertText( "[" + markToTime( timing ) + "]" ); cur.insertText( "\n" ); cur.insertText( "[" + markToTime( timing + 10 ) + "]" ); cur.endEditBlock(); } bool Editor::event ( QEvent * event ) { if ( event->type() == QEvent::ToolTip ) { QHelpEvent *helpEvent = static_cast(event); QTextCursor cur = cursorAtPoint( helpEvent->pos() ); if ( !cur.isNull() ) { qint64 mark = timeForPosition( cur ); if ( mark != -1 ) { QString text = tr("Timing at this point: %1") .arg( markToTime(mark) ); QToolTip::showText( helpEvent->globalPos(), text ); return true; } } } return QTextEdit::event( event ); } void Editor::insertImageTag( const QString& file ) { QTextCursor cur = textCursor(); cur.beginEditBlock(); cur.insertText( "{ IMAGE=" + file + " }" ); cur.endEditBlock(); } void Editor::insertVideoTag( const QString& file ) { QTextCursor cur = textCursor(); cur.beginEditBlock(); cur.insertText( "{ VIDEO=" + file + " }" ); cur.endEditBlock(); } void Editor::insertColorChangeTag( const QString& name ) { QTextCursor cur = textCursor(); cur.beginEditBlock(); cur.insertText( "@@" + name ); cur.endEditBlock(); } void Editor::addMissingTimingMarks() { QString text = toPlainText(); QStringList lines = text.split( '\n' ); QRegExp endTimingPattern( ".*\\[(\\d+:\\d+\\.\\d+)\\]$"); QRegExp beginTimingPattern( "^\\[(\\d+:\\d+\\.\\d+)\\]"); for ( int l = 0; l < lines.size(); l++ ) { QString line = lines[l]; if ( line.trimmed().isEmpty() ) continue; // If the line has the last timing already, ignore it if ( line.indexOf( endTimingPattern ) != -1 ) continue; // This line doesn't. Analyze it QRegExp pattern( "\\[(\\d+:\\d+\\.\\d+)\\]([^\\[]*)"); int pos = 0; QList< qint64 > timings; QList< int > lengths; while ( (pos = pattern.indexIn( line, pos )) != -1 ) { timings.push_back( timeToMark( pattern.cap( 1 ) ) ); lengths.push_back( pattern.cap( 2 ).length() ); pos += pattern.matchedLength(); } if ( timings.isEmpty() ) continue; // Now get the average length per character qint64 newtime = timings.back() + lengths.back() * 10; if ( timings.size() > 1 ) { QList< qint64 > perchartimings; for ( int i = 1; i < timings.size(); i++ ) perchartimings.push_back( (timings[i] - timings[i-1]) / lengths[i-1] ); qSort( perchartimings.begin(), perchartimings.end() ); qint64 median = perchartimings[ perchartimings.size() / 2 ]; newtime = timings.back() + lengths.back() * median; } // Make sure we don't cross to the next line beginning, if any for ( int ll = l + 1; ll < lines.size(); ll++ ) { if ( lines[ll].isEmpty() ) continue; if ( lines[ll].indexOf( beginTimingPattern ) != -1 ) { qint64 nextBegin = timeToMark( beginTimingPattern.cap( 1 ) ); if ( nextBegin < newtime ) newtime = nextBegin - 1; } break; } lines[l] = lines[l].trimmed() + "[" + markToTime( newtime ) + "]"; } // Replace the text by a new one, preserving the undo selectAll(); insertPlainText( lines.join( "\n") ); } void Editor::adjustTimings() { DialogTimeAdjustment dlg; if ( dlg.exec() != QDialog::Accepted ) return; // Iterate through all the timing marks QString text = toPlainText(); QRegExp pattern( "\\[(\\d+:\\d+\\.\\d+)\\]"); int pos = 0; while ( (pos = pattern.indexIn( text, pos )) != -1 ) { qint64 timing = timeToMark( pattern.cap( 1 ) ); timing *= dlg.m_valueMultiply; timing += dlg.m_valueAdd; QString newtime = "[" + markToTime( timing ) + "]"; text.remove( pos, pattern.matchedLength() ); text.insert( pos, newtime ); pos += newtime.length(); } // Replace the text by a new one, preserving the undo selectAll(); insertPlainText( text ); } karlyriceditor-1.11/src/editor.h000066400000000000000000000100341233766701300167170ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef EDITOR_H #define EDITOR_H #include #include #include #include #include "lyrics.h" #include "validator.h" class Project; class BackgroundEvents; static inline QString markToTime( qint64 mark ) { int min = mark / 60000; int sec = (mark - min * 60000) / 1000; int msec = mark - (min * 60000 + sec * 1000 ); return QString().sprintf( "%02d:%02d.%02d", min, sec, msec / 10 ); } static inline qint64 timeToMark( QString data ) { QRegExp rxtime( "^(\\d+):(\\d+)\\.(\\d+)$"); if ( data.indexOf( rxtime ) == -1 ) return -1; return rxtime.cap( 1 ).toInt() * 60000 + rxtime.cap( 2 ).toInt() * 1000 + rxtime.cap( 3 ).toInt() * 10; } // // Architecturally only editor is responsible for formats, and know everything about // any specific lyrics format. This is done here and not in Project to provide syntax // highlighting. // class Editor : public QTextEdit { Q_OBJECT public: static const char * PLACEHOLDER; static const char * PLACEHOLDER_VALUE; Editor( QWidget * parent ); void setProject( Project* proj ); void insertTimeTag( qint64 timing ); void insertImageTag( const QString& file ); void insertVideoTag( const QString& file ); void insertColorChangeTag( const QString& name ); void removeLastTimeTag(); void addMissingTimingMarks(); void removeAllTimeTags(); void removeExtraWhitespace(); void adjustTimings(); // Validate the lyrics bool validate( const QFont * font = 0, const QSize * fitsize = 0 ); void validate( QList& errors, const QFont * font = 0, const QSize * fitsize = 0 ); // Export/Import functions to process lyrics. This generally does not work // for lyrics in "editing" phase, and requires the lyrics to be validated. bool exportLyrics( Lyrics * lyrics ); void importLyrics( const Lyrics& lyrics ); // Export/Import functions to store/load lyrics from a project file or // temporary storage. Not really useful for anything else. QString exportToString(); bool importFromString( const QString& lyricstr ); bool importFromOldString( const QString& lyricstr ); public slots: void textModified(); void splitLine(); protected: bool canInsertFromMimeData ( const QMimeData * source ) const; QMimeData * createMimeDataFromSelection () const; void insertFromMimeData ( const QMimeData * source ); bool event ( QEvent * event ); private: QString validateSpecial( const QString& string ); QTextCursor cursorAtPoint( const QPoint& point ); qint64 timeForPosition( QTextCursor cur ); // Ensure the cursor is in the middle of the screen if possible void ensureCursorMiddle(); void cursorToLine( int line, int column ); Project * m_project; unsigned int m_timeId; // for remove tag }; #endif // EDITOR_H karlyriceditor-1.11/src/editorhighlighting.cpp000066400000000000000000000111041233766701300216370ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include "editorhighlighting.h" #include "lyricsevents.h" #include "settings.h" #include "editor.h" EditorHighlighting::EditorHighlighting( QTextEdit *parent ) : QSyntaxHighlighter( parent ) { updateSettings(); // TODO: configuration change } void EditorHighlighting::updateSettings() { m_hlValidTiming.setBackground( pSettings->m_timeMarkTimeBackground ); m_hlValidTiming.setForeground( pSettings->m_timeMarkTimeText ); m_hlInvalidTiming.setBackground( Qt::red ); m_hlInvalidTiming.setForeground( Qt::black ); m_hlValidSpecial.setBackground( Qt::green ); m_hlValidSpecial.setForeground( Qt::gray ); m_hlPlaceholder.setBackground( pSettings->m_timeMarkPlaceholderBackground ); m_hlPlaceholder.setForeground( pSettings->m_timeMarkPlaceholderText ); m_hlComment.setForeground( Qt::cyan ); } void EditorHighlighting::highlightBlock ( const QString & line ) { if ( line.trimmed().isEmpty() ) return; // Highlight comments if ( line.trimmed().startsWith("#") ) { setFormat( 0, line.length(), m_hlComment ); return; } // from Editor::validate int time_tag_start = 0; int special_tag_start = 0; bool in_time_tag = false; bool in_special_tag = false; bool errors_in_time_tag = false; bool errors_in_special_tag = false; for ( int col = 0; col < line.size(); col++ ) { if ( in_special_tag ) { // Special tag ends? if ( line[col] == '}' ) { if ( !errors_in_special_tag ) { QString special = line.mid( special_tag_start, col - special_tag_start ); if ( !special.isEmpty() ) { QString errmsg = LyricsEvents::validateEvent( special ); if ( !errmsg.isEmpty() ) setFormat( special_tag_start - 1, special.length() + 2, m_hlInvalidTiming ); else setFormat( special_tag_start - 1, special.length() + 2, m_hlValidSpecial ); } else setFormat( special_tag_start - 1, 2, m_hlInvalidTiming ); } in_special_tag = false; errors_in_special_tag = false; continue; } } else if ( in_time_tag ) { // Time tag ends? if ( line[col] == ']' ) { if ( !errors_in_time_tag ) { QString time = line.mid( time_tag_start, col - time_tag_start ); // If Special is not empty, it hovers the last bracket (extra char) int time_len = time.length() + 2; if ( time == Editor::PLACEHOLDER_VALUE ) setFormat( time_tag_start - 1, time_len, m_hlPlaceholder ); else { QRegExp rxtime( "^(\\d+):(\\d+)\\.(\\d+)$" ); if ( time.indexOf( rxtime ) == -1 || rxtime.cap( 2 ).toInt() >= 60 ) setFormat( time_tag_start - 1, time_len, m_hlInvalidTiming ); else setFormat( time_tag_start - 1, time_len, m_hlValidTiming ); } } in_time_tag = false; errors_in_time_tag = false; continue; } // Only accept those characters; --:-- is placeholder so also valid if ( !line[col].isDigit() && line[col] != ':' && line[col] != '.' && line[col] != '-' ) { setFormat( col, 1, m_hlInvalidTiming ); errors_in_time_tag = true; } } else if ( line[col] == '[' ) { in_time_tag = true; time_tag_start = col + 1; continue; } else if ( line[col] == '{' ) { in_special_tag = true; special_tag_start = col + 1; continue; } else if ( line[col] == ']' || line[col] == '}' ) setFormat( col, 1, m_hlInvalidTiming ); } } karlyriceditor-1.11/src/editorhighlighting.h000066400000000000000000000034651233766701300213170ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef EDITORHIGHLIGHTING_H #define EDITORHIGHLIGHTING_H #include class EditorHighlighting : public QSyntaxHighlighter { Q_OBJECT public: EditorHighlighting( QTextEdit *parent ); void highlightBlock ( const QString & text ); private slots: void updateSettings(); private: QTextCharFormat m_hlValidTiming; QTextCharFormat m_hlValidSpecial; QTextCharFormat m_hlInvalidTiming; QTextCharFormat m_hlPlaceholder; QTextCharFormat m_hlComment; }; #endif // EDITORHIGHLIGHTING_H karlyriceditor-1.11/src/export_params.cpp000066400000000000000000000404331233766701300206560ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include #include #include #include "textrenderer.h" #include "export_params.h" #include "playerwidget.h" #include "licensing.h" #include "version.h" #include "util.h" #include "cdg.h" DialogExportOptions::DialogExportOptions( Project * project, const Lyrics& lyrics, bool video, QWidget *parent ) : QDialog(parent), Ui::DialogExportParams(), m_renderer(0, 0) { m_videomode = video; m_project = project; m_lyrics = lyrics; m_time = 0; // UIC stuff setupUi( this ); // Buttons connect( btnDetectSize, SIGNAL(clicked()), this, SLOT(autodetectFontSize()) ); connect( btnTestSize, SIGNAL(clicked()), this, SLOT(testFontSize()) ); connect( btnBrowse, SIGNAL(clicked()), this, SLOT(browseOutputFile()) ); // Tabs connect( tabWidget, SIGNAL(currentChanged(int)), this, SLOT(activateTab(int)) ); connect( seekSlider, SIGNAL(valueChanged(int)), this, SLOT(previewSliderMoved(int)) ); // Video comboboxes connect( boxVideoTarget, SIGNAL(currentIndexChanged(int)), this, SLOT(videoTargetChanged(int)) ); connect( boxVideoMedium, SIGNAL(currentIndexChanged(int)), this, SLOT(videoMediumChanged(int)) ); // Details label connect( lblVideoDetailsLink, SIGNAL(linkActivated(QString)), this, SLOT(videoShowDetails()) ); if ( video ) { // Set the video output params boxVideoMedium->addItems( pVideoEncodingProfiles->videoMediumTypes() ); btnVideoColorActive->setColor( m_project->tag( Project::Tag_Video_activecolor, "blue" ) ); btnVideoColorBg->setColor( m_project->tag( Project::Tag_Video_bgcolor, "black" ) ); btnVideoColorInactive->setColor( m_project->tag( Project::Tag_Video_inactivecolor, "green" ) ); btnVideoColorInfo->setColor( m_project->tag( Project::Tag_Video_infocolor, "white" ) ); fontVideo->setCurrentFont( QFont( m_project->tag( Project::Tag_Video_font, "arial" ) ) ); fontVideoSize->setValue( m_project->tag( Project::Tag_Video_fontsize, "8" ).toInt() ); titleVideoMin->setValue( m_project->tag( Project::Tag_Video_titletime, "5" ).toInt() ); cbVideoPreamble->setChecked( m_project->tag( Project::Tag_Video_preamble, "1" ).toInt() ); setWindowTitle( tr("Specify video parameters") ); } else { // Hide video part groupVideo->hide(); btnVideoColorActive->setColor( m_project->tag( Project::Tag_CDG_activecolor, "blue" ) ); btnVideoColorBg->setColor( m_project->tag( Project::Tag_CDG_bgcolor, "black" ) ); btnVideoColorInactive->setColor( m_project->tag( Project::Tag_CDG_inactivecolor, "green" ) ); btnVideoColorInfo->setColor( m_project->tag( Project::Tag_CDG_infocolor, "white" ) ); fontVideo->setCurrentFont( QFont( m_project->tag( Project::Tag_CDG_font, "arial" ) ) ); fontVideoSize->setValue( m_project->tag( Project::Tag_CDG_fontsize, "8").toInt() ); titleVideoMin->setValue( m_project->tag( Project::Tag_CDG_titletime, "5").toInt() ); cbVideoPreamble->setChecked( m_project->tag( Project::Tag_CDG_preamble, "1").toInt() ); setWindowTitle( tr("Specify CDG parameters") ); lblOutput->setText( tr("Write CD+G data to file:") ); leOutputFile->setText( Util::removeFileExtention( m_project->musicFile() ) + "cdg" ); // resize as we hid the video part resize( width(), 1 ); } fontVideo->setFontFilters( QFontComboBox::ScalableFonts | QFontComboBox::MonospacedFonts | QFontComboBox::ProportionalFonts ); // Title window leTitle->setText( m_project->tag( Project::Tag_Title, "") ); leArtist->setText( m_project->tag( Project::Tag_Artist, "") ); if ( pLicensing->isValid() ) { leTitleCreatedBy->setText( QString("Created by %1
http://www.ulduzsoft.com") .arg(APP_NAME) ); } else { leTitleCreatedBy->setEnabled( false ); leTitleCreatedBy->setText( "Application not registered, this field cannot be modified" ); } } void DialogExportOptions::setBoxIndex( Project::Tag tag, QComboBox * box ) { QString val = m_project->tag( tag ); if ( val.isEmpty() ) { box->setCurrentIndex( 0 ); } else { int idx = box->findText( val ); if ( idx != -1 ) box->setCurrentIndex( idx ); else box->setCurrentIndex( 0 ); } } void DialogExportOptions::autodetectFontSize() { QFont font = fontVideo->currentFont(); if ( !m_videomode ) font.setStyleStrategy( QFont::NoAntialias ); // Ask the renderer TextRenderer renderer( 100, 100 ); renderer.setLyrics( m_lyrics ); int fsize = renderer.autodetectFontSize( getVideoSize(), font ); fontVideoSize->setValue( fsize ); } bool DialogExportOptions::testFontSize() { QFont font = fontVideo->currentFont(); font.setPointSize( fontVideoSize->value() ); if ( !m_videomode ) font.setStyleStrategy( QFont::NoAntialias ); TextRenderer renderer( 100, 100 ); renderer.setLyrics( m_lyrics ); if ( !renderer.verifyFontSize( getVideoSize(), font ) ) { QMessageBox::critical( 0, tr("Font size too large"), tr("The output text cannot fit into screen using the specified font size") ); return false; } return true; } void DialogExportOptions::browseOutputFile() { QString outfile; if ( m_videomode ) { QString exportdir = QSettings().value( "general/exportdirvideo", "" ).toString(); outfile = QFileDialog::getSaveFileName( 0, tr("Export video to a file"), exportdir ); } else { QString exportdir = QSettings().value( "general/exportdircdg", "" ).toString(); outfile = QFileDialog::getSaveFileName( 0, tr("Export CD+G graphics to a file"), exportdir, "CD+G (*.cdg)" ); } if ( outfile.isEmpty() ) return; QFileInfo finfo( outfile ); if ( m_videomode ) QSettings().setValue( "general/exportdirvideo", finfo.dir().absolutePath() ); else QSettings().setValue( "general/exportdircdg", finfo.dir().absolutePath() ); leOutputFile->setText( outfile ); } void DialogExportOptions::accept() { m_outputVideo = leOutputFile->text(); if ( m_outputVideo.isEmpty() ) { QMessageBox::critical( 0, tr("Output file not specified"), tr("You must specify the output video file") ); return; } if ( !testFontSize() ) return; // Store title params m_artist = leArtist->text(); m_title = leTitle->text(); m_createdBy = leTitleCreatedBy->text(); // Store encoding params if ( m_videomode ) { m_currentVideoFormat = pVideoEncodingProfiles->videoFormat( boxVideoProfile->currentText() ); m_currentProfile = pVideoEncodingProfiles->videoProfile( boxVideoTarget->currentText() ); if ( !m_currentProfile || !m_currentVideoFormat ) return; m_audioEncodingMode = boxVideoAudio->currentIndex(); m_quality = boxVideoQuality->itemData( boxVideoQuality->currentIndex() ).toInt(); // Store rendering params m_project->setTag( Project::Tag_Video_activecolor, btnVideoColorActive->color().name() ); m_project->setTag( Project::Tag_Video_bgcolor, btnVideoColorBg->color().name() ); m_project->setTag( Project::Tag_Video_inactivecolor, btnVideoColorInactive->color().name() ); m_project->setTag( Project::Tag_Video_infocolor, btnVideoColorInfo->color().name() ); m_project->setTag( Project::Tag_Video_font, fontVideo->currentFont().family() ); m_project->setTag( Project::Tag_Video_fontsize, QString::number( fontVideoSize->value() ) ); m_project->setTag( Project::Tag_Video_titletime, QString::number( titleVideoMin->value() ) ); m_project->setTag( Project::Tag_Video_preamble, cbVideoPreamble->isChecked() ? "1" : "0" ); } else { // Store rendering params m_project->setTag( Project::Tag_CDG_activecolor, btnVideoColorActive->color().name() ); m_project->setTag( Project::Tag_CDG_bgcolor, btnVideoColorBg->color().name() ); m_project->setTag( Project::Tag_CDG_inactivecolor, btnVideoColorInactive->color().name() ); m_project->setTag( Project::Tag_CDG_infocolor, btnVideoColorInfo->color().name() ); m_project->setTag( Project::Tag_CDG_font, fontVideo->currentFont().family() ); m_project->setTag( Project::Tag_CDG_fontsize, QString::number( fontVideoSize->value() ) ); m_project->setTag( Project::Tag_CDG_titletime, QString::number( titleVideoMin->value() ) ); m_project->setTag( Project::Tag_CDG_preamble, cbVideoPreamble->isChecked() ? "1" : "0" ); } QDialog::accept(); } void DialogExportOptions::videoMediumChanged(int newvalue) { // This filters off target-specific profiles such as DVD/PAL and DVD/NTSC for DVD QStringList profilenames = pVideoEncodingProfiles->videoProfiles(); boxVideoTarget->clear(); for ( int i = 0; i < profilenames.size(); i++ ) { const VideoEncodingProfile * p = pVideoEncodingProfiles->videoProfile( profilenames[i] ); if ( !p ) continue; if ( p->type != newvalue ) continue; boxVideoTarget->addItem( profilenames[i] ); } // Update the qualities videoTargetChanged( 0 ); } void DialogExportOptions::videoTargetChanged( int ) { // This filters off video profile-specific formats (i.e. no PAL video encodings for NTSC formats), // as well as quality settings. boxVideoProfile->clear(); boxVideoQuality->clear(); m_currentProfile = pVideoEncodingProfiles->videoProfile( boxVideoTarget->currentText() ); if ( !m_currentProfile ) return; // Filter out the video formats QStringList videoFormats = pVideoEncodingProfiles->videoFormats(); Q_FOREACH ( QString format, videoFormats ) { if ( !m_currentProfile->limitFormats.empty() && !m_currentProfile->limitFormats.contains( format ) ) continue; boxVideoProfile->addItem( format ); } if ( m_currentProfile->bitratesEnabled[VideoEncodingProfile::BITRATE_HIGH] ) boxVideoQuality->addItem( tr("High"), VideoEncodingProfile::BITRATE_HIGH ); if ( m_currentProfile->bitratesEnabled[VideoEncodingProfile::BITRATE_MEDIUM] ) boxVideoQuality->addItem( tr("Medium"), VideoEncodingProfile::BITRATE_MEDIUM ); if ( m_currentProfile->bitratesEnabled[VideoEncodingProfile::BITRATE_LOW] ) boxVideoQuality->addItem( tr("Low"), VideoEncodingProfile::BITRATE_LOW ); boxVideoProfile->setEnabled( boxVideoProfile->count() > 1 ); boxVideoQuality->setEnabled( boxVideoQuality->count() > 1 ); } void DialogExportOptions::videoShowDetails() { m_currentVideoFormat = pVideoEncodingProfiles->videoFormat( boxVideoProfile->currentText() ); m_currentProfile = pVideoEncodingProfiles->videoProfile( boxVideoTarget->currentText() ); if ( !m_currentProfile || !m_currentVideoFormat ) return; m_audioEncodingMode = boxVideoAudio->currentIndex(); m_quality = boxVideoQuality->itemData( boxVideoQuality->currentIndex() ).toInt(); QString data = tr("" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "
Video parameters
Codec:$videoCodec
Container:$videoContainer
Resolution:$videoResolution
Frame rate:$frameRate FPS
Bitrate:$videoBitrate
Display aspect ratio:$displayAspectRatio
Sample aspect ratio:$sampleAspectRatio
Progressive:$progressive
 
Audio parameters
Codec:$audioCodec
Sample rate:$audioSampleRate
Channels:$audioChannels
Bitrate:$audioBitrate
"); QMap< QString, QString > variables; variables["$videoCodec"] = m_currentProfile->videoCodec; variables["$videoContainer"] = m_currentProfile->videoContainer; variables["$videoResolution"] = QString("%1x%2") .arg(m_currentVideoFormat->width) .arg(m_currentVideoFormat->height); variables["$frameRate"] = QString::number( (double) m_currentVideoFormat->frame_rate_den / m_currentVideoFormat->frame_rate_num, 'g', 2 ); variables["$videoBitrate"] = QString("%1Kbps") .arg( m_currentProfile->bitratesVideo[m_quality] ); variables["$displayAspectRatio"] = QString("%1:%2") .arg( m_currentVideoFormat->display_aspect_num ) .arg( m_currentVideoFormat->display_aspect_den ); variables["$sampleAspectRatio"] = QString("%1:%2") .arg( m_currentVideoFormat->sample_aspect_num ) .arg( m_currentVideoFormat->sample_aspect_den ); variables["$progressive"] = (m_currentVideoFormat->flags & VIFO_INTERLACED) ? "false" : "true"; variables["$audioCodec"] = m_currentProfile->audioCodec; variables["$audioSampleRate"] = QString::number( m_currentProfile->sampleRate ); variables["$audioChannels"] = QString::number( m_currentProfile->channels ); variables["$audioBitrate"] = QString("%1Kbps") .arg( m_currentProfile->bitratesAudio[m_quality] ); Q_FOREACH( QString key, variables.keys() ) { data.replace( key, variables[key]); } QWhatsThis::showText( mapToGlobal(lblVideoDetailsLink->pos()), data ); } QSize DialogExportOptions::getVideoSize() { if ( !m_videomode ) { // CD+G size is predefined return QSize( CDG_DRAW_WIDTH, CDG_DRAW_HEIGHT ); } // Get the current video profile value const VideoFormat * vf = pVideoEncodingProfiles->videoFormat( boxVideoProfile->currentText() ); if ( !vf ) return QSize( 100, 100 ); return QSize( vf->width, vf->height ); } bool DialogExportOptions::videoParams(const VideoEncodingProfile **profile, const VideoFormat **format, unsigned int *audioMode, unsigned int *qualty) { *profile = m_currentProfile; *format = m_currentVideoFormat; *audioMode = m_audioEncodingMode; *qualty = m_quality; return true; } void DialogExportOptions::activateTab( int index ) { // We're only interested in Preview tab if ( index != 1 ) { adjustSize(); return; } // Prepare the text renderer using current params QFont font = fontVideo->currentFont(); font.setPointSize( fontVideoSize->value() ); m_renderer = TextRenderer( getVideoSize().width(), getVideoSize().height() ); // Initialize colors from m_project m_renderer.setLyrics( m_lyrics ); m_renderer.setRenderFont( font ); m_renderer.setColorBackground( btnVideoColorBg->color() ); m_renderer.setColorTitle( btnVideoColorInfo->color() ); m_renderer.setColorSang( btnVideoColorInactive->color() ); m_renderer.setColorToSing( btnVideoColorActive->color() ); // CD+G? if ( !m_videomode ) m_renderer.forceCDGmode(); // Title m_renderer.setTitlePageData( leArtist->text(), leTitle->text(), pLicensing->isValid() ? leTitleCreatedBy->text() : "", titleVideoMin->value() * 1000 ); // Preamble if ( cbVideoPreamble->isChecked() ) m_renderer.setPreambleData( 4, 5000, 8 ); // Update the image previewUpdateImage(); adjustSize(); } void DialogExportOptions::previewUpdateImage() { // Update the image m_renderer.update( m_time ); QImage img = m_renderer.image(); // For CD+G mode we enlarge the image as it is too small if ( !m_videomode ) { QSize scaledsize( 2 * CDG_DRAW_WIDTH, 2 * CDG_DRAW_HEIGHT ); img = img.scaled( scaledsize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation ); } lblImage->setPixmap( QPixmap::fromImage( img ) ); // Update the timings qint64 reminder = m_project->getSongLength() - m_time; lblCurrent->setText( PlayerWidget::tickToString( m_time ) ); lblTotal->setText( PlayerWidget::tickToString( reminder ) ); } void DialogExportOptions::previewSliderMoved( int newvalue ) { m_time = newvalue * m_project->getSongLength() / seekSlider->maximum(); previewUpdateImage(); } karlyriceditor-1.11/src/export_params.h000066400000000000000000000056251233766701300203270ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef VIDEOEXPORTOPTIONS_H #define VIDEOEXPORTOPTIONS_H #include #include "ui_dialog_export_params.h" #include "videoencodingprofiles.h" #include "textrenderer.h" #include "lyrics.h" #include "project.h" class DialogExportOptions : public QDialog, public Ui::DialogExportParams { Q_OBJECT public: DialogExportOptions( Project * project, const Lyrics& lyrics, bool video = true, QWidget *parent = 0 ); // For both CD+G and video modes QSize getVideoSize(); // Returns the selected video profile const VideoEncodingProfile * videoProfile() const; // Return the video encoding parameters bool videoParams( const VideoEncodingProfile ** profile, const VideoFormat ** format, unsigned int * audioMode, unsigned int * qualty ); private slots: void activateTab( int index ); void autodetectFontSize(); void browseOutputFile(); bool testFontSize(); void previewUpdateImage(); void previewSliderMoved( int newvalue ); void accept(); void videoMediumChanged( int newvalue ); void videoTargetChanged(int); void videoShowDetails(); public: QString m_outputVideo; QString m_artist; QString m_title; QString m_createdBy; private: void setBoxIndex( Project::Tag tag, QComboBox * box ); private: bool m_videomode; Project* m_project; Lyrics m_lyrics; // For preview TextRenderer m_renderer; qint64 m_time; // For current video selection tracking const VideoEncodingProfile * m_currentProfile; const VideoFormat * m_currentVideoFormat; unsigned int m_audioEncodingMode; unsigned int m_quality; }; #endif // VIDEOEXPORTOPTIONS_H karlyriceditor-1.11/src/ffmpeg_headers.cpp000066400000000000000000000027341233766701300207330ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include "ffmpeg_headers.h" static bool ffmpeg_initialized = false; void ffmpeg_init_once() { if ( !ffmpeg_initialized ) { avcodec_register_all(); av_register_all(); } } karlyriceditor-1.11/src/ffmpeg_headers.h000066400000000000000000000033211233766701300203710ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef FFMPEG_HEADERS_H #define FFMPEG_HEADERS_H #include #define UINT64_C(c) c ## ULL #define INT64_C(c) c ## LL extern "C" { #include #include #include #include #include }; void ffmpeg_init_once(); #define FFMPEG_FILENAME(string) ( qPrintable(string) ) #endif // FFMPEG_HEADERS_H karlyriceditor-1.11/src/ffmpegvideodecoder.cpp000066400000000000000000000157461233766701300216240ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include "ffmpeg_headers.h" #include "ffmpegvideodecoder.h" class FFMpegVideoDecoderPriv { public: void init(); bool readFrame( int frame ); public: unsigned int skipFrames; AVFormatContext *pFormatCtx; int videoStream; AVCodecContext *pCodecCtx; AVCodec *pCodec; AVFrame *pFrame; AVFrame *pFrameRGB; SwsContext *img_convert_ctx; QByteArray m_buffer; QString m_errorMsg; int m_maxFrame; int m_fps_den; int m_fps_num; int m_currentFrameNumber; QImage m_currentFrameImage; }; void FFMpegVideoDecoderPriv::init() { pFormatCtx = 0; pCodecCtx = 0; pCodec = 0; pFrame = 0; pFrameRGB = 0; img_convert_ctx = 0; m_maxFrame = 0; m_currentFrameNumber = 0; } // // A wrapper interface class // FFMpegVideoDecoder::FFMpegVideoDecoder() { ffmpeg_init_once(); d = new FFMpegVideoDecoderPriv(); // Reset the pointers d->init(); } FFMpegVideoDecoder::~FFMpegVideoDecoder() { close(); delete d; } bool FFMpegVideoDecoder::openFile( const QString& filename, unsigned int seekto ) { // See http://dranger.com/ffmpeg/tutorial01.html close(); // Open video file if ( avformat_open_input( &d->pFormatCtx, FFMPEG_FILENAME( filename ), NULL, 0 ) != 0 ) { d->m_errorMsg = "Could not open video file"; return false; } // Retrieve stream information if ( avformat_find_stream_info( d->pFormatCtx, 0 ) < 0 ) { d->m_errorMsg = "Could not find stream information in the video file"; return false; } // Find the first video stream d->videoStream = -1; for ( unsigned i = 0; i < d->pFormatCtx->nb_streams; i++ ) { if ( d->pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) { d->videoStream = i; break; } } if ( d->videoStream == -1 ) return false; // Didn't find a video stream d->m_fps_den = d->pFormatCtx->streams[d->videoStream]->r_frame_rate.den; d->m_fps_num = d->pFormatCtx->streams[d->videoStream]->r_frame_rate.num; if ( d->m_fps_den == 60000 ) d->m_fps_den = 30000; // Get a pointer to the codec context for the video stream d->pCodecCtx = d->pFormatCtx->streams[d->videoStream]->codec; // Find the decoder for the video stream d->pCodec = avcodec_find_decoder( d->pCodecCtx->codec_id ); if ( d->pCodec == NULL ) { d->m_errorMsg = "Could not find the decoder for the video file"; return false; } // Open codec if ( avcodec_open2( d->pCodecCtx, d->pCodec, 0 ) < 0 ) { d->m_errorMsg = "Could not open the codec for the video file"; return false; } // Allocate video frame d->pFrame = avcodec_alloc_frame(); // Allocate an AVFrame structure d->pFrameRGB = avcodec_alloc_frame(); if ( !d->pFrame || !d->pFrameRGB ) { d->m_errorMsg = "Could not allocate memory for video frames"; return false; } // Determine required buffer size and allocate buffer int numBytes = avpicture_get_size( PIX_FMT_RGB24, d->pCodecCtx->width, d->pCodecCtx->height ); d->m_buffer.resize( numBytes ); // Assign appropriate parts of buffer to image planes in pFrameRGB avpicture_fill( (AVPicture *) d->pFrameRGB, (uint8_t*) d->m_buffer.data(), PIX_FMT_RGB24, d->pCodecCtx->width, d->pCodecCtx->height ); d->skipFrames = seekto; return true; } QString FFMpegVideoDecoder::errorMsg() const { return d->m_errorMsg; } void FFMpegVideoDecoder::close() { // Free the YUV frame if ( d->pFrame ) av_free( d->pFrame ); // Free the RGB frame if ( d->pFrameRGB ) av_free( d->pFrameRGB ); // Close the codec if ( d->pCodecCtx ) avcodec_close( d->pCodecCtx ); // Close the video file if ( d->pFormatCtx ) avformat_close_input( &d->pFormatCtx ); // Reset the pointers d->init(); } bool FFMpegVideoDecoderPriv::readFrame( int frame ) { AVPacket packet; int frameFinished; while ( m_currentFrameNumber < frame ) { // Read a frame if ( av_read_frame( pFormatCtx, &packet ) < 0 ) return false; // Frame read failed (e.g. end of stream) if ( packet.stream_index == videoStream ) { // Is this a packet from the video stream -> decode video frame avcodec_decode_video2( pCodecCtx, pFrame, &frameFinished, &packet ); // Did we get a video frame? if ( frameFinished ) { m_currentFrameNumber++; if ( m_currentFrameNumber >= frame ) { int w = pCodecCtx->width; int h = pCodecCtx->height; img_convert_ctx = sws_getCachedContext(img_convert_ctx,w, h, pCodecCtx->pix_fmt, w, h, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL); if ( img_convert_ctx == NULL ) { printf("Cannot initialize the conversion context!\n"); return false; } sws_scale( img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize ); // Convert the frame to QImage m_currentFrameImage = QImage( w, h, QImage::Format_RGB888 ); for ( int y = 0; y < h; y++ ) memcpy( m_currentFrameImage.scanLine(y), pFrameRGB->data[0] + y * pFrameRGB->linesize[0], w*3 ); } } } av_free_packet( &packet ); } return true; } QImage FFMpegVideoDecoder::frame( qint64 timems ) { // Use current frame? int frame_for_time = ((timems * d->m_fps_num) / d->m_fps_den) / 1000; if ( frame_for_time == 0 ) frame_for_time = 1; frame_for_time += d->skipFrames; // Loop if we know how many frames we have total if ( d->m_maxFrame > 0 ) frame_for_time %= d->m_maxFrame; while ( 1 ) { if ( d->readFrame( frame_for_time ) ) break; // End of video; restart d->m_maxFrame = d->m_currentFrameNumber - 1; d->m_currentFrameNumber = 0; frame_for_time = 0; if ( av_seek_frame( d->pFormatCtx, d->videoStream, 0, 0 ) < 0 ) { qWarning("Cannot seek video back to zero"); return QImage(); } d->skipFrames = 0; avcodec_flush_buffers( d->pCodecCtx ); } return d->m_currentFrameImage; } karlyriceditor-1.11/src/ffmpegvideodecoder.h000066400000000000000000000033361233766701300212610ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef FFMPEGVIDEODECODER_H #define FFMPEGVIDEODECODER_H #include class FFMpegVideoDecoderPriv; class FFMpegVideoDecoder { public: FFMpegVideoDecoder(); ~FFMpegVideoDecoder(); bool openFile( const QString& file, unsigned int seekto = 0 ); QString errorMsg() const; void close(); // Player function QImage frame( qint64 timems ); protected: FFMpegVideoDecoderPriv * d; }; #endif // FFMPEGVIDEODECODER_H karlyriceditor-1.11/src/ffmpegvideoencoder.cpp000066400000000000000000000630201233766701300216220ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ /************************************************************************** * This class uses ideas from QTFFmpegWrapper - QT FFmpeg Wrapper Class * * Copyright (C) 2009,2010: Daniel Roggen, droggen@gmail.com * * All rights reserved. * **************************************************************************/ #include "ffmpeg_headers.h" #include "ffmpegvideoencoder.h" #include "videoencodingprofiles.h" #include "audioplayer.h" #include "audioplayerprivate.h" #define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio class FFMpegVideoEncoderPriv { public: FFMpegVideoEncoderPriv(); ~FFMpegVideoEncoderPriv(); bool createFile( const QString& filename ); bool close(); int encodeImage( const QImage & img, qint64 time ); void flush(); public: // Video output parameters const VideoEncodingProfile * m_profile; const VideoFormat * m_videoformat; bool m_convertaudio; unsigned int m_videobitrate; unsigned int m_audiobitrate; // Do we also have an audio source? AudioPlayerPrivate * m_aplayer; // Error message QString m_errorMsg; private: bool convertImage_sws(const QImage &img); // FFmpeg stuff AVFormatContext * outputFormatCtx; AVOutputFormat * outputFormat; AVCodecContext * videoCodecCtx; AVCodecContext * audioCodecCtx; AVStream * videoStream; AVStream * audioStream; AVCodec * videoCodec; AVCodec * audioCodec; // Video frame data AVFrame * videoFrame; uint8_t * videoImageBuffer; // Audio frame data AVFrame * audioFrame; uint8_t * audioSampleBuffer; unsigned int audioSampleBuffer_size; // Conversion SwsContext * videoConvertCtx; // FIXME: Audio resample AVAudioResampleContext* audioResampleCtx; // File has been outputFileOpened successfully bool outputFileOpened; // Video frame PTS unsigned int videoFrameNumber; unsigned int audioPTStracker; // Total output size unsigned int outputTotalSize; }; static bool isAudioSampleFormatSupported( const enum AVSampleFormat * sample_formats, AVSampleFormat format ) { while ( *sample_formats != AV_SAMPLE_FMT_NONE ) { if ( *sample_formats == format ) return true; sample_formats++; } return false; } FFMpegVideoEncoderPriv::FFMpegVideoEncoderPriv() { ffmpeg_init_once(); outputFormatCtx = 0; outputFormat = 0; videoCodecCtx = 0; videoStream = 0; audioStream = 0; audioCodec = 0; videoCodec = 0; videoFrame = 0; audioResampleCtx = 0; audioFrame = 0; audioCodecCtx = 0; videoImageBuffer = 0; audioSampleBuffer = 0; videoConvertCtx = 0; outputFileOpened = false; } FFMpegVideoEncoderPriv::~FFMpegVideoEncoderPriv() { close(); } bool FFMpegVideoEncoderPriv::close() { if ( outputFormatCtx ) { if ( outputFileOpened ) { flush(); av_write_trailer( outputFormatCtx ); // close video and audio avcodec_close( videoStream->codec ); avcodec_close( audioStream->codec ); // Close the file avio_close( outputFormatCtx->pb ); } // free the streams for ( unsigned int i = 0; i < outputFormatCtx->nb_streams; i++ ) { av_freep(&outputFormatCtx->streams[i]->codec); av_freep(&outputFormatCtx->streams[i]); } // Free the format av_free( outputFormatCtx ); } delete[] videoImageBuffer; delete[] audioSampleBuffer; if ( videoFrame ) av_free(videoFrame); if ( audioFrame ) av_free( audioFrame ); outputFormatCtx = 0; outputFormat = 0; videoCodecCtx = 0; videoStream = 0; audioStream = 0; videoCodec = 0; videoFrame = 0; audioFrame = 0; videoImageBuffer = 0; audioSampleBuffer = 0; videoConvertCtx = 0; return true; } FFMpegVideoEncoder::FFMpegVideoEncoder() { d = new FFMpegVideoEncoderPriv(); } FFMpegVideoEncoder::~FFMpegVideoEncoder() { delete d; } bool FFMpegVideoEncoder::close() { return d->close(); } int FFMpegVideoEncoder::encodeImage( const QImage & img, qint64 time ) { return d->encodeImage( img, time ); } QString FFMpegVideoEncoder::createFile( const QString &filename, const VideoEncodingProfile *profile, const VideoFormat * videoformat, unsigned int quality, bool convert_audio, AudioPlayer *audio ) { d->m_aplayer = audio ? audio->impl() : 0; d->m_profile = profile; d->m_videoformat = videoformat; d->m_convertaudio = convert_audio; d->m_videobitrate = profile->bitratesVideo[quality] * 1000; d->m_audiobitrate = profile->bitratesAudio[quality] * 1000; if ( d->createFile( filename ) ) return QString(); return d->m_errorMsg; } void FFMpegVideoEncoderPriv::flush() { int err, got_output; AVPacket pkt; av_init_packet( &pkt ); // packet data will be allocated by the encoder - av_init_packet() does NOT do that! pkt.data = 0; pkt.size = 0; // Get the delayed frames for ( got_output = 1; got_output; ) { if ( (err = avcodec_encode_audio2( audioCodecCtx, &pkt, 0, &got_output )) < 0 ) return; if ( got_output ) { // Set up the packet index pkt.stream_index = audioStream->index; // Newer ffmpeg versions do it anyway, but just in case pkt.flags |= AV_PKT_FLAG_KEY; if ( audioCodecCtx->coded_frame && audioCodecCtx->coded_frame->pts != AV_NOPTS_VALUE ) pkt.pts = av_rescale_q( audioCodecCtx->coded_frame->pts, audioCodecCtx->time_base, audioStream->time_base ); // And write the file if ( (err = av_interleaved_write_frame( outputFormatCtx, &pkt )) < 0 ) { qWarning("Failed to write the audio packet: error %d", err ); return; } outputTotalSize += pkt.size; av_free_packet( &pkt ); } } } bool FFMpegVideoEncoderPriv::createFile( const QString& fileName ) { int err, size; // If we had an open video, close it. close(); av_log_set_level(AV_LOG_VERBOSE); // Find the output container format outputFormat = av_guess_format( qPrintable( m_profile->videoContainer ), qPrintable( m_profile->videoContainer ), 0 ); if ( !outputFormat ) { m_errorMsg = QString("Could not guess the output format %1") .arg( m_profile->videoContainer ); goto cleanup; } // Allocate the output context outputFormatCtx = avformat_alloc_context(); if ( !outputFormatCtx ) { m_errorMsg = "Error allocating format context"; goto cleanup; } outputFormatCtx->oformat = outputFormat; // Find the video encoder videoCodec = avcodec_find_encoder_by_name( qPrintable(m_profile->videoCodec) ); if ( !videoCodec ) { m_errorMsg = QString( "Video codec %1 is not found") .arg( m_profile->videoCodec ); goto cleanup; } // Allocate the video codec context videoCodecCtx = avcodec_alloc_context3( videoCodec ); if ( !videoCodecCtx ) { m_errorMsg = QString( "Context for video codec %1 cannot be allocated") .arg( m_profile->videoCodec ); goto cleanup; } // Set the video encoding parameters videoCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO; videoCodecCtx->width = m_videoformat->width; videoCodecCtx->height = m_videoformat->height; videoCodecCtx->sample_aspect_ratio.den = m_videoformat->sample_aspect_den; videoCodecCtx->sample_aspect_ratio.num = m_videoformat->sample_aspect_num; videoCodecCtx->time_base.num = m_videoformat->frame_rate_num; videoCodecCtx->time_base.den = m_videoformat->frame_rate_den; videoCodecCtx->gop_size = (m_videoformat->frame_rate_den / m_videoformat->frame_rate_num) / 2; // GOP size is framerate / 2 videoCodecCtx->pix_fmt = PIX_FMT_YUV420P; videoCodecCtx->bit_rate = m_videobitrate; videoCodecCtx->bit_rate_tolerance = m_videobitrate * av_q2d(videoCodecCtx->time_base); // Set up the colorspace if ( m_videoformat->colorspace == 601 ) videoCodecCtx->colorspace = ( 576 % videoCodecCtx->height ) ? AVCOL_SPC_SMPTE170M : AVCOL_SPC_BT470BG; else if ( m_videoformat->colorspace == 709 ) videoCodecCtx->colorspace = AVCOL_SPC_BT709; // Enable interlacing if needed if ( m_videoformat->flags & VIFO_INTERLACED ) videoCodecCtx->flags |= CODEC_FLAG_INTERLACED_DCT; // Enable multithreaded encoding: breaks FLV! //videoCodecCtx->thread_count = 4; // Video format-specific hacks switch ( videoCodec->id ) { case AV_CODEC_ID_H264: av_opt_set( videoCodecCtx->priv_data, "preset", "slow", 0 ); break; case AV_CODEC_ID_MPEG2VIDEO: videoCodecCtx->max_b_frames = 2; videoCodecCtx->bit_rate_tolerance = m_videobitrate * av_q2d(videoCodecCtx->time_base) * 2; break; case AV_CODEC_ID_MPEG1VIDEO: // Needed to avoid using macroblocks in which some coeffs overflow. videoCodecCtx->mb_decision = 2; break; default: break; } // If we have a global header for the format, no need to duplicate the codec info in each keyframe if ( outputFormatCtx->oformat->flags & AVFMT_GLOBALHEADER ) videoCodecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER; // Open the codec if ( ( err = avcodec_open2( videoCodecCtx, videoCodec, 0 )) < 0 ) { m_errorMsg = QString("Could not open video codec: error %1") .arg( err ); goto cleanup; } // Create the video stream, index videoStream = avformat_new_stream( outputFormatCtx, videoCodecCtx->codec ); if ( !videoStream ) { m_errorMsg = "Could not allocate video stream"; goto cleanup; } // Specify the coder for the stream videoStream->codec = videoCodecCtx; // Set the video stream timebase if not set if ( videoStream->time_base.den == 0 ) videoStream->time_base = videoCodecCtx->time_base; // Do we also have audio stream? if ( m_aplayer ) { // Are we copying the stream data? if ( !m_convertaudio ) { // Add the audio stream, index 1 audioStream = avformat_new_stream( outputFormatCtx, 0 ); if ( !audioStream ) { m_errorMsg = "Could not allocate audio stream"; goto cleanup; } AVStream * origAudioStream = m_aplayer->pFormatCtx->streams[m_aplayer->audioStream]; audioStream->time_base = origAudioStream->time_base; audioStream->disposition = origAudioStream->disposition; audioStream->pts.num = origAudioStream->pts.num; audioStream->pts.den = origAudioStream->pts.den; AVCodecContext * newCtx = audioStream->codec; // We're copying the stream memcpy( newCtx, m_aplayer->aCodecCtx, sizeof(AVCodecContext) ); if ( newCtx->block_align == 1 && newCtx->codec_id == CODEC_ID_MP3 ) newCtx->block_align= 0; if ( newCtx->codec_id == CODEC_ID_AC3 ) newCtx->block_align= 0; } else { // Find the audio codec audioCodec = avcodec_find_encoder_by_name( qPrintable( m_profile->audioCodec ) ); if ( !audioCodec ) { m_errorMsg = QString("Could not use the audio codec %1") .arg( m_profile->audioCodec ); goto cleanup; } // Hack to use the fixed AC3 codec if available if ( audioCodec->id == CODEC_ID_AC3 && avcodec_find_encoder_by_name( "ac3_fixed" ) ) audioCodec = avcodec_find_encoder_by_name( "ac3_fixed" ); // Allocate the audio context audioCodecCtx = avcodec_alloc_context3( audioCodec ); if ( !audioCodecCtx ) { m_errorMsg = QString( "Context for audio codec %1 cannot be allocated") .arg( m_profile->audioCodec ); goto cleanup; } audioCodecCtx->codec_id = audioCodec->id; audioCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO; audioCodecCtx->bit_rate = m_audiobitrate; audioCodecCtx->channels = m_profile->channels; audioCodecCtx->sample_rate = m_profile->sampleRate; audioCodecCtx->channel_layout = av_get_channel_layout( m_profile->channels == 1 ? "mono" : "stereo" ); audioCodecCtx->time_base.num = 1; audioCodecCtx->time_base.den = m_profile->sampleRate; if ( outputFormatCtx->oformat->flags & AVFMT_GLOBALHEADER ) audioCodecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER; // Since different audio codecs support different sample formats, look up which one is supported by this specific codec if ( isAudioSampleFormatSupported( audioCodec->sample_fmts, AV_SAMPLE_FMT_FLTP ) ) { qDebug("Audio format %s: using AV_SAMPLE_FMT_FLTP", qPrintable( m_profile->audioCodec ) ); audioCodecCtx->sample_fmt = AV_SAMPLE_FMT_FLTP; } else if ( isAudioSampleFormatSupported( audioCodec->sample_fmts, AV_SAMPLE_FMT_S16 ) ) { qDebug("Audio format %s: using AV_SAMPLE_FMT_S16", qPrintable( m_profile->audioCodec ) ); audioCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16; } else if ( isAudioSampleFormatSupported( audioCodec->sample_fmts, AV_SAMPLE_FMT_S16P ) ) { qDebug("Audio format %s: using AV_SAMPLE_FMT_S16P", qPrintable( m_profile->audioCodec ) ); audioCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16P; } else { QString supported; for ( const enum AVSampleFormat * fmt = audioCodec->sample_fmts; *fmt != AV_SAMPLE_FMT_NONE; fmt++ ) supported+= QString(" %1") .arg( *fmt ); m_errorMsg = QString("Could not find the sample format supported by the audio codec %1; supported: %2") . arg( m_profile->audioCodec ) .arg(supported); goto cleanup; } // Open the audio codec err = avcodec_open2( audioCodecCtx, audioCodec, 0 ); if ( err < 0 ) { m_errorMsg = QString("Could not open the audio codec: %1") . arg( err ); goto cleanup; } // Allocate the audio stream audioStream = avformat_new_stream( outputFormatCtx, audioCodec ); if ( !audioStream ) { m_errorMsg = "Could not allocate audio stream"; goto cleanup; } // Remember the codec for the stream audioStream->codec = audioCodecCtx; // Setup the audio resampler audioResampleCtx = avresample_alloc_context(); if ( !audioResampleCtx ) { m_errorMsg = QString("Could not open the audio resampler"); goto cleanup; } // Some formats (i.e. WAV) do not produce the proper channel layout if ( m_aplayer->aCodecCtx->channel_layout == 0 ) av_opt_set_int( audioResampleCtx, "in_channel_layout", av_get_channel_layout( m_profile->channels == 1 ? "mono" : "stereo" ), 0 ); else av_opt_set_int( audioResampleCtx, "in_channel_layout", m_aplayer->aCodecCtx->channel_layout, 0 ); av_opt_set_int( audioResampleCtx, "in_sample_fmt", m_aplayer->aCodecCtx->sample_fmt, 0); av_opt_set_int( audioResampleCtx, "in_sample_rate", m_aplayer->aCodecCtx->sample_rate, 0); av_opt_set_int( audioResampleCtx, "in_channels", m_aplayer->aCodecCtx->channels,0); av_opt_set_int( audioResampleCtx, "out_channel_layout", audioCodecCtx->channel_layout, 0); av_opt_set_int( audioResampleCtx, "out_sample_fmt", audioCodecCtx->sample_fmt, 0); av_opt_set_int( audioResampleCtx, "out_sample_rate", audioCodecCtx->sample_rate, 0); av_opt_set_int( audioResampleCtx, "out_channels", audioCodecCtx->channels, 0); err = avresample_open( audioResampleCtx ); if ( err < 0 ) { m_errorMsg = QString("Could not open the audio resampler: %1") . arg( err ); goto cleanup; } audioFrame = avcodec_alloc_frame(); if ( !audioFrame ) { m_errorMsg = "Could not allocate audio frame"; goto cleanup; } audioFrame->nb_samples = audioStream->codec->frame_size; audioFrame->format = audioStream->codec->sample_fmt; audioFrame->channel_layout = audioStream->codec->channel_layout; // Tthe codec gives us the frame size, in samples,we calculate the size of the samples buffer in bytes audioSampleBuffer_size = av_samples_get_buffer_size( NULL, audioCodecCtx->channels, audioCodecCtx->frame_size, audioCodecCtx->sample_fmt, 0 ); audioSampleBuffer = (uint8_t*) av_malloc( audioSampleBuffer_size ); if ( !audioSampleBuffer ) { m_errorMsg = "Could not allocate audio buffer"; goto cleanup; } // Setup the data pointers in the AVFrame if ( avcodec_fill_audio_frame( audioFrame, audioStream->codec->channels, audioStream->codec->sample_fmt, (const uint8_t*) audioSampleBuffer, audioSampleBuffer_size, 0 ) < 0 ) { m_errorMsg = "Could not set up audio frame"; goto cleanup; } if ( audioStream->codec->block_align == 1 && audioStream->codec->codec_id == CODEC_ID_MP3 ) audioStream->codec->block_align= 0; if ( audioStream->codec->codec_id == CODEC_ID_AC3 ) audioStream->codec->block_align= 0; } // Rewind the audio player m_aplayer->reset(); } // Allocate the buffer for the picture size = avpicture_get_size( videoCodecCtx->pix_fmt, videoCodecCtx->width, videoCodecCtx->height ); videoImageBuffer = new uint8_t[size]; if ( !videoImageBuffer ) { m_errorMsg = "Could not open allocate picture buffer"; goto cleanup; } // Allocate the YUV frame videoFrame = avcodec_alloc_frame(); if ( !videoFrame ) { m_errorMsg = "Could not open allocate picture frame buffer"; goto cleanup; } // Reset the PTS videoFrame->pts = 0; // Setup the planes avpicture_fill( (AVPicture *)videoFrame, videoImageBuffer,videoCodecCtx->pix_fmt, videoCodecCtx->width, videoCodecCtx->height ); // Create the file and write the header strncpy( outputFormatCtx->filename, FFMPEG_FILENAME( fileName ), sizeof(outputFormatCtx->filename) ); if ( avio_open( &outputFormatCtx->pb, FFMPEG_FILENAME( fileName ), AVIO_FLAG_WRITE) < 0 ) { m_errorMsg = "Could not create the video file"; goto cleanup; } avformat_write_header( outputFormatCtx, 0 ); /* // Dump output streams for ( unsigned int i = 0; i < outputFormatCtx->nb_streams; i++) { qDebug( "Output stream %d: %s %.02f FPS, ", i, outputFormatCtx->streams[i]->codec->codec->name, ((double) outputFormatCtx->streams[i]->codec->time_base.den / outputFormatCtx->streams[i]->codec->time_base.num) ); if ( outputFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) qDebug("width %d height %d bitrate %d\n", outputFormatCtx->streams[i]->codec->width, outputFormatCtx->streams[i]->codec->height, outputFormatCtx->streams[i]->codec->bit_rate ); if ( outputFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO ) qDebug( "channels %d sample_rate %d bitrate %d\n", outputFormatCtx->streams[i]->codec->channels, outputFormatCtx->streams[i]->codec->sample_rate, outputFormatCtx->streams[i]->codec->bit_rate ); } */ videoFrameNumber = 0; audioPTStracker = 0; outputTotalSize = 0; outputFileOpened = true; return true; cleanup: close(); return false; } int FFMpegVideoEncoderPriv::encodeImage( const QImage &img, qint64 ) { int err; AVPacket pkt; int got_packet; // If we have audio, first add all audio packets for this time if ( m_aplayer ) { while ( true ) { double audio_pts = (double) audioStream->pts.val * av_q2d( audioStream->time_base ); double video_pts = (double) videoStream->pts.val * av_q2d( videoStream->time_base ); //qDebug( "PTS check: A: %g V: %g", audio_pts, video_pts ); if ( video_pts < audio_pts ) break; AVPacket pkt; // Read a frame if ( av_read_frame( m_aplayer->pFormatCtx, &pkt ) < 0 ) return false; // Frame read failed (e.g. end of stream) if ( pkt.stream_index != m_aplayer->audioStream ) { av_free_packet( &pkt ); continue; } // Initialize the frame AVFrame srcaudio; avcodec_get_frame_defaults( &srcaudio ); // Decode the original audio into the srcaudio frame int got_audio; err = avcodec_decode_audio4( m_aplayer->aCodecCtx, &srcaudio, &got_audio, &pkt ); if ( err < 0 ) { qWarning( "Error decoding audio frame: %d", err ); return -1; } // We don't need the AV packet anymore av_free_packet( &pkt ); // Next packet if we didn't get audio if ( !got_audio ) continue; // Resample the input into the audioSampleBuffer until we proceed the whole decoded data if ( (err = avresample_convert( audioResampleCtx, NULL, 0, 0, srcaudio.data, 0, srcaudio.nb_samples )) < 0 ) { qWarning( "Error resampling decoded audio: %d", err ); return -1; } while( avresample_available( audioResampleCtx ) >= audioFrame->nb_samples ) { // Read a frame audio data from the resample fifo if ( avresample_read( audioResampleCtx, audioFrame->data, audioFrame->nb_samples ) != audioFrame->nb_samples ) { qWarning( "Error reading resampled audio: %d", err ); return -1; } // Prepare the packet av_init_packet( &pkt ); // packet data will be allocated by the encoder - av_init_packet() does NOT do that! pkt.data = 0; pkt.size = 0; // this only works for theora+vorbis // if ( videoCodec->id == AV_CODEC_ID_THEORA && audioCodec->id == AV_CODEC_ID_VORBIS ) { audioFrame->pts = av_rescale_q( audioPTStracker, (AVRational){1, audioCodecCtx->sample_rate}, audioCodecCtx->time_base); audioPTStracker += audioFrame->nb_samples; } // and encode the audio into the audiopkt if ( (err = avcodec_encode_audio2( audioCodecCtx, &pkt, audioFrame, &got_packet )) < 0 ) { qWarning("Audio encoder failed with error %d", err ); return -1; } if ( got_packet ) { // Set up the packet index pkt.stream_index = audioStream->index; // Newer ffmpeg versions do it anyway, but just in case pkt.flags |= AV_PKT_FLAG_KEY; // Rescale output packet timestamp values from codec to stream timebase pkt.pts = av_rescale_q_rnd( pkt.pts, audioCodecCtx->time_base, audioStream->time_base, (AVRounding) (AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX) ); pkt.dts = av_rescale_q_rnd( pkt.dts, audioCodecCtx->time_base, audioStream->time_base, (AVRounding) (AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX) ); pkt.duration = av_rescale_q( pkt.duration, audioCodecCtx->time_base, audioStream->time_base); // And write the file if ( (err = av_interleaved_write_frame( outputFormatCtx, &pkt )) < 0 ) { qWarning("Failed to write the audio packet: error %d", err ); return -1; } outputTotalSize += pkt.size; av_free_packet( &pkt ); } } } } // SWS conversion convertImage_sws(img); // Setup frame data videoFrame->interlaced_frame = (m_videoformat->flags & VIFO_INTERLACED) ? 1 : 0; videoFrame->pts = videoFrameNumber++; //qDebug("Video time: %g", ((double) videoFrameNumber * videoCodecCtx->time_base.num) / videoCodecCtx->time_base.den ); av_init_packet( &pkt ); // packet data will be allocated by the encoder - av_init_packet() does NOT do that! pkt.data = 0; pkt.size = 0; err = avcodec_encode_video2( videoCodecCtx, &pkt, videoFrame, &got_packet ); if ( err < 0 ) { qWarning( "Error encoding video: %d", err ); return -1; } if ( got_packet ) { // Convert the PTS from the packet base to stream base if ( pkt.pts != AV_NOPTS_VALUE ) pkt.pts = av_rescale_q( pkt.pts, videoCodecCtx->time_base, videoStream->time_base ); // Convert the DTS from the packet base to stream base if ( pkt.dts != AV_NOPTS_VALUE ) pkt.dts = av_rescale_q( pkt.dts, videoCodecCtx->time_base, videoStream->time_base ); if ( pkt.duration > 0 ) pkt.duration = av_rescale_q( pkt.duration, videoCodecCtx->time_base, videoStream->time_base ); if ( videoCodecCtx->coded_frame->key_frame ) pkt.flags |= AV_PKT_FLAG_KEY; pkt.stream_index = videoStream->index; err = av_interleaved_write_frame( outputFormatCtx, &pkt ); if ( err < 0 ) return -1; outputTotalSize += pkt.size; av_free_packet( &pkt ); } return outputTotalSize; } /** \brief Convert the QImage to the internal YUV format SWS conversion Caution: the QImage is allocated by QT without guarantee about the alignment and bytes per lines. It *should* be okay as we make sure the image is a multiple of many bytes (8 or 16)... ... however it is not guaranteed that sws_scale won't at some point require more bytes per line. We keep the custom conversion for that case. **/ bool FFMpegVideoEncoderPriv::convertImage_sws(const QImage &img) { // Check if the image matches the size if ( img.width() != (int) m_videoformat->width || img.height() != (int) m_videoformat->height ) { printf("Wrong image size!\n"); return false; } if ( img.format()!=QImage::Format_RGB32 && img.format() != QImage::Format_ARGB32 ) { printf("Wrong image format\n"); return false; } videoConvertCtx = sws_getCachedContext( videoConvertCtx, m_videoformat->width, m_videoformat->height, PIX_FMT_BGRA, m_videoformat->width, m_videoformat->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL ); if ( videoConvertCtx == NULL ) { printf("Cannot initialize the conversion context\n"); return false; } uint8_t *srcplanes[3]; srcplanes[0]=(uint8_t*)img.bits(); srcplanes[1]=0; srcplanes[2]=0; int srcstride[3]; srcstride[0]=img.bytesPerLine(); srcstride[1]=0; srcstride[2]=0; sws_scale( videoConvertCtx, srcplanes, srcstride,0, m_videoformat->height, videoFrame->data, videoFrame->linesize); return true; } karlyriceditor-1.11/src/ffmpegvideoencoder.h000066400000000000000000000045311233766701300212710ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ /************************************************************************** * This class uses ideas from QTFFmpegWrapper - QT FFmpeg Wrapper Class * * Copyright (C) 2009,2010: Daniel Roggen, droggen@gmail.com * * All rights reserved. * **************************************************************************/ #ifndef FFMPEGVIDEOENCODER_H #define FFMPEGVIDEOENCODER_H #include #include #include "videoencodingprofiles.h" class AudioPlayer; class FFMpegVideoEncoderPriv; class FFMpegVideoEncoder { public: FFMpegVideoEncoder(); virtual ~FFMpegVideoEncoder(); // Returns non-empty error message if failed QString createFile( const QString& filename, const VideoEncodingProfile * profile, const VideoFormat * videoformat, unsigned int quality, bool convert_audio, AudioPlayer * audio ); bool close(); int encodeImage( const QImage & img, qint64 time ); private: FFMpegVideoEncoderPriv * d; }; #endif // FFMPEGVIDEOENCODER_H karlyriceditor-1.11/src/gentlemessagebox.cpp000066400000000000000000000042711233766701300213260ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include #include "gentlemessagebox.h" GentleMessageBox::GentleMessageBox( QWidget * parent, QMessageBox::Icon icon, const QString & title, const QString & text ) : QDialog( parent ), Ui::GentleMessageBox() { setupUi( this ); // Copy-paste icon from original messagebox QMessageBox msgbox; msgbox.setIcon( icon ); lblIcon->setPixmap( msgbox.iconPixmap() ); setWindowTitle( title ); lblText->setText( text ); } void GentleMessageBox::warning ( QWidget * parent, const QString & setting, const QString & title, const QString & text ) { QSettings settings; QString valuename = "donotshowagain/" + setting; if ( settings.contains( valuename ) ) return; GentleMessageBox gmbox( parent, QMessageBox::Warning, title, text ); if ( gmbox.exec() == QDialog::Accepted ) { if ( gmbox.cbNotAgain->isChecked() ) settings.setValue( valuename, (int) 1 ); } } karlyriceditor-1.11/src/gentlemessagebox.h000066400000000000000000000036701233766701300207750ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef GENTLEMESSAGEBOX_H #define GENTLEMESSAGEBOX_H #include #include #include "ui_gentlemessagebox.h" // Appends a "Do not show this message again" checkbox to the message box, // and does not show it if this checkbox has been checked before. class GentleMessageBox : public QDialog, public Ui::GentleMessageBox { Q_OBJECT public: static void warning ( QWidget * parent, const QString & setting, const QString & title, const QString & text ); private: GentleMessageBox( QWidget * parent, QMessageBox::Icon icon, const QString & title, const QString & text ); }; #endif // GENTLEMESSAGEBOX_H karlyriceditor-1.11/src/gentlemessagebox.ui000066400000000000000000000052551233766701300211640ustar00rootroot00000000000000 GentleMessageBox 0 0 437 267 0 0 Dialog icon 1 1 Dialog text Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter true 9 true Do not show this message again Qt::Horizontal QDialogButtonBox::Ok buttonBox accepted() GentleMessageBox accept() 248 254 157 274 buttonBox rejected() GentleMessageBox reject() 316 260 286 274 karlyriceditor-1.11/src/images/000077500000000000000000000000001233766701300165275ustar00rootroot00000000000000karlyriceditor-1.11/src/images/application_icon.png000066400000000000000000000023001233766701300225430ustar00rootroot00000000000000PNG  IHDR>a pHYs frIDATxkr gFIwh< T,cD7r"B!D~n(GsW hg8 wOn0 L#z fJ54\glw/qH,{7g*ˆx 62\3ZO#x7)_`dPh nCCC`pvutW          aѪF'FphphpFj,g PG힉? bs2.k gjEhӋ/B_#aT@S"Ihw]g֐i:zju?e+ bܺg5Sxj踖BVy2TAF==Oz) @M0}7X4ɨ{M\4de oHA(pD}|?=#aB!B!B,<ۿ>  _ !t7,[g~=,!/8^XH]u]g]l۶})7qµH/yr%ʲ,&#B BxXUqϳmaA1A/Ɯpoœ3[h=g] ey}ߏ0iHve0\#@u[͙14S4`HQ$ٖpFeFv@a8qU3F몘IJm`YCq` ko'f$f9Yr@3:$/UbI;7-¸1OoYR''i 4U R(Pl*mWFp":LF#ӀnfJLkLtCcTe #PNTh/aJLQW갬l:tktYDԬܵ֓ČJaTtqt43d5"W^{.D fݼ=Prցhr[BzQȄOd suO&M|cRxG>!(,vVIENDB`karlyriceditor-1.11/src/images/casio.jpg000066400000000000000000000216671233766701300203430ustar00rootroot00000000000000JFIFZXC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222^" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?i^F+wOZsNk2"!nhHX!DF'U 3`f4iti ZsY1隤v܏Z6էd1ؕIv7ׁP$q#>krĿU_f*Gc934g^mCm<[6mp}|G/x=Ce$M_UhLD:(t"HQGVROw NJEb!Wy+pF?ҽmLSE?cP㸎?5:ohΑg|di"(f4C( Z(F(R~R 2$ff٨ozȮw'} TU/ X&hϘłՑ{aXI"iɭ}머j+9F^Urvhi۪ZlgKӗƖO֔cWqoգ+?8t :m{k*g4ՔD:^#?uJ'?gnlM8ԛߍ)uA&a>eKq_;˪{y3}\wF3ϩQA-}fHICʚgWؙ؞拰=otc䶝xwıbƆ0Xyn2iMd-gXZqWg$@k:'E?.Wij1#TYԊ^R+(s-?jxk*קqYWQ\#WQp^t/F۞*̈ܓ=k: 8@Hv;sV r.9W,d w+Hm"G)k 3P>p7qZHrZ{2b8Y%{ƮCs%cȾK/gKL㈟|'y í~|yr.WwG0-&B)/qs] RQ챓kJփuFv[sJۜJ{ t8uCԟZ|? ӑD~͋½.*2nXgº U2 P(FDи# 04h4$6fRf(Lah5 ͎c-"k#B5sٚ(OEڼ3HW p+]<%fbfV< Gt+b':qq%adFkjFmE:A*/RE*lr(a Ԇ@ _N>#M6X΅['/ĝ0v#֪lµ Eyܿ4w?J_#Yij|dzEf_Gi gj4S3h GzH5~@+Y"IfyYǧ#C)kƋ2 V:tKts!򳨥scPz8_0啌CBtgG+7j( 3oabLcneg" vOℨ ۲Fj_*dZZSv ;STћ^)90Ӿtuo1jd!?A]*(1staՍUHG8S*j;TA.\* XοL2Z̮O$FƙmE+gҥA^#Fʝ7lu)1rb՘7 >|,}l|[Ovp+>:ëz}ms7VeE-UzjǷ< 4QT!sE% - T-;sHXmq~cN[՞yzUC,GU=+%V5>·o}5~'˶EL xҜ1[HmYˉ@mGWYo iCWTfA- :X!xZPCG m>] ҶR!ŋCZBDǧĽ#QwB(袥[QVޑPOu9 ڭ*Y1}C?f+ohq'SOUܟƋٝ&(sgS0΍#G5O;ue_EY6(k[Qtg[f/#* ٕ` "hb`sGZc\O}Mok6m噁 > gxUk*oCjib vkbCmA*V;sTBɏ/IO  [sҴlty#,<](k(܄Ɓ xС[$Aί$]MEڿҤ aSSq+ RUbns-RrVfX+jpjvAvgG} 6v1jzF eǤB‹!]@v袝FF)).;4&he8}9< U;sګspkPbC6x>@O)+3:y\MvJ랻[agɏeS +ܨVmS>*x{q^X0e9jpձZ>]n'C<_{P1R1b?L?@GL؃#bzWJc'R篟AeJ(i~Hy epEFŻS\opWgv?Bُy)$Bh?w*nO&I']|T3nxzOm\s^>FO2yDhԛi\kV}n0|)k!c qm[1Зo>jӫ>7GjX:!8I%s,;YUwE 3e-9) W6;ڲCW'8L}*֙6,&)uhy?/Q6ɥ?D5 ^ikr^DuCsVp15-pe$t`~IrzUj:孫$nYz1a8RO/i-;tR.1R4=Vᾗ"%-rx[]dV.YU0|< 37fTiU9'2T)|+ 8m4WMr>pjB J?v}p}s*)\ҕRiL@El?Ѩ|2?yp?hjեYӂhGs#5rºsms[zUqM:lSFU4F~+^ tL (% o*x[IAVh=*=+e#'<7)1SP߱[1RǥW1<*CC*UmKxVScҟ0 شEhncTTR** R(֘ Z(O75^ySTi埥p鉏G^Qey78}Lv?ƽp?\؉[Ҏ0Ąf|NO>?-Z V8".Q)oMͪV1#0+vV$byeSMa#1<}-@)FlMuƕ3YSR)&3?d?ƞ'q3ֺpia\SA0`[ĪFyFZ{.r%ek  LjŒJ<-\~+&v α^'?HSubs U4 3FM "ڟ ׿,>yiXsVR&$\!Nw}++Pn_1{J5Y.& 3W@|c銷4>!"(57zvHj)W@Ջ9* QK( 3\0=EeJ12b  s9Xqm855 aStMr~S+Et ED(27$aYH8 oa=b ͏m*/B~cWanf i_šKu?VnD8$y)FF&PI89[_h:z\{tx?A{CÄ/B Az^>x}xƯZ|=0`wsIaY'Gش kR9x?jsҤ Ůqu`9lSCxOˤX M.>_ ey]8JBsR4T*srs\74Nƞ!5vYr+D&LI5pn.TQ\ը6הl[֜ȹ5/rx-Ҵێ+Fx}@ž$Nu-9&{k'O|*|@J.bd4yؾׅ'ď)3ldEꐟz2yOr8F|D-ԉo^+Kco΅{*eQ[xΟݷA"M63?9ɥ̘l{.\ K4 E 4.h|wýg^g5-ݴg֪Oe.]lv6':MɦI5@f3_FA9'[;kŜ 5r?푦6mfX*l;ADPcY9絹_  s4 ǫ3l:R$,E)/s@TҾ#Jn̉kz/晨nZRZZڶR5?GLa_&~$hh@V ? }xV:,i}!{@=(1PJOPj'䙢ctĶvMyԀ\zWJ12W5hMʍ66,қ<o Pk8FE$qZpڌ+1|ح{-jڊ(j%q:Ɲ'ݼ$z;W~+u#1fG?*Bz2u*gR!Xf[2;SLSgVeN=bnG?mʈFx^\RzQйx '3P?nN?H} -ZWELبɽ? miX+W&F8Tx.(R-Ґtw(Ҋ4Ÿ~n6:pzJ7gqH艇-THY +]:׈^5Hܳ?Wh^MZƴw2ъ8<@+qi'Eh r+MSFXg(b+FW_|t[s\FT~$vv?`a<_i: mX@Q[3 {X[ꂭ6F4'MFp*z&6_[7AM}JX[š}* If:?:ohm?r)ݔvGEuxxIV|Ԛ_G8k0 +Ah/1Y4rSBcފ7ym4Q̅ce}dj7**9q!T'ְh; }Mc`+s_[q$nt=ˍG[U"[kkp), 3C.*EjW MJ&8kʌ:K-䐊qs.=1H;CE2Z5=f^pը](˪HuhϡJWpz`I?Zhpj~/ݮ<66vB+FĺdVafz4<ןMcR}Z|MmxȥN³:KTI. Zv(ƍ֙A4X4Tkarlyriceditor-1.11/src/images/clear.png000066400000000000000000000014421233766701300203240ustar00rootroot00000000000000PNG  IHDRabKGD pHYsu85tIME5$AFIDAT8˕YHTaΜљtt\,Kh, $#"  ""(. ҋS.$,K1YYUQ}/0y|GMnXLfTz*t[ti =^&0B|ӿui~jni:-A,/H<\ƝGtʂGvVeS`#QzvrXǓ]:X5 hաH[50`ʻ-S3M쫪*DH@覯|SwRi*tfϘ[g&fǵ5M작[cx @PC_ !]l>G"A-tU FvCQ(I G7yrQ0 ;&Ƣ$\2g 074D n@ ٬6Olݑ锯$:j*q5vengn9=#~Mp\*TŲUlc)dm(2$(t@@r$B/D=hrSkVb0n)In覚 $Z \&BG  hFze4 ښ L |R aX2F; 34Oy'Ik7m1>_B7fpS8"0|9#1uHmS0!tÀ ( P:i7kpqxaGRBnmׄ0~dwo;v=ܳLYFa#4YCYSIa)! 0D"OaMB!Lc/>uJbavn&0F1i7*-Nىe {R}6qo:Jg N] Nykҥ9T9O&#TN޾V/;\_#G>7x(o=]xh"ABsIENDB`karlyriceditor-1.11/src/images/dryicons_application_accept.png000066400000000000000000000043231233766701300247730ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs B4tEXtSoftwareAdobe FireworksONQIDATXŗY^Uk}7{ҹ!h0 A "A%FIHEH^D<@ Xi@#C;}ΰ|5@=䜳*Vv㚞ZG gAf'V/LAY2 ",ɼ2'f{7v|>kBJKNWV DVֈۮ[  p?X{ ATi#(YNI2:E0vM'}yT,97/Ⱦ'Tx˰E @]"$I(AR)PB3'@=L8L|F4QVIӔ4(gM ʹ85)b(E`PY SN-H)Hߛ^ge{ ,,ExQ0EZ%{̂|X7C0/Zom??KZSN?_ WQs9r0t3#*9*PRU[6N /!I=>dr/^{3֋iK:Wyuk}ٹ#> iZĘRAŜ+1oYOu3UM`aվƕ~rxv#ljD\=8r5߻Iz&AZ]W[XԢ>2# x1pgU" ^&?T 'dyY;0>R˯c`lq|Z8+̝G{" ?BMG\0Ɛnȓ&IQ'#|JeIF,X?-.Rtgi}}xjTgNlRUTS,˨V* ;?{ͻ_e鼙}g+{u}t9ÍQ~p؉AN[;G/ZZs |=߉"b۴[-ƭæZ攱F Y8..&reygYzElLw9So[voqw3N'4)bc}Ķ[kX٫)C/Y #^7gժ/]:0 Ei-6nx~Te}U迷IENDB`karlyriceditor-1.11/src/images/dryicons_application_add.png000066400000000000000000000030321233766701300242600ustar00rootroot00000000000000PNG  IHDR szz pHYs   cHRMz%u0`:o_FIDATxė_lWsٵډơI(V-i)j+h}@Px3H J((ԥDI*vu;v׻3sYIA2#Ν;s~ty``l%p+VFn[~*;\ r`uo/t[5V2 /TPRWI[ ! 8ɖ? tE #܄ p!zfy׉o:)rv oϵymk+&*"zq[sóZFe79Ix8Te6)Dm눣aA6zZUT=|}?9A1Q(RM |ͳDAJ"aM/<>y'FAZCd̈́:lxfd>盯$gg#x\V ?;yrhbШU@շ3ez ǹ{hV?08ko kp/]=? U'`7!B 94Oz{etaftFk1qY m'WGЧiD(hԶ4 ̟'f[!kUdC##S yqr}ÏUm WАCt"QrKݚo^;v/pη&ϧI6+iRmA`B~ η߮~9.ods"w0(z0E{VpXMKU;RȞ F(cݟ) Rg5b9]{"@{. _TE038b#m~s]Ir[b %HC}?y N]^&|ƾr1d:KY=zAf[dz~?**1sKӼ8S̠('p(YX%2w~B5Jy[;eӇTܕ^|| ]1}cfOT*ɝ K$-W &3g-1ڽgWbcYA`^^YNĝC{{`gvd2Wx@ g6]i*qIENDB`karlyriceditor-1.11/src/images/dryicons_application_deny.png000066400000000000000000000041571233766701300245000ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs B4tEXtSoftwareAdobe FireworksONIDATXŗ]Uk=sΙslr[Z `JШ$Qc"~+&V}0'#1F#D|?hAx@DSliPr܏s=3>̹A ĕLɜ:cixlEޞbĀ3 @=N-tՕt>A#o@2D.h[su|7fS.Fc5fcd5tsajl|n2ٳ뽤Vg|ͦl* >HA$N8Myhkꦋ6N|c8uB@ǡ X@иNu;#FzjGO??SM*}Y*W4ĊF|9<ס  X ,-/#b@B#D!ISKSccx39Յ^|/3 q}9 c,E^`P%FzB8(ġi+YH!ƈ1U|8S1ʱ.xX'洀 cAXب8U\(D@Aњe~b"U1"xH+KiƬ)eR.ahju Cȭ#;sXgIU%@ek8GX@oTLie֢w#n}A% VQҵsn!hѨ1Z1LLLjQH*T!!ت^{ 8 $"D/UѨe˖uX1]!PHP{OaeEA+&S,ά+.ZP%OˑFIWc(;x=GH#QakX:yIsF*XђHdyiA CA%뱰H֭C{vҀ8Z?,Hk 12UOq)e ,憕ʏ%*IjLMo`=q*^NC o\ݻ=q /kL!LFN"T!0, $#GS"$1FFZϷַ=~z}chhvͱ1KH'ac@ JF .ws 6@T{T fbBbY3 J k["#iƁ(?E͌%ezeV;:+.jrc|+G6>/QUobb(@"8?482*>2*6!|x)U!yS"$Y1kxl󅄅XzTf}Ȉ0bu] F#4(JQU  k,{sd&Ƙ\ty\/röۄVmUo g5ʯKGu.\؀l56F̙Cj4j(hd |'8ٴqrS jNE3V ̹m'YY-k5heufv"1`PDX+ïts >q^_RxOJ|붖e<_s]-,IENDB`karlyriceditor-1.11/src/images/dryicons_application_edit.png000066400000000000000000000023441233766701300244620ustar00rootroot00000000000000PNG  IHDR szz pHYs   cHRMz%u0`:o_FjIDATxėoUE?ߜsno(@iG b 1DBD]7H 10.;4B4ԅh$ %ZL By)r[z93.`rd6g|1?#j !bz`ψ" @eJ#.qBhYhWl`{=o)b m$c-T'-?`t E{O'xA(pjr~ƩwS)[VeaƘ`4A y/Ru655EiN( UFN"ʂ٬Yƙ?eI{t ٔRz:P43:0Pg &ovT)\|sRkќLSXr^ +f{B 2_ϱjk\UG^ڒ4jm 0B%Dܲ5kС L@sdi,KG>YJeR}, u508h< @aV{[mb#],ߵ2AL;S6q2 z,DG+6q-f|3tjY TmS M =ys  9 _!׆M˿1br t ODts°΂LB.k>6<;߆(ej-׭%A}-0kl]0xϭ/0׆p.xq64l r_>5J8q߽p⟃D.MBIrB K_"Cvߏ$bK AyLD"͚ 6-* M\l7f_333s0}I2a=prˣFMS \h??rOskG~6&Pc9 !k-P7j#Z ]#"`#hMP ɚ?8k-)e@ Z Bg4g(b)[nZ1pTƉBaH$E[Ҁ O@3O|ã pJc!6ybFkɲ=0}D&έo1&#;~;˝L<3=ooci|PRc9Gg?#_.3e+o$@H8i6R^d?F(k1F N/BV +Sܲ90aH ]m8C!/e~rn&1^7  vXʡgɤRxZu ZyLF!~nH;Z0 |ß1}  r1zV|5~cib_k1,&u5޳YY x+bܛθ]A_h6"*OiH3:'vG_v>W,`<}Q땆":9l $+VRb1Kj^ׂJt]Jߪ;dsx!le>C;&N[!4&dxS+x4%G%aJ 4׽I0@.U]W$HtH[;B}4c"JS]T Xg9ߣn貯T |j?}!IpVrbdF 8'`Ӥ?;l*t]m 7> ~D3r_%BkgrvQ14ɣGБ%w;F˕]2όS>J Q3Ǒ}1Yw{:|0 9#W2J88IړutMͅ-p~vglѐhW4?'>Mۃ4B{jDH$o >ztĿD1(g/Ey\綔˥~ՃJeia%gf#Y R hS8$DIENDB`karlyriceditor-1.11/src/images/dryicons_application_search.png000066400000000000000000000041201233766701300247740ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs B4tEXtSoftwareAdobe FireworksONIDATXŗ[]UͭBA#- ><1D(oI`bcULj02Xmә̙sΜZa9TVYgo;JDx3ѿ!10Z#"$v3x҆ chC(abɷ>&>plل9{a`1,)F햧to@0d-"XIؔX] |C7ry~;o o>E3u+ .vM"Te E`NM!E&wi'Os=siyι7nRQ30BLۧ Z+`=dq &ѽ%.Hszk4xRk: c(%B3s"Ee VFU.g~P‡@+wek^5(=!T5Fs")]aIǐUBӍr%x(?bzv]ʢ 5yGDX[97~W+#K3jH(sf54)cD]^"Jiʲl4ٱ}|~4~4-$4Ə=~;iQaLU4tĦl*il̴=yޠj1YFajr]wc{#0n<@X[j,ƠD>K+\Y\Yee~WΜr, UEtm&''QCc/w}`E}TUE62Ah~}3b+# 1qh6#!Znɼh9Yx󜉉 ,g;z9<(̡=w6c Y5dY2t:N#)ɘE{Lff֭=[8xMną">g1U%;wdޒ;Gy$$4BУԕc%WHND͛9z/8ӕzٓܰ͘uďrU z.\Y0ֲx)-ԏ~m'g}X[BJǔ;%KQQa}pǙ''G/~eۄo7cMV qrIENDB`karlyriceditor-1.11/src/images/dryicons_cd.png000066400000000000000000000041001233766701300215300ustar00rootroot00000000000000PNG  IHDR szz pHYs   cHRMz%u0`:o_FIDATxڜ;W;W0أ :A4 !!DA!$ hीQ) BHB@ IPd3{w;8ߝ9g˯6UCU03R)"?7x <,#(YU8U*WjgNrJ|6CuϩNDD@Oȣ"(" **ιvmmc3څҳj:|8"CTPT*G?޶͗Uc[[[O.߈n< oa":Tu,RU5u]Ӷkٜ]رcO\~l߉:Wi_R*ꦦiR10N,V|AQ\ܼG} R2][~-z |zGŎKb>HΙt")gRtR'o'0Fw=pt4\#\w ݨ#b:B +B)s{n2<&"C Q=/ބNTyUn;Ջ4 MOt]b x4rLĔ 92N?[r~XDP.⾸OMWZ;Z7gV]gY_EEqZ!"W0DX.W>?iűٿ N{L+XެI$zՌssnm܏hD]xٹbDE>B cά l\]RCϟo>u쉐9N i$I~z3^|zt>O-bI9b_|@`` SH5gN|\"!U'f4cPq=qe9a-I!@1 +0pN>.M:@Owߋ !D++SM]_Zvoo^Z KbHYR9cfX1Y.ޒVԽ!Rp0hNP/&Ttݧb\S㝑bRef3rwdefϹz*{̌ `F)tP~9sE=f3PQAThcU=|ED1,"oyzN׺:eYsPp8'C 4ow;.VF{FfB8)" |>߉D"KH&S1uꡪ4-6*vmۿUk/ 0޸+ǏgsY,xF:jNF@<,b( R*X^^ y>fELz =9zp!md}y̻|ǩJ%.]>rX,]:=jfo>RWgf"##d21  I&pB˗/L&98:J0dvnvr}m=U}RD{8988r~>% @Uw֊coܻ;ɐN|/^>bw7DoѱQ$/NE9|-ɷcqaqٍCq(1CCO$yF=}&|388,@ѱ1Tu&^Uetl@ϧ~ʾa2X>UUM`*LfrC\f|Ab|~*v\6G4ͨ~˲^ XXX=~ݎFH__d˲^PUHxFx<4q`POy0Ms_(BUD$b݈gYRz#= ee&G8g>x~xVL;FH"廩:Nw$ &'*7V``o#<XU:ӴgާyH(oVa&&&`vvv;cywl`Zh4grگFl@)O6+Ki󷘉᯾Lq,Ujy5N;NC8CF$ԩ.?dπ>/ h@ݩjlӆ WUC˲K6WD!"muOy"|1"]IRm66*׋t:"6{r}mP D" U eBR)ɝXDv"tQ,ob'ϳ뺯}Dd)h3suV#jX]]U+܇;VWWiZ eEVqꖑlkԵk>r NF64Mpg fff"8,/-aYCCضիWY[]s*~k~~q':D*nQ.UC! ~˲M6f&i?2-++Q(mr UOU_ORGFF f . Z6Je˘$ D@O(ԃi8++,,,Q rd%yv}}jz"2 }$pH$N'jTu٨TmB]=X0M u]ZZZ]]}.bD" ifSs֪ԝ'N-9imͩܫ=7M&{vɞwUIENDB`karlyriceditor-1.11/src/images/dryicons_clock_add.png000066400000000000000000000050011233766701300230460ustar00rootroot00000000000000PNG  IHDR szz pHYs   cHRMz%u0`:o_F IDATxڌkyיًٙw{}`b6D!QP[FćDU*J@$bP.R 8c/xgvg潞K>찾PHG}}<ֲzDQD}yNۓ4ZSs,(B6c ,~hPu=1dYF\X,^O| RՎpgzy$>b(YY)yp14MpN]n}(h/ND' `pdgNlZB@l!MwgJe\.w(dzQS:EeF0DXp $1 _Oyr dIOi~[\ۺz=^T\Oc?1\Pp)^(EQ| ڎ+VkVKUb]X~0E)'ff?9Y!(baaa'`z(ST֬F*Zq#%R^}rk)Om#Ffwq<4Y02J/L͗B@L24%hiYN9G2}V.".2)+%NM#$Idyf ?hbAkJ~4%nDExyڭ\enS F9`V@J1k,J+RkB!A[&7zkrxOJ)IҔN*5Fkc1E1˘.A$׭Ǔy+ c-ƕX6-5.Q?FƴZ815_B7h/@Auۉ"Ϝ+n$A\\kusq&H^݇>?Jdc4f"|ױXE]^%~3pjo̰oo*N`F' Ax/ ]a"p",dk=%[cb4`qS}e/_d7GtskC:Ξ䞧z.REe koqnCB,\1o?d2yBad ĭVqkC,g#%Jk2R`:ffcZ^xy~v?^8"Bjx ׿"(OJW_Ln{ԯD.~K'gp=7cLê#9 ɡ&x#kn4[Jhcȳ 8$II}1nq;+?98C艷˜31@qh{cGǺii,^zL;?/^y¡H7_twAeCZLSZE6нX3zы:.ݷ> ]UJRay(PZn <jQ uګµgC`-  o~pk,^቉~)UkD,M\p]g8z]_`nJknOu"N!h[,{*,t1ETcNc-[nޠu5vḸ7~7An#Tizptt }1B"MY܁ׁgqDu- L;MDVJ(wJUK7h066y~?)d_ojIENDB`karlyriceditor-1.11/src/images/dryicons_clock_remove.png000066400000000000000000000050671233766701300236270ustar00rootroot00000000000000PNG  IHDR szz pHYs   cHRMz%u0`:o_F IDATxڌُ\uUw}ʬ9Np(I30wQ?@1/I I,@ٖh,YmD)Ҕ%S܆aݫ*3Ӝ!\tu~WN c kZ-\EJI$ap_E5l3;U˲@Rug3l6m;hbH.['>yH)YZjjLv:ل8" L<Q(AB8*K?V*՗DQt:`H~ﵛDΣR%G GF-..4X^glP[-WFq9BaYN{gb̳9)'=Fp3K@! Aud0Ð楋4sz}ku.J݌'/Zk][ܷ}?74Nw 8&I䆥ع .>RuO^y˲n~L+.o '写٫_K8ﳰ9MIJˑVfnIwӴI'%fwt(ZAH}$Y۽7DP !qD߽D^M;b2IqQnU~z"bݲ !B RR{empRANb;t(0md0ƠF)j~("Ne3 JQ'}$[^;fn4JelUk\ DkцT4E/Xj;}SLf+o tJ)0 IVVZbA@.Mq{z&wPaHnhGJZ$n71N,A`PJqu:W\a~n\)3j$irıGh|ƱmFyt*ss>m[¬1XiMf^ޚځ| O/\1ϟi>k7f@ SD&clx >q#`*X{fLO_{/~cGSv\#x,ˢ\ۇx eu7I )hlεE-G0+݌ oÛ;zj8- xV_S~^?m MvB^UIRj1[0FS}B? [~uYjy3MYaZMNtFo6Q|( 9zHWmSټe+It^}?p]/Z_6"_kdŁAֈ}Zk<ףÓ;v޻2==MPqfff0F3<<! .^_':Ql.Ҕ;t6N~DI-HI/˯IS*fZ9q!~ۺ3B ,e*RL׶.-ɗJ̟~4MIB)Қ$ihmkVDQѱu"=r[r4!Zk&I^?w>3n$3镭$ Bx~FFG_R سw/?{A֚4I) x/j}mo}şJ^)ItY4]V$MIㄡa!c\_CCݜ3+U?C(bl|JzRjZf=\>|d3HsqlF+)UjG&6nhZ\8o}hygopt:?w$I<7=B&q#/Fc_'u:%jx+eXiV$ߞ6q=~-'͢" s9um(bԱnqNmgsRwN`~1<;0t*߹Yo:9ܕlI;$&ݼLg`pöB>A>Xm5i::i&(%f)!HӔ( t6Lziֳqkvk<"dN.?ό^Zqo2:>dyIENDB`karlyriceditor-1.11/src/images/dryicons_computer.png000066400000000000000000000043421233766701300230100ustar00rootroot00000000000000PNG  IHDR szz pHYs   cHRMz%u0`:o_FhIDATxtϯ]Wu?k}9g9q S*U#TUVJU)0vYJ B0蠐DP64I%vb\;{{:8eK[:gYwmo,Ud&```$bTcyMsnƜRDtԤ":??7ED1ͧbYyƍ/v1]_!VXdADPMBR\AfIhJiRbmNI4 Frڮ{ )W}/o M " 晧,yQG8p(,Q7Rٍ&\3v?I,!B8 h^Yz6`[81y&`(:Gˮl_DwtfT" L33#x pKrECRQYY$9s M@ܡx0nvn&# D1@E"&BchD@ &- S  *ʉ:JN\("0BUd\H*I$ jtY?"Nx(cQs&)G:ô\i$"L:DPUIDfTTP" !BJJ;S}C>MAJJJVvHHe$sTt+^%ٹ -&gx4pnW'}ܲm_$FhE! Ń`rgSOƃɸ;VnT8 H08Tʼn ޒ?p'DUnw~K苳)fl諱dǩ҅vQ>CԀYhR$+y0Ѵ Y`\a@(?ئfNDL;%z24jsHR$'$͵a c5 S=0&l TF# pJ5"L82IG85%<)H Xh #Qܩ;1 6kԌZq1Dl4IiS’b:OQa,F.6vmE8S<<Yoky@@"0J)g-QA5ȊD̅HDUB@!"8`>o\"="Xy61k"gE*G*4aHNRu_f{e뒺ИKv }zW! QH Lؘ`6/{RMZ)2NPYq0dDLys Ÿ*řBDΖ/̩J^fьjKJJ5PFUo9P8 LS= sTGa<4Ma4NA78RJiL wg,T'D *2۵iTQL .% Yc,[n]wJ7 l~07,qsNA*8Qkp1G8 [o5ݾ}{K)_u-Mp-qܶ:W\j;o[ݺh'OO(V7n ;puV^ׂv-m۱h|@J LX׌0X5D{k׮}W۷Kqy!Hy.F)'aV֛5ANA'3O۶;ί~ m۝p\* SFN{=mۭ+o0Cϋ/CrnkwC4֗x⟮^7<5~3T .P͸r 7oޤ?Vyp>oՊ~a^^[7of960vGO?r^}啯Ǟis7 C Rs{a{I)7|KwW~+>Fs<ߐ 403ڮ㉮wټk?wK"o9g߿~~ySGGGt9մ(ǫ/ݽ{ݽM"Hcf/]IENDB`karlyriceditor-1.11/src/images/dryicons_forward.png000066400000000000000000000107121233766701300226140ustar00rootroot00000000000000PNG  IHDR00W pHYs   cHRMz%u0`:o_FPIDATxڬI\gpn)c;qg/\R(9Gb-XS-~Vۿم~v[Kι;?T [xocz;[6o~Rʩ7/sUP"JqLAE)@xZ$a8L Cf7=X\X/|8)RnZOl޲e6 CV:ʩ7BPVʔh;=8{cu{t:=<;oţG0Uƥ//..>;]ŀ!7t-M]ǿ-˫kU*r $ s߈,sZk(^ҩY]p3\>q뭷 )z}~Qܱy`ϦM2;U)_O'z=S*Z0ԀDTh)B oUR{w8 F5V|쑇ؿw7^]~Q[ʓ 2i?sxJYEsҚU~cT+ev2;;{p0|=>it), M>[*hw<!М٘" @OXk ֚ 0- )Zi?hZ|V)d@,ཏyOtz}Z zf)M?A&`x!.=VlL)uy_{=rMh__^^>,1'Z{>Шi6jNkBce,+0I]&+=J :9\/rۭq{:Ǎ1BZJ{( iԫQT6%DJDBL48֥<*m3EHVPFFb,/8ιYM>)JS}B]jz5-RJeVV0 R.F!ZkcsaHoJ)t X*=Z)QVXZms(hܧ: KOU*NDT4J-dϿ&eOt: QJDk.]^xvgnv@+D:9APR2@blƉzAR %%պL#B40DIAbӂ'%R)0"/q3xxI CQ\tcR $%u)@OLRJA9*1#8RJ- ̔nI&!xKu!C{{a)05Q0)X0Ɛ$՜XkYZk xkgۺ$$rerQ6ƴPR]Ȣ!OLZ\ԪejBoTYpp4Qq8~8kNʉ,1Xc>x;oXZjAV4xX`ik?cE*IhAf+p_J=X mH2 D\:6T*Ⱦ#d0L2Q^(% 3z44ERD?:G$IVT Ř{|ln8.q`Xh`lqC|=H IlZrpXXQ)E`4$Imh42Qzµ80d'<UPطw/Qn߽, ]Pp8I8V`@0gܚ-`A yՔtcv־j*Z)Ġf螄@x`a0{9A} P G#D-r9J{?90oF~{!+dc42$A+I^{G={U !F~ιʥzZT%Ej ?ш1Z(8>j*$Xci!xFaǭ8tܽ.ֺ#<Ӎad 8~ ĉ'PRٶm۳JN@)4gK%YPU+ec9Q҄G q {;h9 Cc`5Ξ=~sR8;n_hMשb$Fh֊p1%sXgSZfxcֻpYzΏcb640ưX1VQJN%%sg,>Vqra͹qM66Ƥe;dFֵbenFm:7F)V+!hÕ?llNjFEasYnNYcɷ-u82)F,$Ih5jll60ư$I~SO啕/]v)?ϕas1TRL@W`w <}2:1s,-.0[NNM5FqvGOy^X6!GT%j28*ة )+Y"Zk.{;WwCešT?eF `+O@dPA iscC޼Ic%0뾑P@Rl+'9:9Us{pn88R"'KgL_6W[I]ϔN`9ӅW{,݆3CwTYlrff4hœ̉ę[68#SP0FȬ=ڱU#8kYj#ABf]agx kA|8֑fvo$"=a=0lRùR)x/m*wEU+TwoV_Mٍki7{;'KP d2s;{^$秞O A UgTglZۃwX]iUB졳xxrǧ>of h566_{[Uն. @ֱ21ZE7on<"2q480b;G4@߉Bãë/I%]pQԳ1q묭=oXJҏ@uuNvEU׏b`9 1hڨ5+Cfh2_T zsє[OKƢ] 1ECVD].jrM&0MhU `K0XʒY8)}HPz*B}8v+PֺU[ Cݴ[F0ՏQ6Rپ@aJ{A( 0~G$S)\PUtV ?1@zHP^'gPp]P$03A/G۟g!㻣H2y@Θ\ҳK> pl7_2[q@e 6@CLВAog0 dA0]̐vQ.T|ʃa3p!؆L7%C?dBNh{͘τh g+1D1s2 !C–H`F_R0ŀj]RlbHZ)]IENDB`karlyriceditor-1.11/src/images/dryicons_open_folder.png000066400000000000000000000023751233766701300234520ustar00rootroot00000000000000PNG  IHDR szz pHYs   cHRMz%u0`:o_FIDATxˋUt}͝H&&j"Ĉ"j@ D;7.+ܸPʅ >+] I.ILH̝ɼOS=69P>W_UuU~.}^՛œGlDy{">A-~p /dᨚ8QUPQ1t J^z1fC􃇟8VͰNso\yWixWUQÓ"bDUFQA%bi! Ht&[mT*ƈ4x֞ }E' h"`& iPxu0%ؽg`u]@ {/='[_$]_YUۻ %]9>;D6)RLcb XcɜT#IWnG,Ts-:R]y*P)(43.o߆ R`'tXKٰF$Al%!#BXwI,Q 焎[ȹr=QR֖Κa>kPaARUBPQ9t^3/|ff f$accLF6b6v'Lw Mњݟ;5E |ḭ_vc3=ZI3TrStheD KbΣw~Չ_e_: &"*͖`S,r5P2`HfƏ#|Nk׍^ ܏N{IENDB`karlyriceditor-1.11/src/images/dryicons_page_search.png000066400000000000000000000040531233766701300234120ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs B4tEXtSoftwareAdobe FireworksONIDATXۏ$U?眺t];gEX/((^hgDH <Y(<r5 ,찻Efoӗ:>t ;c%>U>}%1<H!R!B Dq[3 X__'c\0BxZJuqX1R %%0URbdyX˶1 8Wn!m1cPJaY} }.tJY=8$W_%b>-ʲlys, Ð(Ų,AZ@UQCRxwEt:Ų,\wޡjQy#doom;Yѵ5Xc䶍 CvwwQJ!0PBʲDAt:@Lx<Ʈ*q-,uu*n1$KS84 tI)RH|c25>>vkkloosE^`_1B硔6(OO|ǹ)ǩcreڋ }(fL ۶:4Z-_}|N?NYB{ѵ5m}2m#ӹ cH^]ՊQȲ$I3gF#ƓEQei""J*}<@}eun]<#g]Q90 z){hW3KRnLZB1KQh ۶)۶L&pRJqh("MfiօShqS۰TPJa q'fzadYFQضëpg:x? >0o:N&mw4!z RCןzv˲0Ua/~3oI: >0PR$Im8f X=:4 ʲX_k|>/י3P EU;Ux$)վeeYaıc[߹[!q{\c=zB1Ay~@W˕e텅/{ve=rlr-4M>u{G?~xF=Q,DIENDB`karlyriceditor-1.11/src/images/dryicons_pause.png000066400000000000000000000103121233766701300222610ustar00rootroot00000000000000PNG  IHDR00W pHYs   cHRMz%u0`:o_FPIDATxڬZK$W=WWuWuv<=Flذ !Ă% $Xa@c!!3`{F=6a n뗕޽x/",R UdVdĽs݋/KD)% )%ւD*0 BWJ3dum!W`znx/f3kqh#IJD#UEJJY=aEYf<j:>3y/p"ZgYvsD$%&Ya k65x}cOTQ:PJ}l~iC)͝.\6vF(ֺeWR" z*X_RVCooo߿O{{{$_[';qZ )eᷗBk "kg `Oh(>JkaȒ8>|/>p]B`2͛|ƍE "nGGdeWϾg_x\Ј"4Rqz'eYb6+OLgއ}`ksW^}qw03B)%O>rd5 C FWϾ)4A&H:АUaf`2boc4`|wᑇ@aooW._(8B:t/9SJܛ'.Z f,i@+ @ԎWQ?as5(B+0jN7k:k[8vت7o>BDoNjZWVV:9_<,3 faHH錖B@H[`guL ڱ(1p)>|C޲*?^9|xM)~ZcDA))R:EH f䜱B)( !v{AX___3xggA3#˲>|AߞFuheEʋ^{OD`L  dzWϞF&s}NŠK)emHZ^Yrp4ƿ?Xu 4xʟ+w݁k@Ck , :m{]!o~_VJ}jg0stt}:-yu' xAsx]Ş=A;W]J3_g~++9%`fA~1)%] ji" RB9 ,a0EMJN D/ošC>6>gg!n6_M ^?#BtZ( 9WAJ05"f3׫΃,AD 4DDi,^~,ass[[["4Rڝ}By*wheH)JI+=؅uIfRywo/\55P")t/bT]3lh4I- Gcdi4ML&JH;6XuRhRsBbb6* -R JgR ,á^R\܆T i)Wk?ERV.31ԁZk04dҔc )dA[GfwR(L"i4߱FhGu' cO& (Ӥ鬼ZגٓغTh]-=/,lgSR q# ic4E0<)ǕV0X8 %Qkxu89< k-59F0kA57 1uPeM+3HDQ' !ku JZ(`+GcE:XJii\1:W1" (@k4TEv=!l R&P볇/Ƅ@60LOg(JS  X ֔ Ŝ Z)! IBv)(K LQ.f)%Fɬ9" Xr8/+S"5n!1 }Z8d 90p)vQ "t[MDA<Z &v7N'Z떾Z9/,A(bQoQV*֋\cn$@ yi\q8^>d:1xkJ0 `$4Y@+ cLZ[jnaM cPRIƬ(A Hf01Jk嫔dXƘ#6YH[z㠸! X~rL(Z}ӽ( ljeQgfڈEХ,y(MB@R9@{Q9.!/.YbXKHb6CYgEQ(t+Kqckk-H+cL.@A+4(JK4137M:Z#+h%mRn TM)aD( 4JLu)(&oI#B+sҵJsA4Xue7֢$ Kd-WLŧD+K&)< B3Id:<)V] F B,1X0Xx=uFD#"WCr"ʡ.`d!p8;=_J~FYQBcf HD kGo)HKDa, KzTw6+<|*G|O~ 6b,u0`0d=-tS !םN1/B+G>v: H: B1+|C"41h|&TK=폾O,߷ )!FMD|+gpFDQs’'U᠚؋GjuѭK_Yw8ߗeWb`|<__E(˲vz'2,> v!Epa01- 1[.NMY>Cc (]`vxyG8i qeQ=xHZIӯ48ZhjUƒU%3V7iDxa1gXkM%h$ ͭ~+˫cr`wy碐ׯ9wVqq;\XXx~w <{VNj@"e&!R$eckn)zQ;t`7߼|8}nUt\صkRW_g9RkfuR )3EHiiûLk-J)0ws/ w:c̓B{O$_ڵk׻ $ 3MIR)2UyZmLO(yR8pVўif}c$I]K,..{4}i0<1mt), nϏ^x!$ڭ&a zZXepnhA}~ 0ji /ހv'RhgQr?_5Lv[>zF@F8؁m}wH+iYJ]'_C=nK)e x ÝN)%g_܅+ ڭ:a %,㠰Y*3QwЮPZ()h88{2?#zއ1B t^t-I( i5*QRty3X& Jfw7pR!R:ށ (a0_k@g<"Z:Q򫧱1nS?{$RfuL٥dEJ*jd E!~}3ϰLTPz*?!D) ̍5Ze)Hj1fsEVRa)j9JTY)A$ZZC+-7yDQTT[]ڹ2Zg{A@V9fI+hXZ볺Ņ B @B0$MSI^;u=ELҔe\ kdHZC=R\[^jjdža(RGj.k+kH!mh&5tK)-~g'kN$ <cMJ)Ti F$aB Օ5RjG(tB5I-&(qG%(J)* Yn"Rf\)I% תh1+kժVJݯ0<yzjʫl3+$5L&m,d!%.^kwG{N& J)*qVEaB}J+&XK==IZ ]KjRu @JMϼ{﹇zh)yPЎ(9$"f0BZJP"c RlZmRi*@ig-R)Ɉ_2:=ʡC KHZ &DBusg3Y!¢';CS,1Y*IQ Kj)򷏝#Q*[3 ٲ,,~L&i$FZp8p}}1i;K/!ZQVfmm1%@ g'/ڱLڵ IQzIf-'8I#B,un˗)ڱmBWP$o]>f[M1lllZ{22PRkc l9E( >0ۙ𡃌Zl]l6b}zG!i|_k-Xv!6JZ !H'766~ 0niՙL&V+ 7aa4%PWdgkXWiN9b)KӔN\1Օ_itd/x}c3~}KDa;<簹9\N+%m +˄}Ch--gBn9=56icD! 5&ssps@Uo<xc=Kw*_7dsi(vuuW{fu--b<:(6/~9yJ }K^`YYNŤ@۝Ξ 8{ ijZ!l1k!\Y\y<#F[~3W^Z[[8л镕)?33Ӟh WI'1)dX?'wg4Εs6jZCYτremue#oz瞚iguDtng7t,;zbdw.*Q6_v啕ǽ'M+WXkotfg*&W2QY+7a-Ӵ' ^(b{fgp0k/}Bq uR'-zjcsxD10x; YO)OO~>,8اl)ky"[k.;W>n3}avTFՉ\*IENDB`karlyriceditor-1.11/src/images/dryicons_process.png000066400000000000000000000057201233766701300226310ustar00rootroot00000000000000PNG  IHDR szz pHYs   cHRMz%u0`:o_F VIDATxڔW[pu=ӳ],H,X`H A[\U(Erَ,L*?N\|XSK(ŲE[)SaJ"!L)HH ]}<JEU]3ӷ=}ιǎⓆm۠R )%8zw'sI(p~ 8O)mCUUB>ǁD>0ٻo_w0B0P151QS*8]"9=G-!|>#GF9eYH=@HH˲ܟ;p;}o<񕯌Dlm9vGkF=;lrܲ,UBU*m&::Ιp\ _꩓{T*|Rdײ !@)8b8p7mjRTU9TUB{Y,CQס:KK*`+x?ox<B3RQw֭~EQ( >O[µ5FOnض qiJo]5ךqM)UUr5\~4ޮI)iZZZ|-(w* `~?ZWKRBbxg^yDJhni9}ۯRJ 8~!)Cϝ{%]`(UU f% ButVťT*9Q__}Gwwa׋T2y19 ݿĶmM|h90>66Sd0??)KFM8L&2*2 _4!dhGWܵ8dJٵ457}Gpho`wD"4X\\D[[bvpC40Pm#>UES,NR;vxlkJo]Lu~ Hnx^ܸq\zitzLeJ4"fgf\>RMMNhYִ4gg3y)pGU9cHH& 4q%^^Y^V0f?/#X{ duLӄQ6@)E8\ TZou˲&EsםRRj ˓/G~Prʕ+?\/:iZ1B\f2?YkbZ(tCAd2F[J&~z39ι4-( @ ! A)%<nB|n55%$2S9׷{h.^/Jp4@JRTrl`߾ܹM98|(B`e9@^3 TΑXZ:\.8sǟxE˶`6997l B!`Lpa,v(WR*)PfCH!&K*lۆp(%XYYAX@TB\iY eU+f <(%pZul<X* Nr\ ˶Aٜp$gzQ,Ac i"R_PI &T(L@!8wnEӴMm*]!׳,rKCCO׏$!<{y 5jjj/eV4nk:XZ|R=jFR olhc e@`a!k(%yYM-+Jy]JR\ՕjUB={~)ٰ[X;:;7 ICJ@0]/"ʥӫf%c%Sz{ eYޮBT* ׳\*hnnȤ\m(|cǎ/ݷT~r!D8>fiPZR}/wʬeи*Ƕ&x< ˲077T*%וDQbmcc#4MhY6VWV01>vԷKfYιݻ&''UREA% ۱H,.KCl񢧧BHB)~bf:jkkkG*@BJXP{Z3J_i?񸽦i&t-!e#[C!_aP,J077|n hniA*Ԅsܛ-ˎE"L48/1tmtyС=n&ν_<ۣbi, .fbx<b_˓c㟛~nii1 : 77i^V  ~ۯ͝朣.]ZZ%v ƪDfeRIE@A}}= |pȵWﻵp4 =X(eänzBJ?S,4MCRhkkwn-)p{ܘ~d.'n3C2E}b|<my>D"ulG*|s4rEQӖo۶|ow`**SgrŽ/zXzYՎ<ޡ ~ !PƆ%!TQnQY˲`U5UUo]p7oXgȪRB7\r11>wh[[x<~sH026l˂!N|'΋.$w}OlNc۷?{D2x[UN d5SUO@~KoD"4Q]'vǪ>~ZQ(U mّёcL$q2kY]VW3TIENDB`karlyriceditor-1.11/src/images/dryicons_redo.png000066400000000000000000000026741233766701300221110ustar00rootroot00000000000000PNG  IHDR szz pHYs  gAMA|Q cHRMz%u0`:o_F2IDATx]hUܙM6l6?ڴF[hQBAP+`(/*~"(*>Q֢Ijkl6ܹ>lZ[|0a;Yk_?yyy=cPBP`%,ʕ:\ĀV~5Sy\xȗ*[&YM;h73@D "#B0\G AcSk{ݝ2tMM-ݘRCFgWmQM&pIOfׯ.WՌR*c"\, j,3)arWgW!e]q-1S0WP:ƀ 0P4,1,nMĎnXv*>qi`q\xf+KWAZ7jژFFG{oߎ1&cxݛ;mfʯ[  'F{_-aCIENDB`karlyriceditor-1.11/src/images/dryicons_rewind.png000066400000000000000000000107041233766701300224410ustar00rootroot00000000000000PNG  IHDR00W pHYs   cHRMz%u0`:o_FJIDATxڬ[gߥ>==gǹ$$l m, xXVZ"B<B$x ru8vb;'&N;ƷLLOO_>ezlDK:|9:ĕ!J)8R{!Z8BԝG0KG)BTƘx<>3QJאRm"{JJe_R\*0%%B7XkIxp0~`0xv4={o[_jsNkTվ>l>XשT*[ qhPB VSw^N`1{Go#z333jNTZ3.rEVVY' ֦ˮ$ 5f}6ݤh}v>8`EJY {Off< 'OqxᏜ9w`VJڂ_cORfx޻7#`8._qv>ցw4zΝj?./<̹ EK% *Jɔ3rkI8a01g>~3+6.^<|xK; ;33뮛 ÐF_??.Rj jJ4=8{cFlzin=ύ78'|zyi ι;vx]J)xzf?{5:aRvV{w8 8zG4q wayyypWvܹK)g_BjͶFFR )RH"wd{p]ꌵQP Cֺ;na~~~1gkkkz !=Z;wX9߼p0hMOѨPJ"E*O: sx;U *ZM֬uU+4?FAR >;c2^!$ZӴSA@@gPkcwŹaZ&> ÀVs4B~fgg$umGff723ݤldO G  h>Y_3>ҥJzZͩ4= W+;٨ᅓ왟cբncee忥F(N5!x%V׻4jiR*ôʰ)r|gJ0BƘ-RB4J(PRZ)U4jUV׻9w(h6(H!rjSg/P CjJJ8R[ NiXJIEz=~rAp*rE~Ӭw(JѵRA@Z:{)4VՇ֬t\?#3_hkk44u2:Ǐ$O}j/8kґzBR ^ZJZ}x8Z*uWR r{)ZM Gc@e}Z0A.^֊(2rSԋ |9F>(-QV#25㩕JhXn^T՛VWWZ(y*K)L0 B :Pܹ˜:. Ua0S:‰G_?ƅ @Z_fR9DJBRx0 B=J+b^.y< C㤀@lz|8<,k 1$ < s4zoƤ1lᑘHQ-AElX!n*$Ъ(?(9R`yQ=J*z *J)N6AX"*Aca-C))"\Bjljk4Drk3&S*xYZ^TRTEéP>a18e6\~8!60TJ@)IX(@JYْ$(d)R6Wش*|qUFu!J)GօL{?HSaZil8[na9vFw g-LLfG<d<}*QЬ' ʧ/wMG %U@<1i-Zi~';y#=Fw0#kLXkA;KI'g ΙԉlJQ4'Xس{7o_ xOB4O5j88NR{~w7/22AR"WduB ㍧QKhdkgpx^ڬJ$%%6 zΥÃu)ÁC<~epIGI:2(*=^ϫ0tR}*38YcC45w3ιZ;n# RITʊ͜+`Қ}ﻇA@$X&ccZMx싩#/T#K1&QR)E X,,,|n_VYZpsK%q&rMq2BR0 сe8dpu.7a3? Nqn$ynyy'JQsxH)8/n+H Eľ8;.[ _vQB>KKK?1<79W$hn1Sj^~DnS^͞'O XQ0e13 _zWqn?!|zzz5;3M5-1:ȧ+I=௽;''tsΦP uMhMS/]Z]iޟ;4B{j՚R t=\6jY1]e81#5)G5vI) Iˋ *[&_Z{G̶m[:V)w2Y\LbrxOq ۦӉp0`Ck_B7urg}o<of۶'ժ6=Ekuza:eM'$TkޣQ9EY/t?1~|B\cC HcjfR88 G㴇 `A("j2jJ)*V뱺jwRʬ!4@B:/UկKR:v,D tPh$a0]_?$L![%UHN !*J}r0 ,klb{l`p8|{24PUVR燣~?TZ)hw!%eCHYAZumsm&wT}IENDB`karlyriceditor-1.11/src/images/dryicons_sound.png000066400000000000000000000055201233766701300223010ustar00rootroot00000000000000PNG  IHDR szz pHYs   cHRMz%u0`:o_F IDATxڤYl]uxG-,JjYT<;Rd[Nă--/mUJ"Ggmm8.2cF!<;Fi6yne+Om;gqq ؽgד$eYZ  ~#sxxwÎaom8r0PJ\zVM[PdiaR]LsRJy{X]]\.y 纸yO!_v;sN|Օe{i6ye\ct(++nn9 091q\p]vpMw=}s4 GTs$Wf{Jl4Ȳ @ћ`3 T.SkRZVl)%dJcY2ۆra+ucl)i1Q1ZFM]("Mkkפ8յ*Bے*<[ Wql8Z &G_cn6 :b6aeZ+LV1:Aֺg۶3Y[[EJIDF@L)8!IUMo$2666زe gYZ\}VWW1lf'}z$?0|[+m>1K ;T*&mȔbamq;s8++t]Ņ,LyG:R3~Sad|~̩S@'aHFzvb>﹄aHEQHfVCC[i4mN,X[[%KƘ'n`iDBSQ޽{iZ$iFFt{!R.80$b&i0:sZ-466u$qM)'V@YsP_Ç-ImJD&u"#i0?;Ǟ{,;DQHVc~C5 ;heI2U(333I~)ZkCQL|jqE1Yq5"w}7Ǐ,籱^lhy~íFe\ロ{c?7FcY `0FcҚe,)y#,//ҋ|R\A!DW)֛fY[>Y6Enc][o]wZm$,#cZZ!zo>A,--5˲:8xeXw (&mz8SO2<<0$Mܲ= w}9߽v\>Gcc3SDI?02h_@n)mq ^׏2inժ#N?aZ_W*ۯ~Bk9YIjݚlZ?JRٹ IENDB`karlyriceditor-1.11/src/images/dryicons_stop.png000066400000000000000000000100621233766701300221330ustar00rootroot00000000000000PNG  IHDR00W pHYs   cHRMz%u0`:o_FIDATxڬZKWsN=}_'8ax*D(40BA 6!Ă% $Xa@!!"f H'8a&`ǎ}۷QTuq”t^}<RJ03BZ fDc ` HQ)eRPkg MbZܯ"*q03Yx4'+Ir"Ic8a!H!@؜aA>b<`ef8ϲx43BZRJ})I7Oj5$I4GO8 0Z?Z5ݵZc jt:h49p5ln`oiaQbe{YǽXj5“ vvvzu;{ xa~oeevCJ)X8sa=I1Н|,åw޹u[!CB)(TYJ@)T (RźҁBFny*` r)0D-MBR$R~TEax< XdcI'hT0g~g|ƂYa(0}~.*&R" !&1Q! や8*6QXxBU8:|^)f d vܪ 5 =y,oB{aUB b@ ^$p)uߍ>$%\B̨ *qSj, %Y}1i%vPJ.Եnatu_1B d2 ѥ m`Zcy%fEˤ> -u8XH"s({QBJX/tzVk0i%rdJoYUa80[>7\ (G#;C'66.}`: Bt^Maezֈ`8>ߜU-rb&뫵.fk#Xc(1oFЬUDkLs0Jf^G-G(`tJ 4U0[ Ë7MGוּH* B HAJAjϞ2 wτZ[qT֕yW`LTWS$q,ːeLeǣSIbBokkB< &&l5dۏXh/Y(0^Xc \yZ`hs""g'4}Ӫ#c2!DA̰Lp(BXW!+2Cw 2rxƛCKz0Ɯ) ~8! vq1-E#%-0sHܙ~ߢ.ZP`cmB>RZJ!N^vf tf5kefl҂aag=ׯB`c[t^o5j`~}of[pfnF]U{x q"sܼqZ}O3[d=WXcv>YPon#O\H׊cB;HV:Xj(pWvwvJDg Rw;NމdSiRv}tw0F:շȍ[y0CJz5RvVVzo޼d"t郱sFfq׏qx!P8B5&H`0~;Ƙ !.Cq'ܵ KI~V}Dgܿ'Xrv/8Q1 {s\ZN999qyph0FHm_:'Y\?4kM4HBH_FkKWA,pF!5`[SrhD %14 m3HDpm$78~f`|CrH9 Bpg 3OE^7tdϋ:w& ?& K;Uu۞srghUŌ;r@ftU2ŁۇF&.QP$1)8B 5846 R G7G(;l7]=ՙݑP8c(;՘aR@H9Ckrkܔәߔ\Ԓ8G8gUSٱI dŧGO8gZ9g`_y] !a [|u:7Y|10)9>l[F]\`cs0kiBru?K-$g3  ܕX]"6 2x"(͠{:eV%Rba-PJ0 G{Gߞ"GB?%w}eJfM@}S!C"]=m v17>1LEoZ[͎N ͟\zLM64),ʯLZڸ7{v>Q5:%_[}5;6=” C CAJepZ߮hkƇ_#GH74[mk_ޚdfJe8f@) aWdM{S@) Tc;|}HAa27T_}`ӆ}~ӎGϭ %V!w^{Ӻ˖" `uؖŭP(1B2Îm X̍շՑHv ۦE'Up: -0<.DK8l9q'ں6,Q! b떍(9.Χ/B!xŒxf:0Bh6_kpΪ!3X8ӔJ"`1}LiXxvh!@{B\Gѐe6li[7^c5|_gj~u>cժ{YKAD0!%8ܲ0#Q>☳CA3#+d<5AֻɩTJD/Xz&_(sQFUA7_00e'9!IENDB`karlyriceditor-1.11/src/images/nocover.png000066400000000000000000000063431233766701300207160ustar00rootroot00000000000000PNG  IHDR0+> pHYs f IDAThYKl$y~w{]r_> GF`dİu=`$KN98H NN9:Hy8{XRÒvW'\˙陞sXrCh jT㷿'ZpI+Rs5v`s,g4y8A2#U$5$\'{9Uzy8DN6 zo}ĕ~zu捉F0 oCT$Iw_iLJ& iG;߳5~}yӲLH,C!!"0ƀss,t]g16bEڏ~X}nQhQL`{ί7=N^4 !?~LJ)i8sDD@D^ AuUJۃ0|mm}ZVi=38"oi7|u]k8BrO#2F1`W>;baT\ǹj]Y{o\xYoٶz7N]˲  c "D,+,]39NMN8?/6o_ġ@w}f=pE)Ar{"0 cIcg_\k|n?Pcķ.zVm[VEJ1 g R j򤭒?HX/-œ~(D$qt0m >1'KK֢( ||B4L.i:~!,T )!cjz XLjw,S_mzEV>I)DO2 0cj(u)ǶEŪ8d eQDDD?]|=FoղDTQ*} s[?LDt]̲ls)(6LBeS>qƀ31E'J7ox3T12jzᠢH|_^RB02(@)@4'`HU9fYeg I"B0LLNfHS7ԆyhVʷ=\{UnLھ)@J J)R@JшrƀDr`e UDDR"c 9GQ~ 첷zbV6R 8r]bW^8%"H4c"@P2Y9JȔ놡AҨOiKji28i*C8d#!RJP }X&s`[W^Unu[c m˪]MiJ%lwyAumLk’a(;$(qbڗJqMi0se!c ܕ廳$NUք߽sG' c $x^tm9s~ϓٖac7覽e2mTJ鮊Ric:G|+ea8(]pzl੓sz=t='\]]qj%2" l]hy"j;'`89*9'D.o]qL'EdNCD:YݓBwo~¤R 4Ǡiah& s@ yj?N;2wqqΡ^eYW_TIoVQknz?gm=ʙ$MەGJsE]ױV.1MSzy.xnNRrs|J^u<eZ/~7 IG^) χqM4ˌ1btWy)l61Fla $IYF>BDl6T*R1uߗ{?2ަS;~8~ȩ"B^Y,ch6J%n&VOJ%$"RD2M4 p8|X9E)73/ժUYsD۶avvG=!H)4M4  @{\7  p,LJRqm1<'9}zpSD0R@f Z ]AyӈiX*@uY.?7mZn΄ڲ=PJگ?,ً>~vFb\J9n Hly0B-?Xyǚ:t,M΃vJvyZcQ !"A|w?ҘGcf8q^GZ:i'EYۢ,9!IIA<+Mq7N9ʿo<;_j֪_+q!Oc^춄v( ɅsM8^rZXViKw~voE0>eƴi.DD3!a!Pw+h,_mֱKk^W"HӘ@2;Vrjȩ$za ;Cq "<5L+peI` \}qX4"r=w73)q'h0_bY {ZIENDB`karlyriceditor-1.11/src/images/page_swap.png000066400000000000000000000032711233766701300212060ustar00rootroot00000000000000PNG  IHDR szzsBIT|d pHYs B4tEXtCreation Time04/08/08!ctEXtSoftwareAdobe FireworksONIDATXKGSݷkƎxxEH) H!!EdB lذ`I` !`e%  pdVx0q8}oG9EWܙyRU5rm{,D;Ps sLy|߶oҀ}_jAUwsDv콖ZVؤ ՚?Y IENDB`karlyriceditor-1.11/src/images/piano.png000066400000000000000000000003541233766701300203450ustar00rootroot00000000000000PNG  IHDR@@% sRGBbKGD pHYs ftIME 9NlIDAThڱ @ Dϱ∠L%YWFnyaYROb 8&y~IENDB`karlyriceditor-1.11/src/images/quit.png000066400000000000000000000057111233766701300202230ustar00rootroot00000000000000PNG  IHDR00W pHYs^tIME 0bKGD VIDATxZ{pU<#< g0D -"R5ZGtl}2>mKVN֎:bhuJN@6 r\Z;#>ܽgwo^n =Ђ>$4&pP@6 4#$B!CWk6bK%'&ҺeYS $XpDxYTWWKi~]/Wl͛1FF>#IOgc5\Ufe|Tc"Xpҙ3h<=7ѱfϞ8%=ۚ./78|V&3 fSR!b+q}-N8 0GDI&mgل?FnpEs+q> ~z}lJ->ge),4Rlu}8;řfL[)uB6\,Ewvw#8*+߰Q;22"ntDt@-; c皸q z CHwߍX,7vBWGfD2v‚-\^Ǐc /`a iA rux4BAd9G޴ UUUkiO1ZZp%@u᪍/z/4I&x h/d%UTtX㍂4*5s|fY1p:<TRO%q4]Xu y >K~v|8i8ȩ<8c}oC6f| ɔJ)TG]-ī4g;#6 tΙp@^h1m㭘H& z:Ч gs<ķ/B=Y@s: \и6~'ʊ =Hˆ͚hM tNzedU@`aB#h.q.!Epun؉ 5TUOٷW8`DoQG6x1AzODιVPPJ`#:Ra:: 㤘]a[!J)"*(lR{v"ߋӐKK]9cݽȤp9[v~Nh@ 7G@ztT@`) !R^"C0̈ 3d 0C} *OFh(t*?x VrIY^vH0t8mMB"Mn$-9,05ݖpa627`IcS=0|%yp_ U(+o|^ $¾^\0ztlކi M095t|P#U 0@ 򊩕sZs'SBSkA-ի*V SS9(AjΟ_]0ܴ Z>tqZ/q}1M%@kO{Q xF'<( 9P4 `|"ܽ +W|E 6Uq"Au6ڟ>DӒ yz@f c DQ_a7V)D :@B݆s?A]MMc}Ժ4Ay$Ƌ*@%a\v^ǂi@PESRD@|:ywZ'@΢eh]p!¡<Wv x"L E$$/^YЄ?5o}fϗ|[tXLVwȧ#ˣm$"T/9g͝%>r-3X  Ϯy"L]7#M܃CAGK$hek$ϥ$8@z(S]lWXca%9M, G,[I!^;z+یikr(.@}RA۴#'Eޅ᝭$ȃ! ![ q!EΥ?vZ+=47RY<%ys:d;>DqN ʝ'- 'wU<28zb\G:Z r(Rz"t6=8kh4 x[pE{ /!ǝu9s?4{ 5wqO^3;/FާJ9bĎs& `,W1{mk2N uj`SֵId!=_r>,A$b! ʇznr>lZ165~ s*gIENDB`karlyriceditor-1.11/src/images/remove.png000066400000000000000000000005141233766701300205320ustar00rootroot00000000000000PNG  IHDRasBIT|d pHYs:tEXtSoftwarewww.inkscape.org<IDATx풱0@EIt2ѧYɸ;;/Չ7`s#bhOB$:I5-SJ/hP۠A}ǀXcj":8e/ N0 %x]afCws?Uc;]/)MS+V9okO .ez| ;4A+M3_pmZsNI@"e@)ڶ=NEGO.;6λzO[ܡyϿxVvA@ //R!#"V/m!nZ[ PͷO2pԍ0i!0 7ZdTcK(OD(&D x"@لr2k苯׶(嗕a-ϓw:);ߺO2~!<2od@<=Pl|jxe̡(nCK`5JрL@@' J邸0{:WXy@ z̅0,)2)@~_5{ X_<{r 5"-e"c MH[@7I HgmaE`{@D+J 's!')"Z'yK5J񠞒@̠ a:J@kf#ւ 6sbE$GIb16@3XӰܒb@YȠgoqFry{k 18lX,ČIinШpaƔ( rj]Y<++n*O(%G@9ç#$ 逃U@v H*iG`}+P35dt a!\8~ {Y|X+ Xƒ( I0`egn  O..,|@D-0bj! ˬ<8iٱu >Oȉqؽ'SMN@# xbϑ᭣@0Vd,`%&qPٝ$nDEaί}ѣgfY4•W@gf.݅ L3[ryZר8OIn+0ց#cnq}}q: MhH̿ `vN"3 cn a Z]j*:Q4Ժ DLKY}J!ѠP xeC` RR(ubJIDH:Nw{{vwhc~IM9UG&uǏ'N270.S[z.bmm .\N$ S`Cf8IENDB`karlyriceditor-1.11/src/images/saveas.png000066400000000000000000000073201233766701300205210ustar00rootroot00000000000000PNG  IHDR00W pHYs a aJ%tIME  &7'[umbKGD]IDATxZkTյΣ=33<`0APh-CEE+KG55Ws&qhDKDFR1!ϣ'?}ZZ{wWlm3M`Ukut, ]n۳u:~sPdDp|2>{m;/p=ò16ǻB TUf͚2o\d"8[ZQ {5p DhtDgExLOv~vlX\ŭ/{jeYh&BAnhhɲ4LO? #؆H\| \[?#`?hY%W4`0hcY-xsL&uZ%,2 tTD@#{l')^`wT 8LN4Ea]DXeI%qɒ*cw#jԦ0IϛR㯥D6Yߎ@0a/@#Ϩ .\ L Xk,O9K15m+}y`MӡiCD냸3:ȈGOx}xv#e>sDesgQ(y(`hIiP1a,G.ǒKUBAH~S#A2RAQ,WcdYy\]tLlh uJ'Z#i(@uEeKD^X2Jd N`͟b2գ-ʄuGPg D#zו9}[ Vu^-KDزJt+G@HDZo5鳠aZv{.t_Wmb @ȏ׏0C,!13SOb6|_߂ >6W67>ɳU)'+1E*Щލ򊕠$yjLD+ħn޾ܔ̗ƙs [NJ\ir2mSi6b[`:¡H^ȲpҜ3&X9'Kb^#׏xQt^!^y*k Bm7ZL#cHhx[̼+>DfsP< Ti2ar7L^A8ƶ;U% +ؐXnJ|l,*V!4$T5qO`p`Ets$|/+E$=sVU A:(Y])WJ2,@PNat?Eog(td".S `wK0]Ks@@ܭ}΄VNBl)ڷořu{qŅUZ\ -L}SJã߲av;PaL%۹P(ރ@ɶʍX8@8ن|n^O=έN  fv 5IZDD30I:" ̼k P_ڎSZ aQCi{?[%{5 ;۞f?RbRo^FR1!pc#9 ߜǜ#*FCge[Lˊ"q͏e4Ńh:R:>chhEڲkqy LqӍ̨yeg 8J({}gPk۹w>}GdqDP qpO9 \6 q>>c=kGgLEkmD{j&-bݺ/WX`ɗqA޷[],|ݟ3%dRT⺶H2**LTT1纻p;r~87Wxj5k_`s o}}"R경L9D4'ܪ-17|/I"2:u.&|m3O4`^45/0֍'YǰihG۝et qNL'ZԼI7ʊ ؎eJ4 [Ɖl[ZPzjȏWse|cޖř-lnkIBa<{&pvq8C}y%D>aHrO0 <^㾥8 Vǰ8'" )dNާ%0ұ4z}Wɦp<{7ăbH_.m4Š#)^.K, _g/{R)6; %}wqgcZf5WDuB^ŤzvQTUq>pD,Pz+&aQFxtӻ6m17bbX'DM&(^cGGGVb AN䵱{:tRk>|̇}g=Z'?GUe?&1AlHqdc[GtV<(qL?XzW(&5ѕ. * **************************************************************************/ #include "karaokelyricstextkar.h" // Parsed lyrics typedef struct { unsigned int clocks; unsigned int track; CStdString text; unsigned int flags; } MidiLyrics; // Parsed tempo change structure typedef struct { unsigned int clocks; unsigned int tempo; } MidiTempo; // Parsed per-channel info typedef struct { unsigned int total_lyrics; unsigned int total_lyrics_space; } MidiChannelInfo; // Based entirely on class MidiTimestamp from pyKaraoke class MidiTimestamp { private: const std::vector& m_tempo; double m_currentMs; unsigned int m_currentClick; unsigned int m_tempoIndex; unsigned int m_division; public: MidiTimestamp( const std::vector& tempo, unsigned int division ) : m_tempo (tempo), m_division (division) { reset(); } void reset() { m_currentMs = 0.0; m_currentClick = 0; m_tempoIndex = 0; } double getTimeForClicks( unsigned int click, unsigned int tempo ) { double microseconds = ( ( float(click) / m_division ) * tempo ); double ms = microseconds / 1000.0; return ms; } // Returns the "advanced" clock value in ms. double advanceClocks( unsigned int click ) { // Moves time forward to the indicated click number. if ( m_currentClick > click ) throw("Malformed lyrics timing"); unsigned int clicks = click - m_currentClick; while ( clicks > 0 && m_tempoIndex < m_tempo.size() ) { // How many clicks remain at the current tempo? unsigned int clicksRemaining = 0; if ( m_tempo[ m_tempoIndex ].clocks - m_currentClick > 0 ) clicksRemaining = m_tempo[ m_tempoIndex ].clocks - m_currentClick; unsigned int clicksUsed = clicks < clicksRemaining ? clicks : clicksRemaining; if ( clicksUsed > 0 && m_tempoIndex > 0 ) m_currentMs += getTimeForClicks( clicksUsed, m_tempo[ m_tempoIndex - 1 ].tempo ); m_currentClick += clicksUsed; clicks -= clicksUsed; clicksRemaining -= clicksUsed; if ( clicksRemaining == 0 ) m_tempoIndex++; } if ( clicks > 0 ) { // We have reached the last tempo mark of the song, so this tempo holds forever. m_currentMs += getTimeForClicks( clicks, m_tempo[ m_tempoIndex - 1 ].tempo ); m_currentClick += clicks; } return m_currentMs; } }; CKaraokeLyricsTextKAR::CKaraokeLyricsTextKAR( const CStdString & midiFile ) : CKaraokeLyricsText() { m_midiData = 0; m_midiFile = midiFile; } CKaraokeLyricsTextKAR::~CKaraokeLyricsTextKAR() { delete[] m_midiData; } /* bool CKaraokeLyricsTextKAR::Load() { XFILE::CFile file; bool succeed = true; // Clear the lyrics array clearLyrics(); if ( !file.Open( m_midiFile, TRUE ) ) return false; m_midiSize = (unsigned int) file.GetLength(); if ( !m_midiSize ) return false; // shouldn't happen, but file.Seek( 0, SEEK_SET ); m_midiData = new unsigned char [ m_midiSize ]; // Read the whole file if ( !m_midiData || file.Read( m_midiData, m_midiSize) != m_midiSize ) return false; file.Close(); // Parse MIDI try { parseMIDI(); } catch ( const char * p ) { CLog::Log( LOGDEBUG, "KAR lyric loader: cannot load file: %s", p ); succeed = false; } delete [] m_midiData; m_midiData = 0; return succeed; } */ // // Got a lot of good ideas from pykaraoke by Kelvin Lawson (kelvinl@users.sf.net). Thanks! // void CKaraokeLyricsTextKAR::parseMIDI() { m_midiOffset = 0; // Bytes 0-4: header unsigned int header = readDword(); // If we get MS RIFF header, skip it if ( header == 0x52494646 ) { setPos( currentPos() + 16 ); header = readDword(); } // MIDI header if ( header != 0x4D546864 ) throw( "Not a MIDI file" ); // Bytes 5-8: header length unsigned int header_length = readDword(); // Bytes 9-10: format unsigned short format = readWord(); if ( format > 2 ) throw( "Unsupported format" ); // Bytes 11-12: tracks unsigned short tracks = readWord(); // Bytes 13-14: divisious unsigned short divisions = readWord(); if ( divisions > 32768 ) throw( "Unsupported division" ); // Number of tracks is always 1 if format is 0 if ( format == 0 ) tracks = 1; // Parsed per-channel info std::vector lyrics; std::vector tempos; std::vector channels; channels.resize( tracks ); // Set up default tempo MidiTempo te; te.clocks = 0; te.tempo = 500000; tempos.push_back( te ); int preferred_lyrics_track = -1; int lastchannel = 0; int laststatus = 0; unsigned int firstNoteClocks = 1000000000; // arbitrary large value unsigned int next_line_flag = 0; // Point to first byte after MIDI header setPos( 8 + header_length ); // Parse all tracks for ( int track = 0; track < tracks; track++ ) { char tempbuf[1024]; unsigned int clocks = 0; channels[track].total_lyrics = 0; channels[track].total_lyrics_space = 0; // Skip malformed files if ( readDword() != 0x4D54726B ) throw( "Malformed track header" ); // Next track position int tracklen = readDword(); unsigned int nexttrackstart = tracklen + currentPos(); // Parse track until end of track event while ( currentPos() < nexttrackstart ) { // field length clocks += readVarLen(); unsigned char msgtype = readByte(); // // Meta event // if ( msgtype == 0xFF ) { unsigned char metatype = readByte(); unsigned int metalength = readVarLen(); if ( metatype == 3 ) { // Track title metatype if ( metalength > sizeof( tempbuf ) ) throw( "Meta event too long" ); readData( tempbuf, metalength ); tempbuf[metalength] = '\0'; if ( !strcmp( tempbuf, "Words" ) ) preferred_lyrics_track = track; } else if ( metatype == 5 || metatype == 1 ) { // Lyrics metatype if ( metalength > sizeof( tempbuf ) ) throw( "Meta event too long" ); readData( tempbuf, metalength ); tempbuf[metalength] = '\0'; if ( (tempbuf[0] == '@' && tempbuf[1] >= 'A' && tempbuf[1] <= 'Z') || strstr( tempbuf, " SYX" ) || strstr( tempbuf, "Track-" ) || strstr( tempbuf, "%-" ) || strstr( tempbuf, "%+" ) ) { // Keywords } else { MidiLyrics lyric; lyric.clocks = clocks; lyric.track = track; lyric.flags = next_line_flag; if ( tempbuf[0] == '\\' ) { lyric.flags = CKaraokeLyricsText::LYRICS_NEW_PARAGRAPH; lyric.text = tempbuf + 1; } else if ( tempbuf[0] == '/' ) { lyric.flags = CKaraokeLyricsText::LYRICS_NEW_LINE; lyric.text = tempbuf + 1; } else if ( tempbuf[1] == '\0' && (tempbuf[0] == '\n' || tempbuf[0] == '\r' ) ) { // An empty line; do not add it but set the flag if ( next_line_flag == CKaraokeLyricsText::LYRICS_NEW_LINE ) next_line_flag = CKaraokeLyricsText::LYRICS_NEW_PARAGRAPH; else next_line_flag = CKaraokeLyricsText::LYRICS_NEW_LINE; } else { next_line_flag = (strchr(tempbuf, '\n') || strchr(tempbuf, '\r')) ? CKaraokeLyricsText::LYRICS_NEW_LINE : 0; lyric.text = tempbuf; } lyrics.push_back( lyric ); // Calculate the number of spaces in current syllable for ( unsigned int j = 0; j < metalength; j++ ) { channels[ track ].total_lyrics++; if ( tempbuf[j] == 0x20 ) channels[ track ].total_lyrics_space++; } } } else if ( metatype == 0x51 ) { // Set tempo event if ( metalength != 3 ) throw( "Invalid tempo" ); unsigned char a1 = readByte(); unsigned char a2 = readByte(); unsigned char a3 = readByte(); unsigned int tempo = (a1 << 16) | (a2 << 8) | a3; // MIDI spec says tempo could only be on the first track... // but some MIDI editors still put it on second. Shouldn't break anything anyway, but let's see //if ( track != 0 ) // throw( "Invalid tempo track" ); // Check tempo array. If previous tempo has higher clocks, abort. if ( tempos.size() > 0 && tempos[ tempos.size() - 1 ].clocks > clocks ) throw( "Invalid tempo" ); // If previous tempo has the same clocks value, override it. Otherwise add new. if ( tempos.size() > 0 && tempos[ tempos.size() - 1 ].clocks == clocks ) tempos[ tempos.size() - 1 ].tempo = tempo; else { MidiTempo mt; mt.clocks = clocks; mt.tempo = tempo; tempos.push_back( mt ); } } else { // Skip the event completely setPos( currentPos() + metalength ); } } else if ( msgtype== 0xF0 || msgtype == 0xF7 ) { // SysEx event unsigned int length = readVarLen(); setPos( currentPos() + length ); } else { // Regular MIDI event if ( msgtype & 0x80 ) { // Status byte laststatus = ( msgtype >> 4) & 0x07; lastchannel = msgtype & 0x0F; if ( laststatus != 0x07 ) msgtype = readByte() & 0x7F; } switch ( laststatus ) { case 0: // Note off readByte(); break; case 1: // Note on if ( (readByte() & 0x7F) != 0 ) // this would be in fact Note off { // Remember the time the first note played if ( firstNoteClocks > clocks ) firstNoteClocks = clocks; } break; case 2: // Key Pressure case 3: // Control change case 6: // Pitch wheel readByte(); break; case 4: // Program change case 5: // Channel pressure break; default: // case 7: Ignore this event if ( (lastchannel & 0x0F) == 2 ) // Sys Com Song Position Pntr readWord(); else if ( (lastchannel & 0x0F) == 3 ) // Sys Com Song Select(Song #) readByte(); break; } } } } // The MIDI file is parsed. Now try to find the preferred lyric track if ( preferred_lyrics_track == -1 || channels[preferred_lyrics_track].total_lyrics == 0 ) { unsigned int max_lyrics = 0; for ( unsigned int t = 0; t < tracks; t++ ) { if ( channels[t].total_lyrics > max_lyrics ) { preferred_lyrics_track = t; max_lyrics = channels[t].total_lyrics; } } } if ( preferred_lyrics_track == -1 ) throw( "No lyrics found" ); // We found the lyrics track. Dump some debug information. MidiTimestamp mts( tempos, divisions ); double firstNoteTime = mts.advanceClocks( firstNoteClocks ); // Now go through all lyrics on this track, convert them into time. mts.reset(); for ( unsigned int i = 0; i < lyrics.size(); i++ ) { if ( (int) lyrics[i].track != preferred_lyrics_track ) continue; double lyrics_timing = mts.advanceClocks( lyrics[i].clocks ); // Skip lyrics which start before the first note if ( lyrics_timing < firstNoteTime ) continue; unsigned int mstime = (unsigned int)ceil( (lyrics_timing - firstNoteTime) / 100); addLyrics( lyrics[i].text, mstime, lyrics[i].flags ); } } unsigned char CKaraokeLyricsTextKAR::readByte() { if ( m_midiOffset >= m_midiSize ) throw( "Cannot read byte: premature end of file" ); const unsigned char * p = m_midiData + m_midiOffset; m_midiOffset += 1; return p[0]; } unsigned short CKaraokeLyricsTextKAR::readWord() { if ( m_midiOffset + 1 >= m_midiSize ) throw( "Cannot read word: premature end of file" ); const unsigned char * p = m_midiData + m_midiOffset; m_midiOffset += 2; return p[0] << 8 | p[1]; } unsigned int CKaraokeLyricsTextKAR::readDword() { if ( m_midiOffset + 3 >= m_midiSize ) throw( "Cannot read dword: premature end of file" ); const unsigned char * p = m_midiData + m_midiOffset; m_midiOffset += 4; return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; } int CKaraokeLyricsTextKAR::readVarLen() { int l = 0, c; c = readByte(); if ( !(c & 0x80) ) return l | c; l = (l | (c & 0x7f)) << 7; c = readByte(); if ( !(c & 0x80) ) return l | c; l = (l | (c & 0x7f)) << 7; c = readByte(); if ( !(c & 0x80) ) return l | c; l = (l | (c & 0x7f)) << 7; c = readByte(); if ( !(c & 0x80) ) return l | c; l = (l | (c & 0x7f)) << 7; c = readByte(); if ( !(c & 0x80) ) return l | c; throw( "Cannot read variable field" ); } unsigned int CKaraokeLyricsTextKAR::currentPos() const { return m_midiOffset; } void CKaraokeLyricsTextKAR::setPos(unsigned int offset) { m_midiOffset = offset; } void CKaraokeLyricsTextKAR::readData(void * buf, unsigned int length) { for ( unsigned int i = 0; i < length; i++ ) *((char*)buf + i) = readByte(); } karlyriceditor-1.11/src/karaokelyricstextkar.h000066400000000000000000000065131233766701300217060ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef KARAOKELYRICSTEXTKAR_H #define KARAOKELYRICSTEXTKAR_H // This file is taken from my XBMC Karaoke MIDI parser // It is hacked to keep the code base the same. // It looks kinda ugly, but this makes updating easier. // HACK START #include typedef QByteArray CStdString; class CKaraokeLyricsText { public: enum { LYRICS_NEW_PARAGRAPH, LYRICS_NEW_LINE }; }; // HACK END //! This class loads MIDI/KAR format lyrics class CKaraokeLyricsTextKAR : public CKaraokeLyricsText { public: static QByteArray getLyrics( const QByteArray& arr ) { CKaraokeLyricsTextKAR karfile( "qq" ); if ( karfile.LoadData( arr ) ) return karfile.m_lyrics; return QByteArray(); } protected: CKaraokeLyricsTextKAR( const CStdString & midiFile ); ~CKaraokeLyricsTextKAR(); // HACK START bool LoadData( const QByteArray& arr ) { m_midiSize = arr.size(); m_midiData = (unsigned char*) arr.data(); // Parse MIDI try { parseMIDI(); m_midiData = 0; return true; } catch ( const char * p ) { m_midiData = 0; return false; } } void addLyrics( CStdString text, unsigned int mstime, unsigned int flags ) { // Convert time int min = mstime / 60000; int sec = (mstime - min * 60000) / 1000; int msec = mstime - (min * 60000 + sec * 1000 ); QString timing = QString().sprintf( "[%02d:%02d.%02d]", min, sec, msec / 10 ); if ( flags & LYRICS_NEW_PARAGRAPH ) m_lyrics += "\n\n"; else if ( flags & LYRICS_NEW_LINE ) m_lyrics += "\n"; m_lyrics += timing.toLocal8Bit() + text; } QByteArray m_lyrics; // HACK END private: void parseMIDI(); unsigned char readByte(); unsigned short readWord(); unsigned int readDword(); int readVarLen(); void readData( void * buf, unsigned int length ); unsigned int currentPos() const; void setPos( unsigned int offset ); // MIDI file name CStdString m_midiFile; // MIDI in-memory information unsigned char * m_midiData; unsigned int m_midiOffset; unsigned int m_midiSize; }; #endif karlyriceditor-1.11/src/kfn_file_parser.cpp000066400000000000000000000232131233766701300211200ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include "kfn_file_parser.h" #include #include #include #define KFN_SUPPORT_ENCRYPTION #if defined (KFN_SUPPORT_ENCRYPTION) #include #endif class KFNFileException { }; KFNFileParser::KFNFileParser() { } const char * KFNFileParser::errorMsg() const { return qPrintable( m_errorMsg ); } const QList KFNFileParser::entries() const { return m_entries; } bool KFNFileParser::open( const QString& filename ) { m_entryMusic = -1; m_entrySongIni = -1; // Open the KFN file m_file.setFileName( filename ); if ( !m_file.open( QIODevice::ReadOnly ) ) { m_errorMsg = "Cannot open file for reading"; return false; } try { // See http://www.ulduzsoft.com/2012/10/reverse-engineering-the-karafun-file-format-part-1-the-header/ char name[4]; // Check the signature readBytes( name, 4 ); if ( name[0] != 'K' || name[1] != 'F' || name[2] != 'N' || name[3] != 'B' ) { m_errorMsg = "Not a valid KFN file"; return false; } // Parse the file header while ( 1 ) { readBytes( name, 4 ); quint8 type = readByte(); quint32 len_or_value = readDword(); // Type 2 is variable data length if ( type == 2 ) { QByteArray data = readBytes( len_or_value ); // Store the AES encoding key if ( name[0] == 'F' && name[1] == 'L' && name[2] == 'I' && name[3] == 'D' ) m_aesKey = data; } // End of header? if ( name[0] == 'E' && name[1] == 'N' && name[2] == 'D' && name[3] == 'H' ) break; } // Parse the directory quint32 totalFiles = readDword(); for ( quint32 i = 0; i < totalFiles; i++ ) { Entry entry; entry.filename = readString( readDword() ); entry.type = readDword(); entry.length_out = readDword(); entry.offset = readDword(); entry.length_in = readDword(); entry.flags = readDword(); m_entries.push_back( entry ); } // Since all the offsets are based on the end of directory, readjust them and find the music/lyrics for ( int i = 0; i < m_entries.size(); i++ ) { m_entries[i].offset += m_file.pos(); if ( m_entries[i].type == TYPE_SONGTEXT ) m_entrySongIni = i; if ( m_entries[i].type == TYPE_MUSIC && m_entryMusic == -1 ) m_entryMusic = i; } if ( m_entryMusic == -1 || m_entrySongIni == -1 ) { m_errorMsg = "File doesn't have any music or lyrics"; return false; } return true; } catch ( KFNFileException ex ) { m_errorMsg = "Cannot read data: incomplete file"; return false; } } void KFNFileParser::close() { m_file.close(); } QString KFNFileParser::musicFileExtention() const { if ( m_entryMusic == -1 ) return QString::null; const Entry& entry = m_entries[m_entryMusic]; QString ext = entry.filename; int lastdot = ext.lastIndexOf( '.' ); if ( lastdot == -1 ) return ""; else return ext.mid( lastdot + 1 ); } bool KFNFileParser::writeMusicFile( QFile& outfile ) { if ( m_entryMusic == -1 ) return false; const Entry& entry = m_entries[m_entryMusic]; QByteArray data = extract( entry ); if ( data.isEmpty() ) return false; return outfile.write( data ); } QString KFNFileParser::lyricsAsLRC() { if ( m_entrySongIni == -1 ) return QString::null; const Entry& entry = m_entries[m_entrySongIni]; QByteArray data = extract( entry ); if ( data.isEmpty() ) return QString::null; QString songini = QString::fromUtf8( data.data(), data.size() ); songini.replace( QRegExp("[\r\n]+"), "\n" ); QStringList lines = songini.split( "\n" ); // Parse the song.ini and fill up the sync and text arrays QStringList texts; QList< int > syncs; QRegExp patternSync( "^Sync[0-9]+=(.+)" ); QRegExp patternText( "^Text[0-9]+=(.*)" ); // Analyze each line for ( int i = 0; i < lines.size(); i++ ) { QString line = lines[i]; // Try to match the sync first if ( line.indexOf( patternSync ) != -1 ) { // Syncs are split by comma QStringList values = patternSync.cap( 1 ).split(","); for ( int v = 0; v < values.size(); v++ ) syncs.push_back( values[v].toInt() ); } // Now the text if ( line.indexOf( patternText ) != -1 ) { if ( !patternText.cap( 1 ).isEmpty() ) { // Text is split by word and optionally by the slash QStringList values = patternText.cap( 1 ).split(" "); for ( int v = 0; v < values.size(); v++ ) { QStringList morevalues = values[v].split( "/" ); for ( int vv = 0; vv < morevalues.size(); vv++ ) texts.push_back( morevalues[vv] ); // We split by space, so add it at the end of each word texts.last() = texts.last() + " "; } } // Line matched, so make it a line if ( texts.size() > 2 && texts[ texts.size() - 2 ] != "\n" ) texts.push_back( "\n" ); } } int curr_sync = 0; bool has_linefeed = false; int lines_no_block = 0; int lastsync = -1; // The original timing marks are not necessarily sorted, so we add them into a map // and then output them from that map QMap< int, QString > sortedLyrics; for ( int i = 0; i < texts.size(); i++ ) { if ( texts[i] == "\n" ) { if ( lastsync == -1 ) continue; if ( has_linefeed ) lines_no_block = 0; else if ( ++lines_no_block > 6 ) { lines_no_block = 0; sortedLyrics[ lastsync ] += "\n"; } has_linefeed = true; sortedLyrics[ lastsync ] += "\n"; continue; } else has_linefeed = false; // Get the time if we have it if ( curr_sync >= syncs.size() ) continue; lastsync = syncs[ curr_sync++ ]; sortedLyrics.insert( lastsync, texts[i] ); } QString lrcoutput = ""; for ( QMap< int, QString >::const_iterator it = sortedLyrics.begin(); it != sortedLyrics.end(); ++it ) { int syncval = it.key(); int min = syncval / 6000; int sec = (syncval - (min * 6000)) / 100; int msec = syncval - (min * 6000 + sec * 100); char timebuf[256]; sprintf( timebuf, "[%d:%02d.%02d]", min, sec, msec ); lrcoutput += timebuf + it.value(); } return lrcoutput.trimmed(); } QByteArray KFNFileParser::extract( const Entry& entry ) { // The file starts here m_file.seek( entry.offset ); // For a non-encrypted files we just return the whole array if ( (entry.flags & 0x01) == 0 ) { QByteArray array = m_file.read( entry.length_in ); if ( array.size() != entry.length_in ) return QByteArray(); return array; } #if defined (KFN_SUPPORT_ENCRYPTION) // A file is encrypted, decrypt it EVP_CIPHER_CTX ctx; EVP_CIPHER_CTX_init( &ctx ); EVP_DecryptInit_ex( &ctx, EVP_aes_128_ecb(), 0, (const unsigned char*) m_aesKey.data(), 0 ); QByteArray array( entry.length_out, 0 ); int total_in = 0, total_out = 0; // Size of the buffer must be a multiple of 16 char buffer[8192], outbuf[8192]; while ( total_in < entry.length_in ) { int toRead = qMin( (unsigned int) sizeof(buffer), (unsigned int) entry.length_in - total_in ); int bytesRead = m_file.read( buffer, toRead ); // We might need to write less than we read since the file is rounded to 16 bytes int toWrite = sizeof(outbuf); if ( bytesRead != toRead ) { EVP_CIPHER_CTX_cleanup( &ctx ); m_errorMsg = "File truncated"; return QByteArray(); } // Decrypt the content if ( !EVP_DecryptUpdate( &ctx, (unsigned char*) outbuf, &toWrite, (unsigned char*) buffer, bytesRead ) ) { EVP_CIPHER_CTX_cleanup( &ctx ); m_errorMsg = "Decryption failed"; return QByteArray(); } memcpy( array.data() + total_out, outbuf, toWrite ); total_out += toWrite; total_in += bytesRead; } EVP_CIPHER_CTX_cleanup( &ctx ); return array; #else m_errorMsg = "File is encrypted, but decryption support is not compiled in"; return QByteArray(); #endif } quint8 KFNFileParser::readByte() { quint8 byte; if ( m_file.read( (char*) &byte, 1) != 1 ) throw KFNFileException(); return byte; } quint16 KFNFileParser::readWord() { quint8 b1 = readByte(); quint8 b2 = readByte(); return b2 << 8 | b1; } quint32 KFNFileParser::readDword() { quint8 b1 = readByte(); quint8 b2 = readByte(); quint8 b3 = readByte(); quint8 b4 = readByte(); return b4 << 24 | b3 << 16 | b2 << 8 | b1; } QByteArray KFNFileParser::readBytes( unsigned int len ) { QByteArray arr = m_file.read( len ); if ( arr.size() != (int) len ) throw KFNFileException(); return arr; } QString KFNFileParser::readString( unsigned int len ) { QByteArray arr = readBytes( len ); return QString::fromUtf8( arr.data(), arr.size() ); } void KFNFileParser::readBytes( char * buf, unsigned int len ) { if ( m_file.read( buf, len ) != len ) throw KFNFileException(); } karlyriceditor-1.11/src/kfn_file_parser.h000066400000000000000000000057671233766701300206030ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef KFN_FILE_PARSER_H #define KFN_FILE_PARSER_H #include #include // Parser for the KFN file format class KFNFileParser { public: // File types stored in the KFN file enum { TYPE_SONGTEXT = 1, TYPE_MUSIC = 2, TYPE_IMAGE = 3, TYPE_FONT = 4, TYPE_VIDEO = 5 }; // Directory entry class Entry { public: QString filename; // the original file name in the original encoding int type; // the file type; see TYPE_ int length_in; // the file length in the KFN file int length_out; // the file lenght on disk; if the file is encrypted it is the same or smaller than length_in int offset; // the file offset in the KFN file starting from the directory end int flags; // the file flags; 0 means "not encrypted", 1 means "encrypted" }; KFNFileParser(); // Open the KFN file bool open( const QString& filename ); // Close the file void close(); // If error, returns the message const char * errorMsg() const; // Extracts and writes the music file in a separate file bool writeMusicFile( QFile& outfile ); // Returns lyrics as LRC data QString lyricsAsLRC(); // Original music file extention QString musicFileExtention() const; // Directory entries const QList entries() const; private: quint8 readByte(); quint16 readWord(); quint32 readDword(); void readBytes( char * buf, unsigned int len ); QByteArray readBytes( unsigned int len ); QString readString( unsigned int len ); QByteArray extract( const Entry& entry ); private: int m_entryMusic; int m_entrySongIni; QFile m_file; QString m_errorMsg; QByteArray m_aesKey; QList m_entries; }; #endif // KFN_FILE_PARSER_H karlyriceditor-1.11/src/licensing.cpp000066400000000000000000000164111233766701300177440ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include #include #if defined (USE_LICENSING) #include #include #endif #include "licensing.h" Licensing * pLicensing; static const char * CA_DER_CERT = "MIICkDCCAfmgAwIBAgIJAJvgo443LFCmMA0GCSqGSIb3DQEBBQUAMIGAMTEwLwYD" "VQQDDChrYXJseXJpY2VkaXRvci5jb20uY2VydGlmaWNhdGUuYXV0aG9yaXR5MRMw" "EQYDVQQIDApDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzEpMCcGCSqGSIb3DQEJARYa" "c3VwcG9ydEBrYXJseXJpY2VkaXRvci5jb20wHhcNMTEwNDIwMDg1MTE2WhcNMjEw" "NDE3MDg1MTE2WjCBgDExMC8GA1UEAwwoa2FybHlyaWNlZGl0b3IuY29tLmNlcnRp" "ZmljYXRlLmF1dGhvcml0eTETMBEGA1UECAwKQ2FsaWZvcm5pYTELMAkGA1UEBhMC" "VVMxKTAnBgkqhkiG9w0BCQEWGnN1cHBvcnRAa2FybHlyaWNlZGl0b3IuY29tMIGf" "MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCv6WOtHgsndu8IaZyP2xgke0rHAWJv" "y5cPpRNWNB/2G5ogbL629A9a3ehVIRpAbJyHHiSuNX+wc4YiczwxZjW32KU3QFKf" "XtCPDOVX5OdToMnKIEngSD65QiYSv/RqCW45z+Mc0LqWAE9BftEybpdUfubYV5pY" "r6pckkKWpPb1xQIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUA" "A4GBAC08oP+QzbT8TfQUqXTnAxSQNVWZAPHL4wOQMbC/MDumdg2N8iYDPFEX3QKE" "vfWiqr3nwmOrAZxAQh6iqMa73JqaVm6h2oqrAcf9XnaoR63G+gk1EluDD9AK8MKf" "b3IE9nt1TDhlJkbGG8rMD692HkpMGggsco93PQLWmtvcyIIo"; static const char * CA_SUBJECT = "karlyriceditor.com.certificate.authority"; class LicensingPrivate { public: bool m_valid; QString m_errmsg; QString m_subject; QDate m_expires; }; Licensing::Licensing() { d = new LicensingPrivate(); d->m_valid = false; } Licensing::~Licensing() { delete d; } bool Licensing::isEnabled() const { #if defined (USE_LICENSING) return true; #else return false; #endif } QString Licensing::subject() const { return d->m_valid ? d->m_subject : QString(); } QDate Licensing::expires() const { return d->m_valid ? d->m_expires : QDate(); } bool Licensing::init() { #if defined (USE_LICENSING) OpenSSL_add_all_algorithms(); return true; #else return false; #endif } bool Licensing::validate( const QString& license ) { #if defined (USE_LICENSING) char subject[1024]; ASN1_TIME *naft; X509 * cert = 0; X509 * cacert = 0; X509_STORE_CTX * storeContext = 0; X509_STORE * certStore = 0; // Reset the license d->m_valid = false; // Get the DER cert data QByteArray certdata = QByteArray::fromBase64( license.toUtf8() ); if ( certdata.isEmpty() ) { d->m_errmsg = "Invalid encoded license content"; return false; } // Convert the DER-encoded certificate to an X509 object long len = certdata.size(); const unsigned char * pcertdata = (const unsigned char *) certdata.data(); cert = d2i_X509( 0, &pcertdata, len ); if ( !cert ) { d->m_errmsg = "Invalid license content (error 2010)"; goto cleanup; } // Create a cert store certStore = X509_STORE_new(); if ( !certStore ) { d->m_errmsg = "Cannot allocate memory"; goto cleanup; } if ( X509_STORE_add_cert( certStore, cert ) <= 0 ) { d->m_errmsg = "Invalid license content (error 2012)"; goto cleanup; } // Get the DER cert data for CA certdata = QByteArray::fromBase64( QByteArray(CA_DER_CERT) ); if ( certdata.isEmpty() ) { d->m_errmsg = "Invalid internal CA certificate (error 2020)"; goto cleanup; } // Convert the DER-encoded CA certificate to an X509 object len = certdata.size(); pcertdata = (const unsigned char *) certdata.data(); cacert = d2i_X509( 0, &pcertdata, len ); if ( !cacert ) { d->m_errmsg = "Invalid internal CA content (error 2025)"; goto cleanup; } // Add the CA cert to the store if ( X509_STORE_add_cert( certStore, cacert ) <= 0 ) { d->m_errmsg = "Invalid license content (error 2030)"; goto cleanup; } storeContext = X509_STORE_CTX_new(); if ( !storeContext ) { d->m_errmsg = "Cannot allocate memory"; goto cleanup; } // Prepare the chain if ( X509_STORE_CTX_init( storeContext, certStore, cert, NULL ) <= 0 ) { d->m_errmsg = "Cannot prepare cert chain"; goto cleanup; } if ( X509_verify_cert( storeContext ) <= 0 ) { int err = X509_STORE_CTX_get_error( storeContext ); switch ( err ) { case X509_V_ERR_CERT_NOT_YET_VALID: d->m_errmsg = "License is not yet valid"; break; case X509_V_ERR_CERT_HAS_EXPIRED: d->m_errmsg = "License expired"; break; default: d->m_errmsg = QString("License verification error (error %1)") .arg( err ); break; } goto cleanup; } // Cert is valid, check the issuer if ( X509_NAME_get_text_by_NID( X509_get_issuer_name( cert ), NID_commonName, subject, sizeof(subject) ) < 1 ) { d->m_errmsg = "Cannot parse the license issuer"; goto cleanup; } if ( strcmp( subject, CA_SUBJECT) ) { d->m_errmsg = "License is not issued by the valid issuer"; goto cleanup; } // Parse the CN if ( X509_NAME_get_text_by_NID( X509_get_subject_name( cert ), NID_commonName, subject, sizeof(subject) ) < 1 ) { d->m_errmsg = "Cannot parse the license subject"; goto cleanup; } // Our CA public cert is also valid as it is self-signed, so we need to check CNAME! if ( !strcmp( subject, CA_SUBJECT) ) { d->m_errmsg = "Cannot use the CA certificate as the license"; goto cleanup; } d->m_subject = subject; // Parse the expiration date naft = X509_get_notAfter( cert ); // We're not interested in time int year, month, day; if ( naft->type == V_ASN1_UTCTIME && sscanf( (const char *) naft->data, "%02d%02d%02d", &year, &month, &day ) == 3 ) { // Fix the year year += 2000; d->m_expires.setDate( year, month, day ); d->m_valid = true; } else if ( naft->type == V_ASN1_GENERALIZEDTIME && sscanf( (const char *) naft->data, "%04d%02d%02d", &year, &month, &day ) != 3 ) { d->m_expires.setDate( year, month, day ); d->m_valid = true; } cleanup: if ( cert ) X509_free( cert ); if ( cacert ) X509_free( cacert ); if ( storeContext ) X509_STORE_CTX_free( storeContext ); if ( certStore ) X509_STORE_free( certStore ); return d->m_valid; #else return false; #endif } bool Licensing::isValid() const { return d->m_valid; } QString Licensing::errMsg() const { return d->m_errmsg; } karlyriceditor-1.11/src/licensing.h000066400000000000000000000042641233766701300174140ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef LICENSING_H #define LICENSING_H #include class LicensingPrivate; // // A simple licensing class based on X509 certificates. // // The application author must create the CA and store the CA public key in the cpp file as well // as the CA issuer common name (CN). Then the author may generate certs for users (private keys // could be discarded), sign them using the CA private key, and provide the certificate content // as license key. // class Licensing { public: Licensing(); virtual ~Licensing(); bool isEnabled() const; bool init(); bool validate( const QString& cert ); QString errMsg() const; bool isValid() const; // Those return the licensing information; should only be called // if validate() returned true. QString subject() const; QDate expires() const; private: LicensingPrivate * d; }; extern Licensing * pLicensing; #endif // LICENSING_H karlyriceditor-1.11/src/lyrics.cpp000066400000000000000000000107711233766701300173010ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include "lyrics.h" Lyrics::Lyrics() { m_scanning = false; m_added_eofs = 0; m_currentLyric.timing = -1; m_currentLyric.pitch = -1; } Lyrics::~Lyrics() { } void Lyrics::beginLyrics() { m_scanning = true; m_added_eofs = 0; m_currentLyric.timing = -1; m_currentLyric.pitch = -1; m_currentLyric.text.clear(); } void Lyrics::curLyricSetTime( qint64 timems ) { if ( !m_scanning ) abort(); m_currentLyric.timing = timems; } void Lyrics::curLyricSetPitch( int pitch ) { if ( !m_scanning ) abort(); m_currentLyric.pitch = pitch; } void Lyrics::curLyricAppendText( const QString& text ) { if ( !m_scanning ) abort(); m_currentLyric.text += text; } void Lyrics::curLyricAdd() { if ( !m_scanning ) abort(); // If time is not set, do not add if ( m_currentLyric.timing == -1 ) { // Nothing to add m_currentLyric.pitch = -1; m_currentLyric.text.clear(); return; } // Create a new block and new line, and fill the first entry // if there are no blocks or m_added_eofs > 1 if ( m_lyrics.isEmpty() || m_added_eofs > 1 ) { Block block; Line line; line.push_back( m_currentLyric ); block.push_back( line ); m_lyrics.push_back( block ); } else { Block& lastBlock = m_lyrics.back(); // Create a new line and fill the first entry if there // are no lines, or m_added_eofs > 0 if ( lastBlock.isEmpty() || m_added_eofs > 0 ) { Line line; line.push_back( m_currentLyric ); lastBlock.push_back( line ); } else { Line& lastLine = lastBlock.back(); lastLine.push_back( m_currentLyric ); } } #if 0 qDebug("block %d, line %d, entry %d: crlfs %d: %d %s", m_lyrics.size() - 1, m_lyrics.back().size() - 1, m_lyrics.back().back().size() - 1, m_added_eofs, (int) m_currentLyric.timing, qPrintable( m_currentLyric.text ) ); #endif // Reset the fields m_added_eofs = 0; m_currentLyric.timing = -1; m_currentLyric.pitch = -1; m_currentLyric.text.clear(); } void Lyrics::curLyricAddEndOfLine() { if ( !m_scanning ) abort(); curLyricAdd(); m_added_eofs++; } void Lyrics::endLyrics() { curLyricAdd(); m_scanning = false; } int Lyrics::totalBlocks() const { return m_lyrics.size(); } const Lyrics::Block& Lyrics::block( int index ) const { return m_lyrics[ index ]; } bool Lyrics::isEmpty() const { return m_lyrics.isEmpty(); } void Lyrics::clear() { m_lyrics.clear(); } QString Lyrics::pitchToNote( int pitch, bool ) { return QString::number( pitch ); } bool Lyrics::addBackgroundEvent( qint64 timing, const QString& text ) { return m_events.addEvent( timing, text ); } LyricsEvents Lyrics::events() const { return m_events; } /* int Lyrics::isSpecialSequence( const QString& line, int offset ) { if ( line[offset] != '@' || offset + 1 >= line.length() ) return 0; offset++; if ( line[offset] == '#' && offset + 6 < line.length() ) return 7; if ( (line[offset] == '!' || line[offset] == 'S') && offset + 1 < line.length() ) return 1; return 0; } QString Lyrics::stripSpecialSequences( const QString& line ) { QString out; for ( unsigned int i = 0; i < line.length(); i++ ) { int speclen = isSpecialSequence( line, i ); if ( speclen != 0 ) { i += speclen; continue; } out.push_back( line[i]); } return out; } */ karlyriceditor-1.11/src/lyrics.h000066400000000000000000000063171233766701300167470ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef LYRICS_H #define LYRICS_H #include #include #include #include #include "lyricsevents.h" class Lyrics { public: Lyrics(); ~Lyrics(); static const int PITCH_NOTE_FREESTYLE = (1 << 17); static const int PITCH_NOTE_GOLDEN = (1 << 18); // // Those functions are used to access lyrics // typedef struct { qint64 timing; QString text; // May be empty int pitch; // -1 if not set, used for Ultrastar } Syllable; typedef QVector Line; typedef QList Block; // Lyrics are composed from one or more blocks, depending on mode // A block can contain one or more lines (up to the whole text) // A line can contain one or more syllables bool isEmpty() const; int totalBlocks() const; const Block& block( int index ) const; // // Those functions are used during lyric scanning // // Indicates the lyrics are being built void beginLyrics(); // Set the time for current lyric void curLyricSetTime( qint64 timems ); // Add background event bool addBackgroundEvent( qint64 timing, const QString& text ); // Set the pitch for current lyric void curLyricSetPitch( int pitch ); // Append text to current lyric void curLyricAppendText( const QString& text ); // Adds the currently set lyric void curLyricAdd(); // Add "end of line". Multiple end of lines mean end of paragraph. This implies curLyricAdd() void curLyricAddEndOfLine(); // Indicates the lyrics are being built. No curlyric* functions may be called. void endLyrics(); // Clear the lyrics void clear(); // Pitch text representation static QString pitchToNote( int pitch, bool show_octave = true ); // Returns the events LyricsEvents events() const; private: QList m_lyrics; LyricsEvents m_events; // Used during scanning lyrics bool m_scanning; int m_added_eofs; Syllable m_currentLyric; }; #endif // LYRICS_H karlyriceditor-1.11/src/lyricsevents.cpp000066400000000000000000000162231233766701300205240ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include #include #include "lyricsevents.h" #include "background.h" enum { TYPE_IMAGE, TYPE_VIDEO, }; LyricsEvents::LyricsEvents() { m_lastUpdate = -1; m_nextUpdate = 0; m_eventTiming = 0; } LyricsEvents::~LyricsEvents() { cleanPrepared(); } LyricsEvents::LyricsEvents( const LyricsEvents& ev ) { // We do not copy m_preparedEvents and m_cachedImage m_events = ev.m_events; m_lastUpdate = -1; m_nextUpdate = 0; m_eventTiming = 0; } bool LyricsEvents::addEvent( qint64 timing, const QString& text ) { Event ev; if ( !parseEvent( text, &ev, 0 ) ) return false; ev.timing = timing; m_events[ timing ] = ev; return true; } bool LyricsEvents::isEmpty() const { return m_events.isEmpty(); } QString LyricsEvents::validateEvent( const QString& text ) { QString errmsg; if ( !parseEvent( text, 0, &errmsg) ) return errmsg; return ""; } bool LyricsEvents::parseEvent( const QString& text, Event * event, QString * errmsg ) { QRegExp check("^(\\w+)=(.*)$"); if ( text.trimmed().indexOf( check ) == -1 ) return "Invalid event format; must be like IMAGE=path"; QString key = check.cap( 1 ); QString value = check.cap( 2 ); if ( key == "IMAGE" ) { if ( !QFile::exists( value ) ) { if ( errmsg ) *errmsg = QString("Image file %1 does not exist") .arg(value); return false; } QImage img; if ( !img.load( value ) ) { if ( errmsg ) *errmsg = QString("File %1 is not a supported image") .arg(value); return false; } if ( event ) { event->type = TYPE_IMAGE; event->data = value; } return true; } else if ( key == "VIDEO" ) { QString filename = value; QRegExp videopathstart("^(.*);STARTFRAME=(\\d+)$"); if ( value.indexOf( videopathstart ) != -1 ) filename = videopathstart.cap(1); if ( !QFile::exists( filename ) ) { if ( errmsg ) *errmsg = QString("Video file %1 does not exist") .arg(filename); return false; } FFMpegVideoDecoder vd; if ( !vd.openFile( filename ) ) { if ( errmsg ) *errmsg = QString("File %1 is not a supported video") .arg(filename); return false; } if ( event ) { event->type = TYPE_VIDEO; event->data = value; } return true; } if ( errmsg ) *errmsg = QString("Invalid event name '%1'") .arg(key); return false; } void LyricsEvents::cleanPrepared() { for ( QMap< qint64, Background* >::iterator it = m_preparedEvents.begin(); it != m_preparedEvents.end(); ++it ) delete it.value(); m_preparedEvents.clear(); } bool LyricsEvents::prepare( QString * errmsg ) { cleanPrepared(); for ( QMap< qint64, Event >::const_iterator it = m_events.begin(); it != m_events.end(); ++it ) { Background * bgev = 0; switch ( it.value().type ) { case TYPE_IMAGE: bgev = new BackgroundImage( it.value().data ); break; case TYPE_VIDEO: bgev = new BackgroundVideo( it.value().data ); break; } if ( !bgev ) continue; if ( !bgev->isValid() ) { delete bgev; if ( errmsg ) *errmsg = "Invalid event"; return false; } m_preparedEvents[ it.key() ] = bgev; } return true; } void LyricsEvents::adjustTime( qint64 timing, qint64 newtiming ) { QMap< qint64, Background* >::iterator it = m_preparedEvents.find( timing ); if ( it != m_preparedEvents.end()) { m_preparedEvents[ newtiming ] = m_preparedEvents[timing]; m_preparedEvents.erase( it ); } } bool LyricsEvents::updated( qint64 timing ) const { if ( m_nextUpdate == -1 ) return false; if ( m_nextUpdate == 0 ) return true; return ( m_nextUpdate <= timing ); } void LyricsEvents::draw( qint64 timing, QImage& image ) { // Do we have precompiled events? if ( !m_events.isEmpty() && m_preparedEvents.isEmpty() ) return; bool cache_changed = false; // Find current event QMap< qint64, Background* >::const_iterator found = m_preparedEvents.end(); for ( QMap< qint64, Background* >::const_iterator it = m_preparedEvents.begin(); it != m_preparedEvents.end(); ++it ) { if ( it.key() <= timing ) found = it; } if ( found == m_preparedEvents.end() ) return; Background * bg = found.value(); // Same event as before? if ( found.key() != m_eventTiming ) { m_eventTiming = found.key(); cache_changed = true; bg->reset(); } else if ( m_lastUpdate > timing ) { // Time went backward? bg->reset(); } m_lastUpdate = timing; if ( cache_changed || ( m_nextUpdate != -1 && ( m_nextUpdate == 0 || timing >= m_nextUpdate ) ) ) { m_cachedImage.fill( 0 ); m_nextUpdate = bg->doDraw( m_cachedImage, timing - m_eventTiming ); } QPainter p( &image ); // Stretch the image to fit the window. // We cannot use Qt::KeepAspectRatioByExpanding as it takes the left top of the image, while we would // center it //QImage scaled = m_cachedImage.scaled( image.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation ); //p.drawImage( (image.width() - scaled.width()) / 2, (image.height() - scaled.height()) / 2, scaled ); // Calculate which way to scale/shrink the image double width_ratio = (double) m_cachedImage.width() / (double) image.width(); double height_ratio = (double) m_cachedImage.height() / (double) image.height(); // Use the smallest ratio double ratio = qMin( width_ratio, height_ratio ); // qDebug("using ratio %g, %dx%d -> %gx%g", ratio, m_cachedImage.width(), m_cachedImage.height(), m_cachedImage.width() / ratio, m_cachedImage.height() / ratio ); // Get the scaled image QImage scaled = m_cachedImage.scaled( m_cachedImage.width() / ratio, m_cachedImage.height() / ratio, Qt::KeepAspectRatio, Qt::SmoothTransformation ); // Draw the part of scaled image p.drawImage( 0, 0, scaled, (scaled.width() - image.width()) / 2, (scaled.height() - image.height()) / 2, image.width(), image.height() ); // Adjust nextUpdate as there may be more events if ( m_nextUpdate == -1 ) { found++; if ( found != m_preparedEvents.end() ) m_nextUpdate = found.key() - 250; // in advance } } karlyriceditor-1.11/src/lyricsevents.h000066400000000000000000000050021233766701300201620ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef LYRICSEVENTS_H #define LYRICSEVENTS_H #include #include class Background; class LyricsEvents { public: LyricsEvents(); ~LyricsEvents(); // copy LyricsEvents( const LyricsEvents& ); // check bool isEmpty() const; // Add the events bool addEvent( qint64 timing, const QString& text ); // When playing bool prepare( QString * errmsg = 0 ); void adjustTime( qint64 timing, qint64 newtiming ); bool updated( qint64 timing ) const; void draw( qint64 timing, QImage& image ); const QColor * iColor( qint64 timing ) const; static QString validateEvent( const QString& text ); private: class Event { public: qint64 timing; int type; QString data; }; void cleanPrepared(); static bool parseEvent( const QString& text, Event * event = 0, QString * errmsg = 0 ); // Event storage which is copied QMap< qint64, Event > m_events; // Color change storage QMap< qint64, QColor > m_colors; // Prepared event storage which is NOT copied QMap< qint64, Background* > m_preparedEvents; // State QImage m_cachedImage; qint64 m_lastUpdate; qint64 m_nextUpdate; // Current event timing qint64 m_eventTiming; }; #endif // LYRICSEVENTS_H karlyriceditor-1.11/src/lyricsrenderer.cpp000066400000000000000000000027151233766701300210270ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include "lyricsrenderer.h" LyricsRenderer::LyricsRenderer() { } LyricsRenderer::~LyricsRenderer() { } QImage LyricsRenderer::image() const { return m_image; } karlyriceditor-1.11/src/lyricsrenderer.h000066400000000000000000000036601233766701300204740ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef LYRICSRENDERER_H #define LYRICSRENDERER_H #include // This is an abstract rendering class which covers CD+G and text rendering class LyricsRenderer { public: enum { UPDATE_NOCHANGE, // image did not change at all UPDATE_COLORCHANGE, // only colors of some image characters changed UPDATE_FULL, // a whole image changed UPDATE_RESIZED, // not only the whole image changed, but also the image size changed }; LyricsRenderer(); virtual ~LyricsRenderer(); virtual int update( qint64 timing ) = 0; QImage image() const; protected: // Rendered image QImage m_image; }; #endif // LYRICSRENDERER_H karlyriceditor-1.11/src/lyricswidget.cpp000066400000000000000000000062541233766701300205060ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include #include "lyricswidget.h" #include "testwindow.h" #include "settings.h" #include "textrenderer.h" #include "cdgrenderer.h" static const unsigned int PADDING_X = 10; static const unsigned int PADDING_Y = 8; LyricsWidget::LyricsWidget( QWidget *parent ) : QWidget(parent) { m_renderer = 0; m_lastImage = QImage( 720, 480, QImage::Format_ARGB32 ); } LyricsWidget::~LyricsWidget() { delete m_renderer; } QSize LyricsWidget::sizeHint () const { return minimumSizeHint(); } QSizePolicy LyricsWidget::sizePolicy() const { return QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); } QSize LyricsWidget::minimumSizeHint() const { return QSize( m_lastImage.width() + 2 * PADDING_X, m_lastImage.height() + 2 * PADDING_Y ); } void LyricsWidget::paintEvent( QPaintEvent * ) { QPainter p( this ); p.fillRect( QRect( 0, 0, width() - 1, height() - 1 ), Qt::black ); int x = (width() - m_lastImage.width()) / 2; int y = (height() - m_lastImage.height() ) / 2; p.drawImage( x, y, m_lastImage ); } void LyricsWidget::setLyrics( const Lyrics& lyrics, const QString& artist, const QString& title ) { TextRenderer * re = new TextRenderer( 720, 480 ); re->setLyrics( lyrics ); re->setPreambleData( 5, 5000, 10 ); if ( !artist.isEmpty() && !title.isEmpty() ) re->setTitlePageData( artist, title, "", 5000 ); m_renderer = re; updateGeometry(); update(); } void LyricsWidget::setCDGdata( const QByteArray& cdgdata ) { CDGRenderer * re = new CDGRenderer(); re->setCDGdata( cdgdata ); m_renderer = re; updateGeometry(); update(); } void LyricsWidget::updateLyrics( qint64 tickmark ) { if ( isHidden() || !m_renderer ) return; int status = m_renderer->update( tickmark ); if ( status == LyricsRenderer::UPDATE_NOCHANGE ) return; m_lastImage = m_renderer->image(); if ( status == LyricsRenderer::UPDATE_RESIZED ) updateGeometry(); update(); } karlyriceditor-1.11/src/lyricswidget.h000066400000000000000000000040021233766701300201400ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef LYRICSWIDGET_H #define LYRICSWIDGET_H #include #include "lyrics.h" #include "cdgrenderer.h" #include "lyricsrenderer.h" class LyricsWidget : public QWidget { Q_OBJECT public: LyricsWidget( QWidget * parent ); ~LyricsWidget(); // For lyrics void setLyrics( const Lyrics& lyrics, const QString& artist = "", const QString& title = "" ); // For CD+G void setCDGdata( const QByteArray& cdgdata ); public slots: void updateLyrics( qint64 tickmark ); protected: void paintEvent( QPaintEvent * ); QSize sizeHint () const; QSizePolicy sizePolicy () const; QSize minimumSizeHint() const; private: LyricsRenderer * m_renderer; QImage m_lastImage; }; #endif // LYRICSWIDGET_H karlyriceditor-1.11/src/main.cpp000066400000000000000000000040231233766701300167110ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include "mainwindow.h" #include int main(int argc, char *argv[]) { Q_INIT_RESOURCE(resources); QApplication app(argc, argv); QCoreApplication::setOrganizationName("karlyriceditor.com"); QCoreApplication::setOrganizationDomain("karlyriceditor.com"); QCoreApplication::setApplicationName("karlyriceditor"); MainWindow wnd; wnd.show(); return app.exec(); } #if defined (_WIN32) // SDL defines its own main() function in SDL_main. And so does Qt, so if we continue without // the #define below we'll end up with the following link error: // libqtmain.a(qtmain_win.o):qtmain_win.cpp:(.text+0x159): undefined reference to `qMain(int, char**)' int qMain( int argc, char ** argv ) { return main( argc, argv ); } #endif karlyriceditor-1.11/src/mainwindow.cpp000066400000000000000000000564411233766701300201540ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "audioplayer.h" #include "wizard_newproject.h" #include "mainwindow.h" #include "playerwidget.h" #include "project.h" #include "settings.h" #include "viewwidget.h" #include "testwindow.h" #include "version.h" #include "projectsettings.h" #include "recentfiles.h" #include "gentlemessagebox.h" #include "lyricswidget.h" #include "ui_dialog_about.h" #include "videogenerator.h" #include "cdggenerator.h" #include "videoencodingprofiles.h" #include "licensing.h" #include "ui_dialog_registration.h" MainWindow * pMainWindow; MainWindow::MainWindow() : QMainWindow(), Ui::MainWindow() { // A lot of widgets use pMainWindow in constructors pMainWindow = this; // Settings pSettings = new Settings(); // Call UIC-generated code setupUi( this ); // Video profiles pVideoEncodingProfiles = new VideoEncodingProfiles(); // Initialize stuff m_project = 0; m_testWindow = 0; // Licensing pLicensing = new Licensing(); if ( pLicensing->init() ) { QString key = QSettings().value( "general/registrationkey", "" ).toString(); pLicensing->validate( key ); } // Create dock widgets m_player = new PlayerWidget( this ); addDockWidget( Qt::BottomDockWidgetArea, m_player ); actionShow_Player_dock_wingow->setChecked( true ); // Create a lyric viewer window, hidden so far m_viewer = new ViewWidget( this ); // Recent files m_recentFiles = new RecentFiles( menuFile, actionQuit ); connect( m_recentFiles, SIGNAL( openRecentFile(const QString&) ), this, SLOT( openRecentFile( const QString&)) ); // Do the rest connectActions(); createToolbars(); // Add WhatsThis action to the help menu QAction * whatsthis = QWhatsThis::createAction( this ); menuHelp->insertAction( actionAbout, whatsthis ); // Validator icons m_validatorIconRegular.addFile( ":/images/dryicons_application_search.png", QSize(), QIcon::Normal, QIcon::Off ); m_validatorIconAccepted.addFile( ":/images/dryicons_application_accept.png", QSize(), QIcon::Normal, QIcon::Off ); m_validatorIconFailed.addFile( ":/images/dryicons_application_deny.png", QSize(), QIcon::Normal, QIcon::Off ); // Restore current directory QDir::setCurrent( QSettings().value( "general/currentdirectory", "." ).toString() ); // Update window state updateState(); checkNewVersionAvailable(); } MainWindow::~MainWindow() { } void MainWindow::checkNewVersionAvailable() { QSettings settings; if ( !pSettings->m_checkForUpdates ) return; if ( settings.contains( "advanced/lastupdate" ) ) { QDateTime lastupdate = settings.value( "advanced/lastupdate" ).toDateTime(); if ( lastupdate.secsTo( QDateTime::currentDateTime() ) < 86400 ) return; } // Create a New version available object if necessary. This object will auto-delete itself CheckNewVersion * pNewVer = new CheckNewVersion(); connect( pNewVer, SIGNAL(error(int)), this, SLOT(newVerAvailError(int)) ); connect( pNewVer, SIGNAL(newVersionAvailable( NewVersionMetaMap )), this, SLOT(newVerAvailable(NewVersionMetaMap)) ); pNewVer->setUrl( "http://www.ulduzsoft.com.com/karlyriceditor_latestversion.txt" ); pNewVer->setCurrentVersion( QString("%1.%2").arg( APP_VERSION_MAJOR ) . arg( APP_VERSION_MINOR ) ); pNewVer->start(); } void MainWindow::closeEvent(QCloseEvent *event) { pAudioPlayer->stop(); if ( m_project && !tryCloseCurrentProject() ) { event->ignore(); return; } // Save current directory QSettings().setValue( "general/currentdirectory", QDir::currentPath() ); QMainWindow::closeEvent( event ); event->accept(); } void MainWindow::connectActions() { // All those actions are defined in .ui file connect( actionNew_project, SIGNAL( triggered()), this, SLOT( act_fileNewProject()) ); connect( action_openProject, SIGNAL( triggered()), this, SLOT( act_fileOpenProject()) ); connect( actionSave, SIGNAL( triggered()), this, SLOT( act_fileSaveProject()) ); connect( actionSave_as, SIGNAL( triggered()), this, SLOT( act_fileSaveProjectAs()) ); connect( actionInsert_tag, SIGNAL( triggered()), this, SLOT( act_editInsertTag()) ); connect( actionRemove_tag, SIGNAL( triggered()), this, SLOT( act_editRemoveTag()) ); connect( actionRemove_all_tags, SIGNAL( triggered()), this, SLOT(act_editRemoveAllTags()) ); connect( actionOpen_lyric_file, SIGNAL( triggered()), this, SLOT(act_projectOpenLyricFile()) ); connect( actionQuit, SIGNAL( triggered()), qApp, SLOT( quit()) ); connect( actionClear_text, SIGNAL( triggered()), this, SLOT( act_editClearText()) ); connect( actionTrimspaces, SIGNAL( triggered()), this, SLOT( act_editTrimspaces()) ); connect( actionGeneral, SIGNAL( triggered()), this, SLOT(act_settingsGeneral()) ); connect( actionSplit_current_line, SIGNAL( triggered()), this, SLOT(act_editSplitLine()) ); connect( actionAbout, SIGNAL( triggered()), this, SLOT(act_helpAbout()) ); connect( actionProject_settings, SIGNAL( triggered()), this, SLOT(act_projectSettings()) ); connect( actionExport_lyric_file, SIGNAL( triggered()), this, SLOT(act_projectExportLyricFile()) ); connect( actionExport_video_file, SIGNAL( triggered()), this, SLOT(act_projectExportVideoFile()) ); connect( actionExport_CD_G_file, SIGNAL( triggered()), this, SLOT(act_projectExportCDGFile()) ); connect( actionEdit_header_data, SIGNAL( triggered()), this, SLOT( act_projectEditHeader()) ); connect( actionValidate_lyrics, SIGNAL( triggered()), this, SLOT( act_projectValidateLyrics()) ); connect( actionView_lyric_file, SIGNAL( triggered()), this, SLOT( act_projectViewLyricFile()) ); connect( actionTest_lyric_file, SIGNAL( triggered()), this, SLOT( act_projectTest()) ); connect( actionTest_CDG_lyrics, SIGNAL( triggered()), this, SLOT( act_projectTestCDG()) ); connect( actionShow_Player_dock_wingow, SIGNAL(triggered(bool)), this, SLOT(act_settingsShowPlayer(bool)) ); connect( actionInsert_picture, SIGNAL(triggered(bool)), this, SLOT(act_editInsertPicture() ) ); connect( actionInsert_video, SIGNAL(triggered(bool)), this, SLOT(act_editInsertVideo() ) ); connect( actionInsert_color_change, SIGNAL(triggered(bool)), this, SLOT(act_editInsertColorChange() ) ); connect( actionAdd_eol_timing_marks, SIGNAL(triggered(bool)), this, SLOT(act_addMissingTimingMarks() ) ); connect( actionTime_adjustment, SIGNAL(triggered(bool)), this, SLOT(act_adjustTiming() ) ); // docks connect( m_player,SIGNAL(visibilityChanged(bool)), this, SLOT(visibilityPlayer(bool)) ); // Text editor connect( actionUndo, SIGNAL( triggered()), editor, SLOT( undo()) ); connect( actionRedo, SIGNAL( triggered()), editor, SLOT( redo()) ); // Registration connect( actionRegistration, SIGNAL( triggered()), this, SLOT( act_helpRegistration()) ); if ( !pLicensing->isEnabled() ) actionRegistration->setVisible( false ); } void MainWindow::createToolbars() { // Main toolbar QToolBar * main = addToolBar( "Main toolbar" ); main->addAction( actionNew_project ); main->addAction( actionSave ); // Editor toolbar QToolBar * editor = addToolBar( "Editor toolbar" ); editor->addAction( actionUndo ); editor->addAction( actionRedo ); editor->addSeparator(); editor->addAction( actionInsert_tag ); editor->addAction( actionRemove_tag ); editor->addSeparator(); editor->addAction( actionValidate_lyrics ); editor->addAction( actionView_lyric_file ); editor->addAction( actionTest_lyric_file ); // Style for all main window toolbars setIconSize ( QSize( 32, 32 ) ); setToolButtonStyle ( Qt::ToolButtonTextUnderIcon ); } void MainWindow::editor_undoAvail(bool available) { actionUndo->setEnabled(available); } void MainWindow::editor_redoAvail(bool available) { actionRedo->setEnabled( available ); } void MainWindow::act_fileNewProject() { if ( m_project ) { if ( QMessageBox::question( 0, tr("Close existing project"), tr("Do you really want to close existing project, and create a new project?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No ) return; if ( !tryCloseCurrentProject() ) return; } Project * newproj = new Project( editor ); // Show the new project wizard WizardNewProject::Wizard wi( newproj, this ); if ( wi.exec() == QDialog::Rejected ) { delete newproj; return; } // Save the project m_project = newproj; if ( !act_fileSaveProjectAs() ) { delete newproj; return; } setCurrentProject( newproj ); } void MainWindow::act_fileOpenProject() { if ( m_project ) { if ( QMessageBox::question( 0, tr("Close existing project"), tr("Do you really want to close existing project, and create a new project?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No ) return; if ( !tryCloseCurrentProject() ) return; } QString fileName = QFileDialog::getOpenFileName( this, tr("Open a project file"), QString::null, tr("Project files (*.kleproj)") ); if ( fileName.isEmpty() ) return; loadProject( fileName ); } void MainWindow::openRecentFile( const QString& file ) { loadProject( file ); } bool MainWindow::loadProject( const QString& fileName ) { Project * newproj = new Project( editor ); if ( !newproj->load( fileName ) ) { m_recentFiles->removeRecentFile( fileName ); delete newproj; return false; } m_recentFiles->setCurrentFile( fileName ); m_projectFile = fileName; setCurrentProject( newproj ); // Change the current dir QFileInfo finfo( fileName ); QDir::setCurrent( finfo.path() ); return true; } bool MainWindow::act_fileSaveProject() { if ( m_projectFile.isEmpty() ) return act_fileSaveProjectAs(); else return saveProject( m_projectFile ); } bool MainWindow::act_fileSaveProjectAs() { QString fileName = QFileDialog::getSaveFileName( this, tr("Save as a project file"), QString::null, tr("Project files (*.kleproj)") ); if ( fileName.isEmpty() ) return false; if ( !saveProject( fileName ) ) return false; m_projectFile = fileName; setWindowTitle( tr("%1[*] - Lyric Editor") .arg( m_projectFile ) ); // Change the current dir QFileInfo finfo( fileName ); QDir::setCurrent( finfo.path() ); return true; } bool MainWindow::saveProject( const QString& fileName ) { bool res = m_project->save( fileName ); updateState(); if ( res ) m_recentFiles->setCurrentFile( fileName ); return res; } bool MainWindow::tryCloseCurrentProject() { if ( m_project->isModified() ) { int rc = QMessageBox::question( 0, tr("Unsaved changes"), tr("The current project has unsaved changes. Do you want to save them?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Cancel ); if ( rc == QMessageBox::Cancel ) return false; if ( rc == QMessageBox::Yes ) { if ( !act_fileSaveProject() ) return false; } } delete m_project; m_project = 0; editor->setProject( 0 ); editor->clear(); updateState(); return true; } void MainWindow::setCurrentProject( Project * proj ) { m_project = proj; setWindowTitle( tr("%1[*] - Lyric Editor") .arg( m_projectFile ) ); statusBar()->showMessage( tr("Loading the music file %1") .arg(m_project->musicFile()), 2000); // Set the music file into player if ( m_player->openMusicFile( m_project ) ) { qint64 totaltime = m_player->totalTime(); if ( totaltime > 0 ) m_project->setSongLength( totaltime ); } } void MainWindow::act_editInsertTag() { if ( m_player->isPlaying() ) editor->insertTimeTag( m_player->currentTime() ); else editor->insertTimeTag( 0 ); } void MainWindow::act_editSplitLine() { editor->splitLine(); } void MainWindow::act_editRemoveTag() { editor->removeLastTimeTag(); } void MainWindow::act_editInsertPicture() { QString fileName = QFileDialog::getOpenFileName( this, tr("Open an image file"), QString::null, QString::null ); if ( fileName.isEmpty() ) return; editor->insertImageTag( fileName ); } void MainWindow::act_editInsertVideo() { QString fileName = QFileDialog::getOpenFileName( this, tr("Open a video file"), QString::null, QString::null ); if ( fileName.isEmpty() ) return; editor->insertVideoTag( fileName ); } void MainWindow::act_editInsertColorChange() { QColor newcolor = QColorDialog::getColor(); if ( newcolor.isValid() ) editor->insertColorChangeTag( newcolor.name() ); } void MainWindow::act_editClearText() { if ( QMessageBox::question( 0, tr("Clearing all the text"), tr("Are you sure you want to clear the text?\nThis operation cannot be undone!"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::Yes ) editor->clear(); } void MainWindow::act_editTrimspaces() { editor->removeExtraWhitespace(); } void MainWindow::act_editRemoveAllTags() { editor->removeAllTimeTags(); } void MainWindow::act_projectViewLyricFile() { if ( !editor->validate() ) return; QString lyrics = m_project->exportLyrics(); if ( lyrics.isEmpty() ) return; m_viewer->showText( lyrics ); } void MainWindow::act_projectValidateLyrics() { if ( editor->validate() ) actionValidate_lyrics->setIcon( m_validatorIconAccepted ); else actionValidate_lyrics->setIcon( m_validatorIconFailed ); } void MainWindow::act_projectOpenLyricFile() { QString fileName = QFileDialog::getOpenFileName( this, tr("Open a lyric file"), QString::null, tr("LRC files (*.lrc);;UltraStar/PowerKaraoke files (*.txt);;KAR/MIDI files (*.mid *.midi *.kar);;KaraFun files (*.kfn);;DEL Karaoke files (*.kok)") ); if ( fileName.isEmpty() ) return; m_project->importLyrics( fileName ); } void MainWindow::act_projectEditHeader() { ProjectSettings ps( m_project, false, this ); ps.setWindowTitle( tr("Edit lyric header data" ) ); ps.exec(); } void MainWindow::act_projectExportLyricFile() { if ( !editor->validate() ) return; QString filter_LRC1 = tr("LRC version 1 (*.lrc1)"); QString filter_LRC2 = tr("LRC version 2 (*.lrc)"); QString filter_UStar = tr("UltraStar (*.txt)"); QString filter, selected, outfilter; switch ( m_project->type() ) { case Project::LyricType_LRC1: filter = filter_LRC1 + ";;" + filter_LRC2 + ";;" + filter_UStar; selected = filter_LRC1; break; case Project::LyricType_LRC2: filter = filter_LRC2 + ";;" + filter_LRC1 + ";;" + filter_UStar; selected = filter_LRC2; break; case Project::LyricType_UStar: filter = filter_UStar + ";;" + filter_LRC2 + ";;" + filter_LRC1; selected = filter_UStar; break; } QString outfile = QFileDialog::getSaveFileName( 0, tr("Export lyrics to a file"), QString::null, filter, &outfilter ); QByteArray lyrics; if ( outfile.isEmpty() ) return; if ( outfilter == filter_LRC1 ) lyrics = m_project->exportLyricsAsLRC1(); else if ( outfilter == filter_LRC2 ) lyrics = m_project->exportLyricsAsLRC2(); else if ( outfilter == filter_UStar ) lyrics = m_project->exportLyricsAsUStar(); else { QMessageBox::critical( 0, "Unknown filter", "Unknown filter" ); return; } if ( lyrics.isEmpty() ) return; QFile file( outfile ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) { QMessageBox::critical( 0, tr("Cannot write lyric file"), tr("Cannot write lyric file %1: %2") .arg(outfile) .arg(file.errorString()) ); return; } file.write( lyrics ); } void MainWindow::act_projectSettings() { ProjectSettings ps( m_project, true, this ); ps.setWindowTitle( tr("Edit project settings" ) ); if ( ps.exec() == QDialog::Accepted ) { if ( ps.musicFileChanged() ) m_player->openMusicFile( m_project ); } } void MainWindow::act_settingsGeneral() { pSettings->edit(); } void MainWindow::act_helpAbout() { QDialog dlg; Ui::DialogAbout ui_about; ui_about.setupUi( &dlg ); ui_about.labelAbout->setText( tr("Karaoke Lyrics Editor version %1.%2

" "Copyright (C) George Yunaev 2009-2013, support@ulduzsoft.com

" "Web site: www.ulduzsoft.com/karlyriceditor

" "This program is licensed under terms of GNU General Public License " "version 3; see LICENSE file for details.") .arg(APP_VERSION_MAJOR) .arg(APP_VERSION_MINOR) ); dlg.exec(); } void MainWindow::updateState() { bool project_available = m_project ? true : false; bool project_ready = m_project ? m_player->isReady() : false; actionSave->setEnabled( project_available ); actionSave_as->setEnabled( project_available ); actionUndo->setEnabled( project_available ); actionRedo->setEnabled( project_available ); actionInsert_tag->setEnabled( project_ready ); actionRemove_tag->setEnabled( project_ready ); actionEdit_header_data->setEnabled( project_available ); actionValidate_lyrics->setEnabled( project_available ); actionTest_lyric_file->setEnabled( project_ready ); actionTest_CDG_lyrics->setEnabled( project_ready ); actionRemove_all_tags->setEnabled( project_available ); actionOpen_lyric_file->setEnabled( project_available ); actionClear_text->setEnabled( project_available ); actionTrimspaces->setEnabled( project_available ); actionInsert_picture->setEnabled( project_available ); actionInsert_video->setEnabled( project_available ); actionExport_lyric_file->setEnabled( project_available ); actionExport_video_file->setEnabled( project_available ); actionExport_CD_G_file->setEnabled( project_available ); actionEdit_header_data->setEnabled( project_available ); actionProject_settings->setEnabled( project_available ); actionView_lyric_file->setEnabled( project_ready ); actionAdd_eol_timing_marks->setEnabled( project_ready ); editor->setEnabled( project_available ); if ( project_ready ) { if ( m_project->isModified() ) { actionValidate_lyrics->setIcon( m_validatorIconRegular ); setWindowModified( true ); actionSave->setEnabled( true ); } else { setWindowModified( false ); actionSave->setEnabled( false ); } } } void MainWindow::newVerAvailError( int ) { statusBar()->showMessage( tr("Unable to check whether a new version is available"), 2000 ); } void MainWindow::newVerAvailable( NewVersionMetaMap metadata ) { QSettings().setValue( "advanced/lastupdate", QDateTime::currentDateTime() ); if ( QMessageBox::question( 0, tr("New version available"), tr("A new version %1 of Karaoke Lyrics Editor is available!\n\n" "Do you want to visit the application web site %2?") .arg( metadata["Version"] ) .arg( metadata["URL"] ), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes ) == QMessageBox::No ) return; QDesktopServices::openUrl ( QUrl(metadata["URL"]) ); } void MainWindow::act_settingsShowPlayer( bool checked ) { if ( checked ) m_player->show(); else m_player->hide(); } void MainWindow::visibilityPlayer( bool visible ) { actionShow_Player_dock_wingow->setChecked( visible ); } void MainWindow::act_projectTest() { if ( !editor->validate() ) return; Lyrics lyrics; if ( !editor->exportLyrics( &lyrics ) ) return; if ( !m_testWindow ) { m_testWindow = new TestWindow( this ); connect( m_player, SIGNAL(tick(qint64)), m_testWindow, SLOT(tick(qint64)) ); connect( m_testWindow, SIGNAL(closed()), this, SLOT( testWindowClosed() ) ); } LyricsWidget * lw = new LyricsWidget( m_testWindow ); lw->setLyrics( lyrics, m_project->tag( Project::Tag_Artist ), m_project->tag( Project::Tag_Title ) ); m_testWindow->setLyricWidget( lw ); m_testWindow->show(); m_player->startPlaying(); } void MainWindow::testWindowClosed() { m_player->btn_playerStop(); delete m_testWindow; m_testWindow = 0; } void MainWindow::act_projectTestCDG() { QString fileName = QFileDialog::getOpenFileName( this, tr("Open a CD+G file"), ".", tr("CD+G (*.cdg)") ); if ( fileName.isEmpty() ) return; QFile f( fileName ); if ( !f.open( QIODevice::ReadOnly ) ) return; QByteArray cdgdata = f.readAll(); if ( cdgdata.isEmpty() ) return; if ( !m_testWindow ) { m_testWindow = new TestWindow( this ); connect( m_player, SIGNAL(tick(qint64)), m_testWindow, SLOT(tick(qint64)) ); connect( m_testWindow, SIGNAL(closed()), this, SLOT( testWindowClosed() ) ); } LyricsWidget * lw = new LyricsWidget( m_testWindow ); lw->setCDGdata( cdgdata ); m_testWindow->setLyricWidget( lw ); m_testWindow->show(); m_player->startPlaying(); } void MainWindow::act_projectExportVideoFile() { if ( !editor->validate() ) return; Lyrics lyrics; if ( !editor->exportLyrics( &lyrics ) ) return; // Stop the player if playing m_player->btn_playerStop(); VideoGenerator videogen( m_project ); videogen.generate( lyrics, m_player->totalTime() ); } void MainWindow::act_projectExportCDGFile() { if ( !editor->validate() ) return; Lyrics lyrics; if ( !editor->exportLyrics( &lyrics ) ) return; // Stop the player if playing m_player->btn_playerStop(); CDGGenerator gen( m_project ); gen.generate( lyrics, m_player->totalTime() ); } void MainWindow::act_helpRegistration() { if ( !pLicensing->isEnabled() ) return; if ( !pLicensing->isValid() ) { while ( 1 ) { // Prepare the dialog QDialog dlg( this ); Ui::DialogRegistration ui; ui.setupUi( &dlg ); // Hide the registration info form and shrink the dialog ui.groupShowInfo->hide(); dlg.adjustSize(); dlg.setWindowTitle( tr("Enter registration key") ); if ( dlg.exec() != QDialog::Accepted ) return; QString key = ui.leKey->toPlainText(); if ( pLicensing->validate( key ) ) { QSettings().setValue( "general/registrationkey", key ); break; } QMessageBox::critical( 0, tr("Registration failed"), tr("Registration failed: %1") .arg( pLicensing->errMsg() ) ); continue; } } // Show the registration info dialog QDialog dlg( this ); Ui::DialogRegistration ui; ui.setupUi( &dlg ); // Set the data ui.lblExpires->setText( pLicensing->expires().toString() ); ui.lblSubject->setText( pLicensing->subject() ); // Remove the "cancel" button from the button box ui.buttonBox->removeButton( ui.buttonBox->button(QDialogButtonBox::Cancel )); // Hide the "enter key" form and shrink the dialog ui.groupEnterKey->hide(); dlg.adjustSize(); dlg.setWindowTitle( tr("Registration information") ); dlg.exec(); } void MainWindow::act_addMissingTimingMarks() { editor->addMissingTimingMarks(); } void MainWindow::act_adjustTiming() { editor->adjustTimings(); } karlyriceditor-1.11/src/mainwindow.h000066400000000000000000000072141233766701300176130ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include "checknewversion.h" #include "ui_mainwindow.h" class Project; class ViewWidget; class PlayerWidget; class TestWindow; class RecentFiles; class MainWindow : public QMainWindow, public Ui::MainWindow { Q_OBJECT public: MainWindow(); ~MainWindow(); public slots: void updateState(); void editor_undoAvail(bool); void editor_redoAvail(bool); private slots: void act_fileNewProject(); void act_fileOpenProject(); bool act_fileSaveProject(); bool act_fileSaveProjectAs(); void act_editInsertTag(); void act_editRemoveTag(); void act_editRemoveAllTags(); void act_editClearText(); void act_editTrimspaces(); void act_editSplitLine(); void act_editInsertPicture(); void act_editInsertVideo(); void act_editInsertColorChange(); void act_addMissingTimingMarks(); void act_adjustTiming(); void act_projectOpenLyricFile(); void act_projectEditHeader(); void act_projectValidateLyrics(); void act_projectViewLyricFile(); void act_projectTest(); void act_projectTestCDG(); void act_projectExportLyricFile(); void act_projectExportVideoFile(); void act_projectExportCDGFile(); void act_projectSettings(); void act_settingsGeneral(); void act_settingsShowPlayer( bool checked ); void act_helpAbout(); void act_helpRegistration(); void openRecentFile( const QString& file ); // New version available void newVerAvailError( int errorcode ); void newVerAvailable( NewVersionMetaMap metadata ); // Dock widgets void visibilityPlayer( bool visible ); // Test window void testWindowClosed(); protected: void closeEvent(QCloseEvent *event); private: void checkNewVersionAvailable(); void connectActions(); void createToolbars(); void setCurrentProject( Project * proj ); bool saveProject( const QString& filename ); bool loadProject( const QString& file ); // If the project is changed, and user selected "Cancel", returns false bool tryCloseCurrentProject(); PlayerWidget * m_player; Project * m_project; ViewWidget * m_viewer; TestWindow * m_testWindow; RecentFiles * m_recentFiles; QString m_projectFile; // Validator icons QIcon m_validatorIconRegular; QIcon m_validatorIconAccepted; QIcon m_validatorIconFailed; }; extern MainWindow * pMainWindow; #endif // MAINWINDOW_H karlyriceditor-1.11/src/mainwindow.ui000066400000000000000000000544101233766701300200010ustar00rootroot00000000000000 MainWindow 0 0 800 600 Karaoke Lyrics Editor :/images/application_icon.png:/images/application_icon.png true Lyrics editor 0 0 800 21 File Edit Settings Help Project :/images/dryicons_open_folder.png:/images/dryicons_open_folder.png Import lyric file... Imports the lyrics file Imports the lyrics file in any format, replacing the current content. :/images/dryicons_new.png:/images/dryicons_new.png New project... Creates a new project Closes an existing project (if any), and creates a new one. Ctrl+N :/images/save.png:/images/save.png Save project Saves current project to disk. Saves current project to disk. Ctrl+S :/images/saveas.png:/images/saveas.png Save project as ... Saves current project to disk with the file name specified by user. Saves current project to disk with the file name specified by user. :/images/quit.png:/images/quit.png Quit Exit the application Exit the application :/images/dryicons_undo.png:/images/dryicons_undo.png Undo Undo last action Undo last action Ctrl+Z :/images/dryicons_redo.png:/images/dryicons_redo.png Redo Redo last action Redo last action Ctrl+Shift+Z :/images/dryicons_clock_add.png:/images/dryicons_clock_add.png Insert mark Insert a placeholder or timing mark <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This action inserts a placeholder (if the player is stopped), or a timing mark representing current time (if the song is played). Placeholders are used as "stoppers" - when the song will be played and you will be inserting timing marks, the cursor will automatically stop on all placeholders, allowing you to concentrate on timings, and not on cursor movement.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">After the mark is set, the cursor will move to one of the following, whatever is closer:</p> <ul style="-qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Next placeholder or timing tag on this line (which will be overwritten by new value);</li> <li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Beginning of next line;</li> <li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Next character after just placed timing mark if placed on placeholder, and the "doubling tag" settings is enabled in settings;</li> <li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">End of current line if enabled in settings;</li> <li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Beginning of next word if enabled in settings;</li></ul> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> F5 :/images/dryicons_clock_remove.png:/images/dryicons_clock_remove.png Remove last Remove last inserted placeholder or mark This action removes the placeholder or timing mark which was inserted the last, and moves cursor to its position. The editor remembers all timing marks added since the project was loaded in current session, so this action will work for all added tags, not for the last one. If you want to delete the timing mark or placeholder under cursor, just press'Del'. F8 :/images/quit.png:/images/quit.png Remove all marks Remove all placeholders and timing marks This action removes all placeholders and timing marks in the lyrics. :/images/clear.png:/images/clear.png Clear text Clear the text This action removes all text and timing marks, leaving the project with empty lyrics editor. :/images/dryicons_process.png:/images/dryicons_process.png Application Settings... Change the application settings Change the application settings About... About the application :/images/dryicons_open_folder.png:/images/dryicons_open_folder.png Open project... Open and load the project This action opens an existing project file, and loads it. Current project, if opened, will be closed. :/images/dryicons_process.png:/images/dryicons_process.png Project settings... Changes project-specific settings This action changes project-specific settings, including mostly lyric tags. :/images/saveas.png:/images/saveas.png Export lyric file... Export lyric file in various formats This action lets you export a lyric file in a specific format. With some restrictions it allows export in other formats besides the main project format. :/images/dryicons_page_search.png:/images/dryicons_page_search.png View lyrics View the generated lyrics This action shows the generated lyrics according to project lyrics type as they would be written into the file. :/images/dryicons_computer.png:/images/dryicons_computer.png Test lyrics Test the lyrics using built-in karaoke This action shows the lyrics test window, which is a mini Karaoke player. Once you hit "Play" button on player, the lyrics will be shown in the test window when the song is played. This way you can test the lyrics synchronization, and make corrections if necessary. The lyrics are copied into the test window when it is opened. The changes made in the editor are NOT silently propagated into the test window, and to update the test window, it must be closed and reopened again. Note that for formats which support blocks nothing will be shown until it's less than five seconds before the first sillable is being sung. :/images/dryicons_application_edit.png:/images/dryicons_application_edit.png Edit header data... Edit lyric header data This action shows a dialog to edit lyric header tags. Same as project settings without two tabs. :/images/dryicons_application_search.png:/images/dryicons_application_search.png Validate lyrics Validates the lyrics This action validates the lyrics according to current format. Validation makes sure the format is correct, that all required timing tags are present, and all restrictions set up in settings are met. Export, view and testing lyrics also execute validation before doing the action. true :/images/dryicons_music.png:/images/dryicons_music.png Show Player dock wingow :/images/dryicons_computer.png:/images/dryicons_computer.png Test CD+G file... Test the lyrics if you gonna export them as CD+G This action shows the lyrics test window, which is a mini Karaoke player, but is limited in size as CD+G, so it can be used to test the lyrics to see how they would be played on a real CD+G player. :/images/clear.png:/images/clear.png Trim whitespace Removes lead and tail spaces from every line :/images/page_swap.png:/images/page_swap.png Split current line F10 :/images/save.png:/images/save.png Export video file... :/images/dryicons_cd.png:/images/dryicons_cd.png Export CD+G file... Registration... :/images/dryicons_application_add.png:/images/dryicons_application_add.png Insert picture... :/images/dryicons_open_folder.png:/images/dryicons_open_folder.png Insert video... :/images/saveas.png:/images/saveas.png Insert color change... :/images/dryicons_application_edit.png:/images/dryicons_application_edit.png Add end timing marks Adds missing end-of-line timing marks automatically. Doesn't touch existing marks. :/images/dryicons_clock.png:/images/dryicons_clock.png Time adjustment ... Pops up the time adjustment dialog Editor QWidget
editor.h
1
karlyriceditor-1.11/src/playerbutton.cpp000066400000000000000000000056021233766701300205210ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include #include "playerbutton.h" PlayerButton::PlayerButton( QWidget *parent ) : QPushButton( parent ) { m_inButton = false; setFocusPolicy(Qt::NoFocus); } QPoint PlayerButton::getAlignedCoords( const QPixmap& pix ) { QSize diff = (m_iconRaised.size() - pix.size()) / 2; return QPoint( diff.width(), diff.height() ); } void PlayerButton::setPixmap( const QPixmap& icon ) { m_iconNormal = icon; // Create a larger icon, and a smaller icon m_iconRaised = m_iconNormal.scaled( m_iconNormal.size() * 1.1, Qt::KeepAspectRatio, Qt::SmoothTransformation ); m_iconDown = m_iconNormal.scaled( m_iconNormal.size() * 0.9, Qt::KeepAspectRatio, Qt::SmoothTransformation ); m_pointNormal = getAlignedCoords( m_iconNormal ); m_pointDown = getAlignedCoords( m_iconDown ); updateGeometry(); update(); } QSize PlayerButton::minimumSizeHint() const { return sizeHint(); } QSize PlayerButton::sizeHint() const { return m_iconRaised.size(); } QSizePolicy PlayerButton::sizePolicy () const { return QSizePolicy ( QSizePolicy::Fixed, QSizePolicy::Fixed ); } void PlayerButton::enterEvent(QEvent * e) { m_inButton = true; update(); QPushButton::enterEvent(e); } void PlayerButton::leaveEvent(QEvent * e) { m_inButton = false; update(); QPushButton::leaveEvent(e); } void PlayerButton::paintEvent( QPaintEvent *) { QPainter painter (this); if ( m_inButton && isEnabled() ) { if ( !isDown() ) painter.drawPixmap( 0,0, m_iconRaised ); else painter.drawPixmap( m_pointDown, m_iconDown ); } else painter.drawPixmap( m_pointNormal, m_iconNormal ); painter.end(); } karlyriceditor-1.11/src/playerbutton.h000066400000000000000000000040301233766701300201600ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef PLAYERBUTTON_H #define PLAYERBUTTON_H #include #include // Creates Amarok-like buttons class PlayerButton : public QPushButton { Q_OBJECT public: PlayerButton( QWidget *parent = 0 ); void setPixmap( const QPixmap& icon ); protected: void paintEvent( QPaintEvent *event ); QSize minimumSizeHint() const; QSize sizeHint() const; QSizePolicy sizePolicy () const; void enterEvent(QEvent * e); void leaveEvent(QEvent * e); private: QPoint getAlignedCoords( const QPixmap& pix ); bool m_inButton; QPixmap m_iconNormal; QPixmap m_iconRaised; QPixmap m_iconDown; // Where to draw icons so they're centered QPoint m_pointNormal; QPoint m_pointDown; }; #endif // PLAYERBUTTON_H karlyriceditor-1.11/src/playerwidget.cpp000066400000000000000000000162251233766701300204740ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include #include "audioplayer.h" #include "playerwidget.h" #include "mainwindow.h" #include "project.h" enum { Audio_ErrorState, Audio_PausedState, Audio_StoppedState, Audio_PlayingState }; PlayerWidget::PlayerWidget(QWidget *parent) : QDockWidget(parent), Ui::PlayerWidget() { setupUi(this); m_ready = false; m_sliderDown = false; // Set up icons btnFwd->setPixmap( QPixmap(":images/dryicons_forward.png") ); btnPausePlay->setPixmap( QPixmap(":images/dryicons_play.png") ); btnRew->setPixmap( QPixmap(":images/dryicons_rewind.png") ); btnStop->setPixmap( QPixmap(":images/dryicons_stop") ); // Connect button slots connect( btnFwd, SIGNAL( clicked() ), this, SLOT(btn_playerSeekForward()) ); connect( btnRew, SIGNAL( clicked() ), this, SLOT(btn_playerSeekBackward()) ); connect( btnPausePlay, SIGNAL( clicked() ), this, SLOT(btn_playerPlayPause()) ); connect( btnStop, SIGNAL( clicked() ), this, SLOT(btn_playerStop()) ); // Create the audio player, and initialize it pAudioPlayer = new AudioPlayer(); if ( !pAudioPlayer->init() ) { QMessageBox::critical( 0, tr("Cannot initialize audio"), tr("Audio output device cannot be initialized.\n\nPlease make sure the sound card " "in your computer works fine, that all necessary drivers are installed, and " "that no other application is currently locking the sound device.\n\n" "Program is now aborting.") ); exit( 1 ); } // Connect the player connect( pAudioPlayer, SIGNAL( tick(qint64) ), this, SLOT( slotAudioTick(qint64)) ); // Connect the seek slider connect( seekSlider, SIGNAL(sliderMoved(int)), this, SLOT(seekSliderMoved(int)) ); connect( seekSlider, SIGNAL(sliderPressed()), this, SLOT(seekSliderDown()) ); connect( seekSlider, SIGNAL(sliderReleased()), this, SLOT(seekSliderUp()) ); } PlayerWidget::~PlayerWidget() { delete pAudioPlayer; } void PlayerWidget::slotAudioTick( qint64 tickvalue ) { emit tick( tickvalue ); qint64 remaining = qMax( (qint64) 0, totalTime() - tickvalue ); lblTimeCur->setText( tr("%1") .arg( tickToString( tickvalue ) ) ); lblTimeRemaining->setText( tr("-%1") .arg( tickToString( remaining ) ) ); if ( !m_sliderDown ) { int value = currentTime() * seekSlider->maximum() / totalTime(); if ( seekSlider->value() != value ) seekSlider->setValue( value ); } } QString PlayerWidget::tickToString( qint64 tickvalue ) { int minute = tickvalue / 60000; int second = (tickvalue-minute*60000)/1000; int msecond = (tickvalue-minute*60000-second*1000) / 100; // round milliseconds to 0-9 range} QTime ct( 0, minute, second, msecond ); return ct.toString( "mm:ss.z" ); } bool PlayerWidget::openMusicFile( Project * project ) { if ( !pAudioPlayer->open( project->musicFile() ) ) { QMessageBox::critical( 0, tr("Cannot open media file"), tr("Cannot play media file %1: %2") .arg( project->musicFile() ) .arg( pAudioPlayer->errorMsg() ) ); updatePlayerState( Audio_ErrorState ); return false; } setWindowTitle( tr("Player controls - %1 by %2") .arg( project->tag( Project::Tag_Title ) ) .arg( project->tag( Project::Tag_Artist ) ) ); updatePlayerState( Audio_StoppedState ); return true; } void PlayerWidget::btn_playerStop() { pAudioPlayer->stop(); pAudioPlayer->reset(); updatePlayerState( Audio_StoppedState ); } void PlayerWidget::btn_playerPlayPause() { if ( pAudioPlayer->isPlaying() ) { pAudioPlayer->stop(); updatePlayerState( Audio_PausedState ); } else { pAudioPlayer->play(); updatePlayerState( Audio_PlayingState ); } } void PlayerWidget::btn_playerSeekForward() { if ( pAudioPlayer->isPlaying() ) pAudioPlayer->seekTo( pAudioPlayer->currentTime() + 5000 ); } void PlayerWidget::btn_playerSeekBackward() { if ( pAudioPlayer->isPlaying() ) pAudioPlayer->seekTo( pAudioPlayer->currentTime() - 5000 ); } void PlayerWidget::startPlaying() { if ( pAudioPlayer->isPlaying() ) { pAudioPlayer->stop(); pAudioPlayer->reset(); } pAudioPlayer->play(); updatePlayerState( Audio_PlayingState ); } qint64 PlayerWidget::currentTime() const { return pAudioPlayer->currentTime(); } qint64 PlayerWidget::totalTime() const { return pAudioPlayer->totalTime(); } void PlayerWidget::updatePlayerState( int newstate ) { bool ready = true, enable_playpause = false, enable_seek = false, enable_stop = false; switch ( newstate ) { case Audio_ErrorState: ready = false; break; case Audio_PausedState: btnPausePlay->setPixmap( QPixmap( ":images/dryicons_play.png" ) ); enable_playpause = true; enable_stop = true; break; case Audio_StoppedState: btnPausePlay->setPixmap( QPixmap( ":images/dryicons_play.png" ) ); m_ready = true; enable_playpause = true; break; case Audio_PlayingState: btnPausePlay->setPixmap( QPixmap( ":images/dryicons_pause.png" ) ); enable_seek = true; enable_playpause = true; enable_stop = true; break; } seekSlider->setEnabled( ready ); btnRew->setEnabled( enable_seek ); btnFwd->setEnabled( enable_seek ); btnPausePlay->setEnabled( enable_playpause ); btnStop->setEnabled( enable_stop ); if ( !ready ) { lblTimeCur->setText( "---" ); lblTimeRemaining->setText( "---" ); } else { qint64 remaining = qMax( (qint64) 0, totalTime() - currentTime() ); lblTimeCur->setText( tr("%1") .arg( tickToString( currentTime() ) ) ); lblTimeRemaining->setText( tr("-%1") .arg( tickToString( remaining ) ) ); } pMainWindow->updateState(); } void PlayerWidget::seekSliderUp() { pAudioPlayer->seekTo( seekSlider->value() * totalTime() / seekSlider->maximum() ); m_sliderDown = false; } void PlayerWidget::seekSliderDown() { m_sliderDown = true; } void PlayerWidget::seekSliderMoved( int newvalue ) { pAudioPlayer->seekTo( newvalue * totalTime() / seekSlider->maximum() ); } bool PlayerWidget::isPlaying() const { return pAudioPlayer->isPlaying(); } karlyriceditor-1.11/src/playerwidget.h000066400000000000000000000046311233766701300201370ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef PLAYERWIDGET_H #define PLAYERWIDGET_H #include #include "playerbutton.h" #include "ui_playerwidget.h" class AudioPlayer; class Project; class PlayerWidget : public QDockWidget, public Ui::PlayerWidget { Q_OBJECT public: PlayerWidget(QWidget *parent = 0); ~PlayerWidget(); // Sets the current music file from a project. bool openMusicFile( Project * project ); // Is music file ready to play? bool isReady() const { return m_ready; } // Is playing? bool isPlaying() const; qint64 currentTime() const; qint64 totalTime() const; static QString tickToString( qint64 tick ); signals: void tick( qint64 tickvalue ); public slots: void startPlaying(); void btn_playerStop(); void btn_playerPlayPause(); void btn_playerSeekForward(); void btn_playerSeekBackward(); private slots: void slotAudioTick( qint64 tickvalue ); void seekSliderMoved( int newvalue ); void seekSliderUp(); void seekSliderDown(); private: void updatePlayerState( int state ); private: bool m_ready; bool m_sliderDown; // do not update position }; #endif // PLAYERWIDGET_H karlyriceditor-1.11/src/playerwidget.ui000066400000000000000000000151571233766701300203320ustar00rootroot00000000000000 PlayerWidget 0 0 559 84 0 0 Qt::BottomDockWidgetArea|Qt::TopDockWidgetArea Player controls false Seeks five seconds backward Seeks five seconds backward Rew false Plays or pauses a current playing song Plays or pauses a current playing song Pause Alt+P false Stops the currently playing song Stops the currently playing song Stop false Seeks five seconds forward Seeks five seconds forward Fwd 1 0 0 0 Playing time Time in minutes:seconds.tenth of milliseconds of how long the song has been played. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:16pt; font-weight:600;">---</span></p></body></html> Qt::Horizontal 0 0 Remaining time Remaining time in minutes:seconds.tenth of milliseconds <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:16pt; font-weight:600;">---</span></p></body></html> :/images/dryicons_clock.png PlayerButton QPushButton
playerbutton.h
karlyriceditor-1.11/src/project.cpp000066400000000000000000000737721233766701300174540ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include #include #include "mainwindow.h" #include "project.h" #include "settings.h" #include "version.h" #include "lyrics.h" #include "karaokelyricstextkar.h" #include "cdggenerator.h" #include "kfn_file_parser.h" #include "util.h" // Project data enum // Formats info: // UltraStar: http://ultrastardeluxe.xtremeweb-hosting.net/wiki/doku.php?id=editor:txt_file // LRC: http://en.wikipedia.org/wiki/LRC_%28file_format%29 // enum { PD_SIGNATURE, // BONIFACI PD_VERSION, // save file version; currently 1 PD_MUSICFILE, PD_LYRICS_OLD, PD_LYRICTYPE, PD_TAG_TITLE, PD_TAG_ARTIST, PD_TAG_ALBUM, PD_LYRICS_NEW, PD_TAG_CREATEDBY = 40, // LRC tag PD_TAG_OFFSET, // LRC tag PD_TAG_APPLICATION, // LRC tag PD_TAG_APPVERSION, // LRC tag PD_TAG_LANGUAGE = 50, // UltraStar tag PD_TAG_GENRE, // UltraStar tag PD_TAG_MP3FILE, // UltraStar tag PD_TAG_COVER, // UltraStar tag PD_TAG_BACKGROUND, // UltraStar tag PD_TAG_VIDEO, // UltraStar tag PD_TAG_VIDEOGAP, // UltraStar tag PD_TAG_EDITION, // UltraStar tag PD_TAG_CDG_BCOLOR, // CD+G tag - background color PD_TAG_CDG_ICOLOR, // CD+G tag - info (title/credits) color PD_TAG_CDG_ACOLOR, // CD+G tag - active (not sung yet) color PD_TAG_CDG_NCOLOR, // CD+G tag - inactive (sung) color PD_TAG_CDG_FONT, // CD+G tag - font family PD_TAG_CDG_FONTSIZE, // CD+G tag - font size PD_TAG_CDG_MINTITLE, // CD+G tag - minimum title time PD_TAG_CDG_PREAMBLE, // CD+G tag - whether to show squares PD_TAG_VIDEO_BCOLOR, // Video tag - background color PD_TAG_VIDEO_ICOLOR, // Video tag - info (title/credits) color PD_TAG_VIDEO_ACOLOR, // Video tag - active (not sung yet) color PD_TAG_VIDEO_NCOLOR, // Video tag - inactive (sung) color PD_TAG_VIDEO_FONT, // Video tag - font family PD_TAG_VIDEO_FONTSIZE, // Video tag - font size PD_TAG_VIDEO_MINTITLE, // Video tag - minimum title time PD_TAG_VIDEO_PREAMBLE, // Video tag - whether to show squares PD_TAG_VIDEO_BGFILE, // unused PD_TAG_VIDEO_IMAGESIZE, // index of image size PD_TAG_VIDEO_FPSSIZE, // index of FPS PD_TAG_VIDEO_ENCODING, // index of video encoding PD_TAG_VIDEO_CONTAINER, // index of container format PD_TAG_VIDEO_ALLKEYFRAMES, // if nonzero, every frame is a keyframe PD_TAG_VIDEO_NOAUDIO, // export no audio PD_TAG_VIDEO_PROFILE, // video profile name PD_TAG_VIDEO_FORMAT, // video format name PD_TAG_VIDEO_QUALITY, PD_TAG_VIDEO_AUDIOMODE }; void Project::splitTimeMark( qint64 mark, int * min, int * sec, int * msec ) { *min = mark / 60000; *sec = (mark - *min * 60000) / 1000; *msec = mark - (*min * 60000 + *sec * 1000 ); } Project::Project( Editor* editor ) { m_editor = editor; m_editor->setProject( this ); m_modified = false; clear(); } void Project::clear() { m_projectData.clear(); m_projectData[ PD_SIGNATURE ] = "BONIFACI"; m_projectData[ PD_VERSION ] = 1; m_projectData[ PD_TAG_OFFSET ] = QString::number( pSettings->m_phononSoundDelay ); m_projectData[ PD_TAG_APPLICATION ] = APP_NAME; m_projectData[ PD_TAG_APPVERSION ] = QString("%1.%2").arg( APP_VERSION_MAJOR ).arg( APP_VERSION_MINOR ); // Init CD+G data m_projectData[ PD_TAG_CDG_BCOLOR ] = "black"; m_projectData[ PD_TAG_CDG_ICOLOR ] = "white"; m_projectData[ PD_TAG_CDG_ACOLOR ] = QColor( 0xC0, 0x00, 0xC0 ).name(); m_projectData[ PD_TAG_CDG_NCOLOR ] = QColor( 0x00, 0xC0, 0xC0 ).name(); m_projectData[ PD_TAG_CDG_FONT ] = "Droid Sans"; m_projectData[ PD_TAG_CDG_FONTSIZE ] = "12"; m_projectData[ PD_TAG_CDG_MINTITLE ] = "5"; m_projectData[ PD_TAG_CDG_PREAMBLE ] = "1"; m_totalSongLength = 0; } void Project::setType( LyricType type ) { update( PD_LYRICTYPE, QString::number( type ) ); } void Project::setMusicFile( const QString& musicfile ) { update( PD_MUSICFILE, musicfile ); } QString Project::musicFile() const { return m_projectData[ PD_MUSICFILE ]; } bool Project::load( const QString& filename ) { QFile file( filename ); if ( !file.open( QIODevice::ReadOnly ) ) { QMessageBox::critical( 0, QObject::tr("Cannot open file"), QObject::tr("Cannot open file %1") .arg( filename ) ); return false; } QDataStream stream( &file ); m_projectData.clear(); stream >> m_projectData; if ( m_projectData[ PD_SIGNATURE ] != "BONIFACI" ) { QMessageBox::critical( 0, QObject::tr("Invalid project file"), QObject::tr("The project file %1 is not a valid Lyric Editor project file") .arg( filename ) ); return false; } if ( !m_projectData[ PD_LYRICS_NEW ].isEmpty() ) m_editor->importFromString( m_projectData[ PD_LYRICS_NEW ] ); else m_editor->importFromOldString( m_projectData[ PD_LYRICS_OLD ] ); // Check the lyrics type, and reset it to LRC2 if not specified switch ( type() ) { case LyricType_LRC1: case LyricType_LRC2: case LyricType_UStar: m_modified = false; break; default: setType( LyricType_LRC2 ); m_modified = true; break; } return true; } bool Project::save( const QString& filename ) { QFile file( filename ); if ( !file.open( QIODevice::WriteOnly ) ) { QMessageBox::critical( 0, QObject::tr("Cannot write file"), QObject::tr("Cannot write file %1") .arg( filename ) ); return false; } m_projectData[ PD_LYRICS_NEW ] = m_editor->exportToString(); QDataStream stream( &file ); stream.setVersion( QDataStream::Qt_4_5 ); stream << m_projectData; m_modified = false; return true; } int Project::tagToId( Tag tag ) const { int tagid = -1; switch ( tag ) { case Tag_Title: tagid = PD_TAG_TITLE; break; case Tag_Artist: tagid = PD_TAG_ARTIST; break; case Tag_Album: tagid = PD_TAG_ALBUM; break; case Tag_Language: tagid = PD_TAG_LANGUAGE; break; case Tag_Genre: tagid = PD_TAG_GENRE; break; case Tag_MP3File: tagid = PD_TAG_MP3FILE; break; case Tag_Cover: tagid = PD_TAG_COVER; break; case Tag_Background: tagid = PD_TAG_BACKGROUND; break; case Tag_Video: tagid = PD_TAG_VIDEO; break; case Tag_CreatedBy: tagid = PD_TAG_CREATEDBY; break; case Tag_Offset: tagid = PD_TAG_OFFSET; break; case Tag_Application: tagid = PD_TAG_APPLICATION; break; case Tag_Appversion: tagid = PD_TAG_APPVERSION; break; case Tag_VideoGap: tagid = PD_TAG_VIDEOGAP; break; case Tag_Edition: tagid = PD_TAG_EDITION; break; case Tag_CDG_bgcolor: tagid = PD_TAG_CDG_BCOLOR; break; case Tag_CDG_infocolor: tagid = PD_TAG_CDG_ICOLOR; break; case Tag_CDG_activecolor: tagid = PD_TAG_CDG_ACOLOR; break; case Tag_CDG_inactivecolor: tagid = PD_TAG_CDG_NCOLOR; break; case Tag_CDG_font: tagid = PD_TAG_CDG_FONT; break; case Tag_CDG_fontsize: tagid = PD_TAG_CDG_FONTSIZE; break; case Tag_CDG_titletime: tagid = PD_TAG_CDG_MINTITLE; break; case Tag_CDG_preamble: tagid = PD_TAG_CDG_PREAMBLE; break; case Tag_Video_bgcolor: tagid = PD_TAG_VIDEO_BCOLOR; break; case Tag_Video_infocolor: tagid = PD_TAG_VIDEO_ICOLOR; break; case Tag_Video_activecolor: tagid = PD_TAG_VIDEO_ACOLOR; break; case Tag_Video_inactivecolor: tagid = PD_TAG_VIDEO_NCOLOR; break; case Tag_Video_font: tagid = PD_TAG_VIDEO_FONT; break; case Tag_Video_fontsize: tagid = PD_TAG_VIDEO_FONTSIZE; break; case Tag_Video_titletime: tagid = PD_TAG_VIDEO_MINTITLE; break; case Tag_Video_preamble: tagid = PD_TAG_VIDEO_PREAMBLE; break; } return tagid; } void Project::setTag( Tag tag, const QString& value ) { int tagid = tagToId( tag ); if ( tagid != -1 ) update( tagid, value ); } QString Project::tag( Tag itag, const QString& defvalue ) const { int tagid = tagToId( itag ); if ( tagid != -1 && m_projectData.contains( tagid ) ) return m_projectData[ tagid ]; return defvalue; } Project::LyricType Project::type() const { return (Project::LyricType) m_projectData[ PD_LYRICTYPE ].toInt(); } void Project::update( int id, const QString& value ) { if ( m_projectData[ id ] == value ) return; m_projectData[ id ] = value; setModified(); } void Project::setModified() { m_modified = true; pMainWindow->updateState(); } void Project::appendIfPresent( int id, const QString& prefix, QString& src, LyricType type ) { if ( !m_projectData.contains( id ) || m_projectData[id].isEmpty() ) return; switch ( type ) { case LyricType_LRC1: case LyricType_LRC2: src += "[" + prefix + ": " + m_projectData[id] + "]\n"; break; case LyricType_UStar: src += "#" + prefix + ":" + m_projectData[id] + "\n"; break; } } QString Project::generateLRCheader() { QString hdr; appendIfPresent( PD_TAG_TITLE, "ti", hdr, LyricType_LRC1 ); appendIfPresent( PD_TAG_ARTIST, "ar", hdr, LyricType_LRC1 ); appendIfPresent( PD_TAG_ALBUM, "al", hdr, LyricType_LRC1 ); appendIfPresent( PD_TAG_CREATEDBY, "by", hdr, LyricType_LRC1 ); appendIfPresent( PD_TAG_OFFSET, "offset", hdr, LyricType_LRC1 ); appendIfPresent( PD_TAG_APPLICATION, "re", hdr, LyricType_LRC1 ); appendIfPresent( PD_TAG_APPVERSION, "ve", hdr, LyricType_LRC1 ); return hdr; } QString Project::generateUStarheader() { QString hdr; appendIfPresent( PD_TAG_TITLE, "TITLE", hdr, LyricType_UStar ); appendIfPresent( PD_TAG_ARTIST, "ARTIST", hdr, LyricType_UStar ); appendIfPresent( PD_TAG_LANGUAGE, "LANGUAGE", hdr, LyricType_UStar ); appendIfPresent( PD_TAG_GENRE, "GENRE", hdr, LyricType_UStar ); appendIfPresent( PD_TAG_COVER, "COVER", hdr, LyricType_UStar ); appendIfPresent( PD_TAG_BACKGROUND, "BACKGROUND", hdr, LyricType_UStar ); appendIfPresent( PD_TAG_VIDEO, "VIDEO", hdr, LyricType_UStar ); appendIfPresent( PD_TAG_VIDEOGAP, "VIDEOGAP", hdr, LyricType_UStar ); appendIfPresent( PD_TAG_EDITION, "EDITION", hdr, LyricType_UStar ); return hdr; } QByteArray Project::exportLyrics() { switch ( type() ) { case LyricType_LRC1: return exportLyricsAsLRC1(); case LyricType_LRC2: return exportLyricsAsLRC2(); case LyricType_UStar: return exportLyricsAsUStar(); } return "UNSUPORTED"; } QByteArray Project::exportLyricsAsLRC1() { // Generate warnings about conversion consequences QString warntext; switch ( type() ) { case LyricType_LRC2: warntext = QObject::tr("Current lyrics format is set to LRC version 2.\n\n" "When exporting it as LRC version 1 all time tags except the one in front of the line " "will be ignored.\n\n" "Do you want to proceed with export?"); break; case LyricType_UStar: warntext = QObject::tr("Current lyrics format is set to Ultrastar.\n\n" "When exporting it as LRC version 1 all time tags except the one in front of the line " "will be ignored as well as the pitch information.\n\n" "Do you want to proceed with export?"); break; case LyricType_LRC1: break; } if ( !warntext.isEmpty() ) { if ( QMessageBox::question( 0, QObject::tr("Exporting lyrics"), warntext, QMessageBox::Yes|QMessageBox::No ) == QMessageBox::No ) return QByteArray(); } QString lrc = generateLRCheader(); Lyrics lyrics; if ( !m_editor->exportLyrics( &lyrics) ) return QByteArray(); for ( int bl = 0; bl < lyrics.totalBlocks(); bl++ ) { if ( bl > 0 ) lrc += "\n"; const Lyrics::Block& block = lyrics.block( bl ); for ( int ln = 0; ln < block.size(); ln++ ) { const Lyrics::Line& line = block[ln]; for ( int pos = 0; pos < line.size(); pos++ ) { Lyrics::Syllable lentry = line[pos]; // Insert timing mark QString timetag; int minute, second, msecond; splitTimeMark( lentry.timing, &minute, &second, &msecond ); timetag.sprintf( "%02d:%02d.%02d", minute, second, msecond / 10 ); if ( pos == 0 ) lrc += "[" + timetag + "]"; lrc += lentry.text; } lrc += "\n"; } } return lrc.toUtf8(); } QByteArray Project::exportLyricsAsLRC2() { // Generate warnings about conversion consequences QString warntext; switch ( type() ) { case LyricType_LRC1: case LyricType_LRC2: break; // seamless conversion case LyricType_UStar: warntext = QObject::tr("Current lyrics format is set to Ultrastar.\n\n" "When exporting it as LRC version 2 the pitch information will be ignored.\n\n" "Do you want to proceed with export?"); break; } if ( !warntext.isEmpty() ) { if ( QMessageBox::question( 0, QObject::tr("Exporting lyrics"), warntext, QMessageBox::Yes|QMessageBox::No ) == QMessageBox::No ) return QByteArray(); } Lyrics lyrics; if ( !m_editor->exportLyrics( &lyrics ) ) return QByteArray(); QString lrc = generateLRCheader(); for ( int bl = 0; bl < lyrics.totalBlocks(); bl++ ) { if ( bl > 0 ) lrc += "\n"; const Lyrics::Block& block = lyrics.block( bl ); for ( int ln = 0; ln < block.size(); ln++ ) { const Lyrics::Line& line = block[ln]; for ( int pos = 0; pos < line.size(); pos++ ) { Lyrics::Syllable lentry = line[pos]; // Insert timing mark QString timetag; int minute, second, msecond; splitTimeMark( lentry.timing, &minute, &second, &msecond ); timetag.sprintf( "%02d:%02d.%02d", minute, second, msecond / 10 ); if ( pos == 0 ) lrc += "[" + timetag + "]"; else lrc += "<" + timetag + ">"; lrc += lentry.text; } lrc += "\n"; } } return lrc.toUtf8(); } QByteArray Project::exportLyricsAsUStar() { // 5 beats per second should give us good enough precision int bpm = 300; // Generate warnings about conversion consequences QString warntext; switch ( type() ) { case LyricType_UStar: break; case LyricType_LRC1: warntext = QObject::tr("Current lyrics format is set to LRC version 1.\n\n" "When exporting it as UltraStar format the output lyrics will not contain pitch information.\n" "Also since the lyric syllable duration is a whole line, the output lyrics won't be usable.\n\n" "Do you want to proceed with export?"); break; case LyricType_LRC2: warntext = QObject::tr("Current lyrics format is set to LRC version 2.\n\n" "When exporting it as UltraStar format the output lyrics will not contain pitch information.\n\n" "Do you want to proceed with export?"); break; } if ( !warntext.isEmpty() ) { if ( QMessageBox::question( 0, QObject::tr("Exporting lyrics"), warntext, QMessageBox::Yes|QMessageBox::No ) == QMessageBox::No ) return QByteArray(); } Lyrics lyrics; if ( !m_editor->exportLyrics( &lyrics ) ) return QByteArray(); if ( lyrics.totalBlocks() > 1 ) { QMessageBox::information( 0, QObject::tr("Block separator found"), QObject::tr("UltraStart lyrics cannot contain block separators. Blocks will be merged.") ); } // Calculate gap int beat_time_ms = 1000 / (bpm / 60); int gap = lyrics.block(0).front().front().timing; QString lyricstext = generateUStarheader(); lyricstext += QString("#MP3:%1\n") .arg( musicFile() ); lyricstext += QString("#BPM:%1\n") .arg(bpm); lyricstext += QString("#GAP:%1\n") .arg(gap); for ( int i = 0; i < lyrics.totalBlocks(); i++ ) { const Lyrics::Block& block = lyrics.block( i ); for ( int ln = 0; ln < block.size(); ln++ ) { const Lyrics::Line& line = block[ln]; for ( int pos = 0; pos < line.size(); pos++ ) { Lyrics::Syllable lentry = line[pos]; bool last_entry = (pos == line.size() - 1); // Ultrastar lyrics must have tags at the line end, so the last tag should be empty if ( last_entry && !lentry.text.isEmpty() ) { QMessageBox::critical( 0, QObject::tr("No timing mark at the end of line"), QObject::tr("UltraStart lyrics require timing marks at the end of line. Export aborted.") ); return QByteArray(); } // Calculate timing and duration int duration, timing = (lentry.timing - gap) / beat_time_ms; char prefix; if ( last_entry ) { // We're at the end of line, which is empty. Get the time to calculate duration from there prefix = '-'; if ( ln == block.size() - 1 ) duration = 5000 / beat_time_ms; // assume 5sec duration else duration = (block[ln+1].front().timing - lentry.timing) / beat_time_ms; lyricstext += QString("- %1 %2\n").arg( timing ) .arg( duration ); } else { prefix = ':'; duration = (line[pos+1].timing - lentry.timing) / beat_time_ms; int pitch = lentry.pitch; // If lyrics is not set, ignore it if ( pitch == -1 ) pitch = 0; if ( pitch & Lyrics::PITCH_NOTE_FREESTYLE ) { prefix = 'F'; pitch &= ~Lyrics::PITCH_NOTE_FREESTYLE; } else if ( pitch & Lyrics::PITCH_NOTE_GOLDEN ) { prefix = '*'; pitch &= ~Lyrics::PITCH_NOTE_GOLDEN; } lyricstext += QString("%1 %2 %3 %4 %5\n").arg( prefix ) .arg( timing ) .arg( duration ) .arg( pitch ) .arg( lentry.text ); } } } } lyricstext += "E\n"; return lyricstext.toUtf8(); } bool Project::importLyrics( const QString& filename ) { bool fake_lrc_header = false; QString lyrictext; // If this is KaraFun file, parse it if ( filename.endsWith( "kfn", Qt::CaseInsensitive ) ) { KFNFileParser parser; if ( !parser.open( filename ) ) { QMessageBox::information( 0, QObject::tr("Invalid KFN file"), QObject::tr("The file %1 cannot be opened: %2") .arg( filename ) .arg( parser.errorMsg() ) ); return false; } lyrictext = parser.lyricsAsLRC(); // Fake the LRC header fake_lrc_header = true; } else { QFile file( filename ); // Open the file and read its content if ( !file.open( QIODevice::ReadOnly ) ) { QMessageBox::critical( 0, QObject::tr( "Cannot open file" ), QObject::tr( "Cannot open file %1: %2" ) .arg( filename ) .arg( file.errorString() ) ); return false; } QByteArray data = file.readAll(); // Close the file - the user might want to edit it now when we ask for encoding file.close(); // If this is the MIDI file, parse it first if ( filename.endsWith( "mid", Qt::CaseInsensitive ) || filename.endsWith( "midi", Qt::CaseInsensitive ) || filename.endsWith( "kar", Qt::CaseInsensitive ) ) { data = CKaraokeLyricsTextKAR::getLyrics( data ); if ( data.isEmpty() ) { QMessageBox::critical( 0, QObject::tr( "Cannot read MIDI " ), QObject::tr( "Cannot read lyrics from MIDI file %1" ) .arg( filename ) ); return false; } // Fake the LRC header fake_lrc_header = true; } lyrictext = Util::convertWithUserEncoding( data ); } if ( lyrictext.isEmpty() ) return false; // Prepend a fake LRC header for MIDI import if ( fake_lrc_header ) lyrictext.prepend( "[ar: unknown]\n[ti: unknown]\n" ); // Convert CRLF to CRs and replace LFs lyrictext.replace( "\r\n", "\n" ); lyrictext.remove( '\r' ); QStringList linedtext = lyrictext.split( '\n' ); // Import lyrics Lyrics lyr; bool success = false; if ( filename.endsWith( "txt" ) ) { // LyricType_UStar: lyr.beginLyrics(); success = importLyricsTxt( linedtext, lyr ); lyr.endLyrics(); } else if ( filename.endsWith( "kok" ) ) { lyr.beginLyrics(); success = importLyricsKOK( linedtext, lyr ); lyr.endLyrics(); } else { lyr.beginLyrics(); success = importLyricsLRC( linedtext, lyr ); lyr.endLyrics(); } // importLyrics* already showed error message if ( !success ) return false; m_editor->importLyrics( lyr ); return true; } bool Project::convertLyrics( const QString& content ) { QStringList lines = content.split( '\n' ); Lyrics lyr; lyr.beginLyrics(); bool success = importLyricsLRC( lines, lyr, true ); lyr.endLyrics(); if ( !success ) return false; m_editor->importLyrics( lyr ); return true; } bool Project::importLyricsLRC( const QStringList & readlyrics, Lyrics& lyrics, bool relaxed ) { bool header = true; bool type_lrc2 = false; qint64 last_time = -1; lyrics.clear(); for ( int i = 0; i < readlyrics.size(); i++ ) { QString line = readlyrics[i]; // To simplify the matching line.replace( '[', '<' ); line.replace( ']', '>' ); if ( header ) { QRegExp regex( "^<([a-zA-Z]+):\\s*(.*)\\s*>$" ); regex.setMinimal( true ); if ( regex.indexIn( line ) != -1 ) { QString tag = regex.cap( 1 ); QString value = regex.cap( 2 ); int tagid = -1; if ( tag == "ti" ) tagid = PD_TAG_TITLE; else if ( tag == "ar" ) tagid = PD_TAG_ARTIST; else if ( tag == "al" ) tagid = PD_TAG_ALBUM; else if ( tag == "by" ) tagid = PD_TAG_CREATEDBY; else if ( tag == "offset" ) tagid = PD_TAG_OFFSET; else if ( tag == "re" ) tagid = PD_TAG_APPLICATION; else if ( tag == "ve" ) tagid = PD_TAG_APPVERSION; else qDebug("Unsupported LRC tag found: '%s', ignored", qPrintable( tag ) ); if ( tagid != -1 ) update( tagid, value ); } else { // Tag not found; either header ended, or invalid file if ( i == 0 && !relaxed ) { QMessageBox::critical( 0, QObject::tr("Invalid LRC file"), QObject::tr("Missing LRC header") ); return false; } header = false; } } // We may fall-through, so no else if ( !header ) { QRegExp regex( "<(\\d+):(\\d+)(.\\d+)?>([^<]*)" ); int pos = 0; while ( (pos = regex.indexIn( line, pos )) != -1 ) { if ( pos != 0 ) type_lrc2 = true; QStringList match = regex.capturedTexts(); int minutes = match[1].toInt(); int seconds = match[2].toInt(); QString text = match[3]; int ms = 0; // msecs require more precise handling if ( match.size() > 4 ) { QString strms = match[3]; // We have msecs. Remove the first dot strms.remove( 0, 1 ); // Convert to full msecs. Kinda ugly, but simple while ( strms.length() < 3 ) strms+= "0"; ms = strms.toInt(); text = match[4]; } qint64 timing = minutes * 60000 + seconds * 1000 + ms; if ( timing != last_time && timing != -1 ) { lyrics.curLyricAdd(); lyrics.curLyricSetTime( timing ); last_time = timing; } lyrics.curLyricAppendText( text ); pos++; } lyrics.curLyricAddEndOfLine(); } } // Check the lyric type if ( type() == LyricType_LRC1 && type_lrc2 && !relaxed ) { QMessageBox::warning( 0, QObject::tr("LRC2 file read for LCR1 project"), QObject::tr("The lyric type for current project is set for LRC1, but the lyrics read were LRC2.\n\n" "Either change the project type, or fix the lyrics.") ); } return true; } bool Project::importLyricsTxt( const QStringList & readlyrics, Lyrics& lyrics ) { lyrics.clear(); if ( readlyrics.isEmpty() ) return false; // TXT could be UltraStar or PowerKaraoke if ( readlyrics.first().indexOf( QRegExp( "^#[a-zA-Z]+:\\s*.*\\s*$" ) ) != -1 ) return importLyricsUStar( readlyrics, lyrics ); else if ( readlyrics.first().indexOf( QRegExp( "^([0-9.]+) ([0-9.]+) (.+)" ) ) != -1 ) return importLyricsPowerKaraoke( readlyrics, lyrics ); QMessageBox::critical( 0, QObject::tr("Invalid text file"), QObject::tr("This file is not a valid UltraStar nor PowerKaraoke lyric file") ); return false; } static int powerKaraokeTime( QString time ) { int timing = 0; if ( time.contains(":") ) { QStringList parts = time.split( ":" ); timing = parts[0].toInt() * 60000; time = parts[1]; } timing += (int) (time.toFloat() * 1000 ); return timing; } bool Project::importLyricsKOK( const QStringList & readlyrics, Lyrics& lyrics ) { // J'ai ;7,9050634;tra;8,0651993;vail;8,2144914;lé;8,3789922; foreach ( QString line, readlyrics ) { if ( line.isEmpty() ) continue; QStringList entries = line.split( ";" ); // Must be even if ( (entries.size() % 2) != 0 ) return false; for ( int i = 0; i < entries.size() / 2; i++ ) { QString text = entries[2 * i]; QString timing = entries[2 * i + 1].replace( ",", "." ); int timevalue = (int) ( timing.toFloat() * 1000 ); lyrics.curLyricSetTime( timevalue ); lyrics.curLyricAppendText( text ); if ( i == entries.size() / 2 - 1 ) lyrics.curLyricAddEndOfLine(); else lyrics.curLyricAdd(); } } return true; } bool Project::importLyricsPowerKaraoke( const QStringList & readlyrics, Lyrics& lyrics ) { // For the PowerKaraoke format there is no header, just times. QRegExp regex("^([0-9.:]+) ([0-9.:]+) (.*)"); // Analyze each line for ( int i = 0; i < readlyrics.size(); i++ ) { QString line = readlyrics[i]; if ( line.isEmpty() ) continue; // Try to match the sync first if ( line.indexOf( regex ) == -1 ) { QMessageBox::critical( 0, QObject::tr("Invalid PowerKaraoke file"), QObject::tr("This file is not a valid PowerKaraoke lyric file, error at line %1") .arg( i + 1 ) ); return false; } int start = powerKaraokeTime( regex.cap( 1 ) ); //int end = powerKaraokeTime( regex.cap( 2 ) ); QString text = regex.cap( 3 ).trimmed(); lyrics.curLyricSetTime( start ); if ( text.endsWith( "\\n" ) ) { text.chop( 2 ); lyrics.curLyricAppendText( text ); lyrics.curLyricAddEndOfLine(); } else if ( !text.endsWith( "-" ) ) { text += " "; lyrics.curLyricAppendText( text ); lyrics.curLyricAdd(); } else { text.chop( 1 ); lyrics.curLyricAppendText( text ); lyrics.curLyricAdd(); } } return true; } bool Project::importLyricsUStar( const QStringList & readlyrics, Lyrics& lyrics ) { bool header = true; bool relative = false; int bpm = -1, gap = -1; double msecs_per_beat = 0; int last_time_ms = 0; int next_time_ms = 0; int last_pitch = 0; for ( int i = 0; i < readlyrics.size(); i++ ) { QString line = readlyrics[i]; if ( header ) { QRegExp regex( "^#([a-zA-Z]+):\\s*(.*)\\s*$" ); if ( regex.indexIn( line ) != -1 ) { QString tag = regex.cap( 1 ); QString value = regex.cap( 2 ); int tagid = -1; if ( tag == "TITLE" ) tagid = PD_TAG_TITLE; else if ( tag == "ARTIST" ) tagid = PD_TAG_ARTIST; else if ( tag == "LANGUAGE" ) tagid = PD_TAG_LANGUAGE; else if ( tag == "GENRE" ) tagid = PD_TAG_GENRE; else if ( tag == "MP3FILE" ) tagid = PD_TAG_MP3FILE; else if ( tag == "COVER" ) tagid = PD_TAG_COVER; else if ( tag == "BACKGROUND" ) tagid = PD_TAG_BACKGROUND; else if ( tag == "VIDEO" ) tagid = PD_TAG_VIDEO; else if ( tag == "VIDEOGAP" ) tagid = PD_TAG_VIDEOGAP; else if ( tag == "EDITION" ) tagid = PD_TAG_EDITION; else if ( tag == "BPM" ) bpm = value.toInt(); else if ( tag == "GAP" ) gap = value.toInt(); else if ( tag == "RELATIVE" ) relative = value.compare( "yes" ); else qDebug("Unsupported UltraStar tag found: '%s', ignored", qPrintable( tag ) ); if ( tagid != -1 ) update( tagid, value ); } else { // Tag not found; either header ended, or invalid file if ( bpm == -1 || gap == -1 ) { QMessageBox::critical( 0, QObject::tr("Invalid UltraStar file"), QObject::tr("This file is not a valid UltraStar lyric file; BPM and/or GAP is missing.") ); return false; } msecs_per_beat = (int) ((60.0 / (double) bpm / 4.0) * 1000.0); header = false; } } // We may fall-through, so no else if ( !header ) { if ( line[0] != 'E' && line[0] != ':' && line[0] != '*' && line[0] != 'F' && line[0] != '-' ) { QMessageBox::critical( 0, QObject::tr("Invalid UltraStar file"), QObject::tr("This file is not a valid UltraStar lyric file; error at line %1.") .arg(i+1) ); return false; } // End? if ( line[0] == 'E' ) break; QStringList parsed = line.split( QRegExp("\\s+") ); if ( parsed.size() < 3 ) { QMessageBox::critical( 0, QObject::tr("Invalid UltraStar file"), QObject::tr("This file is not a valid UltraStar lyric file; error at line %1.").arg(i) ); return false; } int timing = relative ? last_time_ms : 0; timing += parsed[1].toInt() * msecs_per_beat; // Should we add an empty field? if ( next_time_ms != 0 && timing > next_time_ms ) { lyrics.curLyricSetTime( next_time_ms ); lyrics.curLyricSetPitch( last_pitch ); lyrics.curLyricAdd(); } next_time_ms = timing + parsed[2].toInt() * msecs_per_beat; lyrics.curLyricSetTime( timing ); if ( parsed[0] == "F" || parsed[0] == "*" || parsed[0] == ":" ) { if ( parsed.size() < 5 ) { QMessageBox::critical( 0, QObject::tr("Invalid UltraStar file"), QObject::tr("This file is not a valid UltraStar lyric file; error at line %1.").arg(i) ); return false; } int pitch = parsed[3].toInt(); if ( parsed[0] == "F" ) pitch |= Lyrics::PITCH_NOTE_FREESTYLE; else if ( parsed[0] == "*" ) pitch |= Lyrics::PITCH_NOTE_GOLDEN; lyrics.curLyricSetPitch( pitch ); lyrics.curLyricAppendText( parsed[4] ); } else if ( parsed[0] == "-" ) lyrics.curLyricAddEndOfLine(); else abort(); // should never happen } } return true; } void Project::setSongLength( qint64 length ) { m_totalSongLength = length; } qint64 Project::getSongLength() const { return m_totalSongLength; } karlyriceditor-1.11/src/project.h000066400000000000000000000103051233766701300171000ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef PROJECT_H #define PROJECT_H #include #include class Editor; class Lyrics; class Project { public: enum LyricType { LyricType_LRC1 = 1, LyricType_LRC2, LyricType_UStar, // if adding more, skip the #4! }; enum Tag { Tag_Title = 1, Tag_Artist, Tag_Album, Tag_Language, Tag_Genre, Tag_MP3File, Tag_Cover, Tag_Background, Tag_Video, Tag_VideoGap, Tag_Edition, Tag_CreatedBy, Tag_Offset, Tag_Application, Tag_Appversion, Tag_CDG_bgcolor, Tag_CDG_infocolor, Tag_CDG_activecolor, Tag_CDG_inactivecolor, Tag_CDG_font, Tag_CDG_fontsize, Tag_CDG_titletime, Tag_CDG_preamble, Tag_Video_bgcolor, Tag_Video_infocolor, Tag_Video_activecolor, Tag_Video_inactivecolor, Tag_Video_font, Tag_Video_fontsize, Tag_Video_titletime, Tag_Video_preamble }; Project( Editor * editor ); // load/save project from/to file bool save( const QString& filename ); bool load( const QString& filename ); // lyric type void setType( LyricType type ); LyricType type() const; // Music file void setMusicFile( const QString& musicfile ); QString musicFile() const; // Set the music tag fields void setTag( Tag tag, const QString& value ); QString tag( Tag tagid, const QString& defvalue = QString::null ) const; // Is project modified? bool isModified() const { return m_modified; } void setModified(); // Export lyrics as current format, and as any specific QByteArray exportLyrics(); QByteArray exportLyricsAsLRC1(); QByteArray exportLyricsAsLRC2(); QByteArray exportLyricsAsUStar(); // Import lyrics. So far the formats are fairly recognizable, so other imports are private bool importLyrics( const QString& filename ); // Convert the autogenerated LRC lyrics into the project bool convertLyrics( const QString& content ); // Clear the project void clear(); // Set/access the song length void setSongLength( qint64 length ); qint64 getSongLength() const; // Split the time mark static void splitTimeMark( qint64 mark, int * min, int * sec, int * msec ); private: bool importLyricsLRC( const QStringList & readlyrics, Lyrics& lyrics, bool relaxed = false ); bool importLyricsUStar( const QStringList & readlyrics, Lyrics& lyrics ); bool importLyricsTxt( const QStringList & readlyrics, Lyrics& lyrics ); bool importLyricsPowerKaraoke( const QStringList & readlyrics, Lyrics& lyrics ); bool importLyricsKOK( const QStringList & readlyrics, Lyrics& lyrics ); void appendIfPresent( int id, const QString& prefix, QString& src, LyricType type ); QString generateLRCheader(); QString generateUStarheader(); void update( int id, const QString& value ); int tagToId( Tag tagid ) const; bool m_modified; Editor* m_editor; qint64 m_totalSongLength; // This container stores all project-related data QMap< int, QString> m_projectData; }; #endif // PROJECT_H karlyriceditor-1.11/src/projectsettings.cpp000066400000000000000000000162441233766701300212240ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include #include #include "projectsettings.h" #include "project.h" ProjectSettings::ProjectSettings( Project* proj, bool showtype, QWidget * parent ) : QDialog( parent ), Ui::DialogProjectSettings() { setupUi( this ); connect( btnBrowse, SIGNAL(clicked()), this, SLOT(browseMusicFile()) ); connect( rbLRC1, SIGNAL( toggled(bool)), this, SLOT(changeProjectType()) ); connect( rbLRC2, SIGNAL( toggled(bool)), this, SLOT(changeProjectType()) ); connect( rbLRC3, SIGNAL( toggled(bool)), this, SLOT(changeProjectType()) ); m_project = proj; m_musicFileChanged = false; m_generalTabShown = showtype; // Set the first tab accoring to options if ( showtype ) { // Add values switch ( m_project->type() ) { case Project::LyricType_LRC1: rbLRC1->setChecked( true ); break; case Project::LyricType_LRC2: rbLRC2->setChecked( true ); break; case Project::LyricType_UStar: rbLRC3->setChecked( true ); break; } leSongFile->setText( m_project->musicFile() ); } else tabSettings->removeTab( 0 ); // Init general params leTitle->setText( m_project->tag( Project::Tag_Title ) ); leArtist->setText( m_project->tag( Project::Tag_Artist ) ); if ( m_project->type() == Project::LyricType_UStar ) { if ( m_project->tag( Project::Tag_MP3File ).isEmpty() ) leUSmp3->setText( QFileInfo( m_project->musicFile() ).fileName() ); else leUSmp3->setText( m_project->tag( Project::Tag_MP3File ) ); } // Init advanced params switch ( m_project->type() ) { case Project::LyricType_LRC1: case Project::LyricType_LRC2: // Hide Ultrastar-specific fields labelUSbpmgap->hide(); labelMusicFile->hide(); leUSmp3->hide(); // Hide ultrastar and CD+G group groupUStar->hide(); // Add LRC params leLRCalbum->setText( m_project->tag( Project::Tag_Album ) ); leLRCapp->setText( m_project->tag( Project::Tag_Application ) ); leLRCappver->setText( m_project->tag( Project::Tag_Appversion ) ); leLRCcreated->setText( m_project->tag( Project::Tag_CreatedBy ) ); leLRCoffset->setText( m_project->tag( Project::Tag_Offset) ); break; case Project::LyricType_UStar: // Hide LRC and CD+G groups groupLRC->hide(); // Add UltraStar params leUSbackground->setText( m_project->tag( Project::Tag_Background ) ); leUScover->setText( m_project->tag( Project::Tag_Cover ) ); leUSedition->setText( m_project->tag( Project::Tag_Edition) ); leUSgenre->setText( m_project->tag( Project::Tag_Genre ) ); leUSlang->setText( m_project->tag( Project::Tag_Language ) ); leUSvideo->setText( m_project->tag( Project::Tag_Video ) ); leUSvideogap->setText( m_project->tag( Project::Tag_VideoGap ) ); break; } // Scale down the dialog as part of it is hidden resize( QSize( width(), 1 ) ); } void ProjectSettings::browseMusicFile() { QString filename = QFileDialog::getOpenFileName( 0, tr("Choose a music file to load"), "." ); if ( filename.isEmpty() ) return; leSongFile->setText( filename ); } void ProjectSettings::changeProjectType() { // Init advanced params if ( rbLRC1->isChecked() || rbLRC2->isChecked() ) { groupLRC->show(); // Hide Ultrastar-specific fields labelUSbpmgap->hide(); labelMusicFile->hide(); leUSmp3->hide(); // Hide ultrastar group groupUStar->hide(); } else if ( rbLRC3->isChecked() ) { // Hide LRC group groupLRC->hide(); // Hide Ultrastar-specific fields labelUSbpmgap->show(); labelMusicFile->show(); leUSmp3->show(); // Hide ultrastar group groupUStar->show(); } } void ProjectSettings::accept() { if ( m_generalTabShown ) { // Music file if ( leSongFile->text().isEmpty() ) { tabSettings->setCurrentIndex( 0 ); QMessageBox::critical( 0, tr("Settings error"), tr( "Music file cannot be empty" ) ); return; } if ( leSongFile->text() != m_project->musicFile() ) { m_project->setMusicFile( leSongFile->text() ); m_musicFileChanged = true; } // Lyric type if ( rbLRC1->isChecked() ) m_project->setType( Project::LyricType_LRC1 ); else if ( rbLRC2->isChecked() ) m_project->setType( Project::LyricType_LRC2 ); else if ( rbLRC3->isChecked() ) m_project->setType( Project::LyricType_UStar ); } // 2nd tab tabSettings->setCurrentIndex( 1 ); if ( leTitle->text().isEmpty() ) { QMessageBox::critical( 0, tr("Settings error"), tr("You must type song title to continue.") ); return; } if ( leArtist->text().isEmpty() ) { QMessageBox::critical( 0, tr("Settings error"), tr("You must type song artist to continue.") ); return; } m_project->setTag( Project::Tag_Title, leTitle->text() ); m_project->setTag( Project::Tag_Artist, leArtist->text() ); if ( m_project->type() == Project::LyricType_UStar ) { if ( leUSmp3->text().isEmpty() ) { QMessageBox::critical( 0, tr("Settings error"), tr("You must enter MP3 file name to continue.") ); return; } m_project->setTag( Project::Tag_MP3File, leUSmp3->text() ); } // 3rd tab tabSettings->setCurrentIndex( 2 ); if ( m_project->type() != Project::LyricType_UStar ) { m_project->setTag( Project::Tag_Album, leLRCalbum->text() ); m_project->setTag( Project::Tag_Application, leLRCapp->text() ); m_project->setTag( Project::Tag_Appversion, leLRCappver->text() ); m_project->setTag( Project::Tag_CreatedBy, leLRCcreated->text() ); m_project->setTag( Project::Tag_Offset, leLRCoffset->text() ); } else { m_project->setTag( Project::Tag_Background, leUSbackground->text() ); m_project->setTag( Project::Tag_Cover, leUScover->text() ); m_project->setTag( Project::Tag_Edition, leUSedition->text() ); m_project->setTag( Project::Tag_Genre, leUSgenre->text() ); m_project->setTag( Project::Tag_Language, leUSlang->text() ); m_project->setTag( Project::Tag_Video, leUSvideo->text() ); m_project->setTag( Project::Tag_VideoGap, leUSvideogap->text() ); } QDialog::accept(); } karlyriceditor-1.11/src/projectsettings.h000066400000000000000000000036171233766701300206710ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef PROJECTSETTINGS_H #define PROJECTSETTINGS_H #include #include "ui_dialog_projectsettings.h" class Project; class ProjectSettings : public QDialog, public Ui::DialogProjectSettings { Q_OBJECT public: ProjectSettings( Project* proj, bool showtype = true, QWidget * parent = 0 ); bool musicFileChanged() const { return m_musicFileChanged; } public slots: void browseMusicFile(); void changeProjectType(); void accept(); private: Project* m_project; bool m_musicFileChanged; // to reinitialize the player if changed bool m_generalTabShown; }; #endif // PROJECTSETTINGS_H karlyriceditor-1.11/src/recentfiles.cpp000066400000000000000000000072671233766701300203050ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include #include #include "recentfiles.h" RecentFiles::RecentFiles( QMenu * menu, QAction * before, int maxfiles, const QString& settingsname ) { if ( maxfiles < 1 ) qFatal( "RecentFiles::RecentFiles: maxfiles (%d) is < 1 ", maxfiles ); m_settingsName = settingsname.isEmpty() ? "recentFileList" : settingsname; m_actions.resize( maxfiles ); // Create the actions for ( int i = 0; i < maxfiles; ++i ) { m_actions[i] = new QAction( this ); m_actions[i]->setVisible(false); connect( m_actions[i], SIGNAL(triggered()), this, SLOT(actionRecent()) ); } // Add them to the menu for ( int i = 0; i < maxfiles; ++i ) menu->insertAction( before, m_actions[i] ); // Add a separator after the last action m_separator = menu->insertSeparator( before ); // Update the actions menu updateMenu(); } RecentFiles::~RecentFiles() { } void RecentFiles::setCurrentFile( const QString& file ) { QStringList files = loadRecentFiles(); files.removeAll( file ); files.prepend( file ); while ( files.size() > m_actions.size() ) files.removeLast(); saveRecentFiles( files ); updateMenu(); } void RecentFiles::removeRecentFile( const QString& file ) { QStringList files = loadRecentFiles(); files.removeAll( file ); saveRecentFiles( files ); updateMenu(); } void RecentFiles::actionRecent() { QAction *action = qobject_cast(sender()); if ( action ) emit openRecentFile( action->data().toString() ); } void RecentFiles::updateMenu() { QStringList files = loadRecentFiles(); int numRecentFiles = qMin( files.size(), m_actions.size() ); for ( int i = 0; i < m_actions.size(); ++i ) { if ( i < numRecentFiles ) { QString text = tr("&%1 %2").arg(i + 1).arg( QFileInfo( files[i] ).fileName() ); m_actions[i]->setText(text); m_actions[i]->setToolTip( files[i] ); m_actions[i]->setData(files[i]); m_actions[i]->setVisible(true); } else m_actions[i]->setVisible(false); } m_separator->setVisible( numRecentFiles > 0 ); } QString RecentFiles::latestFile() { QStringList files = loadRecentFiles(); if ( files.isEmpty() ) return QString::null; else return files[0]; } QStringList RecentFiles::loadRecentFiles() { QSettings settings; return settings.value( m_settingsName ).toStringList(); } void RecentFiles::saveRecentFiles( const QStringList& files ) { QSettings settings; settings.setValue( m_settingsName, files ); } karlyriceditor-1.11/src/recentfiles.h000066400000000000000000000056441233766701300177470ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef RECENTFILES_H #define RECENTFILES_H #include #include #include #include // This class assumes QSettings object can be created using default constructor, i.e. // QCoreApplication::setOrganizationName( ... ); // QCoreApplication::setOrganizationDomain( ... ); // QCoreApplication::setApplicationName( ... ); // have been called. // // This class is based on Qt example // class RecentFiles : public QObject { Q_OBJECT public: // A constructor specifies the menu to add recent files to, and the action to add it before. RecentFiles( QMenu * menu, QAction * before, int maxfiles = 5, const QString& settingsname = QString::null ); virtual ~RecentFiles(); signals: void openRecentFile( const QString& file ); public slots: // Sets the current file to the recent file. Does the following: // - Adds it to the top of recent files list, or moves it to the top; // - Removes the last entry, if necessary; void setCurrentFile( const QString& file ); // Removes the current file from the recent files. Useful, for example, // when attempt to open a recent project failed. void removeRecentFile( const QString& file ); // Returns the last added recent file QString latestFile(); protected: // Override those functions in a derived class to store/load the // list of recent files from a different place QStringList loadRecentFiles(); void saveRecentFiles( const QStringList& files ); private slots: void actionRecent(); void updateMenu(); private: QString m_settingsName; QAction * m_separator; QVector< QAction* > m_actions; }; #endif // RECENTFILES_H karlyriceditor-1.11/src/resources.qrc000066400000000000000000000032251233766701300200050ustar00rootroot00000000000000 images/dryicons_play.png images/dryicons_pause.png images/dryicons_forward.png images/dryicons_rewind.png images/dryicons_stop.png images/dryicons_application_add.png images/dryicons_application_edit.png images/dryicons_application_remove.png images/dryicons_clock.png images/dryicons_sound.png images/dryicons_clock_add.png images/dryicons_clock_remove.png images/dryicons_cd.png images/dryicons_process.png images/dryicons_application_accept.png images/dryicons_application_deny.png images/dryicons_application_search.png images/dryicons_computer.png images/dryicons_new.png images/dryicons_open_folder.png images/dryicons_undo.png images/dryicons_redo.png images/dryicons_music.png images/dryicons_page_search.png images/dryicons_appicon.png images/save.png images/saveas.png images/clear.png images/nocover.png images/quit.png images/casio.jpg images/application_icon.png images/piano.png images/page_swap.png karlyriceditor-1.11/src/settings.cpp000066400000000000000000000204361233766701300176330ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include #include #include "ui_dialog_settings.h" #include "settings.h" Settings * pSettings; Settings::Settings() { QSettings settings; m_phononSoundDelay = settings.value( "advanced/phononsounddelay", 250 ).toInt(); m_checkForUpdates = settings.value( "advanced/checkforupdates", true ).toBool(); m_editorStopAtLineEnd = settings.value( "editor/stopatlineend", true ).toBool(); m_editorStopNextWord = settings.value( "editor/stopatnextword", false ).toBool(); m_editorWordChars = settings.value( "editor/charsinword", 2 ).toInt(); m_editorSkipEmptyLines = settings.value( "editor/skipemptylines", true ).toBool(); m_editorSupportBlocks = settings.value( "editor/supportblocks", true ).toBool(); m_editorMaxBlock = settings.value( "editor/maxlinesinblock", 8 ).toInt(); m_editorFontFamily = settings.value( "editor/fontfamily", "arial" ).toString(); m_editorFontSize = settings.value( "editor/fontsize", 14 ).toInt(); m_editorDoubleTimeMark = settings.value( "editor/doubletimemark", true ).toBool(); m_timeMarkFontFamily = settings.value( "timemark/fontfamily", "arial" ).toString(); m_timeMarkFontSize = settings.value( "timemark/fontsize", 10 ).toInt(); m_timeMarkPlaceholderBackground = QColor( settings.value( "timemark/placeholderbgcolor", "grey" ).toString() ); m_timeMarkTimeBackground = QColor( settings.value( "timemark/timebgcolor", "yellow" ).toString() ); m_timeMarkPlaceholderText = QColor( settings.value( "timemark/placeholdertextgcolor", "black" ).toString() ); m_timeMarkTimeText = QColor( settings.value( "timemark/timetextcolor", "black" ).toString() ); m_timeMarkPitchBackground = QColor( settings.value( "timemark/pitchbgcolor", "green" ).toString() ); m_timeMarkShowPitch = settings.value( "timemark/showpitch", true ).toBool();; m_previewFontFamily = settings.value( "preview/fontfamily", "arial" ).toString(); m_previewFontSize = settings.value( "preview/fontsize", 24 ).toInt(); m_previewBackground = QColor( settings.value( "preview/bgcolor", "black" ).toString() ); m_previewTextInactive = QColor( settings.value( "preview/inactivecolor", "white" ).toString() ); m_previewTextActive = QColor( settings.value( "preview/activecolor", "green" ).toString() ); } void Settings::edit() { QSettings settings; QDialog dlg; Ui::DialogSettings ui; ui.setupUi( &dlg ); // Set variables ui.leTickDelay->setText( QString::number( m_phononSoundDelay ) ); ui.leEditorWordCount->setValue( m_editorWordChars ); ui.leEditorBlockLines->setValue( m_editorMaxBlock ); ui.cbEditorStopAtEnd->setChecked( m_editorStopAtLineEnd ); ui.cbEditorStopAtWords->setChecked( m_editorStopNextWord ); ui.cbEditorDoubleTags->setChecked( m_editorDoubleTimeMark ); ui.cbEditorSupportBlocks->setChecked( m_editorSupportBlocks ); ui.fontEditor->setCurrentFont( QFont( m_editorFontFamily ) ); ui.fontEditorSize->setValue( m_editorFontSize ); ui.fontTimeMark->setCurrentFont( QFont( m_timeMarkFontFamily ) ); ui.fontTimeMarkSize->setValue( m_timeMarkFontSize ); ui.btnTimingColorPhBg->setColor( m_timeMarkPlaceholderBackground ); ui.btnTimingColorPhText->setColor( m_timeMarkPlaceholderText ); ui.btnTimingColorTiBg->setColor( m_timeMarkTimeBackground ); ui.btnTimingColorTiText->setColor( m_timeMarkTimeText ); ui.btnTimingColorTiPitch->setColor( m_timeMarkPitchBackground ); ui.cbShowPitchTimingMark->setChecked( m_timeMarkShowPitch ); ui.fontPreview->setCurrentFont( QFont( m_previewFontFamily ) ); ui.fontPreviewSize->setValue( m_previewFontSize ); ui.btnPreviewColorActive->setColor( m_previewTextActive ); ui.btnPreviewColorBg->setColor( m_previewBackground ); ui.btnPreviewColorInactive->setColor( m_previewTextInactive ); ui.cbCheckForUpdates->setChecked( m_checkForUpdates ); if ( settings.contains( "advanced/lastupdate" ) ) ui.lblLastUpdate->setText( QObject::tr("Last checked: %1") .arg( settings.value( "advanced/lastupdate" ).toDateTime().toString( Qt::SystemLocaleShortDate ) ) ); if ( dlg.exec() == QDialog::Rejected ) return; // Get the values m_phononSoundDelay = ui.leTickDelay->text().toInt(); m_checkForUpdates = ui.cbCheckForUpdates->isChecked(); m_editorWordChars = ui.leEditorWordCount->text().toInt(); m_editorMaxBlock = ui.leEditorBlockLines->text().toInt(); m_editorStopAtLineEnd = ui.cbEditorStopAtEnd->isChecked(); m_editorStopNextWord = ui.cbEditorStopAtWords->isChecked(); m_editorDoubleTimeMark = ui.cbEditorDoubleTags->isChecked(); m_editorSupportBlocks = ui.cbEditorSupportBlocks->isChecked(); m_editorFontFamily = ui.fontEditor->currentFont().family(); m_editorFontSize = ui.fontEditorSize->value(); m_timeMarkFontFamily = ui.fontTimeMark->currentFont().family(); m_timeMarkFontSize = ui.fontTimeMarkSize->value(); m_timeMarkPlaceholderBackground = ui.btnTimingColorPhBg->color(); m_timeMarkPlaceholderText = ui.btnTimingColorPhText->color(); m_timeMarkTimeBackground = ui.btnTimingColorTiBg->color(); m_timeMarkTimeText = ui.btnTimingColorTiText->color(); m_timeMarkPitchBackground = ui.btnTimingColorTiPitch->color(); m_timeMarkShowPitch = ui.cbShowPitchTimingMark->isChecked(); m_previewFontFamily = ui.fontPreview->currentFont().family(); m_previewFontSize = ui.fontPreviewSize->value(); m_previewTextActive = ui.btnPreviewColorActive->color(); m_previewBackground = ui.btnPreviewColorBg->color(); m_previewTextInactive = ui.btnPreviewColorInactive->color(); // And save them settings.setValue( "advanced/phononsounddelay", m_phononSoundDelay ); settings.setValue( "advanced/checkforupdates", m_checkForUpdates ); settings.setValue( "editor/stopatlineend", m_editorStopAtLineEnd ); settings.setValue( "editor/stopatnextword", m_editorStopNextWord ); settings.setValue( "editor/charsinword", m_editorWordChars ); settings.setValue( "editor/skipemptylines", m_editorSkipEmptyLines ); settings.setValue( "editor/supportblocks", m_editorSupportBlocks ); settings.setValue( "editor/maxlinesinblock", m_editorMaxBlock ); settings.setValue( "editor/fontfamily", m_editorFontFamily ); settings.setValue( "editor/fontsize", m_editorFontSize ); settings.setValue( "editor/doubletimemark", m_editorDoubleTimeMark ); settings.setValue( "timemark/fontfamily", m_timeMarkFontFamily ); settings.setValue( "timemark/fontsize", m_timeMarkFontSize ); settings.setValue( "timemark/placeholderbgcolor", m_timeMarkPlaceholderBackground.name() ); settings.setValue( "timemark/timebgcolor", m_timeMarkTimeBackground.name() ); settings.setValue( "timemark/placeholdertextgcolor", m_timeMarkPlaceholderText.name() ); settings.setValue( "timemark/timetextcolor", m_timeMarkTimeText.name() ); settings.setValue( "timemark/pitchbgcolor", m_timeMarkPitchBackground.name() ); settings.setValue( "timemark/showpitch", m_timeMarkShowPitch ); settings.setValue( "preview/fontfamily", m_previewFontFamily ); settings.setValue( "preview/fontsize", m_previewFontSize ); settings.setValue( "preview/bgcolor", m_previewBackground.name() ); settings.setValue( "preview/inactivecolor", m_previewTextInactive.name() ); settings.setValue( "preview/activecolor", m_previewTextActive.name() ); } karlyriceditor-1.11/src/settings.h000066400000000000000000000062701233766701300173000ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef SETTINGS_H #define SETTINGS_H #include #include class Settings { public: Settings(); void edit(); public: // There is a delay between a note is being singed, and Phonon sends its tick() // signal. To compensate for this delay, output lyrics should include it. int m_phononSoundDelay; // Check for updates bool m_checkForUpdates; // When moving the cursor after inserting the tag, // also stop at the line ends. bool m_editorStopAtLineEnd; // When moving the cursor after inserting the tag, // also stop at next words (a word should have more // than m_editorWordChars chars to be considered). bool m_editorStopNextWord; int m_editorWordChars; // When moving the cursor after inserting the tag, // skip the empty lines bool m_editorSkipEmptyLines; // Should editor support blocks bool m_editorSupportBlocks; int m_editorMaxBlock; // Editor font family and size QString m_editorFontFamily; int m_editorFontSize; // If set, and the time placeholder is present, it keeps the cursor // after the time is replaced, letting to enter two timing marks // at the same position. bool m_editorDoubleTimeMark; // Time mark font family and size QString m_timeMarkFontFamily; int m_timeMarkFontSize; QColor m_timeMarkPlaceholderBackground; QColor m_timeMarkTimeBackground; QColor m_timeMarkPitchBackground; // background for timing marks which have both time and pitch QColor m_timeMarkPlaceholderText; QColor m_timeMarkTimeText; // Whether to show pitch in timing mark bool m_timeMarkShowPitch; // Preview window font family and size QString m_previewFontFamily; int m_previewFontSize; // Preview window colors - background, text, active text QColor m_previewBackground; QColor m_previewTextInactive; QColor m_previewTextActive; }; extern Settings * pSettings; #endif // SETTINGS_H karlyriceditor-1.11/src/src.pro000066400000000000000000000050461233766701300166000ustar00rootroot00000000000000TEMPLATE = app TARGET = ../bin/karlyriceditor DEPENDPATH += . LIBS += -lcrypto DEFINES += USE_LICENSING CONFIG += link_pkgconfig PKGCONFIG += libavformat libavcodec libswscale libavresample libavutil sdl win32-g++-cross: { LIBS += -lwsock32 -ldxguid } linux-g++-32: { LIBS += -L. } # Input HEADERS += mainwindow.h \ wizard_newproject.h \ project.h \ playerwidget.h \ playerbutton.h \ editor.h \ settings.h \ viewwidget.h \ testwindow.h \ lyrics.h \ version.h \ projectsettings.h \ recentfiles.h \ colorbutton.h \ dialog_selectencoding.h \ gentlemessagebox.h \ checknewversion.h \ cdg.h \ cdgrenderer.h \ cdggenerator.h \ validator.h \ editorhighlighting.h \ lyricsrenderer.h \ textrenderer.h \ lyricswidget.h \ ffmpegvideodecoder.h \ ffmpegvideoencoder.h \ videogenerator.h \ lyricsevents.h \ background.h \ audioplayer.h \ ffmpeg_headers.h \ audioplayerprivate.h \ export_params.h \ licensing.h \ karaokelyricstextkar.h \ kfn_file_parser.h \ dialog_timeadjustment.h \ util.h \ videoencodingprofiles.h SOURCES += mainwindow.cpp \ main.cpp \ wizard_newproject.cpp \ project.cpp \ playerwidget.cpp \ playerbutton.cpp \ editor.cpp \ settings.cpp \ viewwidget.cpp \ testwindow.cpp \ lyrics.cpp \ projectsettings.cpp \ recentfiles.cpp \ colorbutton.cpp \ dialog_selectencoding.cpp \ gentlemessagebox.cpp \ checknewversion.cpp \ cdgrenderer.cpp \ cdggenerator.cpp \ editorhighlighting.cpp \ lyricsrenderer.cpp \ textrenderer.cpp \ lyricswidget.cpp \ ffmpegvideodecoder.cpp \ ffmpegvideoencoder.cpp \ videogenerator.cpp \ lyricsevents.cpp \ background.cpp \ audioplayer.cpp \ audioplayerprivate.cpp \ ffmpeg_headers.cpp \ export_params.cpp \ licensing.cpp \ karaokelyricstextkar.cpp \ kfn_file_parser.cpp \ dialog_timeadjustment.cpp \ util.cpp \ videoencodingprofiles.cpp RESOURCES += resources.qrc FORMS += mainwindow.ui \ wiznewproject_lyrictype.ui \ wiznewproject_intro.ui \ wiznewproject_finish.ui \ wiznewproject_musicfile.ui \ playerwidget.ui \ viewwidget.ui \ dialog_about.ui \ dialog_projectsettings.ui \ dialog_settings.ui \ dialog_selectencoding.ui \ gentlemessagebox.ui \ dialog_export_params.ui \ dialog_encodingprogress.ui \ dialog_testwindow.ui \ dialog_registration.ui \ dialog_timeadjustment.ui \ video_profile_dialog.ui karlyriceditor-1.11/src/testwindow.cpp000066400000000000000000000043601233766701300202000ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include "testwindow.h" #include "lyricswidget.h" #include "audioplayer.h" #include "playerwidget.h" TestWindow::TestWindow( QWidget *parent ) : QDialog(parent), Ui::DialogTestWindow() { setupUi( this ); m_widget = 0; m_layout = new QVBoxLayout( frame ); } void TestWindow::setLyricWidget( LyricsWidget * lw ) { // Clear the layout QWidget *child = (QWidget*) m_layout->takeAt(0); delete child; m_widget = lw; m_layout->addWidget( lw ); update(); } void TestWindow::closeEvent( QCloseEvent * event ) { emit closed(); QDialog::closeEvent( event ); } void TestWindow::tick( qint64 value ) { m_widget->updateLyrics( value ); qint64 reminder = pAudioPlayer->totalTime() - value; lblCurrent->setText( PlayerWidget::tickToString( value ) ); lblTotal->setText( PlayerWidget::tickToString( reminder ) ); int pval = value * progressBar->maximum() / pAudioPlayer->totalTime(); if ( progressBar->value() != pval ) progressBar->setValue( pval ); } karlyriceditor-1.11/src/testwindow.h000066400000000000000000000034451233766701300176500ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef TESTWINDOW_H #define TESTWINDOW_H #include #include "ui_dialog_testwindow.h" class LyricsWidget; class TestWindow : public QDialog, public Ui::DialogTestWindow { Q_OBJECT public: TestWindow( QWidget *parent = 0 ); void setLyricWidget( LyricsWidget * lw ); signals: void closed(); public slots: void tick( qint64 value ); protected: void closeEvent( QCloseEvent * event ); private: QVBoxLayout * m_layout; LyricsWidget* m_widget; }; #endif // TESTWINDOW_H karlyriceditor-1.11/src/textrenderer.cpp000066400000000000000000000537311233766701300205120ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include #include #include "textrenderer.h" #include "settings.h" #include "project.h" #include "version.h" #include "licensing.h" // Some preamble constants static const int PREAMBLE_SQUARE = 500; // 500ms for each square static const int PREAMBLE_MIN_PAUSE = 5000; // 5000ms minimum pause between verses for the preamble to appear // Font size difference static const int SMALL_FONT_DIFF = 4; // 4px less TextRenderer::TextRenderer( int width, int height ) : LyricsRenderer() { m_image = QImage( width, height, QImage::Format_ARGB32 ); init(); } void TextRenderer::setLyrics( const Lyrics& lyrics ) { init(); // Store the empty block at position 0 which will serve as future title placeholder LyricBlockInfo titleblock; titleblock.timestart = 0; titleblock.timeend = 0; m_lyricBlocks.push_back( titleblock ); // Compile the lyrics for ( int bl = 0; bl < lyrics.totalBlocks(); bl++ ) { const Lyrics::Block& block = lyrics.block( bl ); if ( block.size() == 0 ) continue; bool intitle = false; bool hadtext = false; LyricBlockInfo binfo; binfo.timestart = block.first().first().timing; binfo.timeend = block.last().last().timing; for ( int ln = 0; ln < block.size(); ln++ ) { const Lyrics::Line& line = block[ln]; // Calculate the time the line ends qint64 endlinetime = line.last().timing; // If the last timing tag for this line is empty, this is the end. // We prefer it, but if it is not the case, we'll find something else. if ( !line.last().text.isEmpty() ) { qint64 calcenlinetime; if ( ln + 1 < block.size() ) // Beginning of next line in this block calcenlinetime = block[ln].first().timing; else if ( bl + 1 < lyrics.totalBlocks() ) // Beginning of next block calcenlinetime = block[ln+1].first().timing; else // last line in last block calcenlinetime = endlinetime + 2000; // 2 sec endlinetime = qMin( calcenlinetime, endlinetime + 2000 ); } // Last item must be empty, so it is ok for ( int pos = 0; pos < line.size(); pos++ ) { Lyrics::Syllable lentry = line[pos]; if ( lentry.text.trimmed().isEmpty() && !hadtext ) continue; if ( lentry.text.isEmpty() ) continue; qint64 starttime = line[pos].timing; qint64 endtime = (pos + 1 == line.size()) ? endlinetime : line[pos+1].timing; // Reset the block start time in case all previous lines were empty if ( !hadtext ) { hadtext = true; binfo.timestart = starttime; } compileLine( lentry.text, starttime, endtime, &binfo, &intitle ); } binfo.text += "\n"; binfo.offsets [ endlinetime ] = binfo.text.size() - 1; } binfo.text = binfo.text.trimmed(); // Do not add the blocks with no text if ( !binfo.text.isEmpty() ) m_lyricBlocks.push_back( binfo ); } /* // Dump the result qDebug("Block dump, %d blocks\n", m_lyricBlocks.size() ); for ( int bl = 0; bl < m_lyricBlocks.size(); bl++ ) { qDebug("Block %d: (%d-%d)\n", bl, (int) m_lyricBlocks[bl].timestart, (int) m_lyricBlocks[bl].timeend ); for ( QMap< qint64, unsigned int >::const_iterator it = m_lyricBlocks[bl].offsets.begin(); it != m_lyricBlocks[bl].offsets.end(); ++it ) { int pos = it.value(); qDebug("\tTiming %d, pos %d\n%s|%s", (int) it.key(), pos, qPrintable( m_lyricBlocks[bl].text.left( pos + 1 ) ), qPrintable( m_lyricBlocks[bl].text.mid( pos + 1 ) ) ); } } */ m_lyricEvents = lyrics.events(); prepareEvents(); } void TextRenderer::compileLine( const QString& line, qint64 starttime, qint64 endtime, LyricBlockInfo * binfo, bool *intitle ) { // Drawn text string QString drawntext; // Stores the character offsets which change their color with time QVector timedcharacters; int blocktextstart = binfo->text.size(); for ( int ch = 0; ch < line.length(); ch++ ) { QChar lchar = line[ch]; // Parse the special sequences if ( lchar == '@' ) { // Title start/end if ( ch + 1 < line.length() && line[ch+1] == '$' ) { *intitle = !*intitle; ch++; continue; } // Color change if ( ch + 7 < line.length() && line[ch+1] == '#' ) { // Store the new 'unsung' color for this position QString colorname = line.mid( ch + 1, 7 ); binfo->colors[ blocktextstart + drawntext.length() ] = colorname; ch += 7; continue; } // Font size change down if ( ch + 1 < line.length() && line[ch+1] == '<' ) { binfo->fonts[ blocktextstart + drawntext.length() ] = -SMALL_FONT_DIFF; // make the font smaller ch += 1; continue; } // Font size change up if ( ch + 1 < line.length() && line[ch+1] == '>' ) { binfo->fonts[ blocktextstart + drawntext.length() ] = SMALL_FONT_DIFF; // make the font larger ch += 1; continue; } } // Nothing changes the color in title mode; also skip non-letters if ( !*intitle && lchar.isLetterOrNumber() ) timedcharacters.push_back( drawntext.length() ); drawntext.push_back( lchar ); } // Now we know the total number of characters, and can calc the time step if ( !timedcharacters.isEmpty() ) { int timestep = qMax( 1, (int) ((endtime - starttime) / timedcharacters.size() ) ); for ( int ch = 0; ch < timedcharacters.size(); ch++ ) binfo->offsets [ starttime + ch * timestep ] = blocktextstart + timedcharacters[ch]; } binfo->text += drawntext; } void TextRenderer::setRenderFont( const QFont& font ) { m_renderFont = font; m_forceRedraw = true; } void TextRenderer::setColorBackground( const QColor& color ) { m_colorBackground = color; m_forceRedraw = true; } void TextRenderer::setColorTitle( const QColor& color ) { m_colorTitle = color; m_forceRedraw = true; } void TextRenderer::setColorToSing( const QColor& color ) { m_colorToSing = color; m_forceRedraw = true; } void TextRenderer::setColorSang( const QColor& color ) { m_colorSang = color; m_forceRedraw = true; } void TextRenderer::setColorAlpha( int alpha ) { m_colorTitle.setAlpha( alpha ); m_colorToSing.setAlpha( alpha ); m_colorSang.setAlpha( alpha ); } void TextRenderer::setTitlePageData( const QString& artist, const QString& title, const QString& userCreatedBy, unsigned int msec ) { // setLyrics should be called before setTitlePageData if ( m_lyricBlocks.size() < 2 ) abort(); QString createdBy = QString("@<@%1Created by %2\nhttp://www.ulduzsoft.com/\n") .arg( m_colorToSing.name()) .arg( APP_NAME); if ( pLicensing->isEnabled() && pLicensing->isValid() && !userCreatedBy.isEmpty() ) createdBy = userCreatedBy; createdBy = createdBy.replace( "
", "\n" ); // Block 0 is reserved for us; fill it up QString titletext = QString("@%1%2\n\n%3\n\n%4") .arg( m_colorTitle.name() ) .arg( artist ) .arg( title ) .arg( createdBy ); // Do we have at least 500ms to show the title? if ( m_lyricBlocks[1].timestart < 500 && !m_lyricBlocks[1].offsets.isEmpty() ) return; m_lyricBlocks[0].timestart = 0; m_lyricBlocks[0].timeend = qMin( (qint64) msec, m_lyricBlocks[1].timestart ); // Compile the line, replacing the spec characters bool intitle = true; compileLine( titletext, m_lyricBlocks[0].timestart, m_lyricBlocks[0].timeend, &m_lyricBlocks[0], &intitle ); m_forceRedraw = true; } void TextRenderer::init() { m_forceRedraw = true; m_colorBackground = pSettings->m_previewBackground; m_colorTitle = QColor( Qt::white ); m_colorToSing = pSettings->m_previewTextActive; m_colorSang = pSettings->m_previewTextInactive; setRenderFont( QFont( pSettings->m_previewFontFamily, pSettings->m_previewFontSize ) ); m_preambleHeight = 0; m_preambleLengthMs = 0; m_preambleCount = 0; m_preambleTimeLeft = 0; m_lastDrawnPreamble = 0; m_lastSungTime = 0; m_drawPreamble = false; m_lastBlockPlayed = -2; m_lastPosition = -2; m_beforeDuration = 5000; m_afterDuration = 1000; m_prefetchDuration = 0; m_lyricBlocks.clear(); } void TextRenderer::setPreambleData( unsigned int height, unsigned int timems, unsigned int count ) { m_preambleHeight = height; m_preambleLengthMs = timems; m_preambleCount = count; m_forceRedraw = true; } void TextRenderer::forceCDGmode() { m_forceRedraw = true; } void TextRenderer::setDurations( unsigned int before, unsigned int after ) { m_beforeDuration = before; m_afterDuration = after; m_forceRedraw = true; } void TextRenderer::setPrefetch( unsigned int prefetch ) { m_prefetchDuration = prefetch; m_forceRedraw = true; } int TextRenderer::lyricForTime( qint64 tickmark, int * sungpos ) { int nextblk = -1; // Find the next playable lyric block for ( int bl = 0; bl < m_lyricBlocks.size(); bl++ ) { if ( tickmark < m_lyricBlocks[bl].timestart ) { nextblk = bl; break; } } // If there is a block within the prefetch timing, show it even if it overwrites the currently played block // (this is why this check is on top) if ( m_prefetchDuration > 0 && nextblk != -1 && m_lyricBlocks[nextblk].timestart - tickmark <= m_prefetchDuration ) { // We update the timing, but the preamble show/noshow does not change here m_preambleTimeLeft = qMax( 0, (int) (m_lyricBlocks[nextblk].timestart - tickmark) ); *sungpos = -1; return nextblk; } // Find the block which should be currently played, if any. int curblk = -1; int pos = -1; for ( int bl = 0; bl < m_lyricBlocks.size(); bl++ ) { if ( tickmark < m_lyricBlocks[bl].timestart || tickmark > m_lyricBlocks[bl].timeend ) continue; curblk = bl; QMap< qint64, unsigned int >::const_iterator it = m_lyricBlocks[bl].offsets.find( tickmark ); if ( it == m_lyricBlocks[bl].offsets.end() ) it = m_lyricBlocks[bl].offsets.lowerBound( tickmark ); if ( it == m_lyricBlocks[bl].offsets.end() ) { // This may happen if the whole block is title break; } pos = it.value(); ++it; break; } // Anything to play right now? if ( curblk == -1 ) { // Nothing active to show, so if there is a block within next five seconds, show it. if ( nextblk != -1 && m_lyricBlocks[nextblk].timestart - tickmark <= m_beforeDuration ) { m_preambleTimeLeft = qMax( 0, (int) (m_lyricBlocks[nextblk].timestart - tickmark) ); // We show preamble if there was silence over PREAMBLE_MIN_PAUSE and the block // actually contains any time changes if ( tickmark - m_lastSungTime > PREAMBLE_MIN_PAUSE && !m_lyricBlocks[nextblk].offsets.isEmpty() ) { // m_preambleHeight == 0 disables the preamble if ( m_preambleHeight > 0 ) m_drawPreamble = true; } *sungpos = -1; return nextblk; } // No next block or too far away yet m_drawPreamble = false; return -1; } m_drawPreamble = false; // If we're singing something (i.e. pos > 0), remember the timing if ( pos >= 0 ) m_lastSungTime = tickmark; *sungpos = pos; return curblk; } bool TextRenderer::verifyFontSize( const QSize& size, const QFont& font ) { // Initialize the fonts QFont normalfont = font; // Test all lyrics whether it fits for ( int bl = 0; bl < m_lyricBlocks.size(); bl++ ) { QRect rect = boundingRect( bl, normalfont ); // Still fit? if ( rect.width() >= size.width() || rect.height() >= size.height() ) { // Not fit, use a previous font size return false; } } return true; } int TextRenderer::autodetectFontSize( const QSize& size, const QFont& font ) { QFont normalfont = font; // Start with 8 int fontsize = 8; while ( 1 ) { normalfont.setPointSize( fontsize ); if ( !verifyFontSize( size, normalfont ) ) return fontsize - 1; fontsize++; } } bool TextRenderer::checkFit( const QSize& imagesize, const QFont& font, const QString& text ) { TextRenderer renderer(10,10); renderer.init(); LyricBlockInfo testblock; testblock.timestart = 0; testblock.timeend = 0; bool intitle = true; renderer.compileLine( text, 0, 0, &testblock, &intitle ); renderer.m_lyricBlocks.push_back( testblock ); QRect rect = renderer.boundingRect( 0, font ); return rect.width() <= imagesize.width() && rect.height() <= imagesize.height(); } QRect TextRenderer::boundingRect( int blockid, const QFont& font ) { QString block = m_lyricBlocks[blockid].text; // Calculate the height QFont curFont(font); QFontMetrics metrics = QFontMetrics( curFont ); // Calculate the width and height for every line int linewidth = 0, lineheight = 0, totalheight = 0, totalwidth = 0; int cur = 0; while ( 1 ) { // Line/text end if ( cur >= block.length() || block[cur] == '\n' ) { // Adjust the total height totalheight += lineheight; totalwidth = qMax( totalwidth, linewidth ); if ( cur >= block.length() ) break; // we're done here cur++; linewidth = 0; continue; } // We're calculating line width here, so check for any font change events QMap< unsigned int, int >::const_iterator fontchange = m_lyricBlocks[blockid].fonts.find( cur ); if ( fontchange != m_lyricBlocks[blockid].fonts.end() ) { curFont.setPointSize( curFont.pointSize() + fontchange.value() ); metrics = QFontMetrics( curFont ); } linewidth += metrics.width( block[cur] ); lineheight = qMax( lineheight, metrics.height() ); cur++; } return QRect( 0, 0, totalwidth, totalheight ); } void TextRenderer::drawLyrics( int blockid, int pos, const QRect& boundingRect ) { QString block = m_lyricBlocks[blockid].text; // Prepare the painter QPainter painter( &m_image ); painter.setFont( m_renderFont ); // Used in calculations only QFont curFont( m_renderFont ); QFontMetrics metrics( curFont ); QColor fallbackColor = m_colorToSing; if ( pos == -1 ) painter.setPen( m_colorToSing ); else painter.setPen( m_colorSang ); // Get the height offset from the rect. int start_y; // Draw title in the center, the rest at bottom if ( blockid == 0 ) start_y = (m_image.height() - boundingRect.height()) / 2 + painter.fontMetrics().height(); else start_y = (m_image.height() - boundingRect.height()); // Draw the whole text int linestart = 0; int linewidth = 0; int cur = 0; while ( 1 ) { // Line/text end if ( cur >= block.length() || block[cur] == '\n' ) { // Now we know the width, calculate the start offset int start_x = (m_image.width() - linewidth) / 2; // Draw the line for ( int i = linestart; i < cur; i++ ) { // Handle the font change events QMap< unsigned int, int >::const_iterator fontchange = m_lyricBlocks[blockid].fonts.find( i ); if ( fontchange != m_lyricBlocks[blockid].fonts.end() ) { painter.setFont( QFont(painter.font().family(), painter.font().pointSize() + fontchange.value() ) ); } // Handle the color change events if pos doesn't cover them QMap< unsigned int, QString >::const_iterator colchange = m_lyricBlocks[blockid].colors.find( i ); if ( colchange != m_lyricBlocks[blockid].colors.end() ) { QColor newcolor( colchange.value() ); fallbackColor = newcolor; if ( i > pos ) painter.setPen( newcolor ); } if ( pos != -1 && i >= pos ) { painter.setPen( fallbackColor ); } // Outline const int OL = 1; painter.save(); painter.setPen( Qt::black ); painter.drawText( start_x - OL, start_y - OL, (QString) block[i] ); painter.drawText( start_x + OL, start_y - OL, (QString) block[i] ); painter.drawText( start_x - OL, start_y + OL, (QString) block[i] ); painter.drawText( start_x + OL, start_y + OL, (QString) block[i] ); painter.restore(); painter.drawText( start_x, start_y, (QString) block[i] ); start_x += painter.fontMetrics().width( block[i] ); } if ( cur >= block.length() ) break; // we're done here // Start the next line start_y += painter.fontMetrics().height(); cur++; linewidth = 0; linestart = cur; continue; } // We're calculating line width here, so check for any font change events QMap< unsigned int, int >::const_iterator fontchange = m_lyricBlocks[blockid].fonts.find( cur ); if ( fontchange != m_lyricBlocks[blockid].fonts.end() ) { curFont.setPointSize( curFont.pointSize() + fontchange.value() ); metrics = QFontMetrics( curFont ); } linewidth += metrics.width( block[cur] ); cur++; } } void TextRenderer::drawPreamble() { // Is there anything to draw? if ( m_preambleTimeLeft <= PREAMBLE_SQUARE + 50 ) return; int cutoff_time = m_preambleTimeLeft - PREAMBLE_SQUARE - 50; int preamble_spacing = m_image.width() / 100; int preamble_width = (m_image.width() - preamble_spacing * m_preambleCount ) / m_preambleCount; QPainter painter( &m_image ); painter.setPen( Qt::black ); painter.setBrush( m_colorTitle ); // Draw a square for each PREAMBLE_SQUARE; we do not draw anything for the last one, and speed up it 0.15sec for ( int i = 0; i < (int) m_preambleCount; i++ ) { if ( i * PREAMBLE_SQUARE > cutoff_time ) continue; painter.drawRect( preamble_spacing + i * (preamble_spacing + preamble_width), preamble_spacing, preamble_width, m_preambleHeight ); } m_lastDrawnPreamble = m_preambleTimeLeft; } void TextRenderer::drawBackground( qint64 timing ) { // Fill the image background m_image.fill( m_colorBackground.rgb() ); if ( !m_lyricEvents.isEmpty() ) m_lyricEvents.draw( timing, m_image ); } int TextRenderer::update( qint64 timing ) { int result = UPDATE_COLORCHANGE; bool redrawPreamble = false; int sungpos = 0; // Should we show the title? int blockid = lyricForTime( timing, &sungpos ); /* if ( blockid != -1 ) { qDebug("Time %d: block %d: (%d-%d), pos %d\n", (int) timing, blockid, (int) m_lyricBlocks[blockid].timestart, (int) m_lyricBlocks[blockid].timeend, sungpos ); QString outbuf; for ( int i = 0; i < m_lyricBlocks[blockid].text.length(); i++ ) { if ( sungpos != -1 && i == sungpos ) outbuf += '|'; QMap< unsigned int, int >::const_iterator fontchange = m_lyricBlocks[blockid].fonts.find( i ); if ( fontchange != m_lyricBlocks[blockid].fonts.end() ) outbuf += QString("[FONT:%1]").arg( fontchange.value() ); QMap< unsigned int, QString >::const_iterator colchange = m_lyricBlocks[blockid].colors.find( i ); if ( colchange != m_lyricBlocks[blockid].colors.end() ) outbuf += QString("[COLOR:%1]").arg( colchange.value() ); outbuf.push_back( m_lyricBlocks[blockid].text[i] ); } qDebug("%s", qPrintable(outbuf)); } else qDebug("Time %d: no block!", (int) timing ); */ // Force the full redraw if time went backward if ( timing < m_lastSungTime ) m_forceRedraw = true; // Is it time to redraw the preamble? if ( m_drawPreamble && abs( m_lastDrawnPreamble - m_preambleTimeLeft ) > 450 ) redrawPreamble = true; // Check whether we can skip the redraws bool background_updated = (m_lyricEvents.isEmpty() || !m_lyricEvents.updated( timing )) ? false : true; if ( !m_forceRedraw && !redrawPreamble && !background_updated ) { // Did lyrics change at all? if ( blockid == m_lastBlockPlayed && sungpos == m_lastPosition ) return UPDATE_NOCHANGE; // If the new lyrics is empty, but we just finished playing something, keep it for 5 more seconds // (i.e. post-delay) if ( blockid == -1 && timing - m_lastSungTime < 5000 ) return UPDATE_NOCHANGE; } // Draw the background first drawBackground( timing ); // Did we get lyrics? if ( blockid != -1 ) { // Do the new lyrics fit into the image without resizing? QRect imgrect = boundingRect( blockid, m_renderFont ); if ( imgrect.width() > m_image.width() || imgrect.height() > m_image.height() ) { QSize newsize = QSize( qMax( imgrect.width() + 10, m_image.width() ), qMax( imgrect.height() + 10, m_image.height() ) ); m_image = QImage( newsize, QImage::Format_ARGB32 ); result = UPDATE_RESIZED; // Draw the background again on the resized image drawBackground( timing ); } // Draw the lyrics drawLyrics( blockid, sungpos, imgrect ); // Draw the preamble if needed if ( m_drawPreamble ) drawPreamble(); } // Is the text change significant enough to warrant full screen redraw? if ( blockid != m_lastBlockPlayed || m_forceRedraw ) { if ( result != UPDATE_RESIZED ) result = UPDATE_FULL; } //saveImage(); m_lastBlockPlayed = blockid; m_lastPosition = sungpos; m_forceRedraw = false; return result; } void TextRenderer::prepareEvents() { m_lyricEvents.prepare(); // Events need to be adjusted for the following: // - Those at the start of the block may move; qint64 lastend = 0; for ( int i = 0; i < m_lyricBlocks.size(); i++ ) { qint64 start = m_lyricBlocks[i].timestart; qint64 end = m_lyricBlocks[i].timeend; // Should the next block be shown earlier than expected via prefetch? if ( start - lastend > m_beforeDuration ) { // Adjust by m_beforeDuration m_lyricEvents.adjustTime( start, start - m_beforeDuration ); } else if ( m_prefetchDuration > 0 ) { // Adjust by m_prefetchDuration m_lyricEvents.adjustTime( start, start - m_prefetchDuration ); } else if ( start > lastend ) { // Adjust by start - lastend m_lyricEvents.adjustTime( start, start - (start - lastend) ); } lastend = end; } } karlyriceditor-1.11/src/textrenderer.h000066400000000000000000000135311233766701300201510ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef TEXTRENDERER_H #define TEXTRENDERER_H #include #include #include "lyricsrenderer.h" #include "lyricsevents.h" #include "lyrics.h" class Project; class TextRenderer : public LyricsRenderer { public: TextRenderer( int width, int height ); // Sets the lyrics to render. Resets all previously set params to defaults except setDisplaySize. void setLyrics( const Lyrics& lyrics ); // Lyrics data to render, overrides defaults from settings void setRenderFont( const QFont& font ); void setColorBackground( const QColor& color ); void setColorTitle( const QColor& color ); void setColorToSing( const QColor& color ); void setColorSang( const QColor& color ); void setPreambleData( unsigned int height, unsigned int timems, unsigned int count ); void setTitlePageData( const QString& artist, const QString& title, const QString& userCreatedBy, unsigned int msec ); // duration = 0 - no title, default void setColorAlpha( int alpha ); // 0 - 255 // Force CD+G rendering mode (no anti-aliasing) void forceCDGmode(); // Typically lyrics are shown a little before they are being sung, and kept after they end. // This function overrides default before (5000ms) and after (1000ms) lengths void setDurations( unsigned int before, unsigned int after ); // Some formats (like CD+G) draw lyrics pretty slow, which means if the lyrics screens // change immediately, at the time the next page is being drawn it is already sung. // This parameter changes the prefetch, meaning if its value is 1000, the new lyrics block // is always shown at least 1000ms before it is being sing, even if it is necessary to cut the // old block earlier. Default is zero. void setPrefetch( unsigned int prefetch ); // Draw a new lyrics image virtual int update( qint64 timing ); // Checks if a line or block fits into the requested image. static bool checkFit( const QSize& imagesize, const QFont& font, const QString& text ); // Autodetects the largest font size to fit all lyrics into a specific image size. int autodetectFontSize( const QSize& imagesize, const QFont& font ); // Verifies that all lyrics could be rendered into a specific image size using the provided font bool verifyFontSize( const QSize& imagesize, const QFont& font ); private: // Returns the lyrics bounding box for a line or for paragraph using the font specified, // or the default font if not specified QRect boundingRect( int blockid, const QFont& font ); void init(); void prepareEvents(); int lyricForTime( qint64 tickmark, int * sungpos ); QString titleScreen() const; void fixActionSequences( QString& block ); void drawLyrics( int blockid, int pos, const QRect& boundingRect ); void drawPreamble(); void drawBackground( qint64 timing ); private: // Lyrics to render typedef struct { qint64 timestart; qint64 timeend; // Text is for the whole block, with all special characters stripped down QString text; // Text offsets in block per specific time QMap< qint64, unsigned int > offsets; // Per-character color changes for following (non-sung) characters in the block. // if none, the default color is used QMap< unsigned int, QString > colors; // Per-character font size changes for following (non-sung) characters in the block. // if none, the default color is used QMap< unsigned int, int > fonts; } LyricBlockInfo; QVector< LyricBlockInfo > m_lyricBlocks; // Compile a single line void compileLine( const QString& line, qint64 starttime, qint64 endtime, LyricBlockInfo * binfo, bool *intitle ); // True if the image must be redrawn even if lyrics didn't change bool m_forceRedraw; // Rendering params QColor m_colorBackground; QColor m_colorTitle; QColor m_colorToSing; QColor m_colorSang; QFont m_renderFont; unsigned int m_preambleHeight; // how tall the preamble square should be; 0 - no preamble unsigned int m_preambleLengthMs; // maximum time the preamble is shown unsigned int m_preambleCount; // how many preamble squares to draw for m_preambleLengthMs unsigned int m_beforeDuration; unsigned int m_afterDuration; unsigned int m_prefetchDuration; // Handling the preamble stuff int m_preambleTimeLeft; // Time left to show the current preamble - 5000 ... 0 int m_lastDrawnPreamble; // Timing when the last time the preamble changed qint64 m_lastSungTime; bool m_drawPreamble; int m_lastBlockPlayed; int m_lastPosition; // Background events LyricsEvents m_lyricEvents; }; #endif // TEXTRENDERER_H karlyriceditor-1.11/src/util.cpp000066400000000000000000000041401233766701300167420ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include "util.h" #include "dialog_selectencoding.h" namespace Util { QString convertWithUserEncoding( const QByteArray& data ) { // Before we ask the user for the text encoding, run a loop - if all characters there are < 127, this is ASCII, // and we do not need to ask. for ( int i = 0; i < data.size(); i++ ) { if ( data.at(i) < 0 ) { // This is not ASCII. Ask the text encoding DialogSelectEncoding dlg( data ); if ( dlg.exec() == QDialog::Rejected ) return QString::null; return dlg.codec()->toUnicode( data ); break; } } return QString::fromLatin1( data ); } QString removeFileExtention( const QString& filename ) { int extdot = filename.lastIndexOf( '.' ); if ( extdot == -1 ) return filename; return filename.mid( 0, extdot + 1 ); } }; // namespace karlyriceditor-1.11/src/util.h000066400000000000000000000034471233766701300164200ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef UTIL_H #define UTIL_H namespace Util { // Parse the QByteArray; if the text is ASCII, return the string as-is. Otherwise pop up a dialog asking the user // for encoding, and apply the specified encoding, modifying the string. // Returned an empty string if encoding wasn't guessed and the user didn't choose any QString convertWithUserEncoding( const QByteArray& data ); // Removes the file extention (but keeps the dot) QString removeFileExtention( const QString& filename ); } #endif // UTIL_H karlyriceditor-1.11/src/validator.h000066400000000000000000000031321233766701300174170ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef VALIDATOR_H #define VALIDATOR_H class ValidatorError { public: unsigned int line; unsigned int column; QString error; ValidatorError() { }; ValidatorError( unsigned int l, unsigned int c, const QString& e ) { line = l; column = c; error = e; } }; #endif // VALIDATOR_H karlyriceditor-1.11/src/version.h000066400000000000000000000027421233766701300171250ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef INCLUDE_VERSION_H #define INCLUDE_VERSION_H #define APP_NAME "Karaoke Lyric Editor" #define APP_VERSION_MAJOR 1 #define APP_VERSION_MINOR 11 #endif /* INCLUDE_VERSION_H */ karlyriceditor-1.11/src/video_profile_dialog.ui000066400000000000000000000240661233766701300217760ustar00rootroot00000000000000 video_encoding_dialog 0 0 566 418 Video encoding parameters General Profile name: 2 0 Video options Profile 0 0 TextLabel Format 1 0 Qt::Horizontal 40 20 Codec 1 0 Audio options Codec: 1 0 Sample rate: 1 0 Channels: 1 0 Supported qualities for this codec 1 0 Audio bitrate: 1 0 Low quality Audio bitrate: High quality Medium quality Audio bitrate: Video bitrate: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Video bitrate: Video bitrate: Qt::Vertical 20 40 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() video_encoding_dialog accept() 248 254 157 274 buttonBox rejected() video_encoding_dialog reject() 316 260 286 274 karlyriceditor-1.11/src/videoencodingprofiles.cpp000066400000000000000000000754701233766701300223640ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include "videoencodingprofiles.h" VideoEncodingProfiles * pVideoEncodingProfiles = 0; // Embedded video encoding formats from openshot static VideoFormat video_formats[] = { { "DV/DVD Widescreen PAL (Anamorphic)", 720, 576, 25, 1, 64, 45, 16, 9, VIFO_INTERLACED, 0 }, { "QVGA Widescreen 29.97 fps", 426, 240, 30000, 1001, 1, 1, 16, 9, VIFO_INTERLACED, 601 }, { "HD 720p 23.98 fps", 1280, 720, 24000, 1001, 1, 1, 16, 9, VIFO_INTERLACED, 709 }, { "SVCD Widescreen NTSC", 480, 480, 30000, 1001, 20, 11, 16, 9, 0, 601 }, { "CIF PAL", 352, 288, 25, 1, 59, 54, 4, 3, VIFO_INTERLACED, 601 }, { "QVGA 29.97 fps", 320, 240, 30000, 1001, 1, 1, 4, 3, VIFO_INTERLACED, 601 }, { "1024x576 16:9 PAL", 1024, 576, 25, 1, 1, 1, 16, 9, VIFO_INTERLACED, 601 }, { "HD 1080p 23.98 fps", 1920, 1080, 24000, 1001, 1, 1, 16, 9, VIFO_INTERLACED, 709 }, { "HD 720p 24 fps", 1280, 720, 24, 1, 1, 1, 16, 9, VIFO_INTERLACED, 709 }, { "HD 1080p 29.97 fps", 1920, 1080, 30000, 1001, 1, 1, 16, 9, VIFO_INTERLACED, 709 }, { "HDV 1440x1080p 29.97 fps", 1440, 1080, 30000, 1001, 4, 3, 16, 9, VIFO_INTERLACED, 709 }, { "VCD PAL", 352, 288, 25, 1, 59, 54, 4, 3, VIFO_INTERLACED, 601 }, { "HD 720p 50 fps", 1280, 720, 50, 1, 1, 1, 16, 9, VIFO_INTERLACED, 709 }, { "CIF 15 fps", 352, 288, 15, 1, 59, 54, 4, 3, VIFO_INTERLACED, 601 }, { "384x288 4:3 PAL", 384, 288, 25, 1, 1, 1, 4, 3, VIFO_INTERLACED, 601 }, { "512x288 16:9 PAL", 512, 288, 25, 1, 1, 1, 16, 9, VIFO_INTERLACED, 601 }, { "HD 720p 29.97 fps", 1280, 720, 30000, 1001, 1, 1, 16, 9, VIFO_INTERLACED, 709 }, { "HDV 1440x1080i 25 fps", 1440, 1080, 25, 1, 4, 3, 16, 9, 0, 709 }, { "HD 1080i 25 fps", 1920, 1080, 25, 1, 1, 1, 16, 9, 0, 709 }, { "VCD NTSC", 352, 240, 30000, 1001, 10, 11, 4, 3, VIFO_INTERLACED, 601 }, { "HDV 720 24p", 1280, 720, 24, 1, 1, 1, 16, 9, VIFO_INTERLACED, 0 }, { "HD 1080p 25 fps", 1920, 1080, 25, 1, 1, 1, 16, 9, VIFO_INTERLACED, 709 }, { "NTSC 29.97 fps", 720, 480, 30000, 1001, 8, 9, 4, 3, 0, 601 }, { "HD 1080i 30 fps", 1920, 1080, 30, 1, 1, 1, 16, 9, 0, 709 }, { "HDV 1440x1080i 29.97 fps", 1440, 1080, 30000, 1001, 4, 3, 16, 9, 0, 709 }, { "SVCD PAL", 480, 576, 25, 1, 59, 36, 4, 3, 0, 601 }, { "HDV 1080 25i 1920x1080", 1920, 1080, 25, 1, 1, 1, 16, 9, 0, 0 }, { "DV/DVD NTSC", 720, 480, 30000, 1001, 8, 9, 4, 3, 0, 601 }, { "HD 720p 60 fps", 1280, 720, 60, 1, 1, 1, 16, 9, VIFO_INTERLACED, 709 }, { "HD 720p 30 fps", 1280, 720, 30, 1, 1, 1, 16, 9, VIFO_INTERLACED, 709 }, { "HD 720p 25 fps", 1280, 720, 25, 1, 1, 1, 16, 9, VIFO_INTERLACED, 709 }, { "CVD NTSC", 352, 480, 30000, 1001, 20, 11, 4, 3, 0, 601 }, { "HD 1080p 24 fps", 1920, 1080, 24, 1, 1, 1, 16, 9, VIFO_INTERLACED, 709 }, { "Mobile 360p", 320, 240, 30000, 1001, 1, 1, 4, 3, VIFO_INTERLACED, 0 }, { "QVGA 15 fps", 320, 240, 15, 1, 1, 1, 4, 3, VIFO_INTERLACED, 601 }, { "CIF NTSC", 352, 288, 30000, 1001, 10, 11, 4, 3, VIFO_INTERLACED, 601 }, { "HD 1080p 30 fps", 1920, 1080, 30, 1, 1, 1, 16, 9, VIFO_INTERLACED, 709 }, { "DV/DVD PAL", 720, 576, 25, 1, 16, 15, 4, 3, 0, 601 }, { "VGA Widescreen NTSC", 854, 480, 30000, 1001, 1, 1, 16, 9, VIFO_INTERLACED, 601 }, { "HD 1080i 29.97 fps", 1920, 1080, 30000, 1001, 1, 1, 16, 9, 0, 709 }, { "HDV 1440x1080p 25 fps", 1440, 1080, 25, 1, 4, 3, 16, 9, VIFO_INTERLACED, 709 }, { "HD 1080p 50 fps", 1920, 1080, 50, 1, 1, 1, 16, 9, VIFO_INTERLACED, 709 }, { "CVD PAL", 352, 576, 25, 1, 59, 27, 4, 3, 0, 601 }, { "DV/DVD Widescreen NTSC", 720, 480, 30000, 1001, 32, 27, 16, 9, 0, 601 }, { "768x576 4:3 PAL", 768, 576, 25, 1, 1, 1, 4, 3, VIFO_INTERLACED, 601 }, { "VGA NTSC", 640, 480, 30000, 1001, 1, 1, 4, 3, VIFO_INTERLACED, 601 }, { "HD 720p 59.94 fps", 1280, 720, 60000, 1001, 1, 1, 16, 9, VIFO_INTERLACED, 709 }, { "HD 1080p 60 fps", 1920, 1080, 60, 1, 1, 1, 16, 9, VIFO_INTERLACED, 709 }, { "SVCD NTSC", 480, 480, 30000, 1001, 15, 11, 4, 3, 0, 601 }, { "SVCD Widescreen PAL", 480, 576, 25, 1, 59, 27, 16, 9, 0, 601 }, { "QCIF NTSC", 176, 144, 30000, 1001, 10, 11, 4, 3, VIFO_INTERLACED, 601 }, { "DV/DVD Widescreen PAL", 720, 576, 25, 1, 64, 45, 16, 9, 0, 601 }, { "QCIF 15 fps", 176, 144, 15, 1, 59, 54, 4, 3, VIFO_INTERLACED, 601 }, { "QCIF PAL", 176, 144, 25, 1, 59, 54, 4, 3, VIFO_INTERLACED, 601 }, { "HDV 1080 25p 1920x1080", 1920, 1080, 25, 1, 1, 1, 16, 9, VIFO_INTERLACED, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; VideoEncodingProfiles::VideoEncodingProfiles() { initVideoFormats(); initInternalProfiles(); } QStringList VideoEncodingProfiles::videoMediumTypes() const { // Should match enum Type order QStringList list; list << QObject::tr("Video file") << QObject::tr("Device") << QObject::tr("Blu Ray/AVCHD") << QObject::tr("DVD") << QObject::tr("Web"); return list; } QStringList VideoEncodingProfiles::videoProfiles() const { return m_videoProfiles.keys(); } const VideoEncodingProfile *VideoEncodingProfiles::videoProfile(const QString &name) { QMap< QString, VideoEncodingProfile >::iterator it = m_videoProfiles.find( name ); if ( it == m_videoProfiles.end() ) return 0; return &(it.value()); } QStringList VideoEncodingProfiles::videoFormats() const { return m_videoFormats.keys(); } const VideoFormat *VideoEncodingProfiles::videoFormat(const QString &name) { QMap< QString, VideoFormat * >::iterator it = m_videoFormats.find( name ); if ( it == m_videoFormats.end() ) return 0; return it.value(); } void VideoEncodingProfiles::initInternalProfiles() { // This is auto-generated code loading openshot profile info VideoEncodingProfile p; // Metacafe p.type = VideoEncodingProfile::TYPE_WEB; p.name = "Metacafe"; p.videoContainer = "mp4"; p.videoCodec = "mpeg4"; p.audioCodec = "libmp3lame"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.limitFormats << "VGA NTSC"; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 2048; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 8192; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 256; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 320; m_videoProfiles[ "Metacafe" ] = p; // Vimeo-SD Widescreen p.type = VideoEncodingProfile::TYPE_WEB; p.name = "Vimeo-SD Widescreen"; p.videoContainer = "mp4"; p.videoCodec = "libx264"; p.audioCodec = "libmp3lame"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.limitFormats << "VGA Widescreen NTSC"; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 2048; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 8192; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 256; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 320; m_videoProfiles[ "Vimeo-SD Widescreen" ] = p; // OGG (theora/flac) p.type = VideoEncodingProfile::TYPE_FILE; p.name = "OGG (theora/flac)"; p.videoContainer = "ogg"; p.videoCodec = "libtheora"; p.audioCodec = "flac"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 384; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 15360; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 96; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 192; m_videoProfiles[ "OGG (theora/flac)" ] = p; // MPEG (mpeg2) p.type = VideoEncodingProfile::TYPE_FILE; p.name = "MPEG (mpeg2)"; p.videoContainer = "mpeg"; p.videoCodec = "mpeg2video"; p.audioCodec = "mp2"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 384; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 15360; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 96; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 192; m_videoProfiles[ "MPEG (mpeg2)" ] = p; // MP4 (Xvid) p.type = VideoEncodingProfile::TYPE_FILE; p.name = "MP4 (Xvid)"; p.videoContainer = "mp4"; p.videoCodec = "libxvid"; p.audioCodec = "ac3"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 384; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 15360; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 96; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 192; m_videoProfiles[ "MP4 (Xvid)" ] = p; // Vimeo-SD p.type = VideoEncodingProfile::TYPE_WEB; p.name = "Vimeo-SD"; p.videoContainer = "mp4"; p.videoCodec = "libx264"; p.audioCodec = "libmp3lame"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.limitFormats << "VGA NTSC"; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 2048; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 8192; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 256; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 320; m_videoProfiles[ "Vimeo-SD" ] = p; // Flickr-HD p.type = VideoEncodingProfile::TYPE_WEB; p.name = "Flickr-HD"; p.videoContainer = "mov"; p.videoCodec = "libx264"; p.audioCodec = "ac3"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.limitFormats << "HD 720p 25 fps"<< "HD 720p 29.97 fps"; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 384; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 15360; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 96; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 192; m_videoProfiles[ "Flickr-HD" ] = p; // MP4 (h.264) p.type = VideoEncodingProfile::TYPE_FILE; p.name = "MP4 (h.264)"; p.videoContainer = "mp4"; p.videoCodec = "libx264"; p.audioCodec = "ac3"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 384; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 15360; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 96; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 192; m_videoProfiles[ "MP4 (h.264)" ] = p; // Vimeo-HD p.type = VideoEncodingProfile::TYPE_WEB; p.name = "Vimeo-HD"; p.videoContainer = "mp4"; p.videoCodec = "libx264"; p.audioCodec = "libmp3lame"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.limitFormats << "HD 720p 25 fps"<< "HD 720p 29.97 fps"; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 2048; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 8192; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 256; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 320; m_videoProfiles[ "Vimeo-HD" ] = p; // AVI (h.264) p.type = VideoEncodingProfile::TYPE_FILE; p.name = "AVI (h.264)"; p.videoContainer = "avi"; p.videoCodec = "libx264"; p.audioCodec = "mp2"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 384; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 15360; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 96; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 192; m_videoProfiles[ "AVI (h.264)" ] = p; // AVCHD Disks p.type = VideoEncodingProfile::TYPE_BLURAY; p.name = "AVCHD Disks"; p.videoContainer = "mp4"; p.videoCodec = "libx264"; p.audioCodec = "ac3"; p.sampleRate = 48000; p.channels = 2; p.limitFormats.clear(); p.limitFormats << "HD 1080i 25 fps"<< "HD 1080i 30 fps"<< "HD 1080p 25 fps"; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = false; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 15360; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 0; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 40960; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 256; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 0; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 256; m_videoProfiles[ "AVCHD Disks" ] = p; // OGG (theora/vorbis) p.type = VideoEncodingProfile::TYPE_FILE; p.name = "OGG (theora/vorbis)"; p.videoContainer = "ogg"; p.videoCodec = "libtheora"; p.audioCodec = "libvorbis"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 384; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 15360; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 96; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 192; m_videoProfiles[ "OGG (theora/vorbis)" ] = p; // Picasa p.type = VideoEncodingProfile::TYPE_WEB; p.name = "Picasa"; p.videoContainer = "mp4"; p.videoCodec = "libx264"; p.audioCodec = "libmp3lame"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.limitFormats << "VGA NTSC"; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 2048; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 8192; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 256; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 320; m_videoProfiles[ "Picasa" ] = p; // Wikipedia p.type = VideoEncodingProfile::TYPE_WEB; p.name = "Wikipedia"; p.videoContainer = "ogg"; p.videoCodec = "libtheora"; p.audioCodec = "libvorbis"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.limitFormats << "QVGA 29.97 fps"; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 384; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 15360; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 96; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 192; m_videoProfiles[ "Wikipedia" ] = p; // MOV (h.264) p.type = VideoEncodingProfile::TYPE_FILE; p.name = "MOV (h.264)"; p.videoContainer = "mov"; p.videoCodec = "libx264"; p.audioCodec = "ac3"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 384; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 15360; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 96; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 192; m_videoProfiles[ "MOV (h.264)" ] = p; // DVD-NTSC p.type = VideoEncodingProfile::TYPE_DVD; p.name = "DVD-NTSC"; p.videoContainer = "dvd"; p.videoCodec = "mpeg2video"; p.audioCodec = "ac3"; p.sampleRate = 48000; p.channels = 2; p.limitFormats.clear(); p.limitFormats << "DV/DVD NTSC"<< "DV/DVD Widescreen NTSC"; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 1024; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 3072; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 5120; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 192; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 256; m_videoProfiles[ "DVD-NTSC" ] = p; // AVI (mpeg2) p.type = VideoEncodingProfile::TYPE_FILE; p.name = "AVI (mpeg2)"; p.videoContainer = "avi"; p.videoCodec = "mpeg2video"; p.audioCodec = "mp2"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 384; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 15360; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 96; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 192; m_videoProfiles[ "AVI (mpeg2)" ] = p; // WEBM (vpx) p.type = VideoEncodingProfile::TYPE_FILE; p.name = "WEBM (vpx)"; p.videoContainer = "webm"; p.videoCodec = "libvpx"; p.audioCodec = "libvorbis"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 384; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 15360; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 96; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 192; m_videoProfiles[ "WEBM (vpx)" ] = p; // FLV (h.264) p.type = VideoEncodingProfile::TYPE_FILE; p.name = "FLV (h.264)"; p.videoContainer = "flv"; p.videoCodec = "libx264"; p.audioCodec = "libmp3lame"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 384; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 15360; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 96; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 192; m_videoProfiles[ "FLV (h.264)" ] = p; // MP4 (mpeg4) p.type = VideoEncodingProfile::TYPE_FILE; p.name = "MP4 (mpeg4)"; p.videoContainer = "mp4"; p.videoCodec = "mpeg4"; p.audioCodec = "libmp3lame"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 384; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 15360; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 96; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 192; m_videoProfiles[ "MP4 (mpeg4)" ] = p; // Nokia nHD p.type = VideoEncodingProfile::TYPE_DEVICE; p.name = "Nokia nHD"; p.videoContainer = "avi"; p.videoCodec = "libxvid"; p.audioCodec = "ac3"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.limitFormats << "Mobile 360p"; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 1024; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 3072; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 5120; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 256; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 320; m_videoProfiles[ "Nokia nHD" ] = p; // Xbox 360 p.type = VideoEncodingProfile::TYPE_DEVICE; p.name = "Xbox 360"; p.videoContainer = "avi"; p.videoCodec = "libxvid"; p.audioCodec = "ac3"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.limitFormats << "DV/DVD Widescreen NTSC"<< "HD 720p 29.97 fps"; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 2048; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 8192; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 256; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 320; m_videoProfiles[ "Xbox 360" ] = p; // AVI (mpeg4) p.type = VideoEncodingProfile::TYPE_FILE; p.name = "AVI (mpeg4)"; p.videoContainer = "avi"; p.videoCodec = "mpeg4"; p.audioCodec = "libmp3lame"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 384; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 15360; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 96; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 192; m_videoProfiles[ "AVI (mpeg4)" ] = p; // YouTube p.type = VideoEncodingProfile::TYPE_WEB; p.name = "YouTube"; p.videoContainer = "mpeg"; p.videoCodec = "mpeg2video"; p.audioCodec = "mp2"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.limitFormats << "VGA NTSC"; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 2048; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 8192; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 256; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 320; m_videoProfiles[ "YouTube" ] = p; // Apple TV p.type = VideoEncodingProfile::TYPE_DEVICE; p.name = "Apple TV"; p.videoContainer = "mp4"; p.videoCodec = "libx264"; p.audioCodec = "ac3"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.limitFormats << "HD 720p 30 fps"; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = false; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = false; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 0; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 0; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 5120; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 0; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 0; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 256; m_videoProfiles[ "Apple TV" ] = p; // MOV (mpeg4) p.type = VideoEncodingProfile::TYPE_FILE; p.name = "MOV (mpeg4)"; p.videoContainer = "mov"; p.videoCodec = "mpeg4"; p.audioCodec = "libmp3lame"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 384; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 15360; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 96; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 192; m_videoProfiles[ "MOV (mpeg4)" ] = p; // DVD-PAL p.type = VideoEncodingProfile::TYPE_DVD; p.name = "DVD-PAL"; p.videoContainer = "dvd"; p.videoCodec = "mpeg2video"; p.audioCodec = "ac3"; p.sampleRate = 48000; p.channels = 2; p.limitFormats.clear(); p.limitFormats << "DV/DVD PAL"<< "DV/DVD Widescreen PAL"; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 1024; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 3072; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 5120; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 192; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 256; m_videoProfiles[ "DVD-PAL" ] = p; // YouTube-HD p.type = VideoEncodingProfile::TYPE_WEB; p.name = "YouTube-HD"; p.videoContainer = "mp4"; p.videoCodec = "libx264"; p.audioCodec = "libmp3lame"; p.sampleRate = 44100; p.channels = 2; p.limitFormats.clear(); p.limitFormats << "HD 720p 25 fps"<< "HD 720p 29.97 fps"; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_LOW ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_MEDIUM ] = true; p.bitratesEnabled[ VideoEncodingProfile::BITRATE_HIGH ] = true; p.bitratesVideo[ VideoEncodingProfile::BITRATE_LOW ] = 2048; p.bitratesVideo[ VideoEncodingProfile::BITRATE_MEDIUM ] = 5120; p.bitratesVideo[ VideoEncodingProfile::BITRATE_HIGH ] = 8192; p.bitratesAudio[ VideoEncodingProfile::BITRATE_LOW ] = 128; p.bitratesAudio[ VideoEncodingProfile::BITRATE_MEDIUM ] = 256; p.bitratesAudio[ VideoEncodingProfile::BITRATE_HIGH ] = 320; m_videoProfiles[ "YouTube-HD" ] = p; } void VideoEncodingProfiles::initVideoFormats() { for ( VideoFormat * f = video_formats; f->name; f++ ) { m_videoFormats[ f->name ] = f; } } karlyriceditor-1.11/src/videoencodingprofiles.h000066400000000000000000000064121233766701300220170ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef VIDEOENCODINGPROFILES_H #define VIDEOENCODINGPROFILES_H #include #include #include const static unsigned int VIFO_INTERLACED = (1<<0); typedef struct { const char * name; unsigned int width; unsigned int height; unsigned int frame_rate_den; unsigned int frame_rate_num; unsigned int sample_aspect_num; unsigned int sample_aspect_den; unsigned int display_aspect_num; unsigned int display_aspect_den; unsigned int flags; unsigned int colorspace; } VideoFormat; class VideoEncodingProfile { public: enum { BITRATE_LOW, BITRATE_MEDIUM, BITRATE_HIGH }; enum Type { TYPE_FILE, TYPE_DEVICE, TYPE_BLURAY, TYPE_DVD, TYPE_WEB }; Type type; QString name; // Video params QString videoContainer; // i.e. avi, mp4, flv QString videoCodec; // i.e. h264, libtheora QStringList limitFormats; // limited to specific video encoding params // Audio params QString audioCodec; // i.e. ac3, mp3 unsigned int sampleRate; // 44100 unsigned int channels; // 1 or 2 // Bitrates bool bitratesEnabled[3]; unsigned int bitratesVideo[3]; // in KILOBYTES unsigned int bitratesAudio[3]; // in KILOBYTES }; class VideoEncodingProfiles { public: VideoEncodingProfiles(); // Supported video encoding targets QStringList videoMediumTypes() const; // List of supported video encoding profiles (both internal and external) QStringList videoProfiles() const; // Retrieve the profile const VideoEncodingProfile * videoProfile( const QString& name ); // List of supported video formats QStringList videoFormats() const; // List of supported video formats const VideoFormat * videoFormat( const QString& name ); private: void initInternalProfiles(); void initVideoFormats(); QMap< QString, VideoFormat * > m_videoFormats; QMap< QString, VideoEncodingProfile > m_videoProfiles; }; extern VideoEncodingProfiles * pVideoEncodingProfiles; #endif // VIDEOENCODINGPROFILES_H karlyriceditor-1.11/src/videogenerator.cpp000066400000000000000000000120211233766701300207770ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include #include #include #include "audioplayer.h" #include "videogenerator.h" #include "textrenderer.h" #include "export_params.h" #include "ffmpegvideoencoder.h" #include "editor.h" #include "ui_dialog_encodingprogress.h" VideoGenerator::VideoGenerator( Project * prj ) { m_project = prj; } void VideoGenerator::generate( const Lyrics& lyrics, qint64 total_length ) { // Show the dialog with video options DialogExportOptions dlg( m_project, lyrics, true ); if ( dlg.exec() != QDialog::Accepted ) return; // Get the video info const VideoEncodingProfile * profile; const VideoFormat * format; unsigned int audioEncodingMode; unsigned int quality; if ( !dlg.videoParams( &profile, &format, &audioEncodingMode, &quality ) ) return; // Prepare the renderer TextRenderer lyricrenderer( format->width, format->height ); lyricrenderer.setLyrics( lyrics ); // Initialize colors from m_project lyricrenderer.setRenderFont( QFont( m_project->tag(Project::Tag_Video_font, "arial"), m_project->tag(Project::Tag_Video_fontsize, "8").toInt()) ); lyricrenderer.setColorBackground( m_project->tag( Project::Tag_Video_bgcolor, "black" ) ); lyricrenderer.setColorTitle( m_project->tag( Project::Tag_Video_infocolor, "white" ) ); lyricrenderer.setColorSang( m_project->tag( Project::Tag_Video_inactivecolor, "blue" ) ); lyricrenderer.setColorToSing( m_project->tag( Project::Tag_Video_activecolor, "green" ) ); // Title lyricrenderer.setTitlePageData( dlg.m_artist, dlg.m_title, dlg.m_createdBy, m_project->tag( Project::Tag_Video_titletime, "5" ).toInt() * 1000 ); // Preamble if ( m_project->tag( Project::Tag_Video_preamble).toInt() != 0 ) lyricrenderer.setPreambleData( 4, 5000, 8 ); // Video encoder FFMpegVideoEncoder encoder; // audioEncodingMode: 0 - encode, 1 - copy, 2 - no audio QString errmsg = encoder.createFile( dlg.m_outputVideo, profile, format, quality, audioEncodingMode == 0 ? true : false, audioEncodingMode == 2 ? 0 : pAudioPlayer ); if ( !errmsg.isEmpty() ) { QMessageBox::critical( 0, "Cannot write video", QString("Cannot create video file: %1") .arg(errmsg) ); return; } // Pop up progress dialog QDialog progressdlg; Ui::DialogEncodingProgress ui; ui.setupUi( &progressdlg ); ui.progressBar->setMaximum( 99 ); ui.progressBar->setMinimum( -1 ); ui.progressBar->setValue( -1 ); ui.lblFrames->setText( "0" ); ui.lblOutput->setText( "0 Mb" ); ui.lblTime->setText( "0:00.00" ); progressdlg.show(); qint64 dialog_step = total_length / 100; qint64 time_step = (1000 * format->frame_rate_num) / format->frame_rate_den; int frames = 0, totalframes = total_length / time_step; QTime timing; timing.start(); // Rendering for ( qint64 time = 0; time < total_length; time += time_step ) { frames++; lyricrenderer.update( time ); QImage image = lyricrenderer.image(); int ret = encoder.encodeImage( image, time ); if ( ret < 0 ) { QMessageBox::critical( 0, "Cannot write video", QString("Encoding error while creating the video file: %1") .arg(errmsg) ); encoder.close(); return; } // Should we update the progress dialog? if ( time / dialog_step > ui.progressBar->value() ) { ui.progressBar->setValue( time / dialog_step ); ui.lblFrames->setText( QString("%1 of %2") .arg( frames ) .arg( totalframes ) ); ui.lblOutput->setText( QString( "%1 Mb" ) .arg( ret / (1024*1024) ) ); ui.lblTime->setText( markToTime( timing.elapsed() ) ); ui.image->setPixmap( QPixmap::fromImage( image ).scaled( ui.image->size() ) ); qApp->processEvents( QEventLoop::ExcludeUserInputEvents ); } } encoder.close(); } karlyriceditor-1.11/src/videogenerator.h000066400000000000000000000031321233766701300204470ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef VIDEOGENERATOR_H #define VIDEOGENERATOR_H #include #include "lyrics.h" #include "project.h" class VideoGenerator { public: VideoGenerator( Project * prj ); void generate( const Lyrics& lyrics, qint64 total_length ); private: Project * m_project; }; #endif // VIDEOGENERATOR_H karlyriceditor-1.11/src/videoprofileencodingdialog.cpp000066400000000000000000000030301233766701300233400ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include "videoprofileencodingdialog.h" VideoProfileEncodingDialog::VideoProfileEncodingDialog( QWidget *parent ) : QDialog( parent ), Ui::video_encoding_dialog() { setupUi( this ); } void VideoProfileEncodingDialog::accept() { } karlyriceditor-1.11/src/videoprofileencodingdialog.h000066400000000000000000000032371233766701300230160ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef VIDEOPROFILEENCODINGDIALOG_H #define VIDEOPROFILEENCODINGDIALOG_H #include #include "ui_video_profile_dialog.h" class VideoProfileEncodingDialog : public QDialog, public Ui::video_encoding_dialog { Q_OBJECT public: explicit VideoProfileEncodingDialog(QWidget *parent = 0); public slots: void accept(); }; #endif // VIDEOPROFILEENCODINGDIALOG_H karlyriceditor-1.11/src/viewwidget.cpp000066400000000000000000000030231233766701300201420ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include "viewwidget.h" ViewWidget::ViewWidget( QWidget *parent ) : QDialog( parent ), Ui::ViewWidget() { setupUi( this ); } void ViewWidget::showText( const QString& lyrictext ) { textEdit->setPlainText( lyrictext ); show(); } karlyriceditor-1.11/src/viewwidget.h000066400000000000000000000030751233766701300176160ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef VIEWWIDGET_H #define VIEWWIDGET_H #include #include "ui_viewwidget.h" class ViewWidget : public QDialog, Ui::ViewWidget { Q_OBJECT public: ViewWidget( QWidget *parent = 0 ); void showText( const QString& lyrictext ); }; #endif // VIEWWIDGET_H karlyriceditor-1.11/src/viewwidget.ui000066400000000000000000000021761233766701300200050ustar00rootroot00000000000000 ViewWidget 0 0 560 450 Viewing lyrics file true QDialogButtonBox::Close buttonBox rejected() ViewWidget reject() 279 431 279 224 karlyriceditor-1.11/src/wizard_newproject.cpp000066400000000000000000000205201233766701300215250ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #include #include #include #include #include "audioplayer.h" #include "project.h" #include "version.h" #include "wizard_newproject.h" #include "kfn_file_parser.h" #include "util.h" namespace WizardNewProject { // // "Select lyrics format" page // PageLyricType::PageLyricType( Project * project, QWidget *parent ) : QWizardPage( parent ), Ui::WizNewProject_LyricType() { setupUi( this ); m_project = project; connect( lblHelp, SIGNAL( linkActivated ( const QString &) ), this, SLOT( showhelp() ) ); } bool PageLyricType::validatePage() { // At least one should be selected if ( rbLRC1->isChecked() ) m_project->setType( Project::LyricType_LRC1 ); else if ( rbLRC2->isChecked() ) m_project->setType( Project::LyricType_LRC2 ); else if ( rbLRC3->isChecked() ) m_project->setType( Project::LyricType_UStar ); else return false; return true; } void PageLyricType::showhelp() { QString help = tr( "LRC1 is the first version of LRC format, containing a single " "line with timing mark at the beginning. This format is supported by most players\n\n" "LRC2 is the second version, which can contain timing marks inside " "the line. Supported by less players.\n\n" "UStar/UltraStar format is lyrics format used in SingStar, Sinatra, Performous and " "similar games.\n\nXBMC supports all those formats" ); QWhatsThis::showText ( mapToGlobal( lblHelp->pos() ), help ); } // // "Choose music file" page // PageMusicFile::PageMusicFile( Project * project, QWidget *parent ) : QWizardPage( parent ), Ui::WizNewProject_MusicFile() { setupUi( this ); m_project = project; connect( btnBrowse, SIGNAL( clicked() ), this, SLOT( browse() ) ); } PageMusicFile::~PageMusicFile() { } void PageMusicFile::browse() { QString filename = QFileDialog::getOpenFileName( 0, tr("Choose a music file to load"), "." ); if ( filename.isEmpty() ) return; // We cannot handle MIDI/KAR files if ( filename.endsWith( ".mid", Qt::CaseInsensitive ) || filename.endsWith( ".kar", Qt::CaseInsensitive ) ) { if ( QMessageBox::question( 0, tr("Trying to open a MIDI file?"), tr("It looks like you are trying to open the MIDI file.\n" "MIDI file contains the lyrics embedded into the file in a special format. " "%1 cannot edit regular MIDI files, it is recommended to use RoseGarden instead.\n\n" "Are you sure you still want to try to open this file?") .arg( APP_NAME ), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No ) { return; } } // Handle the KFN file if ( filename.endsWith( ".kfn",Qt::CaseInsensitive ) ) { if ( QMessageBox::question( 0, tr("Trying to open a KaraFun file?"), tr("It looks like you are trying to open the KaraFun file.\n" "Karaoke Lyrics Editor cannot edit those files directly. However it can import music and lyrics from this file, and export them as any supported format " "such as CDG, LRC or UltraStar (but not KFN)\n\n" "Do you want to import the music and lyrics from this file?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No ) { return; } KFNFileParser parser; if ( !parser.open( filename ) ) { QMessageBox::information( 0, tr("Invalid KFN file"), tr("The file %1 cannot be opened: %2") .arg( filename ) .arg( parser.errorMsg() ) ); return; } // Music file without .KFN extention QString musicFileName = filename.left( filename.size() - 3 ) + parser.musicFileExtention(); QFile musicFile( musicFileName ); if ( !musicFile.open( QIODevice::WriteOnly ) ) { QMessageBox::information( 0, tr("Cannot import KFN file"), tr("The file %1 cannot be imported: the music file %2 cannot be created") .arg( filename ) .arg( musicFileName ) ); return; } if ( !parser.writeMusicFile(musicFile) ) { QMessageBox::information( 0, tr("Cannot import KFN file"), tr("The file %1 cannot be imported: the music file %2 cannot be stored: %s") .arg( filename ) .arg( musicFileName ) .arg( parser.errorMsg() ) ); return; } musicFile.close(); filename = musicFileName; // Lyrics m_hasLrcLyrics = parser.lyricsAsLRC(); // Still fall through if ( m_hasLrcLyrics.isEmpty() ) QMessageBox::information( 0, tr("Cannot import lyrics"), tr("The file %1 cannot be imported: the lyrics cannot be parsed: %s") .arg( filename ) .arg( parser.errorMsg() ) ); } // Try to open it if ( !pAudioPlayer->open( filename ) ) { QMessageBox::critical( 0, tr("Cannot open the music file"), tr("Cannot open the music file.\n\n%1") .arg(pAudioPlayer->errorMsg()) ); return; } // Store the music file leSongFile->setText( filename ); leArtist->setText( pAudioPlayer->metaArtist() ); leTitle->setText( pAudioPlayer->metaTitle() ); pAudioPlayer->close(); // If there's an LRC file nearby, ask whether the user wants to load it (and get the values from it) if ( !m_hasLrcLyrics.isEmpty() ) return; QString lrcfile = Util::removeFileExtention( filename ) + "lrc"; QFile file( lrcfile ); if ( file.open( QIODevice::ReadOnly ) ) { if ( QMessageBox::question( 0, tr("Lyrics file found"), tr("It looks like there is a lyrics file %1 matching this music file.\n\n" "Do you want to import it as well?") .arg(lrcfile), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::Yes ) { m_hasLrcLyrics = Util::convertWithUserEncoding( file.readAll() ); } } } bool PageMusicFile::validatePage() { if ( leSongFile->text().isEmpty() ) { QMessageBox::critical( 0, tr("Music file not selected"), tr("You must select a music file to continue.") ); return false; } if ( !pAudioPlayer->open( leSongFile->text() ) ) return false; if ( leTitle->text().isEmpty() ) { QMessageBox::critical( 0, tr("Title field is empty"), tr("You must type song title to continue.") ); return false; } if ( leArtist->text().isEmpty() ) { QMessageBox::critical( 0, tr("Artist field is empty"), tr("You must type song artist to continue.") ); return false; } m_project->setMusicFile( leSongFile->text() ); m_project->setTag( Project::Tag_Title, leTitle->text() ); m_project->setTag( Project::Tag_Artist, leArtist->text() ); if ( !leAlbum->text().isEmpty() ) m_project->setTag( Project::Tag_Album, leAlbum->text() ); if ( !m_hasLrcLyrics.isEmpty() ) m_project->convertLyrics( m_hasLrcLyrics ); return true; } Wizard::Wizard( Project * project, QWidget *parent ) : QWizard( parent ) { addPage( new PageIntro( project, this ) ); addPage( new PageLyricType( project, this ) ); addPage( new PageMusicFile( project, this ) ); addPage( new PageFinish( project, this ) ); #ifndef Q_WS_MAC setWizardStyle(ModernStyle); #endif setOption( HaveHelpButton, false ); setWindowTitle( tr("New karaoke lyrics project") ); setPixmap( QWizard::WatermarkPixmap, QPixmap(":/images/casio.jpg") ); } } // namespace karlyriceditor-1.11/src/wizard_newproject.h000066400000000000000000000056071233766701300212030ustar00rootroot00000000000000/************************************************************************** * Karlyriceditor - a lyrics editor and CD+G / video export for Karaoke * * songs. * * Copyright (C) 2009-2013 George Yunaev, support@ulduzsoft.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 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 . * **************************************************************************/ #ifndef WIZARD_NEWPROJECT_H #define WIZARD_NEWPROJECT_H #include #include "ui_wiznewproject_musicfile.h" #include "ui_wiznewproject_intro.h" #include "ui_wiznewproject_finish.h" #include "ui_wiznewproject_lyrictype.h" class Project; namespace WizardNewProject { // Intro page class PageIntro : public QWizardPage, public Ui::WizNewProject_Intro { Q_OBJECT public: PageIntro( Project *, QWidget *parent = 0 ) : QWizardPage( parent ), Ui::WizNewProject_Intro() { setupUi( this ); } }; // "Select lyrics type" page class PageLyricType : public QWizardPage, public Ui::WizNewProject_LyricType { Q_OBJECT public: PageLyricType( Project * project, QWidget *parent = 0 ); // overriden bool validatePage(); public slots: void showhelp(); private: Project * m_project; }; // "Choose music file" page class PageMusicFile : public QWizardPage, public Ui::WizNewProject_MusicFile { Q_OBJECT public: PageMusicFile( Project * project, QWidget *parent = 0 ); ~PageMusicFile(); // overriden bool validatePage(); public slots: void browse(); private: Project * m_project; QString m_lastMusicFile; QString m_hasLrcLyrics; }; // "Finish" page class PageFinish : public QWizardPage, public Ui::WizNewProject_Finish { Q_OBJECT public: PageFinish( Project *, QWidget *parent = 0 ) : QWizardPage( parent ), Ui::WizNewProject_Finish() { setupUi( this ); } }; // New project wizard class Wizard : public QWizard { Q_OBJECT public: Wizard( Project * project, QWidget *parent = 0 ); }; } // namespace WizardNewProject #endif // WIZARD_NEWPROJECT_H karlyriceditor-1.11/src/wiznewproject_finish.ui000066400000000000000000000056311233766701300221000ustar00rootroot00000000000000 WizNewProject_Finish 0 0 446 313 WizardPage <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The project has been created.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Once you press Finish button, the project will be saved on disk, and you can continue working on it.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">You start with an empty lyrics editor window. If you have lyrics in LRC or Ultrastar format, you can import them via <span style=" font-style:italic;">Project -&gt; Import lyrics</span> menu item. If you have them in a text file or in a Web browser, just copy-paste them.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">See the <a href="http://www.karlyriceditor.com/documentation.html"><span style=" text-decoration: underline; color:#0057ae;">online documentation</span></a> for more details.</p></body></html> true true karlyriceditor-1.11/src/wiznewproject_intro.ui000066400000000000000000000054701233766701300217540ustar00rootroot00000000000000 WizNewProject_Intro 0 0 417 172 WizardPage Introduction <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The karaoke lyrics editor project contains music and lyrics </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">as well as project-specific information. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">During project creation you will be asked the following:</p> <ul style="-qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Main lyrics format you're going to use;</li> <li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A music file, containing the song you want to make </li> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:1; text-indent:0px;">karaoke lyrics for;</p> <li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A lyrics file containing the text (optional);</li></ul></body></html> Qt::RichText false karlyriceditor-1.11/src/wiznewproject_lyrictype.ui000066400000000000000000000065711233766701300226500ustar00rootroot00000000000000 WizNewProject_LyricType 0 0 516 271 WizardPage Select the lyrics type <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Please select the type of lyrics type you want to create for this project. Different types have different input requirements, and while the editor supports export in any format, a full conversion between formats is typically burdensome or impossible. <a href="1"><span style=" text-decoration: underline; color:#0000ff;">More information</span></a> about formats.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you are creating lyrics for XBMC Media Center, or to export them in CD+G or video formats, choose LRCv2.</p></body></html> Qt::RichText true LRC version 1 LRC version 2 (recommended) true UltraStar lyrics Qt::Vertical 20 40 karlyriceditor-1.11/src/wiznewproject_musicfile.ui000066400000000000000000000070241233766701300225760ustar00rootroot00000000000000 WizNewProject_MusicFile 0 0 449 322 WizardPage Select the song to add lyrics to: Music file: true Browse Music file information If available, the editor obtained the information from the music file. Please check the information is correct, and if not, correct or fill it. Title and artist is required, but all other fields can be empty. false Title Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Artist Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Album Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Qt::Vertical 20 40 karlyriceditor-1.11/ts/000077500000000000000000000000001233766701300151215ustar00rootroot00000000000000karlyriceditor-1.11/ts/karlyriceditor_cs.ts000066400000000000000000005131611233766701300212140ustar00rootroot00000000000000 DialogAbout About Karaoke Lyric Editor O Karaoke Lyric Editor About application O programu TextLabel Textový štítek Third-party modules used Použité moduly od třetích stran <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Qt toolkit library</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This application is based on a portable GUI/core library available from <a href="http://qt.nokia.com"><span style=" text-decoration: underline; color:#0057ae;">http://qt.nokia.com</span></a>. This toolkit library is available and used under LGPL license.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Icons</span><br />This application uses icons developed by DryIcons, <a href="http://www.dryicons.com"><span style=" text-decoration: underline; color:#0057ae;">http://www.dryicons.com</span></a></p> <p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">FFMpeg</span><br />For playing music files, decoding video backgrounds and creating video output files this software uses code of <a href="http://ffmpeg.org"><span style=" text-decoration: underline; color:#0057ae;">FFmpeg</span></a> licensed under the <a href="http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html"><span style=" text-decoration: underline; color:#0057ae;">LGPLv2.1</span></a></p> <p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">SDL</span><br />For playing music files this software uses the <a href="http://www.libsdl.org"><span style=" text-decoration: underline; color:#0057ae;">Simple DirectMedia Layer library</span></a> licensed under the <a href="http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html"><span style=" text-decoration: underline; color:#0057ae;">LGPL license</span></a></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Sada nástrojů Qt</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Tento program je založen na přenositelné knihovně GUI/core, která je dostupná na <a href="http://qt.nokia.com"><span style=" text-decoration: underline; color:#0057ae;">http://qt.nokia.com</span></a>. Tato knihovna se sadou nástrojů je dostupná a používána pod licencí LGPL.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Icons</span><br />Tento program používá ikony vyvíjené DryIcons, <a href="http://www.dryicons.com"><span style=" text-decoration: underline; color:#0057ae;">http://www.dryicons.com</span></a></p> <p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">FFMpeg</span><br />Pro přehrávání hudebních souborů, dekódování pozadí videí a vytváření souborů s výstupním obrazovým záznamem tento program používá kód <a href="http://ffmpeg.org"><span style=" text-decoration: underline; color:#0057ae;">FFmpeg</span></a> licencovaný pod <a href="http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html"><span style=" text-decoration: underline; color:#0057ae;">LGPLv2.1</span></a></p> <p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">SDL</span><br />Pro přehrávání hudebních souborů tento program používá knihovnu<a href="http://www.libsdl.org"><span style=" text-decoration: underline; color:#0057ae;">Simple DirectMedia Layer</span></a> licencovanou pod licencí <a href="http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html"><span style=" text-decoration: underline; color:#0057ae;">LGPL</span></a></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This application uses the following 3rd party modules:</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <ul> <li>Qt toolkit library, <a href="http://qt.nokia.com"><span style=" text-decoration: underline; color:#0000ff;">http://qt.nokia.com</span></a> <li>Icons by DryIcons, <a href="http://www.dryicons.com"><span style=" text-decoration: underline; color:#0000ff;">http://www.dryicons.com</span></a> </ul> </body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Tento program používá následující moduly třetích stran:</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <ul> <li>Knihovna se sadou nástrojů Qt, <a href="http://qt.nokia.com"><span style=" text-decoration: underline; color:#0000ff;">http://qt.nokia.com</span></a> <li>Ikony od DryIcons, <a href="http://www.dryicons.com"><span style=" text-decoration: underline; color:#0000ff;">http://www.dryicons.com</span></a> </ul> </body></html> DialogEncodingProgress Generating video file Vytváří se videosoubor Video output statistics Statistika výstupu videa Current time: Nynější čas: 0 0 Frames processed: Zpracováno snímků: Output file size: Velikost výstupního souboru image Obraz DialogExportOptions Specify video parameters Stanovit parametry videa Specify CDG parameters Stanovit parametry CDG Write CD+G data to file: Zapsat data CD+G do souboru: Font size too large Velikost písma je příliš velká The output text cannot fit into screen using the specified font size Výstupní text se při použití stanovené velikosti písma nevejde na obrazovku Export video to a file Vyvést video do souboru Export CD+G graphics to a file Vyvést grafiku CD+G do souboru Output file not specified Výstupní soubor nestanoven You must specify the output video file Musíte stanovit výstupní videosoubor DialogExportParams Specify video parameters Stanovit parametry videa Options Volby Video encoding parameters Parametry pro kódování videa Image size: Velikost obrazu: Specifies the output image size. Note that some output formats (VideoCD, DVD, HD) require a specific image size. Stanovuje výstupní velikost obrazu. Všimněte si, že některé výstupní formáty (VideoCD, DVD, HD) vyžadují zvláštní velikost obrazu. 352x288 (VCD PAL) 352x288 (VCD PAL) 352x240 (VCD NTSC) 352x240 (VCD NTSC) 720x576 (DVD PAL) 720x576 (DVD PAL) 720x480 (DVD NTSC) 720x480 (DVD NTSC) 1280x720 (HD 720p) 1280x720 (HD 720p) 1980x1080 (HD 1080p) 1980x1080 (HD 1080p) Which codec to use for video encoding Který kodek použít pro kódování videa Video encoding: Kódování videa: xvid xvid Frame rate: Rychlost snímkování: Output frame rate. PAL frame rate is 25 FPS, NTSC is 29.95 FPS Výstupní rychlost snímkování. Rychlost snímkování PAL je 25 snímků za sekundu (FPS), rychlost snímkování NTSC je 29.95 snímků za sekundu 25 FPS (PAL) 25 FPS (PAL) 29.97 FPS (NTSC) 29.97 FPS (NTSC) Container format: Formát kontejneru: Output file (container) format, i.e. AVI, MKV, MP4 Formát kontejneru výstupního souboru, např. AVI, MKV, MP4 AVI AVI MKV MKV MP4 MP4 Export every video frame as keyframe. It produces MUCH larger video files, but the quality would be the best. Use if you are going to process the video further. Vyvést každý snímek videa jako klíčový snímek. Toto vytváří O HODNĚ větší videosoubory, ale jakost obrazového záznamu je ta nejlepší. Použijte v případě, že se video chystáte zpracovávat dále. Every frame is a keyframe Každý snímek videa je klíčový snímek Do not export audio Nevyvádět zvuk Lyrics rendering Udělání slov písně Font: Písmo: Font family for preview window Rodina písma pro náhledové okno Size: Velikost: Font size for preview window. Larger values are suggested (24 and up) Velikost písma pro náhledové okno. Navrhuje se použít větších hodnot (24 bodů a větší) Detect size Zjistit velikost Test size Vyzkoušet velikost Keep title at least Udržovat název alespoň seconds sekund Colors Barvy Not sung yet Ještě nezazpíváno Preview window text color for the text which has been sung. Barva textu v okně s náhledem pro text, který byl zazpíván. PushButton Tlačítko ke stlačení Sung Zazpíváno Preview window text color for the text which is not being sung yet.. Barva textu v okně s náhledem pro text, jenž ještě zazpíván nebyl. Information Informace Preview window background color. Barva pozadí okna s náhledem. Backround Pozadí Draw squares showing where singing starts Kreslit čtverečky ukazující, kde zpívání začíná Title window parameters Parametry titulkového okna Artist Umělec Title Název Created by: Vytvořeno: Output file Výstupní soubor Write video to file: Zapsat video do souboru: Browse Procházet Preview Náhled DialogProjectSettings Settings Nastavení General Obecné Selected music file: Vybraný hudební soubor: Music file: Hudební soubor: Browse Procházet Project lyrics type Typ projektového textu písně LRC version 1 LRC ve verzi 1 LRC version 2 LRC ve verzi 2 UltraStar lyrics Písňový text UltraStar CD+G lyrics Písňový text CD+G Required fields Vyžadovaná pole All those fields must be set Všechna tato pole musí být nastavena Song title: Název písně: Artist: Umělec: BPM and GAP will be added by the editor ÚZM a GAP budou přidány editorem Optional fields Volitelná pole LRC format optional fields Volitelná pole formátu LRC Album Album Created by: Vytvořeno: Offset: Posun: Application: Program: Version: Verze: Ultrastar format optional fields Volitelná pole formátu Ultrastar Video file: Soubor videa: Video gap: GAP videa: Cover image: Obrázek s obalem alba: Background image: Obrázek s pozadím: Genre: Žánr: Edition: Edice: Language: Jazyk: CD+G CD+G CD+G rendering Udělání CD+G Font: Písmo: Font family for preview window Rodina písma pro okno s náhledem Size: Velikost: Font size for preview window. Larger values are suggested (24 and up) Velikost písma pro okno s náhledem. Jsou navrženy vyšší hodnoty (24 bodů a více) Keep title at least Udržovat název alespoň seconds sekund Colors Barvy Backround Pozadí Preview window background color. Barva pozadí okna s náhledem. PushButton Tlačítko ke stlačení Information Informace Preview window text color for the text which has been sung. Barva textu v okně s náhledem pro text, jenž byl zazpíván. Sung Zazpíváno Preview window text color for the text which is not being sung yet.. Barva textu v okně s náhledem pro text, jenž ještě zazpíván nebyl. Not sung yet Ještě nezazpíváno DialogRegistration Dialog Dialog Enter the registration key Zadat registrační klíč <html><head/><body><p>The application is not registered. See the <a href="http://www.ulduzsoft.com/linux/karaoke-lyrics-editor/kleregistration/"><span style=" text-decoration: underline; color:#0057ae;">web site</span></a> for the registration information.</p><p>If you received the registration key, please enter it here:</p></body></html> <html><head/><body><p>Program není registrován. Podívejte se na stránky <a href="http://www.ulduzsoft.com/linux/karaoke-lyrics-editor/kleregistration/"><span style=" text-decoration: underline; color:#0057ae;"></span></a> na informace o registraci.</p><p>Pokud jste obdržel registrační klíč, zadejte jej, prosím zde:</p></body></html> Registration information Informace o registraci Registered to: Registrováno: TextLabel Textový štítek Expires: Vyprší: DialogSelectEncoding Select text encoding Vybrat kódování textu Select encoding Vybrat kódování Select the text encoding: Vybrat kódování textu: Save this encoding as default choice for future Uložit toto kódování jako výchozí hodnotu pro budoucnost Text example using the selected encoding Příklad textu používajícího vybrané kódování Encoding not selected Kódování nevybráno Please select text encoding Vyberte, prosím, kódování textu DialogSettings Application settings Nastavení programu Text editor Textový editor General Obecné Editor font style: Styl písma v editoru: Font style for the main editor window where the text is being edited. Styl písma v hlavním okně editoru, v němž je text upravován. size: Velikost: Font size for main editor window Velikost písma v hlavním okně editoru <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">LRC format only.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">No karaoke player can show the whole lyrics on screen at the same time. Most players can only show 4-8 lines of text on screen. If the lyrics are continuous, the player decides itself where to split the text - usually applying simple logics, which may not necessary provide good results. However if the player supports blocks (XBMC does), you can add an empty line between verses, and the player will use those markers to separate verses when showing text on screen.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If enabled, the validator will allows block separators (single empty lines), and will check that the block size does not exceed a specific number of lines.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Pouze formát LRC.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Žádný přehrávač karaoke nemůže na obrazovce ukázat celý text písně ve stejnou dobu. Většina přehrávačů může na obrazovce ukázat jen 4 až 8 řádků textu. Pokud je text písně souvislý, přehrávač se sám rozhoduje, kde text rozdělit - obvykle použije jednoduchou logiku, která nutně nemusí poskytnout dobré výsledky. Pokud ovšem přehrávač podporuje bloky (XBMC to tak dělá), můžete mezi verše přidat prázdné řádky, a přehrávač těchto značek užije k oddělení veršů, když text ukazuje na obrazovce.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Je-li to povoleno, schvalovatel umožní oddělovače bloků (jednoduché prázdné řádky), a ověří, že velikost bloku nepřekračuje přesně stanovený počet řádků.</p></body></html> Support blocks; each block can have up to Podporuje bloky; každý blok může mít až If blocks are enabled, this parameter specifies the maximum number of lines allowed in block. Most players cannot show more than 8 lines, so before using a larger parameter, please make sure your player supports it. Pokud jsou povoleny bloky, tento parametr udává největší počet řádků dovolených v bloku. Většina přehrávačů nemůže ukázat více než osm řádků, takže před použitím většího parametru se, prosím, ujistěte, že jej váš přehrávač podporuje. lines řádky Automatic stop during tagging Automaticky zastavit během označování <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">LRC version 2 or UltraStar only. </span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">When tagging the song (using Insert Tag, which inserts current time tag at the cursor position), the cursor moves forward, typically to the beginning of next line. If this checkbox is set, the cursor will move to the end of current line, allowing you to put a time tag there (and after that it will move to the beginning of next line). This is important, because having a time tag at the end of line allows the player to calculate how much time the current line takes, and therefore provides more accurate character highlighting.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">LRC ve verzi 2 nebo pouze UltraStar. </span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Při označování písně (pomocí Vložit značku, čímž je v poloze ukazovátka vložena značka se současným časem), se ukazovátko přesunuje dopředu, typicky na začátek dalšího řádku. Pokud je nastaveno toto zaškrtávací okénko, ukazovátko se přesune na konec nynějšího řádku, čímž vám umožní dát časovou značku tam (a poté se přesune na začátek dalšího řádku). To je důležité, protože to, že má časovou značku na konci řádku, přehrávači umožňuje spočítat, kolik času současný řádek zabere, a na základě toho poskytuje přesnější zvýrazňování znaků.</p></body></html> Stop at the end of line (to put a time tag there) Zastavit na konci řádku (kvůli umístění časové značky tam) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">LRC2 and UltraStar formats</span>.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Normally when inserting time tags, a placeholder is replaced by a single timing mark, and cursor moves forward. If this checkbox is set, the cursor will move one character ahead, skipping the new time mark, and will allow you to put two time tags in the place of placeholder. This is useful when there is a hearable pause between words on the line, and you want to put two timing tags, when the first would represent "first word end" and the second would represent "second word start". For example, in Hotel California refrain:</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">Welcome to the Hotel California</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A word "Welcome" is being sung approximately the same time as the words "to the Hotel". If you only put two time tags at the line beginning and ending, the player will assume all the words have equal length, and will speed up "welcome" and slow down "to the hotel". Instead you would put a placeholder between "welcome" and "to the hotel", press "Insert Tag" at the end of "welcome", and press it again at start "to the hotel", therefore providing better timings to the player.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Formáty LRC2 a UltraStar</span>.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Obvykle je při vkládání časových značek zástupný znak nahrazen jedinou časovou značkou, a ukazovátko se přesune dopředu. Pokud je nastaveno toto zaškrtávací okénko, ukazovátko se přesune o jeden znak směrem dopředu, přeskočí novou časovou značku, a umožní vám dát na místo zástupného znaku dvě časové značky. Toto je užitečné v případě, že je mezi slovy na řádku slyšitelná přestávka, a vy chcete umístit to put dvě časové značky, přičemž první představuje "konec prvního slova" a druhé představuje "začátek druhého slova". Například v refrénu k Hotel California:</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">Welcome to the Hotel California</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Slovo "Welcome" je zazpíváno přibližně ve stejnou dobu jako slova "to the Hotel". Pokud postavíte pouze dvě časové značky na začátku a na konci řádku, přehrávač se bude domnívat, že všechna slova mají stejnou délku, a zrychlí "welcome" a zpomalí "to the hotel". Namísto toho dáte zástupný znak mezi "welcome" a "to the hotel", stisknete "Vložit značku" na konci "welcome", a stisknete to znovu na začátku "to the hotel", tím pádem poskytnete přehrávači lepší načasování.</p></body></html> Replace time placeholder by two time tags instead of one Nahradit zástupný znak pro čas dvěma časovými značkami namísto jednou <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">LRC version 2 or UltraStar only. </span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">When tagging the song (using Insert Tag, which inserts current time tag at the cursor position), the cursor moves forward, typically to the beginning of next line or next placeholder. If this checkbox is set, the cursor will move to the beginning of next word. To skip prepositions and articles, it also skips the words which have less than specified number of characters.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">LRC ve verzi 2 nebo pouze UltraStar. </span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Při označování písně (pomocí Vložit značku, čímž je v poloze ukazovátka vložena značka se současným časem), se ukazovátko přesunuje dopředu, typicky na začátek dalšího řádku nebo k dalšímu zástupnému symbolu. Pokud je nastaveno toto zaškrtávací okénko, ukazovátko se přesune na začátek dalšího slova. Aby byly přeskočeny předložky a členy, přeskakuje i slova mající menší než je zadaný počet znaků.</p></body></html> Stop at the beginning of next word; a word must be longer than Zastavit na začátku dalšího slova; slovo musí být delší než chars Znaky Timing marks Značky načasování Font Písmo Time mark font style: Styl písma časové značky: Font style for time placeholder. Since the font is usually smaller than editor font, do not chose serif fonts here. Styl písma pro zástupný znak pro čas. Protože písmo je obvykle menší než písmo editoru, nepoužívejte zde patková písma. Placeholder font size. Should be at least 2px smaller than editor text font size. Velikost písma zástupného znaku. Měla by být alespoň o 2 pixely menší než velikost písma textu v editoru. Placeholder colors Barvy zástupných znaků Background: Pozadí: Placeholder background color. Barva pozadí zástupného znaku. PushButton Tlačítko ke stlačení Text: Text: Placeholder text color. Barva textu zástupného znaku. Timing mark colors Barvy značek načasování Timing mark background color. Barva pozadí značky načasování. Timing mark foreground color. Barva popředí značky načasování. Ultrastar-specific Zvláštní pro Ultrastar Background color for a timing mark which contains both time and pitch. Making it different from timing mark background color above would make it easier to detect timing marks which do not have pitch seet yet. Barva pozadí pro značku načasování, která obsahuje jak čas tak výšku tónu. Tím, že se odliší od barvy pozadí značky načasování výše, se usnadní zjištění značek načasování, jež ještě výšku tónu nastavenu nemají. Background color: Barva pozadí: If checked, the timing mark will also show the pitch. It takes more screen space, but also provides more information. The pitch will only be shown for lyric formats which suport it (only UltraStar so far). Jeli zaškrtnuta tato volba, bude značka načasování ukazovat i výšku tónu. Zabere se tím více místa na obrazovce, ale dostane se zase více údajů. Výška tónů se bude ukazovat jen u formátů písňových textů, které ji podporují (zatím jen UltraStar). Show pitch in timing mark if available Ukázat výšku tónu ve značce načasování, je-li dostupná Preview window Náhledové okno Font family for preview window Písmová rodina pro náhledové okno Font size for preview window. Larger values are suggested (24 and up) Velikost písma pro náhledové okno. Navrhují se větší hodnoty (24 a více) Colors Barva Backround Pozadí Preview window background color. Barva pozadí náhledového okna. Inactive Nečinné Preview window text color for the text which is not being sung yet.. Barva textu náhledového okna pro text, který ještě nebyl zazpíván. Active Činné Preview window text color for the text which has been sung. Barva textu náhledového okna pro text, který už byl zazpíván. Advanced Pokročilé Phonon Phonon Audio delay introduced by Phonon: Zpoždění zvuku zavedené Phononem: There is a delay between the Phonon (internal player) architecture plays the song, and before the tick() signal (which is used to synchronize lyrics) is played. This delay does not affect preview, since the delay is the same. However actual lyrics when tested with a karaoke player, might appear "shifted" (usually a little behind). This value(in milliseconds) represents the delay. It will be added to the exported lyrics automatically, and therefore the time might not match for UltraStar format. For LRC formats it is added via "offset" field. Mezi okamžikem, kdy stavba Phononu (vnitřní přehrávač) přehrává písničku, a tím, když se přehrává signál tick() (jenž je používán k seřízení slov písně), je zpoždění. Toto zpoždění neovlivňuje náhled, protože zpoždění je stejné. Skutečné texty písní, když jsou zkoušeny s přehrávačem karaoke, se ovšem mohou jevit "posunuté" (obvykle jsou trošku pozadu). Tato hodnota (v milisekundách) představuje zpoždění. Bude automaticky přidána do vyvedeného písňového textu, a proto nemusí u formátu UltraStar odpovídat čas. U formátů LRC je hodnota přidána pomocí pole "posun". ms ms Check for updates Prověřit, zda jsou aktualizace If this option is enabled, the application will check for updates (when new version of application is released). If a new version is available, the program will show a messagebox dialog informing you about new version. This check will only performed once in 24 hours. No user information is requested or transmitted from your computer during this check. Je-li povolena tato volba, program provede zkoušku, zda jsou aktualizace (když je vydána nová verze programu). Je-li dostupná nová verze, program ukáže zprávu, jíž vás zpraví o nové verzi. Toto ověřování se provede jen jednou za dvacetčtyři hodin. Během této zkoušky není požadována nebo z počítače přenesena žádná informace o uživateli. Check once a day, and warn me when a new version is available Prověřit jednou za den a upozornit, když je dostupná nová verze Last checked: Never Naposledy přezkoušeno: Nikdy DialogTestWindow Dialog Dialog DialogTimeAdjustment Time adjustment Upravení načasování Global timing adjustment Celkové upravení načasování Add milliseconds to each timing: Přidat ke každému načasování milisekundy: 0 0 Multyply each timing (happens first): Násobit každé načasování (stane se první): 1.0 1.0 Test the adjustment Provést zkoušku upravení Time value of Časová hodnota will become se stane Editor Validation error found Nalezena chyba v uznání platným Error at line %1: Chyba na řádku %1: Empty line found. An empty line represents a block boundary, but blocks are currently disabled in settings Nalezen prázdný řádek. Prázdný řádek představuje hranice bloku, ale bloky jsou nyní v nastavení zakázány Double empty line found. A single empty line represents a block boundary; double lines are not supported. Nalezen dvojitý prázdný řádek. Jeden prázdný řádek představuje hranice bloku; dvojité řádky nejsou podporovány. Paragraph height exceed. This paragraph cannot fit into the screen using the selected font Výška odstavce překročena. Tento odstavec se při použití vybraného písma nevejde na obrazovku Block size exceeded. The block contains more than %1 lines. Most karaoke players cannot show too large blocks because of limited screen space. Please split the block by adding a block separator (an empty line). Překročena velikost bloku. Tento blok obsahuje více než %1 řádků. Většina přehrávačů karaoke nemůže ukázat příliš rozsáhlé bloky z důvodu omezenosti místa na obrazovce. Rozdělte, prosím, blok přidáním oddělovače bloku (prázdný řádek). Missing opening time tag. Every line must start with a [mm:ss.ms] time tag Chybí otevírající časová značka. Každý řádek musí začínat časovou značkou [mm:ss.ms] Missing closing time tag. For this lyrics type every line must end with a [mm:ss.ms] time tag Chybí zavírající časová značka. U tohoto typu písňového textu musí každý řádek končit časovou značkou [mm:ss.ms] Invalid special tag. %1 Neplatná zvláštní značka. %1 Placeholders should not be present in the production file. Zástupné znaky by ve výrobním souboru neměly být přítomny. Invalid time, number of seconds cannot exceed 59. Neplatný čas, počet sekund nemůže překročit 59. Time goes backward, previous time value is greater than current value. Čas jde zpět, předchozí časová hodnota je větší než nynější hodnota. Invalid time tag. Time tag must be in format [mm:ss.ms] where mm is minutes, ss is seconds and ms is milliseconds * 10 Neplatná časová značka. Časová značka musí být ve formátu [mm:ss.ms], kde mm jsou minuty, ss jsou sekundy a ms jsou milisekundy * 10 Invalid character in the time tag. Time tag must be in format [mm:ss.ms] where mm is minutes, ss is seconds and ms is milliseconds * 10 Neplatný znak v časové značce. Časová značka musí být ve formátu [mm:ss.ms], kde mm jsou minuty, ss jsou sekundy a ms jsou milisekundy * 10 Invalid closing bracket usage outside the time block Neplatné použití zavírající závorky vně časového bloku Invalid closing bracket usage outside the special block Neplatné použití zavírající závorky vně zvláštního bloku Line width exceed. This line cannot fit into the screen using the selected font Šířka řádku překročena. Tento řádek se při použití vybraného písma nevejde na obrazovku Time tag is not closed properly Časová značka není zavřena správně Timing at this point: %1 Načasování v tomto bodě: %1 GentleMessageBox Dialog Dialog icon Ikona Dialog text Text dialogu Do not show this message again Neukazovat tuto zprávu znovu MainWindow Karaoke Lyrics Editor Karaoke editor slov písně Lyrics editor Editor slov písně File Soubor Edit Úpravy Settings Nastavení Help Nápověda Project Projekt Import lyric file... Zavést soubor se slovy písně... Imports the lyrics file Zavede soubor se slovy písně Imports the lyrics file in any format, replacing the current content. Zavede soubor se slovy písně v jakémkoli formátu, přičemž dojde k nahrazení současného obsahu. New project... Nový projekt... Creates a new project Vytvoří nový projekt Closes an existing project (if any), and creates a new one. Zavře stávající projekt (je-li jaký) a vytvoří nový. Ctrl+N Ctrl+N Save project Uložit projekt Saves current project to disk. Uloží nynější projekt na disk. Ctrl+S Ctrl+S Save project as ... Uložit projekt jako... Saves current project to disk with the file name specified by user. Uloží nynější projekt na disk s názvem souboru zadaným uživatelem. Quit Ukončit Exit the application Ukončit program Undo Zpět Undo last action Vrátit poslední krok zpět Ctrl+Z Ctrl+Z Redo Znovu Redo last action Udělat poslední krok znovu Ctrl+Shift+Z Ctrl+Shift+Z Insert mark Vložit značku Insert a placeholder or timing mark Vložit zástupný znak nebo značku načasování <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This action inserts a placeholder (if the player is stopped), or a timing mark representing current time (if the song is played). Placeholders are used as "stoppers" - when the song will be played and you will be inserting timing marks, the cursor will automatically stop on all placeholders, allowing you to concentrate on timings, and not on cursor movement.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">After the mark is set, the cursor will move to one of the following, whatever is closer:</p> <ul style="-qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Next placeholder or timing tag on this line (which will be overwritten by new value);</li> <li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Beginning of next line;</li> <li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Next character after just placed timing mark if placed on placeholder, and the "doubling tag" settings is enabled in settings;</li> <li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">End of current line if enabled in settings;</li> <li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Beginning of next word if enabled in settings;</li></ul> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Tento krok vloží zástupný znak (pokud je přehrávač zastaven), nebo značku načasování představující nynější čas (pokud je píseň přehrávána). Zástupných znaků se používá jako "zastavovatelů" - když se písnička bude přehrávat a vy budete vkládat značky načasování, ukazovátko bude automaticky zastavovat na všech zástupných znacích, čímžý vám umožní soustředit se na časování, a ne na pohyb ukazovátka.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Poté, co je nastavena značka, se ukazovátko přesune k jednomu z následujícího, k tomu, co je blíž:</p> <ul style="-qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Další zástupný znak nebo značka načasování na tomto řádku (jenž/jež bude přepsán(a) novou hodnotou);</li> <li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Začátek dalšího řádku;</li> <li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Další znak po právě umístěné značce načasování, pokud je umístěn na zástupný znak, a v nastavení je povoleno nastavení "dvojité značky";</li> <li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Konec nynějšího řádku, je-li povoleno v nastavení;</li> <li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Začátek dalšího slova, je-li povoleno v nastavení;</li></ul> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> F5 F5 Remove last Odstranit poslední Remove last inserted placeholder or mark Odstranit poslední vložený zástupný znak nebo značku načasování This action removes the placeholder or timing mark which was inserted the last, and moves cursor to its position. The editor remembers all timing marks added since the project was loaded in current session, so this action will work for all added tags, not for the last one. If you want to delete the timing mark or placeholder under cursor, just press'Del'. Tento krok odstraní zástupný znak, který byl vložen naposledy, nebo značku načasování, která byla vložena naposledy, a přesune ukazovátko na jeho/její místo. Editor si pamatuje všechny značky s načasováním, jež byly přidány od doby, kdy byl projekt nahrán do současného sezení, takže tento krok bude pracovat pro všechny přidané značky, nejen pro tu poslední. Pokud chcet smazat značku načasování nebo zástupný znak pod ukazovátkem, stiskněte klávesu 'Del'. F8 F8 Remove all marks Odstranit všechny značky Remove all placeholders and timing marks Odstranit všechny zástupné znaky nebo značky načasování This action removes all placeholders and timing marks in the lyrics. Tímto krokem dojde v textu písně k odstranění všech zástupných znaků a značek načasování. Clear text Smazat text Clear the text Smazat text This action removes all text and timing marks, leaving the project with empty lyrics editor. Tímto krokem dojde k odstranění všeho textu a značek načasování. Zbyde projekt s prázdným editorem slov písní. Application Settings... Nastavení programu... Change the application settings Změnit nastavení programu About... O programu... About the application O programu Open project... Otevřít projekt... Open and load the project Otevřít a nahrát projekt This action opens an existing project file, and loads it. Current project, if opened, will be closed. Tímto krokem dojde k otevření stávajícího souboru s projektem a k jeho nahrání. Nynější projekt, je-li otevřen, bude zavřen. Project settings... Nastavení projektu... Changes project-specific settings Změní určitá nastavení projektu This action changes project-specific settings, including mostly lyric tags. Tímto krokem dojde ke změně určitých nastavení projektu, včetně převážně textových značek. Export lyric file... Vyvést soubor se slovy písně... Export lyric file in various formats Vyvést soubor se slovy písně do různých formátů This action lets you export a lyric file in a specific format. With some restrictions it allows export in other formats besides the main project format. Tento krok vás nechá vyvést soubor se slovy písně do zvláštního formátu. S některými omezeními dovolí vyvedení do jiných formátů mimo hlavní formát projektu. View lyrics Zobrazit slova písně View the generated lyrics Zobrazit vytvořená slova písně This action shows the generated lyrics according to project lyrics type as they would be written into the file. Tento krok ukáže vytvořený text s písní podle typu písňového textu v projektu, jak se zapíše do souboru. Test lyrics Vyzkoušet slova písně Test the lyrics using built-in karaoke Zkoušet slova písně pomocí vestavěného karaoke This action shows the lyrics test window, which is a mini Karaoke player. Once you hit "Play" button on player, the lyrics will be shown in the test window when the song is played. This way you can test the lyrics synchronization, and make corrections if necessary. The lyrics are copied into the test window when it is opened. The changes made in the editor are NOT silently propagated into the test window, and to update the test window, it must be closed and reopened again. Note that for formats which support blocks nothing will be shown until it's less than five seconds before the first sillable is being sung. Tento krok ukáže zkušební okno se slovy písně, což je malý přehrávač karaoke. Jakmile na přehrávači stisknete tlačítko "Přehrát", slova písně se při přehrávání písně budou ukazovat ve zkušebním okně. Tímto způsobem můžete zkoušet seřízení písňového textu a provádět opravy, když je to nutné. Slova písně jsou, když je otevřeno, kopírována do zkušebního okna. Změny udělané v editoru NEJSOU potichu předávány do zkušebního okna, a kvůli obnovení zkušebního okna musí být okno zavřeno a znovu otevřeno. Všimněte si, že u formátů podporujících bloky se nic neukáže, dokud doba před zazpíváním první slabiky není kratší než pět sekund. Edit header data... Upravit data záhlaví... Edit lyric header data Upravit data záhlaví textu písně This action shows a dialog to edit lyric header tags. Same as project settings without two tabs. Tento krok ukáže okno pro úpravy značek v záhlaví písňového textu.Stejné okno jako je okno pro nastavení projektu bez dvou karet. Validate lyrics Schválit slova písně Validates the lyrics Schválí slova písně This action validates the lyrics according to current format. Validation makes sure the format is correct, that all required timing tags are present, and all restrictions set up in settings are met. Export, view and testing lyrics also execute validation before doing the action. Tento krok schválí slova písně podle nynějšího formátu. Schvalování zajistí, že formát je správný, že všechny požadované značky načasování jsou přítomny, a že všechna omezení nastavená v nastavení jsou dodržena. Schvalování spustí také ještě před uděláním těchto kroků vyvádění slov písně, nahlížení na ně a jejich zkoušení. Show Player dock wingow Ukázat okno s přehrávačem Test CD+G file... Vyzkoušet soubor CD+G... Split current line Rozdělit nynější řádek F10 F10 Export video file... Vyvést video do souboru... Export CD+G file... Vyvést soubor CD+G... Registration... Registrace... Insert picture... Vložit obrázek... Insert video... Vložit video... Insert color change... Vložit změnu barvy... Add end timing marks Přidat koncové značky načasování Adds missing end-of-line timing marks automatically. Doesn't touch existing marks. Přidá chybějící značky načasování na konci řádků automaticky. Nedotkne se stávajících značek. Time adjustment ... Upravení načasování... Pops up the time adjustment dialog Ukáže dialog pro upravení načasování Test lyrics as CD+G Zkoušet slova písně jako CD+G Test the lyrics if you gonna export them as CD+G Zkoušet slova písně, pokud se je chystáte vyvést jako CD+G This action shows the lyrics test window, which is a mini Karaoke player, but is limited in size as CD+G, so it can be used to test the lyrics to see how they would be played on a real CD+G player. Tento krok ukáže zkušební okno pro slova písně, což je malý přehrávač karaoke, ale je omezeno na velikost CD+G, takže jej lze používat ke zkoušení písňových textů, aby se vidělo, jak by byly přehrávány ve skutečném přehrávači CD+G. Trim whitespace Ustřihnout prázdné mezery Removes lead and tail spaces from every line Odstraní mezery na začátku a na konci z každého řádku Close existing project Zavřít stávající projekt Do you really want to close existing project, and create a new project? Opravdu chcete zavřít stávající projekt a vytvořit nový projekt? Open a project file Otevřít projektový soubor Project files (*.kleproj) Projektové soubory (*.kleproj) Save as a project file Uložit jako projektový soubor Unsaved changes Neuložené změny The current project has unsaved changes. Do you want to save them? Nynější projekt má neuložené změny. Chcete je uložit? Registration failed Registrace se nepodařila Registration failed: %1 Registrace se nepodařila: %1 %1[*] - Lyric Editor %1[*] - Editor textů písní Loading the music file %1 Nahrává se soubor s hudbou %1 Open an image file Otevřít soubor s obrázkem Open a video file Otevřít soubor s obrazovým záznamem Clearing all the text Maže se všechen text Are you sure you want to clear the text? This operation cannot be undone! Jste si jistý, že chcete smazat veškerý text? Tuto operaci nelze vrátit zpět! Open a lyric file Otevřít soubor se slovy písně LRC files (*.lrc);;UltraStar files (*.txt) Soubory LRC (*.lrc);;Soubory UltraStar (*.txt) LRC version 1 (*.lrc1) LRC ve verzi 1 (*.lrc1) LRC version 2 (*.lrc) LRC ve verzi 2 (*.lrc) UltraStar (*.txt) UltraStar (*.txt) <b>Karaoke Lyrics Editor version %1.%2</b><br><br>Copyright (C) George Yunaev 2009-2013, <a href="mailto:support@ulduzsoft.com">support@ulduzsoft.com</a><br><br>Web site: <a href="http://www.ulduzsoft.com">www.ulduzsoft.com/karlyriceditor</a><br><br>This program is licensed under terms of GNU General Public License version 3; see LICENSE file for details. <b>Karaoke Lyrics Editor ve verzi %1.%2</b><br><br>Autorské právo (C) George Yunaev 2009-2013, <a href="mailto:support@karlyriceditor.com">support@karlyriceditor.com</a><br><br>Stránky: <a href="http://www.karlyriceditor.com">www.ulduzsoft.com/karlyriceditor</a><br><br>Na tento program je poskytnuto povolení za podmínek GNU General Public License<br>verze 3; podívejte se na soubor LICENSE na podrobnosti. Open a CD+G file Otevřít soubor CD+G CD+G (*.cdg) CD+G (*.cdg) Export lyrics to a file Vyvést slova písně do souboru LRC files (*.lrc);;UltraStar/PowerKaraoke files (*.txt);;KAR/MIDI files (*.mid *.midi *.kar);;KaraFun files (*.kfn);;DEL Karaoke files (*.kok) Soubory LRC (*.lrc);;Soubory UltraStar/PowerKaraoke (*.txt);;Soubory KAR/MIDI (*.mid *.midi *.kar);;Soubory KaraFun (*.kfn);;Soubory DEL Karaoke (*.kok) Cannot write lyric file Nelze zapsat soubor se slovy písně Cannot write lyric file %1: %2 Nelze zapsat soubor se slovy písně %1: %2 Edit project settings Upravit nastavení projektu <b>Karaoke Lyrics Editor version %1.%2</b><br><br>Copyright (C) George Yunaev 2009, <a href="mailto:support@karlyriceditor.com">support@karlyriceditor.com</a><br><br>Web site: <a href="http://www.karlyriceditor.com">www.karlyriceditor.com</a><br><br>This program is licensed under terms of GNU General Public License<br>version 3; see LICENSE file for details. <b>Karaoke Lyrics Editor ve verzi %1.%2</b><br><br>Autorské právo (C) George Yunaev 2009, <a href="mailto:support@karlyriceditor.com">support@karlyriceditor.com</a><br><br>Stránky: <a href="http://www.karlyriceditor.com">www.karlyriceditor.com</a><br><br>Na tento program je poskytnuto povolení za podmínek GNU General Public License<br>verze 3; podívejte se na soubor LICENSE na podrobnosti. Unable to check whether a new version is available Nelze ověřit, zda je dostupná nová verze New version available Nová verze je dostupná <html>A new version <b>%1</b> of Karaoke Lyrics Editor is available! Do you want to visit the application web site %2? <html>Nová verze <b>%1</b> Karaoke Lyrics Editor je dostupná! Chcete navštívit stránky programu %2? PlayerWidget Player controls Ovládání přehrávače Seeks five seconds backward Hledá pět sekund nazpět Rew Zpět Plays or pauses a current playing song Přehraje nebo pozastaví nyní přehrávanou píseň Pause Pozastavit Alt+P Alt+P Stops the currently playing song Zastaví nyní přehrávanou píseň Stop Zastavit Seeks five seconds forward Hledá pět sekund dopředu Fwd Vpřed Playing time Doba přehrávání Time in minutes:seconds.tenth of milliseconds of how long the song has been played. Čas v minutách:sekundách.desetinách milisekund udávající, jak dlouho byla píseň přehrávána. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:16pt; font-weight:600;">---</span></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:16pt; font-weight:600;">---</span></p></body></html> Shows current position in a song Ukazuje nynější polohu v písni Remaining time Zbývající čas Remaining time in minutes:seconds.tenth of milliseconds Zbývající čas v minutách:sekundách.desetinách milisekund Volume control Ovládání hlasitosti Cannot play media file Hudební soubor nelze přehrát Cannot play media file %1: %2 Nelze přehrát hudební soubor %1: %2 <b>%1</b> <b>%1</b> <b>-%1</b> <b>-%1</b> Cannot open media file Nelze otevřít hudební soubor Player controls - %1 by %2 Ovládání přehrávače - %1 od %2 ProjectSettings Choose a music file to load Vybrat soubor s hudbou k nahrání Settings error Chyba v nastavení Music file cannot be empty Soubor s hudbou nemůže být prázdný You must type song title to continue. Abyste mohl pokračovat, musíte napsat název písně. You must type song artist to continue. Abyste mohl pokračovat, musíte napsat umělce písně. You must enter MP3 file name to continue. Abyste mohl pokračovat, musíte napsat název souboru MP3. QObject This paragraph contains too many lines, and the result height of %1 cannot fit into a CD+G screen using the font selected. Tento odstavec obsahuje příliš mnoho řádků, a výsledná výška %1 se za použití vybraného písma nevejde do obrazovky CD+G. Line too long. The line width %1 cannot fit into a CD+G screen using the font selected. Řádek je moc dlouhý. Šířka řádku %1 se za použití vybraného písma nevejde do obrazovky CD+G. Cannot open file Nelze otevřít soubor Cannot open file %1 Nelze otevřít soubor %1 Invalid project file Neplatný projektový soubor The project file %1 is not a valid Lyric Editor project file Projektový soubor %1 není platným projektovým souborem Lyric Editor Cannot write file Soubor nelze zapsat Cannot write file %1 Nelze zapsat soubor %1 Current lyrics format is set to CD+G. When exporting it as LRC version 1 all time tags except the one in front of the line will be ignored. Do you want to proceed with export? Nynější formát písňového textu je nastaven na CD+G. Při jeho vyvedení jakožto verze LRC 1 se budou přehlížet všechny značky pro čas vyjma té, která je na začátku řádku. Chcete pokračovat ve vyvádění? Current lyrics format is set to LRC version 2. When exporting it as LRC version 1 all time tags except the one in front of the line will be ignored. Do you want to proceed with export? Nynější formát písňového textu je nastaven na LRC ve verzi 2. Při jeho vyvedení jakožto verze LRC 1 se budou přehlížet všechny značky pro čas vyjma té, která je na začátku řádku. Chcete pokračovat ve vyvádění? Current lyrics format is set to Ultrastar. When exporting it as LRC version 1 all time tags except the one in front of the line will be ignored as well as the pitch information. Do you want to proceed with export? Nynější formát písňového textu je nastaven na UltraStar. Při jeho vyvedení jakožto verze LRC 1 se budou přehlížet všechny značky pro čas vyjma té, která je na začátku řádku, a stejně tak i informace o výškách tónů. Chcete pokračovat ve vyvádění? Exporting lyrics Vyvádí se slova písně Current lyrics format is set to Ultrastar. When exporting it as LRC version 2 the pitch information will be ignored. Do you want to proceed with export? Nynější formát písňového textu je nastaven na UltraStar. Při jeho vyvedení jakožto verze LRC 2 se bude přehlížet informace o výšce tónu. Chcete pokračovat ve vyvádění? Current lyrics format is set to LRC version 1. When exporting it as UltraStar format the output lyrics will not contain pitch information. Also since the lyric syllable duration is a whole line, the output lyrics won't be usable. Do you want to proceed with export? Nynější formát písňového textu je nastaven na LRC ve verzi 2. Při jeho vyvedení jakožto formátu UltraStar nebudou výstupní písňové texty obsahovat informace o výškách tónů. Protože tedy délka trvání slabiky slova písně je celým řádkem, nebudou výstupní písňové texty použitelné. Chcete pokračovat ve vyvádění? Current lyrics format is set to LRC version 2. When exporting it as UltraStar format the output lyrics will not contain pitch information. Do you want to proceed with export? Nynější formát písňového textu je nastaven na LRC ve verzi 2. Při jeho vyvedení jakožto formátu UltraStar nebudou výstupní písňové texty obsahovat informace o výškách tónů. Chcete pokračovat ve vyvádění? Current lyrics format is set to CD+G. When exporting it as UltraStar format the output lyrics will not contain pitch information. Do you want to proceed with export? Nynější formát písňového textu je nastaven na CD+G. Při jeho vyvedení jakožto formátu UltraStar nebudou výstupní písňové texty obsahovat informace o výškách tónů. Chcete pokračovat ve vyvádění? Block separator found Nalezen oddělovač bloku UltraStart lyrics cannot contain block separators. Blocks will be merged. Písňové texty UltraStar nemohou obsahovat oddělovače bloku. Bloky budou sloučeny. No timing mark at the end of line Žádná značka načasování na konci řádku UltraStart lyrics require timing marks at the end of line. Export aborted. Písňové texty UltraStar vyžadují značky načasování na konci řádku. Vyvedení zrušeno. Invalid KFN file Neplatný soubor KFN The file %1 cannot be opened: %2 Soubor %1 nelze otevřít: %2 Cannot open file %1: %2 Nelze otevřít soubor %1: %2 Cannot read MIDI Nelze přečíst MIDI Cannot read lyrics from MIDI file %1 Nelze přečíst soubor se slovy písně ze souboru MIDI %1 Invalid LRC file Neplatný soubor LRC Missing LRC header Chybí hlavička LRC LRC2 file read for LCR1 project Čtení souboru LRC 2 pro projekt LRC 1 The lyric type for current project is set for LRC1, but the lyrics read were LRC2. Either change the project type, or fix the lyrics. Typ textu písně pro nynější projekt je nastaven na LRC 1, ale čtení textu písně bylo lRC 2 Buď změňte typ projektu, anebo text písně opravte. Invalid text file Neplatný textový soubor This file is not a valid UltraStar nor PowerKaraoke lyric file Tento soubor není platným souborem se slovy písně UltraStar, ani PowerKaraoke Invalid PowerKaraoke file Neplatný soubor PowerKaraoke This file is not a valid PowerKaraoke lyric file, error at line %1 Tento soubor není platným souborem se slovy písně PowerKaraoke; chyba na řádku %1 Invalid UltraStar file Neplatný soubor UltraStar This file is not a valid UltraStar lyric file; BPM and/or GAP is missing. Tento soubor není platným souborem se slovy písně UltraStar; ÚZM a/nebo GAP chybí. This file is not a valid UltraStar lyric file; error at line %1. Tento soubor není platným souborem se slovy písně UltraStar; chyba na řádku %1. Last checked: %1 Naposledy přezkoušeno: %1 SDL init failed Inicializace SDL se nezdařila Cannot initialize audio subsystem: %1 Nelze inicializovat zvukový podsystém: %1 Cannot initialize audio device: %1 Nelze inicializovat zvukové zařízení: %1 Cannot allocate frame memory buffer Nelze přidělit vyrovnávací paměť snímkové paměti Cannot write CD+G file Nelze zapsat soubor CD+G Cannot write CD+G file %1: %2 Nelze zapsat soubor CD+G %1: %2 Cannot write CD+G file: %1 Nelze zapsat soubor CD+G: %1 RecentFiles &%1 %2 &%1 %2 TestWindow Testing lyrics Zkouší se slova písně TextLabel Textový štítek Ui Advice: set Phonon backend to Xine? Rada: nastavit jádro Phononu na Xine? GStreamer Phonon backend is less reliable than Xine for reporting time, so it is suggested to use Xine backend. Jádro Phononu GStreamer je méně spolehlivé než Xine na ohlašování času, takže z toho důvodu se navrhuje používání jádra Xine. Cannot initialize audio Nelze inicializovat zvuk Audio output device cannot be initialized. Please make sure the sound card in your computer works fine, that all necessary drivers are installed, and that no other application is currently locking the sound device. Program is now aborting. Zvukové výstupní zařízení nelze inicializovat. Ujistěte se, prosím, že zvuková karta ve vašem počítači pracuje správně, že jsou nainstalovány všechny nezbytné ovladače, a že žádný jiný program nyní neuzamyká zvukové zařízení. Program se nyní ukončuje. ViewWidget Viewing lyrics file Zobrazuje se soubor se slovy písně WizNewProject_Finish WizardPage Stránka průvodce <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The project has been created.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Once you press Finish button, the project will be saved on disk, and you can continue working on it.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">You start with an empty lyrics editor window. If you have lyrics in LRC or Ultrastar format, you can import them via <span style=" font-style:italic;">Project -&gt; Import lyrics</span> menu item. If you have them in a text file or in a Web browser, just copy-paste them.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">See the <a href="http://www.karlyriceditor.com/documentation.html"><span style=" text-decoration: underline; color:#0057ae;">online documentation</span></a> for more details.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The project has been created.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Once you press Finish button, the project will be saved on disk, and you can continue working on it. You need to do the following:</p> <ul> <li>Find the lyrics in the Internet if you don't have them, or type them from the song. <li>Play the song at least once, familiarize yourself with how the lyrics are matched by the song. Correct all mismatches in text. <li>Rewind the song, and start playing it, pressing the "Insert time" key each time the song matches with the start of a line or text in the lyrics file. <li>Test the song, and correct all mistakes. <li>Export the lyrics. </ul> </body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Projekt byl vytvořen.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Hned jak stisknete tlačítko Dokončit, projekt bude uložen na disk, a vy můžete pokračovat v práci na něm.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Začnete s prázdným oknem editoru slov písně. Pokud máte texty ve formátu LRC nebo Ultrastar, můžete je zavést přes položku nabídky <span style=" font-style:italic;">Projekt -&gt; Zavést slova písně</span>. Pokud je máte v textovém souboru nebo v internetovém prohlížeči, prostě je zkopírujte a vložte.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Podívejte se na <a href="http://www.karlyriceditor.com/documentation.html"><span style=" text-decoration: underline; color:#0057ae;"> dokumentaci na internetu</span></a>, kde najdete další podrobnosti.</p></body></html> WizNewProject_Intro WizardPage Stránka průvodce Introduction Úvod <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The karaoke lyrics editor project contains music and lyrics </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">as well as project-specific information. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">During project creation you will be asked the following:</p> <ul style="-qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Main lyrics format you're going to use;</li> <li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A music file, containing the song you want to make </li> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:1; text-indent:0px;">karaoke lyrics for;</p> <li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A lyrics file containing the text (optional);</li></ul></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Editor písňových textů pro karaoke obsahuje hudbu a texty písní </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">a také obsahuje pro projekt zvláštní informace. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Během vytváření projektu budete dotazován na následující věci:</p> <ul style="-qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Hlavní formát písňového textu, který hodláte používat;</li> <li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Hudební soubor, který obsahuje písničku, pro kterou chcete udělat </li> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:1; text-indent:0px;">písňový text pro karaoke;</p> <li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Soubor se slovy písně, který obsahuje text (volitelné);</li></ul></body></html> WizNewProject_LyricType WizardPage Stránka průvodce Select the lyrics type Vyberte typ textu písně <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Please select the type of lyrics type you want to create for this project. Different types have different input requirements, and while the editor supports export in any format, a full conversion between formats is typically burdensome or impossible. <a href="1"><span style=" text-decoration: underline; color:#0000ff;">More information</span></a> about formats.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you are creating lyrics for XBMC Media Center, or to export them in CD+G or video formats, choose LRCv2.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Please select the type of lyrics type you want to create for this project.</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Different types have different input requirements, and while the editor </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">supports export in any format, a full conversion between formats is </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">typically burdensome or impossible. <a href="1"><span style=" text-decoration: underline; color:#0000ff;">More information</span></a> about formats.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you are creating lyrics for XBMC Media Center or just unsure, </p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">choose LRCv2. It can be exported as LRCv1 as well.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Liberation Sans'; font-size:12pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Vyberte, prosím, typ písňového textu, který chcete vytvořit pro tento projekt. Rozdílné typy mají odlišné vstupní požadavky, a zatímco editor podporuje vyvedení do jakéhokoli formátu, úplný převod mezi formáty je obvykle obtížný nebo nemožný. <a href="1"><span style=" text-decoration: underline; color:#0000ff;">Více informací</span></a> o formátech.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Pokud vytváříte text písně pro XBMC Media Center, nebo pro jejich uložení v CD+G nebo videoformátech, zvolte LRC ve verzi 2.</p></body></html> LRC version 1 LRC ve verzi 1 LRC version 2 (recommended) LRC ve verzi 2 (doporučeno) LRC version 2 LRC ve verzi 2 UltraStar lyrics Písňový text UltraStar CD+G lyrics Písňový text CD+G WizNewProject_Lyrics WizardPage Stránka průvodce Lyrics source Zdroj slov písně The editor can only edit the lyrics, it cannot create them. So to start with, the editor need to get the lyrics somewhere. It will look for lyrics on disk, and in the music file. If none is found, your only option would be to either specify a file containing lyrics, or continue with empty editor and type the lyrics by yourself, or find them on Internet and copy them. Editor může slova písně pouze upravovat, nedokáže je vytvořit. Takže, aby mohl začít, editor potřebuje odněkud získat slova písně. Písňový text bude hledat na disku a v hudebním souboru. Pokud není nalezen žádný, je vaší jedinou možností buď určit soubor obsahující text písně, nebo pokračovat s prázdným editorem, a psát si slova písně sám, nebo je najít na internetu a okopírovat je. Load from file Nahrát ze souboru Browse... Procházet... Use lyrics found in the music file Použít slova písně nalezená v souboru s hudbou Continue with empty editor; I'll type lyrics myself. Pokračovat s prázdným editorem. Napíšu si slova k písni sám. WizNewProject_MusicFile WizardPage Stránka průvodce Select the song to add lyrics to: Vyberte píseň, do které se má přidat text písně: Music file: Hudební soubor: Browse Procházet Music file information Informace o hudebním souboru If available, the editor obtained the information from the music file. Please check the information is correct, and if not, correct or fill it. Title and artist is required, but all other fields can be empty. If available, the editor obtained the information from the music file. Please check the information is correct, and if not, correct or fill it. Title is required, but all other fields can be empty. Byla-li dostupná, obdržel editor informace z hudebního souboru. Ověřte, prosím, že informace jsou správné, a v případě že nejsou, opravte je nebo vyplňte. Název je vyžadován, ale všechna ostatní pole mohou být prázdná. Title Název Artist Umělec Album Album Pic Obrázek WizardNewProject::PageLyricType LRC1 is the first version of LRC format, containing a single line with timing mark at the beginning. This format is supported by most players LRC2 is the second version, which can contain timing marks inside the line. Supported by less players. UStar/UltraStar format is lyrics format used in SingStar, Sinatra, Performous and similar games. XBMC supports all those formats LRC 1 je první verzí formátu LRC, která obsahuje jeden řádek se značkou načasování na začátku. Tento formát je podporován většinou přehrávačů. LRC 2 je druhou verzí, která může obsahovat značky načasování uvnitř řádku. Formát je podporován menšinou přehrávačů. Formát UStar/UltraStar je formátem písňového textu používaným ve hrách SingStar, Sinatra, Performous a jim podobných. XBMC podporuje všechny tyto formáty WizardNewProject::PageLyrics Open a lyric file Otevřít soubor se slovy písně LRC files (*.lrc);;UltraStar files (*.txt) Soubory LRC (*.lrc);;Soubory UltraStar (*.txt) Lyrics file not found Soubor se slovy písně nenalezen Selected lyrics file is not found. Vybraný soubor se slovy písně není nalezen. WizardNewProject::PageMusicFile Choose a music file to load Vyberte hudební soubor k nahrání Trying to open a MIDI file? Pokoušíte se otevřít soubor MIDI? It looks like you are trying to open the MIDI file. MIDI file contains the lyrics embedded into the file in a special format. %1 cannot edit regular MIDI files, it is recommended to use RoseGarden instead. Are you sure you still want to try to open this file? Vypadá to, že se pokoušíte otevřít soubor MIDI. Soubory MIDI obsahují slova k písni vložená do souboru ve zvláštním formátu. %1 nedokáže upravovat běžné soubory MIDI. Na to se doporučuje použít RoseGarden. Jste si jistý, že tento soubor stále ještě chcete otevřít? Trying to open a KaraFun file? Pokoušíte se otevřít soubor KaraFun? It looks like you are trying to open the KaraFun file. Karaoke Lyrics Editor cannot edit those files directly. However it can import music and lyrics from this file, and export them as any supported format such as CDG, LRC or UltraStar (but not KFN) Do you want to import the music and lyrics from this file? Vypadá to, že se pokoušíte otevřít soubor KaraFun. Karaoke Lyrics Editor tyto soubory nedokáže upravovat přímo. Může ovšem z tohoto souboru zavést hudbu a slova písně a vyvést je v podobě kteréhokoli z podporovaných formátů, jako jsou CDG, LRC nebo UltraStar (ale ne KFN). Chcete z tohoto souboru zavést hudbu a slova písně? Invalid KFN file Neplatný soubor KFN The file %1 cannot be opened: %2 Soubor %1 nelze otevřít: %2 Cannot import KFN file Nelze zavést soubor KFN The file %1 cannot be imported: the music file %2 cannot be created Soubor %1 nelze zavést: Hudební soubor %2 nelze vytvořit The file %1 cannot be imported: the music file %2 cannot be stored: %s Soubor %1 nelze zavést: Hudební soubor %2 nelze uložit: %s Cannot import lyrics Nelze zavést slova písně The file %1 cannot be imported: the lyrics cannot be parsed: %s Soubor %1 nelze zavést: Slova písně nelze zpracovat: %s Cannot open the music file Nelze otevřít soubor s hudbou Cannot open the music file. %1 Nelze otevřít soubor s hudbou. %1 Lyrics file found Soubor se slovy písně nenalezen It looks like there is a lyrics file %1 matching this music file. Do you want to import it as well? Vypadá to, že je tu soubor se slovy písně %1, jenž odpovídá tomuto souboru s hudbou. Chcete jej zavést také? Music file not selected Hudební soubor nevybrán You must select a music file to continue. Abyste mohl pokračovat, musíte vybrat hudební soubor. Title field is empty Pole s názvem je prázdné You must type song title to continue. Abyste mohl pokračovat, musíte napsat název písně. Artist field is empty Pole s umělcem je prázdné You must type song artist to continue. Abyste mohl pokračovat, musíte napsat umělce písně. Error loading file Chyba při nahrávání souboru Music file %1 cannot be loaded. Soubor s hudbou %1 nemůže být nahrán. WizardNewProject::Wizard New karaoke lyrics project Nový projekt se slovy písně pro karaoke