pax_global_header00006660000000000000000000000064137626654670014540gustar00rootroot0000000000000052 comment=76e60bab588aedd1dbc78029ee30ab5c7c4f6728 abcm2ps-8.14.11/000077500000000000000000000000001376266546700132435ustar00rootroot00000000000000abcm2ps-8.14.11/.gitignore000066400000000000000000000001521376266546700152310ustar00rootroot00000000000000# gitignore # important! !.gitignore !.travis.yml .* *.o *.gz *~ Out* abcm2ps abcm2ps.1 config.h Makefile abcm2ps-8.14.11/.travis.yml000066400000000000000000000042011376266546700153510ustar00rootroot00000000000000language: c sudo: false # addons: # apt: # sources: # - extras-precise # - lucid # - partner-precise # packages: # - pkg-config # - libpango-1.0-0 # - libcairo2 # - libcairo2-dev # - libpangocairo-1.0-0 # - libfreetype6 # - libfreetype6-dev # - libpangoft2-1.0-0 branches: only: - master - /^abcm2ps-\\d+\\.\\d+(\\.\\d+)?(-\\S*)?$/ notifications: slack: secure: RCTAGKXARo6MkEXigVVZp4Oc6B0AejbTYZp4YJ155DBp8Ygt42WuLlCxJZ60Ruzyjh5ymAOV1rsAKXPS+UJMWO7+pLJuqZEeWK08hGSL/DpIfa3IMYYSQYHCYDTpKiyqBC0ytEpd077Pv3b3ZVvZO2Tjo3F9806M5fTCFuwWgTxxt45302LiHCSOk4q/d8MT1cSK6k6ZvRarK0LG9cnudqcV6+Y8yDlJUz+ixxmSAg3j0jXwXisdyOFmI9hRc0DtNhkJHMORBigm0Cis5ipWRV9XH13MXgT3Vd61jG816mLUipht88PVcGzSfLUPcXlT0vMADy47WmVnUNqNosNyYXO9YHmNNj7wVIR79U7wrPXL6g3zXYBzs6JQwECPl1eGfnhZeOQ3drY+Pc+Sz+W8XT7q1SefzLhIws6vMM3i9UGQhOb74mMH5mrHaQTPaz6P4pOUQ9QaUOwK0ud6z6rC5uLD5GkQmdTuLpTiC2vvER46DxgzHghdqgeJXSwCkQEgBjTrs+ffc4kfeakFFW73B7x1sCz9katxHpzlajb03YYnPDhpODMazUx1AKmDHSJhsKv8mRm5L/7z24GIFs7m0xDmS4YMo1AqeLpAGu0zdhVE+0jN2s9QDo8pHrmikCdkhbaUKwXHZZY4d0ykK2rovROfBiZOld1TqbbaPOOBEck= install: ./configure && make clean && make && make test before_deploy: make dist && make zip-dist # deploy: # - provider: releases # api-key: # secure: hVfF6MJJuqH9TkOqqyLdPtfERv9YyozoLHX3YaQbUuLcsvAVF2GBZvDAFIyInV4VWqbtVu7faOpZyrOvd3TMHJMNzWGwBjWxAUJmM2gVugjpxAv6G+11ReeF8okqOIQGWdigZ9lb29utwrGb3R1hC2I6qcdlziw2JXMdl2GTBr6/zn0cFnAtfJqzT3REgog2g9HBorzNC+DqQXWI5skz6N4tWEvUfEwKERJkjyz3Lqen86MGHv+wEpqqCSmVbsSpiIkSwvbSm1PIsHhMg2zMbuJoqNsvlhz6Muyk6B5vRxXkJMstS5hAuIYAdWwu+3r42ZxK+TB3QWOO5fWRRBS7r2buvI63b+TvUDgCvHIr4cA1qMvyOCd5Xbv5HdRiZRqiJrUubfm6L1gXZ2Wo7KZVN7NVI9YJRqY9yZi59BZOXI6eDzb5AsjmpqSWBwXKhLUhCNwdqL52DPfL9pOjk9HY1i27BpoyL3X3NI0OJ+fUf6Q/utlv/wTiSbwHjiN+7dpnIlbAe09JQRXi7ApqtO0gV5tCBucBSydv6aAO3h5T0dRWFIB90bo6f1t0qeBO/YicP3qrM2OryhJNlZwFoJYPy2XDAzQ8Q5ubCKCp+C7U97vJwqGiXprKKDz9rGVuPWeIV/tLkcVn2TQt8hNyOqkcn4nv8x0j+ukw1p/Svtb3z54= # name: $TRAVIS_TAG # body: $TRAVIS_COMMIT_MESSAGE build $TRAVIS_BUILD_NUMBER # file_glob: true # file: abcm2ps-*.zip # skip_cleanup: true # on: # branch: release abcm2ps-8.14.11/COPYING000066400000000000000000001045131376266546700143020ustar00rootroot00000000000000 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 . abcm2ps-8.14.11/INSTALL000066400000000000000000000066001376266546700142760ustar00rootroot00000000000000 --- abcm2ps installation --- Getting the source files ======================== abcm2ps is now distributed from a GitHub repository. You may get the source files either as a `tar.gz` or `.zip` file from https://github.com/leesavide/abcm2ps.git or by cloning the repository with 'git' https://github.com/leesavide/abcm2ps.git (you may use `--depth=1` if you don't want the full `git` history) Unix(-like) systems =================== After getting the source files, or after updating them by 'git pull', you must generate the configuration files using the local script 'configure'. The configuration variables and their default values are: CC=gcc CFLAGS="-g -O2 -Wall -pipe" srcdir=. prefix=/usr/local INSTALL="/usr/bin/install -c" INSTALL_DATA='${INSTALL} -m 644' INSTALL_PROGRAM='${INSTALL}' exec_prefix='${prefix}' bindir='${exec_prefix}/bin' libdir='${exec_prefix}/lib' datarootdir='${prefix}/share' docdir='${prefix}/doc' mandir='${datarootdir$/man' DEFAULT_FDIR="$prefix/share/abcm2ps" The script 'configure' first tries to read the file 'custom' in which you may set your own values (Bourne shell syntax). For example, if you prefer to use 'clang' instead of 'gcc', the file 'custom' would contain the single line: CC=clang then, you do: ./configure The script 'configure' may also get the values of the variables from the command line. These settings must start with '--': ./configure --CC=clang If you want to use the 'pango' library, install the development files for pango and freetype2, as well as the pkg-config tool prior running 'configure'. In addition to the usual C build tools (mainly, gcc and make), you will require the rst2man tool from the python-docutils package. Creating the binary is done by a standard call to 'make'. An alternate option for this creation is to use 'ninja' (ninja-build) or 'samurai'. These programs use the file 'build.ninja' which may be custumized. Windows or pre-OS X Mac systems =============================== You must create the file 'config.h' from the 'config.h.in' skeleton. Then, the abcm2ps binary must be created by compiling all the '.c' files and by linking them together. The resulting binary file should run immediately from where it has been generated. You may then move it at any place you want. Note: I don't know how to do file mapping (mmap) in ms-windows, so, you must comment the line containing 'HAVE_MMAP' in config.h. Testing ======= To test the program, run it with one of the .abc files as the command line argument: abcm2ps sample The resulting file, 'Out.ps', may be displayed using a PostScript previewer such as ghostscript or zathura, or it may be sent directly to a PostScript printer, or indirectly to a simple printer using a postscript filter. OSX/macOS users can view PostScript natively with the system default Preview app. Windows users can use GSView. About the 'pango' library ========================= abcm2ps may use the 'pango' library to render texts with non latin characters on PostScript output. If you have no such texts or if you do only SVG/(X)HTML output, you don't need this library. In Unix(-like) systems, at configure time, the pango generation elements are searched by pkg-config in the gdk-2.0 library. If this library or pkg_config are not found, the rendering of non latin characters with pango will be disabled. Note also that, when pango is defined, it may be disabled at command line level by '--pango 0'. abcm2ps-8.14.11/Makefile.in000066400000000000000000000033551376266546700153160ustar00rootroot00000000000000# Makefile source for abcm2ps CC = @CC@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ CPPFLAGS = @CPPFLAGS@ CFLAGS = @CFLAGS@ LDFLAGS = @LDFLAGS@ LDLIBS = @LDLIBS@ prefix = @prefix@ exec_prefix = @exec_prefix@ srcdir = @srcdir@ VPATH = @srcdir@ bindir = $(DESTDIR)@bindir@ datadir = $(DESTDIR)@datarootdir@ docdir = $(DESTDIR)@docdir@ mandir = $(DESTDIR)@mandir@ build: abcm2ps abcm2ps.1 # unix OBJECTS=abcm2ps.o \ abcparse.o buffer.o deco.o draw.o format.o front.o glyph.o music.o parse.o \ subs.o svg.o syms.o abcm2ps: $(OBJECTS) $(OBJECTS): abcm2ps.h config.h Makefile abcm2ps.1: abcm2ps.rst if [ -x "$$(command -v rst2man)" ]; then\ rst2man $< $@;\ else\ cp $< $@;\ fi install: build mkdir -p $(bindir) $(INSTALL_PROGRAM) abcm2ps $(bindir) mkdir -p $(datadir)/abcm2ps $(INSTALL_DATA) $(srcdir)/abc2svg.ttf $(datadir)/abcm2ps $(INSTALL_DATA) $(srcdir)/*.fmt $(datadir)/abcm2ps mkdir -p $(docdir)/abcm2ps/examples $(INSTALL_DATA) $(srcdir)/README.md $(docdir)/abcm2ps $(INSTALL_DATA) $(srcdir)/*.abc $(docdir)/abcm2ps/examples $(INSTALL_DATA) $(srcdir)/*.eps $(docdir)/abcm2ps/examples $(INSTALL_DATA) $(srcdir)/*.html $(docdir)/abcm2ps/examples mkdir -p $(mandir)/man1 $(INSTALL_DATA) abcm2ps.1 $(mandir)/man1 uninstall: echo "uninstalling..." rm -f $(bindir)/abcm2ps rm -fr $(datadir)/abcm2ps rm -fr $(docdir)/abcm2ps rm -f $(mandir)/man1/abcm2ps.1 EXAMPLES = accordion.ps \ chinese.ps \ deco.ps \ newfeatures.ps \ sample.ps \ sample2.ps \ sample3.ps \ sample4.ps \ sample5.ps \ voices.ps test: $(EXAMPLES) %.ps: %.abc ./abcm2ps -O $@ $< mostlyclean: rm -f *.o $(EXAMPLES) clean: mostlyclean rm -f abcm2ps abcm2ps.1 distclean: clean rm -f config.h Makefile abcm2ps-8.14.11/README.md000066400000000000000000000036361376266546700145320ustar00rootroot00000000000000# abcm2ps [![Build Status](https://travis-ci.org/leesavide/abcm2ps.svg?branch=master)](https://travis-ci.org/leesavide/abcm2ps) ### Overview abcm2ps is a C program which converts music tunes from the ABC music notation to PostScript or SVG. Based on [abc2ps](https://github.com/methf/abc2ps), the Postscript generator for ABC music notation by Michael Methfessel, it was first developped to print barock organ scores that have independant voices played on one or many keyboards and a pedal-board (the 'm' of abcm2ps stands for many or multi staves/voices). Since this time, it has evolved so it can render many more music kinds. Note that this program is at end of life. Its successor is [abc2svg](https://chiselapp.com/user/moinejf/repository/abc2svg). ### Features The features of abcm2ps are based on the [ABC draft 2.2 (February 2013)](http://abcnotation.com/wiki/abc:standard:v2.2). The differences are listed in the [abcm2ps/abc2svg documentation](http://moinejf.free.fr/abcm2ps-doc/features.html). ### Installation and usage The installation procedure is described in the file INSTALL. To build the program with default settings run ``` ./configure make ``` Basically, the program usage is: abcm2ps [options] file1 [file1_options] file2 [file2_options] ... where file1, file2, .. are the ABC input files. This will generate a Postscript file (default name: `Out.ps`). Run `abcm2ps -h` to know the list of the command line options. ### Documentation - abcm2ps.rst describes all command-line options. When `abcm2ps` is installed, it may be read by `man abcm2ps`. - the features and format parameters are described in http://moinejf.free.fr/abcm2ps-doc/index.html ### Links Author's page: http://moinejf.free.fr/ To know more about the ABC music notation, have a look at http://abcnotation.com/ Guido Gonzato maintains many abcm2ps binaries and more documentation at http://abcplus.sourceforge.net/ abcm2ps-8.14.11/abc2svg.ttf000066400000000000000000000176141376266546700153220ustar00rootroot00000000000000 PFFTMy7+pOS/2XZXVcmapQxcvt "tgasphglyf|4>head&6hhea $hmtxXQFlocadnxfmaxpy8 name)!_}postº@hJe _< ї"IOr  \ 12@.3 PfEd@\33\v"UW^z  '  IJ0 2  n@X,,IS3 D D   2 HP\bi|dgmPU\GP\biz`glPU\ p/|q^>;*"***6f8B,r$R~(R&2>` j  H f "2./<2<2/<2<23!'3#"V"f1!pW 5654'&547&5476545#W?%MM%?W#C6`94f:2K"9aMafL`9%J2:f49`6HG"&462"&462463232654'&''&54762#"&547&#""&  ~  [,('A07#767'3&'5(UxxU(UxxUpk(kppk(kT`(``(b(?FZg632#"&546323254/#"&547>7>7.5467654&# .5467'!2>54&' 5g ?|B+EOQ`@2.B>0*e 9^&-0 oQ;[ynN 7G/23`Il/)Ip;E *&D{<" PMXA,@D-+2 f#d((!uH@yr_'@j^ @0-: Z:Fbrg!.R#1H)Owdh2654'&#"&'&'#3676732654'&#"+&54767632#"'632#"&547323#>J IF< ".." '%1=$R/12 3!DU'#^U7ZH-4?""?4Bf(7V]LrD!305M{{ xJ--lI b'++'b * {B16b( +2 D O4SNo KJ>,9CS4PN<2+&&1=746326767654'.#">32#"&"&5462'"&54632fK,Tu[$ 89(54&#"'.5467&'32#"&5463232654/#"&547>7>7&5467632>54'IMGVB ,9 *L; qUm"z6S3(%51'"#, .L#'Y@/Ia\92c6d7^)_u[O36V  4')&A)8N>ZZySq~ @H1$37$"( 35 jQP ! n3b[mLn$C6bw0V"9$%{3F&&cBcg2654'&#"&'&'#3676732654'&#"+&54767632#"'632#"&547323#~2; :80 %%  '1C%'( *6D#IF-H:%+14(5R -FI=[6* &+bb`;$$W; R"j3t"R"b5+(N  "( 5 @+A>Z <;2#.5A+@?0(#  6&/;746326767654'.#">32#"&"&462'"&54632pR<#xCi^G -- 0R %9 %,9"!M^W3Zl;W3dJ/)f3B6 <&(Su$" 0%0654&#32767"'&'4&5472#"&5>3/<2A'!0+(* UVO;;$@"$ ) "ek3*"XUP99f+'$%9.& ^8?%0654&#"32767##5&'&'4&547532632#"&5>3/< +(* QU#=.;#$@"$ ) z=' "K"XTQhk -9f+if'$%9.&.3 L%%3#3#732764'.'0#"#"&5462(('' U. 3 V. 6AygfzyΪ^  "G#  "G&xED8?G$7!5%3!53#5!#F22H22732764'.'0#"#"&5462o U. 3 V. 6Aygfzy?  "G#  "G&xED8?GlH %&#"326547#"'&54632$ %< &<~HO$~HODa+ a+ CgCCgtJ467632#"&]H3C1A!>T82u0903xG d2"&462G**2**2d  7"67654&72#36*,. "9OR:##zB?6&&2#EPRL4z 757#57e--I43C u775#5575575375377#5Rff9999f<<<<fM%UWSS z5&'0#537'0#537053#03( 3H9339H!4H9!49|;& 3;J22H9"39G"4Hjl-<7276767654'H#362767654'H#36N   + +5/ !  * (7}&+4 &9! 94@F2&./0&6$>/%F2?7#"&54?6/&546321   IO k 51#'AXZ5XJ14632#."!"&4632y9kU3 !%%##,QMnn$0$$#53# 53#,1!!,},}5!!,}}~&#"&54632'7)gl24&8x4%""d.$54KM#- 7#"&546226762'+7&8)"3!0<)( !,o H$#"&546322?#"&546322762'(!+7')A <6+7')H.- (( " )( Mn  67#"&546322?"#"&546322762'#"&5463227(+7')? : +7')H.-U(!+7')A < (( " )( Mn $ (( " E#"&546322?#"&546322?#"&546322?"#"&54622762'(!+7')A :(!+7')A :(+7')? : +7&8)H.-< (( " (( " (( " )( Mn  V%#"&546322?#"&546322?"#"&546322762'#"&546322?#"&5463227?(!+7')A 6(+7')? 4 +7')H.j-U(!+7')A :(!+7')A 4 (( " (( " )( M $ (( " (( "R 53!53#5!# }}4}}&6"&462"&4623P2##2#2##2#~GK#2##2#2##2Z 6"&462"&462333A2##2#w2##2#{Gy{GK#2##2#2##2Z  /%2654'#"5>32#"/&#"632"'&54763v -)$ ( ("%)' B&3$('  "-3$ E 7'777''"exjMiti.|||T||| E%''775377'#@iKeFjOiFF|N|{J|U| "&4632%&5476323265467.=#"'#"&'"&'&#"#"5467> 7654&54?327#"&54>32#"&'732654&'32>7>54'&#"654&'  # N#   *,2C] ^QBA#$<"A0&  -W, :!  B4Zr 5)60=qH~{B%9 4!,\_&F&;  ,5"/+) -#ID X  0/ 9+"p"IYC>  %4&#"326'#"&4632654'&#"&46323254'.54632327>7632#"32632#"&#"#"'.'&#"#"&4654#"#"&5476327654& *%   '#   !+' 8   * &   # " %*% ( >  '#*8# '(&    90$1$=   7!5%3!53#!#F22r2 ( r    @0   abc2svgabc2svgRegularRegularFontForge : abc2svg : 31-12-2016FontForge : abc2svg : 31-12-2016abc2svgabc2svgVersion 1.0Version 1.0abc2svgabc2svg2      !"#$%&'()*+,-./0.nodefuniE000uniE047uniE048uniE050uniE05CuniE062uniE069uniE07AuniE07BuniE07CuniE08AuniE08BuniE0A0uniE0A1uniE0A2uniE0A3uniE0A4uniE101uniE1E7uniE260uniE261uniE262uniE263uniE264uniE4A0uniE4ACuniE4C0uniE4E1uniE4E2uniE4E3uniE4E4uniE4E5uniE4E6uniE4E7uniE4E8uniE4E9uniE4EAuniE4EEuniE500uniE501uniE567uniE56CuniE56DuniE650uniE655uniE95CW-ї"IOrabcm2ps-8.14.11/abcm2ps.c000066400000000000000000000600641376266546700147440ustar00rootroot00000000000000/* * abcm2ps: a program to typeset tunes written in ABC format * using PostScript or SVG * * Copyright (C) 1998-2019 Jean-François Moine (http://moinejf.free.fr) * * Adapted from abc2ps * Copyright (C) 1996-1998 Michael Methfessel (https://github.com/methf/abc2ps/) * * abcm2ps is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. */ #include #include #include #include #include #include "abcm2ps.h" #ifdef HAVE_MMAP #include #include #elif defined(linux) #include #endif /* -- global variables -- */ INFO info; struct SYMBOL *sym; /* (points to the symbols of the current voice) */ int tunenum; /* number of current tune */ int pagenum = 1; /* current page in output file */ int pagenum_nr = 1; /* current page (non-resettable) */ /* switches modified by command line flags: */ int quiet; /* quiet mode */ int secure; /* secure mode */ int annotate; /* output source references */ int pagenumbers; /* write page numbers */ int epsf; /* 1: EPSF, 2: SVG, 3: embedded ABC */ int svg; /* 1: SVG, 2: XHTML */ int showerror; /* show the errors */ int pipeformat = 0; /* format for bagpipes regardless of key */ char outfn[FILENAME_MAX]; /* output file name */ int file_initialized; /* for output file */ FILE *fout; /* output file */ char *in_fname; /* top level input file name */ time_t mtime; /* last modification time of the input file */ static time_t fmtime; /* " " of all files */ int s_argc; /* command line arguments */ char **s_argv; struct tblt_s *tblts[MAXTBLT]; struct cmdtblt_s cmdtblts[MAXCMDTBLT]; int ncmdtblt; /* -- local variables -- */ static char *styd = DEFAULT_FDIR; /* format search directory */ static int def_fmt_done = 0; /* default format read */ static struct SYMBOL notitle; /* memory arena (for clrarena, lvlarena & getarena) */ #define MAXAREAL 3 /* max area levels: * 0; global, 1: tune, 2: generation */ #define AREANASZ 0x4000 /* standard allocation size */ #define MAXAREANASZ 0x20000 /* biggest allocation size */ static int str_level; /* current arena level */ static struct str_a { struct str_a *n; /* next area */ char *p; /* pointer in area */ int r; /* remaining space in area */ int sz; /* size of str[] */ char str[2]; /* start of memory area */ } *str_r[MAXAREAL], *str_c[MAXAREAL]; /* root and current area pointers */ /* -- local functions -- */ static void read_def_format(void); static FILE *open_ext(char *fn, char *ext) { FILE *fp; char *p; if ((fp = fopen(fn, "rb")) != NULL) return fp; if ((p = strrchr(fn, DIRSEP)) == NULL) p = fn; if (strrchr(p, '.') != NULL) return NULL; strcat(p, "."); strcat(p, ext); if ((fp = fopen(fn, "rb")) != NULL) return fp; return NULL; } /* -- open a file for reading -- */ FILE *open_file(char *fn, /* file name */ char *ext, /* file type */ char *rfn) /* returned real file name */ { FILE *fp; char *p; int l; /* if there was some ABC file, try its directory */ if (in_fname && in_fname != fn && (p = strrchr(in_fname, DIRSEP)) != NULL) { l = p - in_fname + 1; strncpy(rfn, in_fname, l); strcpy(&rfn[l], fn); if ((fp = open_ext(rfn, ext)) != NULL) return fp; } /* try locally */ strcpy(rfn, fn); if ((fp = open_ext(rfn, ext)) != NULL) return fp; /* try a format in the format directory */ if (*ext != 'f' || *styd == '\0') return NULL; l = strlen(styd) - 1; if (styd[l] == DIRSEP) sprintf(rfn, "%s%s", styd, fn); else sprintf(rfn, "%s%c%s", styd, DIRSEP, fn); return open_ext(rfn, ext); } /* -- read a whole input file -- */ /* return the real/full file name in tex_buf[] */ static char *read_file(char *fn, char *ext) { size_t fsize; FILE *fin; char *file; if (*fn == '\0') { strcpy(tex_buf, "stdin"); fsize = 0; file = malloc(8192); for (;;) { int l; l = fread(&file[fsize], 1, 8192, stdin); fsize += l; if (l != 8192) break; file = realloc(file, fsize + 8192); } if (ferror(stdin) != 0) { free(file); return 0; } if (fsize % 8192 == 0) file = realloc(file, fsize + 2); time(&fmtime); } else { struct stat sbuf; fin = open_file(fn, ext, tex_buf); if (!fin) return NULL; if (fseek(fin, 0L, SEEK_END) < 0) { fclose(fin); return NULL; } fsize = ftell(fin); rewind(fin); if ((file = malloc(fsize + 2)) == NULL) { fclose(fin); return NULL; } if (fread(file, 1, fsize, fin) != fsize) { fclose(fin); free(file); return NULL; } fstat(fileno(fin), &sbuf); memcpy(&fmtime, &sbuf.st_mtime, sizeof fmtime); fclose(fin); } file[fsize] = '\0'; return file; } /* -- treat an input file and generate the ABC file -- */ static void treat_file(char *fn, char *ext) { char *file; char *abc_fn; int file_type, l; /* initialize if not already done */ if (!fout) { read_def_format(); if (strcmp(fn, tex_buf) == 0) return; // if xx.default.fmt, done } /* read the file into memory */ /* the real/full file name is put in tex_buf[] */ if ((file = read_file(fn, ext)) == NULL) { if (strcmp(fn, "default.fmt") != 0) { error(1, NULL, "Cannot read the input file '%s'", fn); #if defined(unix) || defined(__unix__) perror(" read_file"); #endif } return; } abc_fn = strdup(tex_buf); if (!quiet) fprintf(strcmp(outfn, "-") == 0 ? stderr : stdout, "File %s\n", abc_fn); /* convert the strings */ l = strlen(abc_fn); if (strcmp(&abc_fn[l - 3], ".ps") == 0) { file_type = FE_PS; } else if (strcmp(&abc_fn[l - 4], ".fmt") == 0) { file_type = FE_FMT; } else { file_type = FE_ABC; // in_fname = abc_fn; mtime = fmtime; } frontend((unsigned char *) file, file_type, abc_fn, 0); free(file); if (file_type == FE_PS) /* PostScript file */ frontend((unsigned char *) "%%endps", FE_ABC, abc_fn, 0); if (file_type == FE_ABC) /* if ABC file */ clrarena(1); /* clear previous tunes */ } /* call back to handle %%format/%%abc-include - see front.c */ void include_file(unsigned char *fn) { static int nbfiles; if (nbfiles > 2) { error(1, NULL, "Too many included files"); return; } nbfiles++; treat_file((char *) fn, "fmt"); nbfiles--; } /* -- treat an ABC input file and generate the music -- */ /* this function also treats ABC in XHTML */ static void treat_abc_file(char *fn) { FILE *fin; char *file, *file_tmp; char *abc_fn, *p, *q; size_t fsize, l, l2; int linenum; #ifdef HAVE_MMAP int fd; #endif lvlarena(0); parse.abc_state = ABC_S_GLOBAL; if (epsf != 3) { treat_file(fn, "abc"); /* not '-z' */ return; } if (*fn == '\0') { error(1, NULL, "cannot use stdin with -z - aborting"); exit(EXIT_FAILURE); } fin = open_file(fn, "abc", tex_buf); if (!fin) goto err; if (fseek(fin, 0L, SEEK_END) < 0) { fclose(fin); goto err; } fsize = ftell(fin); rewind(fin); #ifdef HAVE_MMAP fd = fileno(fin); file = mmap(NULL, fsize, PROT_READ, MAP_PRIVATE, fd, 0); if (!file) goto err; #else file = malloc(fsize); if (!file) goto err; if (fread(file, 1, fsize, fin) != fsize) { free(file); goto err; } fclose(fin); #endif /* copy the HTML/XML/XHTML file and generate the music */ abc_fn = strdup(tex_buf); l = fsize; p = file; linenum = 0; while (l > 0) { /* search the start of ABC lines */ #if 1 for (q = p, l2 = l - 10; l2 > 0; l2--, q++) { if (strncmp(q, "\n%abc", 5) == 0 // || strncmp(q, "\n%%", 3) == 0 || strncmp(q, "\nX:", 3) == 0) break; } #else for (q = p, l2 = l - 5; l2 > 0; l2--, q++) if (strncmp(q, "", 5) == 0) break; #endif if (l2 <= 0) { fwrite(p, 1, l, fout); break; } q++; fwrite(p, 1, q - p, fout); l -= q - p; while (p != q) { if (*p++ == '\n') linenum++; } /* search the end of ABC lines */ for (q = p, l2 = l - 10; l2 > 0; l2--, q++) if (*q == '\n' && q[1] == '<') break; if (l2 <= 0) { error(1, NULL, "no end of ABC sequence"); q += 9; // break; } q++; /* must copy ... :( */ l2 = q - p; file_tmp = malloc(l2 + 1); if (!file_tmp) { error(1, NULL, "out of memory"); break; } memcpy(file_tmp, p, l2); file_tmp[l2] = '\0'; frontend((unsigned char *) file_tmp, FE_ABC, abc_fn, linenum); free(file_tmp); clrarena(1); /* clear previous tunes */ file_initialized = -1; /* don't put
before first image */ l -= q - p; while (p != q) { if (*p++ == '\n') linenum++; } } #ifdef HAVE_MMAP munmap(file, fsize); fclose(fin); #else free(file); #endif return; err: error(1, NULL, "input file %s error %s - aborting", fn, strerror(errno)); exit(EXIT_FAILURE); } /* -- read the default format -- */ static void read_def_format(void) { if (def_fmt_done) return; def_fmt_done = 1; treat_file("default.fmt", "fmt"); } /* -- set extension on a file name -- */ void strext(char *fn, char *ext) { char *p, *q; if ((p = strrchr(fn, DIRSEP)) == NULL) p = fn; if ((q = strrchr(p, '.')) == NULL) strcat(p, "."); else q[1] = '\0'; strcat(p, ext); } /* -- write the program version -- */ static void display_version(int full) { FILE *log = strcmp(outfn, "-") == 0 ? stderr : stdout; fputs("abcm2ps-" VERSION " (" VDATE ")\n", log); if (!full) return; fputs("Options:" #ifdef A4_FORMAT " A4_FORMAT" #endif #ifdef DECO_IS_ROLL " DECO_IS_ROLL" #endif #ifdef HAVE_PANGO " PANGO" #endif #if !defined(A4_FORMAT) && !defined(DECO_IS_ROLL) && !defined(HAVE_PANGO) " NONE" #endif "\n", log); if (styd[0] != '\0') fprintf(log, "Default format directory: %s\n", styd); } /* -- display usage and exit -- */ static void usage(void) { display_version(0); printf( "ABC to Postscript/SVG translator.\n" "Usage: abcm2ps [options] file [file_options] ..\n" "where:\n" " file input ABC file, or '-'\n" " options and file_options:\n" " .output file options:\n" " -E produce EPSF output, one tune per file\n" " -g produce SVG output, one tune per file\n" " -v produce SVG output, one page per file\n" " -X produce SVG output in one XHTML file\n" " -z produce SVG output from embedded ABC\n" " -O fff set outfile name to fff\n" " -O = make outfile name from infile/title\n" " -i indicate where are the errors\n" " -k kk size of the PS output buffer in Kibytes\n" " .output formatting:\n" " -s xx set scale factor to xx\n" " -w xx set staff width (cm/in/pt)\n" " -m xx set left margin (cm/in/pt)\n" " -d xx set staff separation (cm/in/pt)\n" " -a xx set max shrinkage to xx (between 0 and 1)\n" " -F foo read format file \"foo.fmt\"\n" " -D bar look for format files in directory \"bar\"\n" " -p format for bagpipes regardless of key\n" " .output options:\n" " -l landscape mode\n" " -I xx indent 1st line (cm/in/pt)\n" " -x add xref numbers in titles\n" " -M don't output the lyrics\n" " -N n set page numbering mode to n=\n" " 0=off 1=left 2=right 3=even left,odd right 4=even right,odd left\n" " -1 write one tune per page\n" " -G no slur in grace notes\n" " -j n[b] number the measures every n bars (or on the left if n=0)\n" " if 'b', display in a box\n" " -b n set the first measure number to n\n" " -f have flat beams\n" " -T n[v] output the tablature 'n' for voice 'v' / all voices\n" " .line breaks:\n" " -c auto line break\n" " -B n break every n bars\n" " .input file selection/options:\n" " -e pattern\n" " tune selection\n" " .help/configuration:\n" " -V show program version\n" " -h show this command summary\n" " -H show the format parameters\n" " -S secure mode\n" " -q quiet mode\n"); exit(EXIT_SUCCESS); } #ifdef linux /* -- where is the default format directory -- */ static void wherefmtdir(void) { char exe[512], *p; FILE *f; int l; if ((l = readlink("/proc/self/exe", exe, sizeof exe)) <= 0) return; if ((p = strrchr(exe, '/')) == NULL) return; p++; if (p > &exe[5] && strncmp(p - 5, "/bin", 4) == 0) { strcpy(p - 4, "share/abcm2ps/"); p += -4 + 14; } /* else, assume this is the source directory */ /* check if a format file is present */ strcpy(p, "tight.fmt"); if ((f = fopen(exe, "r")) == NULL) return; fclose(f); /* change the format directory */ p[-1] = '\0'; styd = strdup(exe); } #endif /* -- parse the tablature command ('-T n[v]') -- */ static struct cmdtblt_s *cmdtblt_parse(char *p) { struct cmdtblt_s *cmdtblt; short val; if (ncmdtblt >= MAXCMDTBLT) { error(1, NULL, "++++ Too many '-T'"); return NULL; } if (*p == '\0') val = -1; else { val = *p++ - '0' - 1; if ((unsigned) val > MAXTBLT) { error(1, NULL, "++++ Bad tablature number in '-T'\n"); return 0; } } cmdtblt = &cmdtblts[ncmdtblt++]; cmdtblt->index = val; cmdtblt->vn = p; return cmdtblt; } /* set a command line option */ static void set_opt(char *w, char *v) { static char prefix = '%'; /* pseudo-comment prefix */ if (!v) v = ""; if (strlen(w) + strlen(v) >= TEX_BUF_SZ - 10) { error(1, NULL, "Command line '%s' option too long", w); return; } sprintf(tex_buf, /* this buffer is available */ "%%%c%s %s lock\n", prefix, w, v); if (strcmp(w, "abcm2ps") == 0) prefix = *v; frontend((unsigned char *) tex_buf, FE_ABC, "cmd_line", 0); } /* -- main program -- */ int main(int argc, char **argv) { unsigned j; char *p, c, *aaa; if (argc <= 1) usage(); outfn[0] = '\0'; init_outbuf(64); /* set the global flags */ s_argc = argc; s_argv = argv; aaa = NULL; while (--argc > 0) { argv++; p = *argv; if (*p != '-' || p[1] == '-') { if (*p == '+' && p[1] == 'F') /* +F : no default format */ def_fmt_done = 1; continue; } while ((c = *++p) != '\0') { /* '-xxx' */ switch (c) { case 'E': svg = 0; /* EPS */ epsf = 1; break; case 'g': svg = 0; /* SVG one file per tune */ epsf = 2; break; case 'H': quiet = 1; // don't output the version break; case 'h': usage(); /* no return */ case 'p': pipeformat = 1; /* format for bagpipe regardless of key */ break; case 'q': quiet = 1; break; case 'S': secure = 1; break; case 'V': display_version(1); return EXIT_SUCCESS; case 'v': svg = 1; /* SVG one file per page */ epsf = 0; break; case 'X': svg = 2; /* SVG/XHTML */ epsf = 0; break; case 'k': { int kbsz; if (p[1] == '\0') { if (--argc <= 0) { error(1, NULL, "No value for '-k' - aborting"); return EXIT_FAILURE; } aaa = *++argv; } else { aaa = p + 1; p += strlen(p) - 1; } sscanf(aaa, "%d", &kbsz); init_outbuf(kbsz); break; } case 'O': if (p[1] == '\0') { if (--argc <= 0) { error(1, NULL, "No value for '-O' - aborting"); return EXIT_FAILURE; } aaa = *++argv; } else { aaa = p + 1; p += strlen(p) - 1; } if (strlen(aaa) >= sizeof outfn) { error(1, NULL, "'-O' too large - aborting"); return EXIT_FAILURE; } strcpy(outfn, aaa); break; case 'z': epsf = 3; /* ABC embedded in XML */ svg = 0; break; default: if (strchr("aBbDdeFfIjmNOsTw", c)) /* if with arg */ p += strlen(p) - 1; /* skip */ break; } } } if (!quiet) display_version(0); /* initialize */ clrarena(0); /* global */ clrarena(1); /* tunes */ clrarena(2); /* generation */ // memset(&info, 0, sizeof info); info['T' - 'A'] = ¬itle; notitle.text = "T:"; set_format(); init_deco(); #ifdef linux /* if not set, try to find where is the default format directory */ if (styd[0] == '\0') wherefmtdir(); #endif #ifdef HAVE_PANGO pg_init(); #endif /* if ABC embedded in XML, open the output file */ if (epsf == 3) { open_fout(); read_def_format(); } /* parse the arguments - finding a new file, treat the previous one */ argc = s_argc; argv = s_argv; while (--argc > 0) { argv++; p = *argv; if ((c = *p) == '\0') continue; if (c == '-') { int i; if (p[1] == '\0') { /* '-' alone */ if (in_fname) { treat_abc_file(in_fname); frontend((unsigned char *) "select\n", FE_FMT, "cmd_line", 0); } in_fname = ""; /* read from stdin */ continue; } i = strlen(p) - 1; if (p[i] == '-' && p[1] != '-' //fixme: 'e' may be preceded by other options && p[1] != 'e' && p[i -1] != 'O') c = '+'; /* switch off flags with '-x-' */ } if (c == '+') { /* switch off flags with '+' */ while (*++p != '\0') { switch (*p) { case '-': break; case 'B': cfmt.barsperstaff = 0; lock_fmt(&cfmt.barsperstaff); break; case 'c': cfmt.continueall = 0; lock_fmt(&cfmt.continueall); break; case 'F': // def_fmt_done = 1; break; case 'G': cfmt.graceslurs = 1; lock_fmt(&cfmt.graceslurs); break; case 'i': showerror = 0; break; case 'j': cfmt.measurenb = -1; lock_fmt(&cfmt.measurenb); break; case 'l': cfmt.landscape = 0; lock_fmt(&cfmt.landscape); break; case 'M': cfmt.fields[1] = 1 << ('w' - 'a'); lock_fmt(&cfmt.fields); break; case 'N': pagenumbers = 0; break; case 'O': outfn[0] = '\0'; break; case 'T': { struct cmdtblt_s *cmdtblt; aaa = p + 1; if (*aaa == '\0') { if (argc > 1 && argv[1][0] != '-') { aaa = *++argv; argc--; } } else { while (p[1] != '\0') /* stop */ p++; if (*p == '-') *p-- = '\0'; /* (not clean) */ } cmdtblt = cmdtblt_parse(aaa); if (cmdtblt != 0) cmdtblt->active = 0; break; } case 'x': cfmt.fields[0] &= ~(1 << ('X' - 'A')); lock_fmt(&cfmt.fields); break; case '0': cfmt.splittune = 0; lock_fmt(&cfmt.splittune); break; case '1': cfmt.oneperpage = 0; lock_fmt(&cfmt.oneperpage); break; default: error(1, NULL, "++++ Cannot switch off flag: +%c", *p); break; } } continue; } if (c == '-') { /* interpret a flag with '-' */ if (p[1] == '-') { /* long argument */ p += 2; if (--argc <= 0) { error(1, NULL, "No argument for '--'"); return EXIT_FAILURE; } argv++; set_opt(p, *argv); continue; } while ((c = *++p) != '\0') { switch (c) { /* simple flags */ case 'A': annotate = 1; break; case 'c': cfmt.continueall = 1; lock_fmt(&cfmt.continueall); break; case 'E': break; case 'f': cfmt.flatbeams = 1; lock_fmt(&cfmt.flatbeams); break; case 'G': cfmt.graceslurs = 0; lock_fmt(&cfmt.graceslurs); break; case 'g': break; case 'H': if (!fout) { read_def_format(); make_font_list(); do_tune(); } print_format(); return EXIT_SUCCESS; case 'i': showerror = 1; break; case 'l': cfmt.landscape = 1; lock_fmt(&cfmt.landscape); break; case 'M': cfmt.fields[1] &= ~(1 << ('w' - 'a')); lock_fmt(&cfmt.fields); break; case 'p': case 'q': case 'S': break; case 'v': case 'X': case 'z': break; case 'x': cfmt.fields[0] |= 1 << ('X' - 'A'); lock_fmt(&cfmt.fields); break; case '0': cfmt.splittune = 1; lock_fmt(&cfmt.splittune); break; case '1': cfmt.oneperpage = 1; lock_fmt(&cfmt.oneperpage); break; /* flag with optional parameter */ case 'N': if (p[1] == '\0' && (argc <= 1 || !isdigit((unsigned) argv[1][0]))) { pagenumbers = 2; /* old behaviour */ break; } /* fall thru */ /* flags with parameter.. */ case 'a': case 'B': case 'b': case 'D': case 'd': case 'e': case 'F': case 'I': case 'j': case 'k': case 'L': case 'm': case 'O': case 's': case 'T': case 'w': aaa = p + 1; if (*aaa == '\0') { aaa = *++argv; if (--argc <= 0 || (*aaa == '-' && c != 'O')) { error(1, NULL, "Missing parameter after '-%c' - aborting", c); return EXIT_FAILURE; } } else { p += strlen(p) - 1; /* stop */ } if (strchr("BbfjkNs", c)) { /* check num args */ for (j = 0; j < strlen(aaa); j++) { if (!strchr("0123456789.", aaa[j])) { if (aaa[j] == 'b' && aaa[j + 1] == '\0' && c == 'j') break; error(1, NULL, "Invalid parameter <%s> for flag -%c", aaa, c); return EXIT_FAILURE; } } } switch (c) { case 'a': set_opt("maxshrink", aaa); break; case 'B': set_opt("barsperstaff", aaa); break; case 'b': set_opt("measurefirst", aaa); break; case 'D': styd = aaa; break; case 'd': set_opt("staffsep", aaa); break; case 'e': set_opt("select", aaa); break; case 'F': treat_file(aaa, "fmt"); break; case 'I': set_opt("indent", aaa); break; case 'j': sscanf(aaa, "%d", &cfmt.measurenb); lock_fmt(&cfmt.measurenb); if (aaa[strlen(aaa) - 1] == 'b') cfmt.measurebox = 1; else cfmt.measurebox = 0; lock_fmt(&cfmt.measurebox); break; case 'k': break; case 'm': set_opt("leftmargin", aaa); break; case 'N': sscanf(aaa, "%d", &pagenumbers); if ((unsigned) pagenumbers > 4) { error(1, NULL, "'-N' value %s - changed to 2", aaa); pagenumbers = 2; } break; case 'O': if (strlen(aaa) >= sizeof outfn) { error(1, NULL, "'-O' too large - aborting"); exit(EXIT_FAILURE); } strcpy(outfn, aaa); break; case 's': set_opt("scale", aaa); break; case 'T': { struct cmdtblt_s *cmdtblt; cmdtblt = cmdtblt_parse(aaa); if (cmdtblt) cmdtblt->active = 1; break; } case 'w': set_opt("staffwidth", aaa); break; } break; default: error(1, NULL, "Unknown flag: -%c ignored", c); break; } } continue; } if (in_fname) { treat_abc_file(in_fname); frontend((unsigned char *) "select\n", FE_FMT, "cmd_line", 0); } in_fname = p; } if (in_fname) treat_abc_file(in_fname); if (multicol_start != 0) { /* lack of %%multicol end */ error(1, NULL, "Lack of %%%%multicol end"); multicol_start = 0; buffer_eob(0); if (!info['X' - 'A'] && !epsf) write_buffer(); } if (!epsf && !fout) { error(1, NULL, "Nothing to generate!"); return EXIT_FAILURE; } close_output_file(); return severity == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } /* -- arena routines -- */ void clrarena(int level) { struct str_a *a_p; if ((a_p = str_r[level]) == NULL) { str_r[level] = a_p = malloc(sizeof *str_r[0] + AREANASZ - 2); a_p->sz = AREANASZ; a_p->n = NULL; } str_c[level] = a_p; a_p->p = a_p->str; a_p->r = a_p->sz; } int lvlarena(int level) { int old_level; old_level = str_level; str_level = level; return old_level; } /* The area is 8 bytes aligned to handle correctly int and pointers access * on some machines as Sun Sparc. */ void *getarena(int len) { char *p; struct str_a *a_p; a_p = str_c[str_level]; len = (len + 7) & ~7; /* align at 64 bits boundary */ if (len > a_p->r) { if (len > MAXAREANASZ) { error(1, NULL, "getarena - data too wide %d - aborting", len); exit(EXIT_FAILURE); } if (len > AREANASZ) { /* big allocation */ struct str_a *a_n; a_n = a_p->n; a_p->n = malloc(sizeof *str_r[0] + len - 2); a_p->n->n = a_n; a_p->n->sz = len; } else if (a_p->n == 0) { /* standard allocation */ a_p->n = malloc(sizeof *str_r[0] + AREANASZ - 2); a_p->n->n = NULL; a_p->n->sz = AREANASZ; } str_c[str_level] = a_p = a_p->n; a_p->p = a_p->str; a_p->r = a_p->sz; } p = a_p->p; a_p->p += len; a_p->r -= len; return p; } abcm2ps-8.14.11/abcm2ps.h000066400000000000000000000724261376266546700147560ustar00rootroot00000000000000/* -- general macros -- */ #include #include #include "config.h" #define MAXVOICE 32 /* max number of voices */ #define MAXHD 8 /* max heads in a chord */ #define MAXDC 32 /* max decorations per symbol */ #define MAXMICRO 32 /* max microtone values (5 bits in accs[]) */ #define DC_NAME_SZ 128 /* size of the decoration name table */ #define BASE_LEN 1536 /* basic note length (semibreve or whole note - same as MIDI) */ #define VOICE_ID_SZ 16 /* max size of the voice identifiers */ /* accidentals */ enum accidentals { A_NULL, /* none */ A_SH, /* sharp */ A_NT, /* natural */ A_FT, /* flat */ A_DS, /* double sharp */ A_DF /* double flat */ }; /* bar types - 4 bits per symbol */ #define B_BAR 1 /* | */ #define B_OBRA 2 /* [ */ #define B_CBRA 3 /* ] */ #define B_COL 4 /* : */ #define B_SINGLE 0x01 /* | single bar */ #define B_DOUBLE 0x11 /* || thin double bar */ #define B_THIN_THICK 0x13 /* |] thick at section end */ #define B_THICK_THIN 0x21 /* [| thick at section start */ #define B_LREP 0x14 /* |: left repeat bar */ #define B_RREP 0x41 /* :| right repeat bar */ #define B_DREP 0x44 /* :: double repeat bar */ #define B_DASH 0x04 /* : dashed bar */ /* slur/tie types (4 bits) */ #define SL_ABOVE 0x01 #define SL_BELOW 0x02 #define SL_HIDDEN 0x03 /* also opposite for gstemdir */ #define SL_AUTO 0x04 #define SL_DOTTED 0x08 /* (modifier bit) */ #define OUTPUTFILE "Out.ps" /* standard output file */ #ifndef WIN32 #define DIRSEP '/' #else #define DIRSEP '\\' #define strcasecmp stricmp #define strncasecmp _strnicmp #define strdup _strdup #define snprintf _snprintf #ifdef _MSC_VER #define fileno _fileno #endif #endif #define CM * 37.8 /* factor to transform cm to pt */ #define PT /* factor to transform pt to pt */ #define IN * 96.0 /* factor to transform inch to pt */ /* basic page dimensions */ #ifdef A4_FORMAT #define PAGEHEIGHT (29.7 CM) #define PAGEWIDTH (21.0 CM) #define MARGIN (1.8 CM) #else #define PAGEHEIGHT (11.0 IN) #define PAGEWIDTH (8.5 IN) #define MARGIN (0.7 IN) #endif /* -- macros controlling music typesetting -- */ #define STEM_YOFF 1.0 /* offset stem from note center */ #define STEM_XOFF 3.5 #define STEM 21 /* default stem height = one octave */ #define STEM_MIN 16 /* min stem height under beams */ #define STEM_MIN2 14 /* ... for notes with two beams */ #define STEM_MIN3 12 /* ... for notes with three beams */ #define STEM_MIN4 10 /* ... for notes with four beams */ #define STEM_CH_MIN 14 /* min stem height for chords under beams */ #define STEM_CH_MIN2 10 /* ... for notes with two beams */ #define STEM_CH_MIN3 9 /* ... for notes with three beams */ #define STEM_CH_MIN4 9 /* ... for notes with four beams */ #define BEAM_DEPTH 3.2 /* width of a beam stroke */ #define BEAM_OFFSET 0.25 /* pos of flat beam relative to staff line */ #define BEAM_SHIFT 5.0 /* shift of second and third beams */ /* To align the 4th beam as the 1st: shift=6-(depth-2*offset)/3 */ #define BEAM_FLATFAC 0.6 /* factor to decrease slope of long beams */ #define BEAM_THRESH 0.06 /* flat beam if slope below this threshold */ #define BEAM_SLOPE 0.5 /* max slope of a beam */ #define BEAM_STUB 7.0 /* length of stub for flag under beam */ #define SLUR_SLOPE 1.0 /* max slope of a slur */ #define GSTEM 15 /* grace note stem length */ #define GSTEM_XOFF 2.3 /* x offset for grace note stem */ #define BETA_C 0.1 /* max expansion for flag -c */ #define BETA_X 1.0 /* max expansion before complaining */ #define VOCPRE 0.4 /* portion of vocals word before note */ #define GCHPRE 0.4 /* portion of guitar chord before note */ /* -- Parameters for note spacing -- */ /* fnn multiplies the spacing under a beam, to compress the notes a bit */ #define fnnp 0.9 /* -- macros for program internals -- */ #define STRL1 256 /* string length for file names */ #define MAXSTAFF 32 /* max staves */ #define BSIZE 512 /* buffer size for one input string */ #define BREVE (BASE_LEN * 2) /* double note (square note) */ #define SEMIBREVE BASE_LEN /* whole note */ #define MINIM (BASE_LEN / 2) /* half note (white note) */ #define CROTCHET (BASE_LEN / 4) /* quarter note (black note) */ #define QUAVER (BASE_LEN / 8) /* 1/8 note */ #define SEMIQUAVER (BASE_LEN / 16) /* 1/16 note */ #define MAXFONTS 30 /* max number of fonts */ #define T_LEFT 0 #define T_JUSTIFY 1 #define T_FILL 2 #define T_CENTER 3 #define T_SKIP 4 #define T_RIGHT 5 #define YSTEP 128 /* number of steps for y offsets */ struct decos { /* decorations */ char n; /* whole number of decorations */ struct { unsigned char t; /* decoration index */ signed char m; /* index in chord when note / -1 */ } tm[MAXDC]; }; struct note_map { /* note mapping */ struct note_map *next; /* note linkage */ char type; /* map type */ #define MAP_ONE 0 #define MAP_OCT 1 #define MAP_KEY 2 #define MAP_ALL 3 signed char pit; /* note pitch and accidental */ unsigned char acc; char *heads; /* comma separated list of note heads */ int color; /* color */ signed char print_pit; /* print pitch */ unsigned char print_acc; /* print acc */ }; struct map { /* voice mapping */ struct map *next; /* name linkage */ char *name; struct note_map *notes; /* mapping of the notes */ }; extern struct map *maps; /* note mappings */ struct note { /* note head */ int len; /* note duration (# pts in [1] if space) */ signed char pit; /* absolute pitch from source - used for ties and map */ unsigned char acc; /* code for accidental & index in micro_tb */ unsigned char sl1; /* slur start */ char sl2; /* number of slur ends */ char ti1; /* flag to start tie here */ char hlen; /* length of the head string */ char invisible; /* alternate note head */ float shhd; /* horizontal head shift (#pts if space) */ float shac; /* horizontal accidental shift */ char *head; /* head */ int color; /* heads when note mapping */ }; struct notes { /* note chord or rest */ struct note notes[MAXHD]; /* note heads */ unsigned char slur_st; /* slurs starting here (2 bits array) */ char slur_end; /* number of slurs ending here */ signed char brhythm; /* broken rhythm */ unsigned char microscale; /* microtone denominator - 1 */ float sdx; /* x offset of the stem */ struct decos dc; /* decorations */ }; extern int severity; extern char *deco[256]; struct FONTSPEC { int fnum; /* index to font tables in format.c */ float size; float swfac; }; extern char *fontnames[MAXFONTS]; /* list of font names */ /* lyrics */ #define LY_HYPH 0x10 /* replacement character for hyphen */ #define LY_UNDER 0x11 /* replacement character for underscore */ #define MAXLY 16 /* max number of lyrics */ struct lyl { struct FONTSPEC *f; /* font */ float w; /* width */ float s; /* shift / note */ char t[256]; /* word (dummy size) */ }; struct lyrics { struct lyl *lyl[MAXLY]; /* ptr to lyric lines */ }; /* guitar chord / annotations */ #define MAXGCH 8 /* max number of guitar chords / annotations */ struct gch { char type; /* ann. char, 'g' gchord, 'r' repeat, '\0' end */ unsigned char idx; /* index in as.text */ unsigned char font; /* font */ char box; /* 1 if in box */ float x, y; /* x y offset / note + (top or bottom) of staff */ float w; /* width */ }; /* positions / directions - see SL_xxx */ struct posit_s { unsigned short dyn:4; /* %%dynamic */ unsigned short gch:4; /* %%gchord */ unsigned short orn:4; /* %%ornament */ unsigned short voc:4; /* %%vocal */ unsigned short vol:4; /* %%volume */ unsigned short std:4; /* %%stemdir */ unsigned short gsd:4; /* %%gstemdir */ }; /* music element */ struct SYMBOL { /* struct for a drawable symbol */ struct SYMBOL *abc_next, *abc_prev; /* source linkage */ struct SYMBOL *next, *prev; /* voice linkage */ struct SYMBOL *ts_next, *ts_prev; /* time linkage */ struct SYMBOL *extra; /* extra symbols (grace notes, tempo... */ char abc_type; /* ABC symbol type */ #define ABC_T_NULL 0 #define ABC_T_INFO 1 /* (text[0] gives the info type) */ #define ABC_T_PSCOM 2 #define ABC_T_CLEF 3 #define ABC_T_NOTE 4 #define ABC_T_REST 5 #define ABC_T_BAR 6 #define ABC_T_EOLN 7 #define ABC_T_MREST 8 /* multi-measure rest */ #define ABC_T_MREP 9 /* measure repeat */ #define ABC_T_V_OVER 10 /* voice overlay */ #define ABC_T_TUPLET 11 unsigned char type; /* symbol type */ #define NO_TYPE 0 /* invalid type */ #define NOTEREST 1 /* valid symbol types */ #define SPACE 2 #define BAR 3 #define CLEF 4 #define TIMESIG 5 #define KEYSIG 6 #define TEMPO 7 #define STAVES 8 #define MREST 9 #define PART 10 #define GRACE 11 #define FMTCHG 12 #define TUPLET 13 #define STBRK 14 #define CUSTOS 15 #define NSYMTYPES 16 unsigned char voice; /* voice (0..nvoice) */ unsigned char staff; /* staff (0..nstaff) */ unsigned char nhd; /* number of notes in chord - 1 */ signed char pits[MAXHD]; /* pitches / clef */ int dur; /* main note duration */ int time; /* starting time */ unsigned int sflags; /* symbol flags */ #define S_EOLN 0x0001 /* end of line */ #define S_BEAM_ST 0x0002 /* beam starts here */ #define S_BEAM_BR1 0x0004 /* 2nd beam must restart here */ #define S_BEAM_BR2 0x0008 /* 3rd beam must restart here */ #define S_BEAM_END 0x0010 /* beam ends here */ #define S_CLEF_AUTO 0x0020 /* auto clef (when clef) */ #define S_IN_TUPLET 0x0040 /* in a tuplet */ #define S_TREM2 0x0080 /* tremolo on 2 notes */ #define S_RRBAR 0x0100 /* right repeat bar (when bar) */ #define S_XSTEM 0x0200 /* cross-staff stem (when note) */ #define S_BEAM_ON 0x0400 /* continue beaming */ #define S_SL1 0x0800 /* some chord slur start */ #define S_SL2 0x1000 /* some chord slur end */ #define S_TI1 0x2000 /* some chord tie start */ #define S_PERC 0x4000 /* percussion */ #define S_RBSTOP 0x8000 // end of repeat bracket #define S_FEATHERED_BEAM 0x00010000 /* feathered beam */ #define S_REPEAT 0x00020000 /* sequence / measure repeat */ #define S_NL 0x00040000 /* start of new music line */ #define S_SEQST 0x00080000 /* start of vertical sequence */ #define S_SECOND 0x00100000 /* symbol on a secondary voice */ #define S_FLOATING 0x00200000 /* symbol on a floating voice */ #define S_NOREPBRA 0x00400000 /* don't print the repeat bracket */ #define S_TREM1 0x00800000 /* tremolo on 1 note */ #define S_TEMP 0x01000000 /* temporary symbol */ #define S_SHIFTUNISON_1 0x02000000 /* %%shiftunison 1 */ #define S_SHIFTUNISON_2 0x04000000 /* %%shiftunison 2 */ #define S_NEW_SY 0x08000000 /* staff system change (%%staves) */ #define S_RBSTART 0x10000000 // start of repeat bracket #define S_OTTAVA 0x20000000 // ottava decoration (start or stop) struct posit_s posit; /* positions / directions */ signed char stem; /* 1 / -1 for stem up / down */ signed char combine; /* voice combine */ signed char nflags; /* number of note flags when > 0 */ char dots; /* number of dots */ unsigned char head; /* head type */ #define H_FULL 0 #define H_EMPTY 1 #define H_OVAL 2 #define H_SQUARE 3 signed char multi; /* multi voice in the staff (+1, 0, -1) */ signed char nohdi1; /* no head index (for unison) / nb of repeat */ signed char nohdi2; signed char doty; /* NOTEREST: y pos of dot when voices overlap * STBRK: forced * FMTCHG REPEAT: infos */ short aux; /* auxillary information: * - CLEF: small clef * - KEYSIG: old key signature * - BAR: new bar number * - TUPLET: tuplet format * - NOTE: tremolo number / feathered beam * - FMTCHG (format change): subtype */ #define PSSEQ 0 /* postscript sequence */ #define SVGSEQ 1 /* SVG sequence */ #define REPEAT 2 /* repeat sequence or measure * doty: # measures if > 0 * # notes/rests if < 0 * nohdi1: # repeat */ int color; float x; /* x offset */ signed char y; /* y offset of note head */ signed char ymn, ymx; /* min, max, note head y offset */ signed char mid; // y offset of the staff middle line float xmx; /* max h-pos of a head rel to top * width when STBRK */ float xs, ys; /* coord of stem end / bar height */ float wl, wr; /* left, right min width */ float space; /* natural space before symbol */ float shrink; /* minimum space before symbol */ float xmax; /* max x offset */ struct gch *gch; /* guitar chords / annotations */ struct lyrics *ly; /* lyrics */ char state; /* symbol state in file/tune */ #define ABC_S_GLOBAL 0 /* global */ #define ABC_S_HEAD 1 /* in header (after X:) */ #define ABC_S_TUNE 2 /* in tune (after K:) */ unsigned short flags; #define ABC_F_ERROR 0x0001 /* error around this symbol */ #define ABC_F_INVIS 0x0002 /* invisible symbol */ #define ABC_F_SPACE 0x0004 /* space before a note */ #define ABC_F_STEMLESS 0x0008 /* note with no stem */ #define ABC_F_LYRIC_START 0x0010 /* may start a lyric here */ #define ABC_F_GRACE 0x0020 /* grace note */ #define ABC_F_GR_END 0x0040 /* end of grace note sequence */ #define ABC_F_SAPPO 0x0080 /* short appoggiatura */ #define ABC_F_RBSTART 0x0100 // start of repeat bracket and mark #define ABC_F_RBSTOP 0x0200 // end of repeat bracket and mark unsigned short colnum; /* ABC source column number */ int linenum; /* ABC source line number */ char *fn; /* ABC source file name */ char *text; /* main text (INFO, PSCOM), * guitar chord (NOTE, REST, BAR) */ union { /* type dependent part */ struct key_s { /* K: info */ signed char sf; /* sharp (> 0) flats (< 0) */ char empty; /* clef alone if 1, 'none' if 2 */ char exp; /* exp (1) or mod (0) */ // char mode; /* mode */ // /* 0: Ionian, 1: Dorian, 2: Phrygian, 3: Lydian, // * 4: Mixolydian, 5: Aeolian, 6: Locrian */ char instr; /* specific instrument */ #define K_HP 1 /* bagpipe */ #define K_Hp 2 #define K_DRUM 3 /* percussion */ signed char nacc; /* number of explicit accidentals */ signed char cue; /* cue voice (scale 0.7) */ signed char octave; /* 'octave=' */ #define NO_OCTAVE 10 /* no 'octave=' */ unsigned char microscale; /* microtone denominator - 1 */ char clef_delta; /* clef delta */ char key_delta; // tonic base char *stafflines; float staffscale; signed char pits[8]; unsigned char accs[8]; } key; struct { /* L: info */ int base_length; /* basic note length */ } length; struct meter_s { /* M: info */ short wmeasure; /* duration of a measure */ unsigned char nmeter; /* number of meter elements */ char expdur; /* explicit measure duration */ #define MAX_MEASURE 16 struct { char top[8]; /* top value */ char bot[2]; /* bottom value */ } meter[MAX_MEASURE]; } meter; struct { /* Q: info */ char *str1; /* string before */ short beats[4]; /* up to 4 beats */ short circa; /* "ca. " */ short tempo; /* number of beats per mn or */ short new_beat; /* old beat */ char *str2; /* string after */ } tempo; struct { /* V: info */ char id[VOICE_ID_SZ]; /* voice ID */ char *fname; /* full name */ char *nname; /* nick name */ float scale; /* != 0 when change */ unsigned char voice; /* voice number */ signed char octave; /* 'octave=' - same as in K: */ char merge; /* merge with previous voice */ signed char stem; /* have stems up or down (2 = auto) */ signed char gstem; /* have grace stems up or down (2 = auto) */ signed char dyn; /* have dynamic marks above or below the staff */ signed char lyrics; /* have lyrics above or below the staff */ signed char gchord; /* have gchord above or below the staff */ signed char cue; /* cue voice (scale 0.7) */ char *stafflines; float staffscale; } voice; struct { /* bar, mrest or mrep */ int type; char repeat_bar; char len; /* len if mrest or mrep */ char dotted; struct decos dc; /* decorations */ } bar; struct clef_s { /* clef */ char *name; /* PS drawing function */ signed char type; #define TREBLE 0 #define ALTO 1 #define BASS 2 #define PERC 3 #define AUTOCLEF 4 char line; signed char octave; /* '+8' / '-8' */ signed char transpose; /* if '^8' / '_8' */ char invis; /* clef 'none' */ char check_pitch; /* check if old abc2ps transposition */ } clef; struct notes note; /* note, rest */ struct { /* user defined accent */ unsigned char symbol; unsigned char value; } user; struct { char type; /* 0: end of line * 1: continuation ('\') * 2: line break ('!') */ } eoln; struct { /* voice overlay */ char type; #define V_OVER_V 0 /* & */ #define V_OVER_S 1 /* (& */ #define V_OVER_E 2 /* &) */ unsigned char voice; } v_over; struct { /* tuplet */ char p_plet, q_plet, r_plet; } tuplet; } u; }; /* parse definition */ struct parse { struct SYMBOL *first_sym; /* first symbol */ struct SYMBOL *last_sym; /* last symbol */ int abc_vers; /* ABC version = (H << 16) + (M << 8) + L */ char *deco_tb[DC_NAME_SZ]; /* decoration names */ unsigned short micro_tb[MAXMICRO]; /* microtone values [ (n-1) | (d-1) ] */ int abc_state; /* parser state */ }; extern struct parse parse; #define FONT_UMAX 10 /* max number of user fonts 0..9 */ enum e_fonts { ANNOTATIONFONT = FONT_UMAX, COMPOSERFONT, FOOTERFONT, GCHORDFONT, HEADERFONT, HISTORYFONT, INFOFONT, MEASUREFONT, PARTSFONT, REPEATFONT, SUBTITLEFONT, TEMPOFONT, TEXTFONT, TITLEFONT, VOCALFONT, VOICEFONT, WORDSFONT, FONT_DYN /* index of dynamic fonts (gch, an, ly) */ }; #define FONT_DYNX 12 /* number of dynamic fonts */ #define FONT_MAX (FONT_DYN + FONT_DYNX) /* whole number of fonts */ struct FORMAT { /* struct for page layout */ float pageheight, pagewidth; float topmargin, botmargin, leftmargin, rightmargin; float topspace, wordsspace, titlespace, subtitlespace, partsspace; float composerspace, musicspace, vocalspace, textspace; float breaklimit, maxshrink, lineskipfac, parskipfac, stemheight; float gutter, indent, infospace, slurheight, tieheight, notespacingfactor, scale; float staffsep, sysstaffsep, maxstaffsep, maxsysstaffsep, stretchlast; int abc2pscompat, alignbars, aligncomposer, autoclef; int barsperstaff, breakoneoln, bstemdown, cancelkey, capo; int combinevoices, contbarnb, continueall, custos; int dblrepbar, decoerr, dynalign, flatbeams, flatbeamgracing, infoline; int gchordbox, graceslurs, graceword,gracespace, hyphencont; int keywarn, landscape, linewarn; int measurebox, measurefirst, measurenb; int nedo, oneperpage; #ifdef HAVE_PANGO int pango; #endif int partsbox, pdfmark; int rbdbstop, rbmax, rbmin; int setdefl, shiftunison, splittune, squarebreve; int staffnonote, straightflags, stretchstaff; int textoption, titlecaps, titleleft, titletrim; int timewarn, transpose, tuplets; char *bgcolor, *dateformat, *header, *footer, *titleformat; char *musicfont; struct FONTSPEC font_tb[FONT_MAX]; char ndfont; /* current index of dynamic fonts */ unsigned char gcf, anf, vof; /* fonts for guitar chords, * annotations and lyrics */ unsigned int fields[2]; /* info fields to print *[0] is 'A'..'Z', [1] is 'a'..'z' */ struct posit_s posit; }; extern struct FORMAT cfmt; /* current format */ extern struct FORMAT dfmt; /* global format */ typedef struct SYMBOL *INFO[26]; /* information fields ('A' .. 'Z') */ extern INFO info; extern char *outbuf; /* output buffer.. should hold one tune */ extern int outbufsz; /* size of outbuf */ extern char *mbf; /* where to PUTx() */ extern int use_buffer; /* 1 if lines are being accumulated */ extern int outft; /* last font in the output file */ extern int tunenum; /* number of current tune */ extern int pagenum; /* current page number */ extern int pagenum_nr; /* current page (non-resettable) */ extern int nbar; /* current measure number */ extern int in_page; extern int defl; /* decoration flags */ #define DEF_NOST 0x01 /* long deco with no start */ #define DEF_NOEN 0x02 /* long deco with no end */ #define DEF_STEMUP 0x04 /* stem up (1) or down (0) */ /* switches modified by flags: */ extern int quiet; /* quiet mode */ extern int secure; /* secure mode */ extern int annotate; /* output source references */ extern int pagenumbers; /* write page numbers */ extern int epsf; /* 1: EPSF, 2: SVG, 3: embedded ABC */ extern int svg; /* 1: SVG, 2: XHTML */ extern int showerror; /* show the errors */ extern int pipeformat; /* format for bagpipes */ extern char outfn[FILENAME_MAX]; /* output file name */ extern char *in_fname; /* current input file name */ extern time_t mtime; /* last modification time of the input file */ extern int file_initialized; /* for output file */ extern FILE *fout; /* output file */ #define MAXTBLT 8 struct tblt_s { char *head; /* PS head function */ char *note; /* PS note function */ char *bar; /* PS bar function */ float wh; /* width of head */ float ha; /* height above the staff */ float hu; /* height under the staff */ short pitch; /* pitch when no associated 'w:' / 0 */ char instr[2]; /* instrument pitch */ }; extern struct tblt_s *tblts[MAXTBLT]; #define MAXCMDTBLT 4 /* max number of -T in command line */ struct cmdtblt_s { short index; /* tablature number */ short active; /* activate or not */ char *vn; /* voice name */ }; extern struct cmdtblt_s cmdtblts[MAXCMDTBLT]; extern int ncmdtblt; extern int s_argc; /* command line arguments */ extern char **s_argv; struct STAFF_S { struct SYMBOL *s_clef; /* clef at start of music line */ char empty; /* no symbol on this staff */ char *stafflines; float staffscale; short botbar, topbar; /* bottom and top of bar */ float y; /* y position */ float top[YSTEP], bot[YSTEP]; /* top/bottom y offsets */ }; extern struct STAFF_S staff_tb[MAXSTAFF]; extern int nstaff; /* (0..MAXSTAFF-1) */ struct VOICE_S { char id[VOICE_ID_SZ]; /* voice id */ /* generation */ struct VOICE_S *next; /* link */ struct SYMBOL *sym; /* associated symbols */ struct SYMBOL *last_sym; /* last symbol while scanning */ struct SYMBOL *lyric_start; /* start of lyrics while scanning */ char *nm; /* voice name */ char *snm; /* voice subname */ char *bar_text; /* bar text at start of staff when bar_start */ struct gch *bar_gch; /* bar text */ struct SYMBOL *tie; /* note with ties of previous line */ struct SYMBOL *rtie; /* note with ties before 1st repeat bar */ struct tblt_s *tblts[2]; /* tablatures */ float scale; /* scale */ int time; /* current time (parsing) */ struct SYMBOL *s_clef; /* clef at end of music line */ struct key_s key; /* current key signature */ struct meter_s meter; /* current time signature */ struct key_s ckey; /* key signature while parsing */ struct key_s okey; /* original key signature (parsing) */ unsigned hy_st; /* lyrics hyphens at start of line (bit array) */ unsigned ignore:1; /* ignore this voice (%%staves) */ unsigned second:1; /* secondary voice in a brace/parenthesis */ unsigned floating:1; /* floating voice in a brace system */ unsigned bar_repeat:1; /* bar at start of staff is a repeat bar */ unsigned norepbra:1; /* don't display the repeat brackets */ unsigned have_ly:1; /* some lyrics in this voice */ unsigned new_name:1; /* redisplay the voice name */ unsigned space:1; /* have a space before the next note (parsing) */ unsigned perc:1; /* percussion */ unsigned auto_len:1; /* auto L: (parsing) */ short wmeasure; /* measure duration (parsing) */ short transpose; /* transposition (parsing) */ short bar_start; /* bar type at start of staff / 0 */ struct posit_s posit; /* positions / directions */ signed char octave; /* octave (parsing) */ signed char ottava; /* !8va(! ... (parsing) */ signed char clone; /* duplicate from this voice number */ signed char over; /* overlay of this voice number */ unsigned char staff; /* staff (0..n-1) */ unsigned char cstaff; /* staff (parsing) */ unsigned char slur_st; /* slurs at start of staff */ signed char combine; /* voice combine */ int color; char *stafflines; float staffscale; /* parsing */ struct SYMBOL *last_note; /* last note or rest */ char *map_name; int ulen; /* unit note length */ unsigned char microscale; /* microtone scale */ unsigned char mvoice; /* main voice when voice overlay */ }; extern struct VOICE_S *curvoice; /* current voice while parsing */ extern struct VOICE_S voice_tb[MAXVOICE]; /* voice table */ extern struct VOICE_S *first_voice; /* first_voice */ extern struct SYMBOL *tsfirst; /* first symbol in the time linked list */ extern struct SYMBOL *tsnext; /* next line when cut */ extern float realwidth; /* real staff width while generating */ #define NFLAGS_SZ 10 /* size of note flags tables */ #define C_XFLAGS 5 /* index of crotchet in flags tables */ extern float space_tb[NFLAGS_SZ]; /* note spacing */ extern float hw_tb[]; // width of note heads struct SYSTEM { /* staff system */ struct SYSTEM *next; short top_voice; /* first voice in the staff system */ short nstaff; struct { short flags; #define OPEN_BRACE 0x01 #define CLOSE_BRACE 0x02 #define OPEN_BRACKET 0x04 #define CLOSE_BRACKET 0x08 #define OPEN_PARENTH 0x10 #define CLOSE_PARENTH 0x20 #define STOP_BAR 0x40 #define FL_VOICE 0x80 #define OPEN_BRACE2 0x0100 #define CLOSE_BRACE2 0x0200 #define OPEN_BRACKET2 0x0400 #define CLOSE_BRACKET2 0x0800 #define MASTER_VOICE 0x1000 char empty; char *stafflines; float staffscale; // struct clef_s clef; float sep, maxsep; } staff[MAXSTAFF]; struct { signed char range; unsigned char staff; char second; char dum; float sep, maxsep; // struct clef_s clef; } voice[MAXVOICE]; }; extern struct SYSTEM *cursys; /* current staff system */ /* -- external routines -- */ /* abcm2ps.c */ void include_file(unsigned char *fn); void clrarena(int level); int lvlarena(int level); void *getarena(int len); void strext(char *fid, char *ext); /* abcparse.c */ void abc_parse(char *p, char *fname, int linenum); void abc_eof(void); char *get_str(char *d, char *s, int maxlen); char *parse_acc_pit(char *p, int *pit, int *acc); /* buffer.c */ void a2b(char *fmt, ...) #ifdef __GNUC__ __attribute__ ((format (printf, 1, 2))) #endif ; void block_put(void); void buffer_eob(int eot); void marg_init(void); void bskip(float h); void check_buffer(void); void init_outbuf(int kbsz); void close_output_file(void); void close_page(void); float get_bposy(void); void open_fout(void); void write_buffer(void); extern int (*output)(FILE *out, const char *fmt, ...) #ifdef __GNUC__ __attribute__ ((format (printf, 2, 3))) #endif ; void write_eps(void); /* deco.c */ void deco_add(char *text); void deco_cnv(struct decos *dc, struct SYMBOL *s, struct SYMBOL *prev); void deco_update(struct SYMBOL *s, float dx); float deco_width(struct SYMBOL *s); void draw_all_deco(void); //int draw_deco_head(int deco, float x, float y, int stem); void draw_deco_near(void); void draw_deco_note(void); void draw_deco_staff(void); float draw_partempo(int staff, float top); void draw_measnb(void); void init_deco(void); void reset_deco(void); void set_defl(int new_defl); float tempo_width(struct SYMBOL *s); void write_tempo(struct SYMBOL *s, int beat, float sc); float y_get(int staff, int up, float x, float w); void y_set(int staff, int up, float x, float w, float y); /* draw.c */ void draw_sym_near(void); void draw_all_symb(void); float draw_systems(float indent); void output_ps(struct SYMBOL *s, int color); struct SYMBOL *prev_scut(struct SYMBOL *s); void putf(float f); void putx(float x); void puty(float y); void putxy(float x, float y); void set_scale(struct SYMBOL *s); void set_sscale(int staff); void set_color(int color); /* format.c */ void define_fonts(void); int get_textopt(char *p); int get_font_encoding(int ft); int get_bool(char *p); void interpret_fmt_line(char *w, char *p, int lock); void lock_fmt(void *fmt); void make_font_list(void); FILE *open_file(char *fn, char *ext, char *rfn); void print_format(void); void set_font(int ft); void set_format(void); void set_voice_param(struct VOICE_S *p_voice, int state, char *w, char *p); struct tblt_s *tblt_parse(char *p); /* front.c */ #define FE_ABC 0 #define FE_FMT 1 #define FE_PS 2 void frontend(unsigned char *s, int ftype, char *fname, int linenum); /* glyph.c */ char *glyph_out(char *p); void glyph_add(char *p); /* music.c */ void output_music(void); void reset_gen(void); void unlksym(struct SYMBOL *s); /* parse.c */ extern float multicol_start; void do_tune(void); void identify_note(struct SYMBOL *s, int len, int *p_head, int *p_dots, int *p_flags); void sort_pitch(struct SYMBOL *s); struct SYMBOL *sym_add(struct VOICE_S *p_voice, int type); /* subs.c */ void bug(char *msg, int fatal); void error(int sev, struct SYMBOL *s, char *fmt, ...); float scan_u(char *str, int type); float cwid(unsigned char c); void get_str_font(int *cft, int *dft); void set_str_font(int cft, int dft); #ifdef HAVE_PANGO void pg_init(void); void pg_reset_font(void); #endif void put_history(void); void put_words(struct SYMBOL *words); void str_font(int ft); #define A_LEFT 0 #define A_CENTER 1 #define A_RIGHT 2 #define A_LYRIC 3 #define A_GCHORD 4 #define A_ANNOT 5 #define A_GCHEXP 6 void str_out(char *p, int action); void put_str(char *str, int action); float tex_str(char *s); extern char tex_buf[]; /* result of tex_str() */ #define TEX_BUF_SZ 512 char *trim_title(char *p, struct SYMBOL *title); void user_ps_add(char *s, char use); void user_ps_write(void); void write_title(struct SYMBOL *s); void write_heading(void); void write_user_ps(void); void write_text(char *cmd, char *s, int job); /* svg.c */ void define_svg_symbols(char *title, int num, float w, float h); void svg_def_id(char *id, int idsz); void svg_font_switch(void); int svg_output(FILE *out, const char *fmt, ...) #ifdef __GNUC__ __attribute__ ((format (printf, 2, 3))) #endif ; void svg_write(char *buf, int len); void svg_close(); /* syms.c */ void define_font(char *name, int num, int enc); void define_symbols(void); abcm2ps-8.14.11/abcm2ps.rst000066400000000000000000000444621376266546700153360ustar00rootroot00000000000000.. Process this file with rst2man from python-docutils to generate a nroff manual page ======= abcm2ps ======= -------------------------------------------------- translate ABC music notation to PostScript or SVG -------------------------------------------------- SYNOPSIS ======== ``abcm2ps`` [global_options] ABC_file [file_options] ... DESCRIPTION =========== ``abcm2ps`` translates tunes written in the ABC music notation format to customary sheet music scores in PostScript or SVG. It is based on ``abc2ps`` 1.2.5 and was developed mainly to print Baroque organ scores that have independent voices played on multiple keyboards and a pedal-board. The program has since been extended to support various other notation conventions in use for sheet music. Options given immediately after the command name apply to the run as a whole; options given after an ABC file name apply to that file. Formatting parameters can also be set in "format files" and in the ABC files themselves. OPTIONS ======= The list of the command line options may be known running:: abcm2ps -h The options may be grouped when they have no argument, but the last one (ex: ``-lnGI20``). The options may be disabled when starting with '+' or ending with '-' (ex: ``+MT1`` is the same as ``-MT1-``). The general output format is the last found in the command line. It may be: -E for Encapsulated PostScript, one file per tune -g for SVG, one file per tune -v for SVG, one file per page -X for XHTML+SVG -z for (X)HTML+SVG with (X)HTML+ABC input (see below for more information) List of the options ------------------- \- Read the abc file from stdin. \-- Set the parameter to . This has the same effect as a format parameter directly in the source file. -a Maximal horizontal compression when staff breaks are chosen automatically. Must be a float between 0 and 1. This correspond to the ``%%maxshrink`` formatting parameter (default: 0.65). -A This option inserts reference elements in the PostScript or SVG output. -B , +B Try to typeset bars on each staff line. This corresponds to the ``%%barsperstaff`` formatting parameter. -b Start measure numbering at . This corresponds to the ``%%measurefirst`` formatting parameter. -c, +c The continuation symbol is implicitly appended to each music line. This amounts to automatic line breaking. This corresponds to the ``%%continueall`` formatting parameter. -D Search the format files in the directory . -d Set the vertical interstaff space to . This corresponds to the ``%%staffsep`` formatting parameter (default: 46pt). -E Produce EPS output instead of simple PS. In this mode, each tune goes to a different file which name is "nnn.eps" or ".eps" (see option '-O') - 'nnn' is a sequence number incremented at each tune Output to stdout is forbidden. EPS files are normally embedded into Postscript documents, but they may be a way to generate graphical images. For example, using GhostScript:: abcm2ps voices -Ee7 gs -sDEVICE=pngmono -r80 -g590x174 \ -dBATCH -dNOPAUSE \ -sOutputFile=quitolis.png Out001.eps \(the values for -g are the values of the bounding box in the .eps, multiplied by (80 / 72), where 80 is the value for -r, and 72 is the default resolution) -e [ <tune index list> ] [ <regular expression> ] Select which tunes from an ABC file to print. <tune index list> is either a comma-separated list of tune numbers (as per the ``X:`` header), or a regular expression which will be matched against the tune headers as a whole. The ``-e`` option must occur after an ABC file name and applies to that file. Ranges of tune numbers may be specified like ``<t1>-<t2>``; <t2> may be omitted which means "all remaining tunes until the end of file". Note that filtering may cause problems, e.g., with global (non-tune) definitions in the ABC file. This corresponds to the ``%%select`` formatting parameter. -F <file>, +F Read the format (or PostScript) file <file>. When omitted, the default type of a format file is '.fmt'. In the form '+F', the default format file ('default.fmt') is not read. -f Enable flat beams (useful for bagpipe tunes). This corresponds to the ``%%flatbeams`` formatting parameter. -G, +G Omit slurs on grace notes. This corresponds to the ``%%graceslurs`` formatting parameter. -g Produce SVG output instead of EPS. In this mode each tune goes to a different file which name is 'Outnnn.svg' (see option '-O'). If the output is stdout (option '-O-'), all the SVG images are output without XML header. -H Display the current format values. -h Quick help, equivalent to "abcm2ps" without any arguments. This also shows the default settings for some parameters. -I <unit> Indent the first line of the tune by <unit> (default: 0). This corresponds to the ``%%indent`` formatting parameter. -i, +i Insert a red cercle around the errors in the PostScript output. -j <int>[b], +j Output a measure number every <int> measures. If <int> is 0, the measure number appears at the left of each staff. The trailing ``b`` causes a box to be drawn around each measure number (default: no measure numbering). This corresponds to the ``%%measurenb`` formatting parameter. -k <int> Set the size of the PostScript output buffer in Kibytes. Setting this value to a higher value permits the generation of big tunes with -E or -g. The default value is 64. -l, +l Generate landscape output. This corresponds to the ``%%landscape`` formatting parameter. -M, +M Suppress lyrics. See the ``%%writefields w`` formatting parameter. -m <unit> Set the left margin to <unit> (default: 1.8cm). This corresponds to the ``%%leftmargin`` formatting parameter. -N <int>, +N Number the pages. <int> indicates the mode: 0 no page numbers 1 at top left 2 at top right 3 at top left on even pages, top right on odd pages 4 at top right on even pages, top left on odd pages For compatibility with previous versions, '+N' is the same as '-N0', and '-N' is the same as '-N2'. If a header is defined ("%%header"), this option is ignored. -n, +n Include notes and history from ABC tune ``N:`` fields. See the ``%%writehistory N`` formatting parameter. -O [ <directory> ] [ <name> ], +O Define the output file directory and/or name. The directory must end with the directory separator ('/' for unix/windows, '\\' for mac). By default, the output file goes to the current directory with the name: 'Out.ps' for PS, 'Outnnn.eps' for EPS (see option '-E'), 'Outnnn.svg' for SVG (see options '-g' and '-v') or 'Out.xhtml' for XHTML+SVG (see options '-X' and '-z'). 'nnn' is a sequence number. When <name> is present, it is the name of the file, or it replaces "Out" in the file name. If <name> is '=', it is replaced by the name of the ABC source file (not for '-z'). If <name> is '-', the result is output to stdout (not for EPS). '+O' resets the output file directory and name to their defaults. -p Bagpipe format. When present, format output for bagpipe regardless of key. -q Quiet mode. When present, only the errors are shown. -s <float> Set the page scale factor to <float>. Note that the header and footer are not scaled (default: 0.75). This corresponds to the ``%%scale`` formatting parameter. -S Secure mode. When present, file inclusion (%%format and %%EPS) and PostScript injection (%%beginps and %%postscript) are disabled. -T <int> [ <voice> ], +T [ <int> [<voice> ] ] Activate or deactivate tablature drawing. - <int> is the tablature number as defined in ``%%tablature``. There may be only 8 different tablatures. - <voice> is the voice name, full name or subname as found in V:. When absent, apply to all voices. Up to 4 such commands may be defined. Ex: '-T1flute +T2' -V Show the version number. -v Produce SVG output instead of simple PS. In this mode each page goes to a different file which name is 'Outnnn.svg' (see option '-O'). -w <unit> Adjust the right margin such that the staff width is <unit> (default: none). This corresponds to the ``%%staffwidth`` formatting parameter. -X Produce XML+SVG output instead of simple PS. The default file name is 'Out.xhtml' (see option '-O'). -x, +x Include the ``X:`` tune number in the title. This corresponds to the ``%%writefields`` formatting parameter. -z Produce SVG images from ABC embedded in markup language files (HTML, XHTML..). The source file is copied to the output file and the ABC sequences are converted to SVG images. The ABC sequences start by either ``%abc..`` or ``X:..`` and stop on the first markup tag (``<..``). The generation creates one image per block, i.e. a music line or a text block. For a same rendering as the other SVG generation (-g, -v or -X), don't forget to set the line space to null, for example enclosing the ABC sequences by:: <div style="line-height:0"> .. </div> There can be only one output file. Note that the default output file is 'Out.xhtml', so, don't forget to change the file type if you generate HTML (.html) or XML (.xml) files. See "sample8.html" for a source example. -0, +0 Split tunes across page breaks if necessary. This corresponds to the ``%%splittune`` formatting parameter. -1, +1 Output one tune per page. This corresponds to the ``%%oneperpage`` formatting parameter. ADDITIONAL FEATURES =================== Clefs Clefs can be given in ``K:`` and ``V:`` headers. The full syntax is:: clef=<type><line>[+8|-8] "clef=" can be omitted when the <type> is a clef name. <type> denotes the clef type. It may be: - A note pitch (``G``, ``C``, or ``F``) The pitch indicates which clef is meant: ``G`` is the treble clef, ``C`` the alto clef and ``F`` the bass clef. It also gives the name of the note that appears on the clef's line. - A clef name The available clef names are ``treble`` (clef gives the pitch for ``G``), ``alto`` or ``tenor`` (``C``), and ``bass`` (``F``). - ``perc`` or ``P`` In percussion mode, accidentals change the glyphs used for note heads. By default, sharp notes are drawn as "x" and flat notes as circled "x". This may be changed by redefining the PostScript functions ``pshhd`` and ``pflhd``. - ``none`` No clef will be displayed. The <line> gives the number of the line within the staff that the base clef will be written on. The default values are 2 for the treble clef, 3 for the alto clef, and 4 for the tenor and bass clefs. The "+8" and "-8" options draw an 8 above or below the staff, respectively. When no clef is specified, clef changes between "bass" and "treble" will be inserted automatically. Multi-voice typesetting Multiple voices may be defined within the header or the tune using:: V:<name> <definition> ... where <name> is a word consisting of letters and digits only (like "violin1"). In the tune body, the following notes refer to this voice until another "V:" is encountered. A <definition> can be one of: * "clef="... See above * "name="<name> or "nm="<name> The <name> will be displayed at the beginning of the first staff. It can contain "\\n" sequences which will force line breaks. If it contains whitespace it must be double-quoted. * "subname="<name> or "snm="<name> The <name> will be displayed at the beginning of all staves except for the first. It can contain "\\n" sequences which will force line breaks. If it contains whitespace it must be double-quoted. * "merge" The voice goes on the same staff as the previous voice. * "up" or "down" Forces the direction of the stems for the voice. * "dyn=up" or "dyn=down" or "dyn=auto" Forces positioning of dynamic marks (above or below the staff) or reverts to automatic positioning (the default). * "gstem=up" or "gstem=down" or "gstem=auto" Forces the direction of the stems of grace notes (always up or always down) or reverts to automatic positioning (the default). * "stem=auto" Reverts to automatic positioning of note stems (up or down) (the default). * "lyrics=up" or "lyrics=down" or "lyrics=auto" Places lyrics above or below the staff or reverts to automatic positioning (the default) * "gchord=up" or "gchord=down" Places guitar chords above (the default) or below the staff. * "stafflines="<value> Sets the number of lines on the staff in question. (default: 5) * "staffscale="<value> Sets the scale of the associated staff up to 3. (default: 1) All other definitions are ignored. Definition of the staff system By default, each voice goes on its own staff. The ``%%staves <definition>`` pseudo-comment can be used to control staff assignment. The <definition> consists of voice names (from ``V:``) and pairs of parentheses, braces or brackets. - When a voice name is not within a pair of special characters, it goes on a separate staff. - For voice names enclosed in brackets, a bracket is displayed at the beginning of each line that joins the staves of the voices in question. - For voice names enclosed in braces, all the voices go on two staves (keyboard score). There can be at most four voices between a single pair of braces. - For voice names enclosed in parentheses, all the voices appear on a single staff. The ``|`` character prevents measure bars from being drawn between two staves. If ``%%staves`` occurs in a tune, all the voices not mentioned will not be output at all. The ``%%score`` directive occurs in the ABC draft 2.0 standard and is similar to the ``%%staves`` specification described above. The rules are: - Voice names within parentheses form a "voice group" and go on a single staff. A voice name that is not within parentheses forms its own voice group and goes on a staff by itself. - Voice groups within braces form a "voice block" and are preceded by a big brace in the output. This is especially useful for keyboard music. - Voice groups or voice blocks within brackets form a "voice block" and will be preceded by a big bracket in the output. - If a ``|`` character occurs between two voice groups or voice blocks, the bar lines in all of the associated staves will be continuous. - A single voice surrounded by two voice groups can be preceded by an asterisk to make it into a "floating" voice. This means that, for each note of the voice, a separate decision is made whether it is printed on the preceding or the following voice group's staff. - Voices that appear in the tune body but not in the ``%%score`` directive will not be output at all. If there is no ``%%score`` directive, each voice will be output on its own staff. - A ``%%score`` directive inside a tune resets the mechanism so voices can be removed or added. Voice overlay You can add notes to a staff without introducing a complete extra voice by using the ampersand (``&``). A single measure can be split into two voices like:: |F2A2Bc&F2c2bc| The ``(&...&...&)`` construction allows splitting multiple measures:: |!f!(&GG<G|GG F=E| E2 E(_D/E)|_D D C D |C4- |C &DC<C|CC_D C|=B,2_B,B, |_A,A,(G,/A,/)B,|F,4-|F,&)zzD=E| A double ampersand (``&&``) will allow overlaying more than two lines of music but this feature has not yet been implemented. Lyrics Aligned lyrics under a staff are written as a ``w:`` line directly below the staff line. For example:: edc2 edc2| w:Three blind mice, three blind mice Each word in the ``w:`` line (delimited by blanks) is associated with one note, in sequence. The following special symbols modify this behaviour: ``*`` Skips one note. ``-`` Splits a word into two syllables which are associated with two adjacent notes. A "-" is drawn between them. ``|`` Advances to the next bar line. ``~`` Is output as a space, but unites two words so they appear under a single note. ``_`` Draws a thin underscore from the previous note to the next. To include more than one line of lyrics, use multiple ``w:`` lines. To include hyphens without splitting a word over multiple notes, use ``\-``. If a word starts with a digit, this is interpreted as a stanza number and outdented a bit to the left. Slurs and ties The direction of slurs and ties may be controlled using the "(," / "('" and "-," / "-'" constructions. Microtone pitches Microtone pitches are indicated by a fraction after an accidental, as in ``^3/4c``. When omitted, the numerator defaultes to 1 and the denominator to 2 (so ``^/c`` is the same as ``^1/2c``). The numerator and denominator values may not exceed 256. There is built-in support for quarter-tone accidentals (1/2 and 3/2 sharps and flats); for other values, rendering functions must be defined using ``%%postscript``. EPS inclusion EPS files may be included inside tunes using the pseudo-comment ``%%EPS <file>``. SEE ALSO ======== A brief introduction referencing further documentation is installed in <docdir>/abcm2ps/README.md. The ABC music notation is at http://abcnotation.com/. Especially, you may find a discussion of differences with the ABC standard at http://moinejf.free.fr/abcm2ps-doc/features.xhtml and a list of formatting options at http://moinejf.free.fr/abcm2ps-doc/. AUTHOR ====== ``abcm2ps`` was written by Jean-François Moine <http://moinejf.free.fr/> starting from ``abc2ps`` by Michael Methfessel. Parts of this manual have been written by Anselm Lingnau <lingnau@debian.org> for the ``Debian`` system. Permission is granted to copy, distribute and/or modify this document as long as its origin is not misrepresented. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������abcm2ps-8.14.11/abcparse.c��������������������������������������������������������������������������0000664�0000000�0000000�00000156130�13762665467�0015175�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Generic ABC parser. * * This file is part of abcm2ps. * * Copyright (C) 1998-2020 Jean-François Moine (http://moinejf.free.fr) * Adapted from abc2ps, Copyright (C) 1996-1998 Michael Methfessel * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. */ #include "config.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include "abcm2ps.h" /* global values */ int severity; /* error severity */ static int ulen; /* unit note length set by M: or L: */ static short meter; /* upper value of time sig for n-plets */ static unsigned char microscale; /* current microtone scale */ static signed char vover; /* voice overlay (1: single bar, -1: multi-bar */ static char lyric_started; /* lyric started */ static char *gchord; /* guitar chord */ static struct decos dc; /* decorations */ static struct SYMBOL *deco_start; /* 1st note of the line for d: / s: */ static struct SYMBOL *deco_cont; /* current symbol when d: / s: continuation */ static int g_abc_vers, g_ulen, g_microscale; static char g_char_tb[128]; static char *g_deco_tb[128]; /* global decoration names */ static unsigned short g_micro_tb[MAXMICRO]; /* global microtone values */ static char *abc_fn; /* current source file name */ static int linenum; /* current source line number */ static int colnum; /* current source column number */ static char *abc_line; /* line being parsed */ static struct SYMBOL *last_sym; /* last symbol for errors */ static short nvoice; /* number of voices (0..n-1) */ struct VOICE_S *curvoice; /* current voice while parsing */ struct parse parse; /* char table for note line parsing */ #define CHAR_BAD 0 #define CHAR_IGN 1 #define CHAR_NOTE 2 #define CHAR_GR_ST 3 #define CHAR_DECO 4 #define CHAR_GCHORD 5 #define CHAR_BSLASH 6 #define CHAR_OBRA 7 #define CHAR_BAR 8 #define CHAR_OPAR 9 #define CHAR_VOV 10 #define CHAR_SPAC 11 #define CHAR_MINUS 12 #define CHAR_CPAR 13 #define CHAR_BRHY 14 #define CHAR_DECOS 15 #define CHAR_SLASH 16 #define CHAR_GR_EN 17 #define CHAR_LINEBREAK 18 static char char_tb[256] = { 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 07 */ 0, CHAR_SPAC, CHAR_LINEBREAK, 0, 0, 0, 0, 0, /* 08 - 0f */ 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 17 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 18 - 1f */ CHAR_SPAC, CHAR_DECOS, CHAR_GCHORD, CHAR_BAD, /* (sp) ! " # */ CHAR_BAD, CHAR_BAD, CHAR_VOV, CHAR_BAD, /* $ % & ' */ CHAR_OPAR, CHAR_CPAR, CHAR_BAD, CHAR_DECOS, /* ( ) * + */ CHAR_BAD, CHAR_MINUS, CHAR_DECO, CHAR_SLASH, /* , - . / */ CHAR_BAD, CHAR_BAD, CHAR_BAD, CHAR_BAD, /* 0 1 2 3 */ CHAR_BAD, CHAR_BAD, CHAR_BAD, CHAR_BAD, /* 4 5 6 7 */ CHAR_BAD, CHAR_BAD, CHAR_BAR, CHAR_BAD, /* 8 9 : ; */ CHAR_BRHY, CHAR_NOTE, CHAR_BRHY, CHAR_BAD, /* < = > ? */ CHAR_BAD, CHAR_NOTE, CHAR_NOTE, CHAR_NOTE, /* @ A B C */ CHAR_NOTE, CHAR_NOTE, CHAR_NOTE, CHAR_NOTE, /* D E F G */ CHAR_DECO, CHAR_DECO, CHAR_DECO, CHAR_DECO, /* H I J K */ CHAR_DECO, CHAR_DECO, CHAR_DECO, CHAR_DECO, /* L M N O */ CHAR_DECO, CHAR_DECO, CHAR_DECO, CHAR_DECO, /* P Q R S */ CHAR_DECO, CHAR_DECO, CHAR_DECO, CHAR_DECO, /* T U V W */ CHAR_NOTE, CHAR_DECO, CHAR_NOTE, CHAR_OBRA, /* X Y Z [ */ CHAR_BSLASH, CHAR_BAR, CHAR_NOTE, CHAR_NOTE, /* \ ] ^ _ */ CHAR_IGN, CHAR_NOTE, CHAR_NOTE, CHAR_NOTE, /* ` a b c */ CHAR_NOTE, CHAR_NOTE, CHAR_NOTE, CHAR_NOTE, /* d e f g */ CHAR_DECO, CHAR_DECO, CHAR_DECO, CHAR_DECO, /* h i j k */ CHAR_DECO, CHAR_DECO, CHAR_DECO, CHAR_DECO, /* l m n o */ CHAR_DECO, CHAR_DECO, CHAR_DECO, CHAR_DECO, /* p q r s */ CHAR_DECO, CHAR_DECO, CHAR_DECO, CHAR_DECO, /* t u v w */ CHAR_NOTE, CHAR_NOTE, CHAR_NOTE, CHAR_GR_ST, /* x y z { */ CHAR_BAR, CHAR_GR_EN, CHAR_DECO, CHAR_BAD, /* | } ~ (del) */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* a0 - af */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* b0 - bf */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* c0 - cf */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d0 - df */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* e0 - ef */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* f0 - ff */ }; static const char all_notes[] = "CDEFGABcdefgab"; static int parse_info(char *p); static char *parse_gchord(char *p); static int parse_line(char *p); static char *parse_note(char *p, int flags); static void syntax(char *msg, char *q); static void vover_new(void); /* -- abcMIDI like errors -- */ static void print_error(char *s, int col) { if (col >= 0) fprintf(stderr, "%s:%d:%d: error: %s\n", abc_fn, linenum, col, s); else fprintf(stderr, "%s:%d: error: %s\n", abc_fn, linenum, s); } /* -- new symbol -- */ static struct SYMBOL *abc_new(int type, char *text) { struct SYMBOL *s; s = getarena(sizeof(struct SYMBOL)); memset(s, 0, sizeof(struct SYMBOL)); if (text) { s->text = getarena(strlen(text) + 1); strcpy(s->text, text); } if (!parse.last_sym) { parse.first_sym = s; } else { if ((s->abc_next = parse.last_sym->abc_next) != NULL) s->abc_next->abc_prev = s; parse.last_sym->abc_next = s; s->abc_prev = parse.last_sym; } last_sym = parse.last_sym = s; s->abc_type = type; s->state = parse.abc_state; s->fn = abc_fn; s->linenum = linenum; s->colnum = colnum; return s; } /* -- parse an ABC line -- */ void abc_parse(char *p, char *fname, int ln) { abc_fn = fname; linenum = ln; abc_line = p; /* parse the music line */ switch (parse_line(p)) { case 2: /* start of tune (X:) */ g_abc_vers = parse.abc_vers; g_ulen = ulen; g_microscale = microscale; meter = 2; memcpy(g_char_tb, char_tb, sizeof g_char_tb); memcpy(g_deco_tb, parse.deco_tb, sizeof g_deco_tb); memcpy(g_micro_tb, parse.micro_tb, sizeof g_micro_tb); break; case 1: /* end of tune */ if (parse.first_sym) { do_tune(); parse.first_sym = parse.last_sym = NULL; } parse.abc_state = ABC_S_GLOBAL; parse.abc_vers = g_abc_vers; ulen = g_ulen; microscale = g_microscale; memcpy(char_tb, g_char_tb, sizeof g_char_tb); memcpy(parse.deco_tb, g_deco_tb, sizeof parse.deco_tb); memcpy(parse.micro_tb, g_micro_tb, sizeof parse.micro_tb); lvlarena(0); if (dc.n > 0) syntax("Decoration without symbol", 0); dc.n = 0; break; } } /* treat the end of file */ void abc_eof(void) { // if (parse.abc_state == ABC_S_HEAD) // severity = 1; do_tune(); parse.first_sym = parse.last_sym = NULL; if (parse.abc_state != ABC_S_GLOBAL) { parse.abc_vers = g_abc_vers; ulen = g_ulen; microscale = g_microscale; memcpy(char_tb, g_char_tb, sizeof g_char_tb); } } /* -- treat the broken rhythm '>' and '<' -- */ static void broken_rhythm(struct SYMBOL *s, int num) /* >0: do dot, <0: do half */ { struct notes *notes = &s->u.note; int l, m, n; num *= 2; if (num > 0) { if (num == 6) num = 8; n = num * 2 - 1; for (m = 0; m <= s->nhd; m++) notes->notes[m].len = (notes->notes[m].len * n) / num; } else { n = -num; if (n == 6) n = 8; for (m = 0; m <= s->nhd; m++) notes->notes[m].len /= n; } l = notes->notes[0].len; for (m = 1; m <= s->nhd; m++) if (notes->notes[m].len < l) l = notes->notes[m].len; } /* -- check for the '!' as end of line (ABC2Win) -- */ static int check_nl(char *p) { while (*p != '\0') { switch (*p++) { case '!': return 0; case '|': case '[': case ':': case ']': case ' ': case '\t': return 1; } } return 1; } /* -- parse extra K: or V: definitions (clef, octave and microscale -- */ static char *parse_extra(char *p, char **p_name, char **p_middle, char **p_stlines, char **p_scale, char **p_octave, char **p_cue, char **p_map) { for (;;) { if (strncmp(p, "clef=", 5) == 0 || strncmp(p, "bass", 4) == 0 || strncmp(p, "treble", 6) == 0 || strncmp(p, "alto", 4) == 0 || strncmp(p, "tenor", 5) == 0 || strncmp(p, "perc", 4) == 0) { if (*p_name) syntax("Double clef name", p); *p_name = p; } else if (strncmp(p, "microscale=", 11) == 0 || strncmp(p, "uscale=", 7) == 0) { int i; p += p[0] == 'm' ? 11 : 7; i = atoi(p); if (i < 4 || i >= 256) syntax("Invalid value in microscale=", p); else microscale = i; } else if (strncmp(p, "middle=", 7) == 0 || strncmp(p, "m=", 2) == 0) { if (*p_middle) syntax("Double clef middle", p); *p_middle = p + (p[1] == '=' ? 2 : 7); } else if (strncmp(p, "octave=", 7) == 0) { if (*p_octave) syntax("Double octave=", p); *p_octave = p + 7; } else if (strncmp(p, "stafflines=", 11) == 0) { int l; char *q; if (*p_stlines) syntax("Double stafflines", p); p += 11; if (isdigit((unsigned char) *p)) { switch (atoi(p)) { case 0: *p_stlines = "..."; break; case 1: *p_stlines = "..|"; break; case 2: *p_stlines = ".||"; break; case 3: *p_stlines = ".|||"; break; case 4: *p_stlines = "||||"; break; case 5: *p_stlines = "|||||"; break; case 6: *p_stlines = "||||||"; break; case 7: *p_stlines = "|||||||"; break; case 8: *p_stlines = "||||||||"; break; default: syntax("Bad number of lines", p); break; } } else { q = p; while (!isspace((unsigned char) *p) && *p != '\0') p++; l = p - q; *p_stlines = getarena(l + 1); strncpy(*p_stlines, q, l); (*p_stlines)[l] = '\0'; } } else if (strncmp(p, "staffscale=", 11) == 0) { if (*p_scale) syntax("Double staffscale", p); *p_scale = p + 11; } else if (strncmp(p, "cue=", 4) == 0) { if (*p_cue) syntax("Double cue", p); *p_cue = p + 4; } else if (strncmp(p, "map=", 4) == 0) { if (*p_map) syntax("Double map", p); *p_map = p + 4; // } else if (strncmp(p, "transpose=", 10) == 0 // || strncmp(p, "t=", 2) == 0) { // ; /* ignored - abcMIDI */ } else { break; } while (!isspace((unsigned char) *p) && *p != '\0') p++; while (isspace((unsigned char) *p)) p++; if (*p == '\0') break; } return p; } /* -- parse a decoration 'xxx<decosep>' -- */ static char *get_deco(char *p, unsigned char *p_dc) { char *q, sep, **t; unsigned i, l; *p_dc = 0; q = p; sep = q[-1]; if (char_tb[(unsigned char) sep] == CHAR_DECOS) { if (sep == '+') { if (*p == '+' && p[1] == '+') p++; /* special case "+++" */ } } else { sep = '\0'; /* Barfly U: */ } while (*p != sep) { if (*p == '\0') { syntax("Decoration not terminated", q); return p; } p++; } l = p - q; if (*p == sep) p++; for (i = 1, t = &parse.deco_tb[1]; *t && i < DC_NAME_SZ; i++, t++) { if (strlen(*t) == l && strncmp(*t, q, l) == 0) { *p_dc = i + 128; return p; } } /* new decoration */ if (i < DC_NAME_SZ) { // if (parse.abc_state != ABC_S_GLOBAL) // lvlarena(0); *t = getarena(l + 1); // if (parse.abc_state != ABC_S_GLOBAL) // lvlarena(1); memcpy(*t, q, l); (*t)[l] = '\0'; *p_dc = i + 128; } else { syntax("Too many decoration types", q); } return p; } /* -- parse a list of accidentals (K:) -- */ static char *parse_acc(char *p, struct SYMBOL *s) { int pit = 0, acc; unsigned nacc; nacc = 0; for (;;) { if (nacc >= sizeof s->u.key.pits) { syntax("Too many accidentals", p); break; } p = parse_acc_pit(p, &pit, &acc); if (acc < 0) break; s->u.key.pits[nacc] = pit; s->u.key.accs[nacc++] = acc; while (isspace((unsigned char) *p)) p++; if (*p == '\0') break; if (*p != '^' && *p != '_' && *p != '=') break; } s->u.key.microscale = microscale; if (s->u.key.empty != 2) s->u.key.nacc = nacc; return p; } /* -- parse a clef (K: or V:) -- */ static void parse_clef(struct SYMBOL *s, char *name, char *middle) { int clef = -1; int transpose = 0; int clef_line = 2; char *warn = NULL; char str[80]; str[0] = '\0'; if (name && strncmp(name, "clef=", 5) == 0) { name += 5; switch (*name) { case '\"': name = get_str(str, name, sizeof str); s->u.clef.name = getarena(strlen(str) + 1); strcpy(s->u.clef.name, str); clef = TREBLE; break; case 'g': warn = name; transpose = -7; case 'G': clef = TREBLE; break; case 'f': warn = name; transpose = -14; clef = BASS; clef_line = 4; break; case 'F': if (name[1] == ',') /* abc2.1.1 clef=F == clef=F, */ transpose = -7; clef = BASS; clef_line = 4; break; case 'c': warn = name; transpose = -7; case 'C': clef = ALTO; clef_line = 3; break; case 'P': clef = PERC; clef_line = 3; break; } if (clef >= 0) { name++; if (*name == ',' || *name== '\'') warn = name; while (*name == ',') { transpose += 7; name++; } while (*name == '\'') { transpose -= 7; name++; } } } if (name && clef < 0) { if (!strncmp(name, "bass", 4)) { clef = BASS; clef_line = 4; s->u.clef.check_pitch = 1; name += 4; } else if (!strncmp(name, "treble", 6)) { clef = TREBLE; name += 6; } else if (!strncmp(name, "alto", 4) || !strncmp(name, "tenor", 5)) { clef = ALTO; clef_line = *name == 'a' ? 3 : 4; s->u.clef.check_pitch = 1; if (*name == 'a') name += 4; else name += 5; } else if (!strncmp(name, "perc", 4)) { clef = PERC; clef_line = 3; name += 4; } else if (!strncmp(name, "auto", 4)) { clef = AUTOCLEF; name += 4; } else if (strncmp(name, "none", 4) == 0) { clef = TREBLE; s->u.clef.invis = 1; s->flags |= ABC_F_INVIS; name += 4; } else { syntax("Unknown clef", name); clef = TREBLE; } } if (clef >= 0) { if (isdigit((unsigned char) *name)) clef_line = *name++ - '0'; if (name[1] == '8') { switch (*name) { case '^': transpose -= 7; case '+': s->u.clef.octave = 1; break; case '_': transpose += 7; case '-': s->u.clef.octave = -1; break; } } } if (middle) { int pit = 0, acc, l; static const char line_tb[7] = {ALTO, TREBLE, ALTO, BASS, ALTO, BASS, ALTO}; warn = middle; /* 'middle=<note pitch>' */ parse_acc_pit(middle, &pit, &acc); if (acc < 0) // if error pit = 22; if (clef < 0) clef = line_tb[(pit + 7) % 7]; switch (clef) { default: l = 20 + 4; break; case ALTO: l = 16 + 4; break; case BASS: l = 12 + 4; break; } clef_line = (l - pit + 28) % 7; if (clef_line & 1) { syntax("Bad 'middle' value for the clef", middle); pit++; } clef_line = clef_line / 2 + 1; transpose = l - (clef_line - 1) * 2 - pit; s->u.clef.check_pitch = 0; } s->u.clef.type = clef; s->u.clef.line = clef_line; s->u.clef.transpose = transpose; if (warn) { int sev_sav; sev_sav = severity; syntax("Warning: Deprecated or non-standard item", warn); severity = sev_sav; } } /* get the octave= value */ static int parse_octave(char *p) { int oct; if (p) { oct = 1; if (*p == '-') { oct = -1; p++; } if (*p >= '0' && *p <= '4') return oct * (*p - '0'); syntax("Bad octave value", p); } return NO_OCTAVE; } /* -- parse a 'K:' -- */ static void parse_key(char *p, struct SYMBOL *s) { int sf, empty, instr; // int mode; char *clef_name, *clef_middle, *clef_stlines, *clef_scale; char *p_octave, *p_cue, *p_map; // set important default values // s->u.key.stafflines = "|||||"; s->u.key.octave = NO_OCTAVE; if (*p == '\0') { s->u.key.empty = 1; return; } sf = 0; // mode = 0; empty = 0; instr = 0; switch (*p++) { case 'F': sf = -1; break; case 'B': sf++; case 'E': sf++; case 'A': sf++; case 'D': sf++; case 'G': sf++; case 'C': break; case 'H': if (*p == 'P') { instr = K_HP; p++; } else if (*p == 'p') { instr = K_Hp; sf = 2; p++; } else { syntax("Unknown bagpipe-like key", p); } break; case 'P': instr = K_DRUM; p++; break; case 'n': if (strncmp(p, "one", 3) == 0) { // none empty = 2; p += 3; while (isspace((unsigned char) *p)) p++; if (*p == '\0') { s->u.key.empty = empty; return; } break; } // fall thru default: p--; empty = 1; break; } s->u.key.empty = empty; if (!empty) { if (*p == '#') { sf += 7; p++; } else if (*p == 'b') { sf -= 7; p++; } while (isspace((unsigned char) *p)) p++; switch (*p) { case 'a': case 'A': if (strncasecmp(p, "aeo", 3) == 0) { sf -= 3; // mode = 5; break; } goto unk; case 'd': case 'D': if (strncasecmp(p, "dor", 3) == 0) { sf -= 2; // mode = 1; break; } goto unk; case 'i': case 'I': if (strncasecmp(p, "ion", 3) == 0) { // mode = 0; break; } goto unk; case 'l': case 'L': if (strncasecmp(p, "loc", 3) == 0) { sf -= 5; // mode = 6; break; } if (strncasecmp(p, "lyd", 3) == 0) { sf += 1; // mode = 3; break; } goto unk; case 'm': case 'M': if (strncasecmp(p, "maj", 3) == 0) break; if (strncasecmp(p, "mix", 3) == 0) { sf -= 1; // mode = 4; break; } if (strncasecmp(p, "min", 3) == 0 || !isalpha((unsigned char) p[1])) { /* 'm' alone */ sf -= 3; // mode = 5; break; } goto unk; case 'p': case 'P': if (strncasecmp(p, "phr", 3) == 0) { sf -= 4; // mode = 2; break; } goto unk; default: unk: empty = 1; // (local value) break; } if (!empty) { while (isalpha((unsigned char) *p)) p++; while (isspace((unsigned char) *p)) p++; } // [exp] accidentals if (strncmp(p, "exp ", 4) == 0) { p += 4; while (isspace((unsigned char) *p)) p++; if (*p == '\0') syntax("no accidental after 'exp'", p); s->u.key.exp = 1; } if (s->u.key.exp && strncmp(p, "none", 4) == 0) { sf = 0; p += 4; while (isspace((unsigned char) *p)) p++; } else switch (*p) { case '^': case '_': case '=': p = parse_acc(p, s); /* accidentals */ break; } } if (sf > 7 || sf < -7) { syntax("Too many sharps/flats", p); if (sf > 0) sf -= 12; else sf += 12; } // extra parameters clef_name = clef_middle = clef_stlines = clef_scale = NULL; p_octave = p_cue = p_map = NULL; parse_extra(p, &clef_name, &clef_middle, &clef_stlines, &clef_scale, &p_octave, &p_cue, &p_map); s->u.key.sf = sf; // s->u.key.mode = mode; s->u.key.instr = instr; s->u.key.octave = parse_octave(p_octave); if (p_cue) { if (strncmp(p_cue, "on", 2) == 0) s->u.key.cue = 1; else s->u.key.cue = -1; } if (clef_stlines) s->u.key.stafflines = clef_stlines; if (clef_scale) { float sc; sc = atof(clef_scale); if (sc >= 0.5 && sc <= 3) s->u.key.staffscale = sc; else syntax("Bad value of staffscale", clef_scale); } if (clef_name || clef_middle) { s = abc_new(ABC_T_CLEF, NULL); parse_clef(s, clef_name, clef_middle); } if (p_map) { strcpy(tex_buf, "%%voicemap "); get_str(&tex_buf[11], p_map, TEX_BUF_SZ - 12); abc_new(ABC_T_PSCOM, tex_buf); } } /* -- set default length from 'L:' -- */ static char *get_len(char *p, struct SYMBOL *s) { int l1, l2, d; char *error_txt = NULL; if (strcmp(p, "auto") == 0) { /* L:auto */ ulen = 15120; // 2*2*2*2*3*3*3*5*7 s->u.length.base_length = -1; return error_txt; } l1 = 0; l2 = 1; if (sscanf(p, "%d /%d ", &l1, &l2) != 2 || l1 == 0) { s->u.length.base_length = ulen ? ulen : BASE_LEN / 8; return "Bad unit note length: unchanged"; } if (l2 == 0) { error_txt = "Bad length divisor, set to 4"; l2 = 4; } d = BASE_LEN / l2; if (d * l2 != BASE_LEN) { error_txt = "Length incompatible with BASE, using 1/8"; d = BASE_LEN / 8; } else { d *= l1; if (l1 != 1 || (l2 & (l2 - 1))) { error_txt = "Incorrect unit note length, using 1/8"; d = BASE_LEN / 8; } } s->u.length.base_length = d; return error_txt; } /* -- parse a 'M:' -- */ static char *parse_meter(char *p, struct SYMBOL *s) { int m1, m2, d, wmeasure, nm, in_parenth; unsigned i; char *q; static char top_err[] = "Cannot identify meter top"; if (*p == '\0') return "Empty meter string"; nm = 0; in_parenth = 0; m1 = 0; if (strncmp(p, "none", 4) == 0) { p += 4; /* no meter */ wmeasure = 1; /* simplify measure numbering and MREST conversion */ } else { wmeasure = 0; while (*p != '\0') { if (*p == '=') break; if (nm >= MAX_MEASURE) return "Too many values in M:"; switch (*p) { case 'C': s->u.meter.meter[nm].top[0] = *p++; if (*p == '|') s->u.meter.meter[nm].top[1] = *p++; m1 = 4; m2 = 4; break; case 'c': case 'o': if (*p == 'c') m1 = 4; else m1 = 3; m2 = 4; s->u.meter.meter[nm].top[0] = *p++; if (*p == '.') s->u.meter.meter[nm].top[1] = *p++; break; case '(': if (p[1] == '(') { /* "M:5/4 ((2+3)/4)" */ in_parenth = 1; s->u.meter.meter[nm++].top[0] = *p++; } q = p + 1; while (*q != '\0') { if (*q == ')' || *q == '/') break; q++; } if (*q == ')' && q[1] == '/') { /* "M:5/4 (2+3)/4" */ p++; /* remove the parenthesis */ continue; } /* "M:5 (2+3)" */ /* fall thru */ case ')': in_parenth = *p == '('; s->u.meter.meter[nm++].top[0] = *p++; continue; default: if (sscanf(p, "%d", &m1) != 1 || m1 <= 0) return top_err; i = 0; m2 = 2; /* default when no bottom value */ for (;;) { while (isdigit((unsigned char) *p) && i < sizeof s->u.meter.meter[0].top) s->u.meter.meter[nm].top[i++] = *p++; if (*p == ')') { if (p[1] != '/') break; p++; } if (*p == '/') { p++; if (sscanf(p, "%d", &m2) != 1 || m2 <= 0) return "Cannot identify meter bottom"; i = 0; while (isdigit((unsigned char) *p) && i < sizeof s->u.meter.meter[0].bot) s->u.meter.meter[nm].bot[i++] = *p++; break; } if (*p != ' ' && *p != '+') break; if (*p == '\0' || p[1] == '(') /* "M:5 (2/4+3/4)" */ break; if (i < sizeof s->u.meter.meter[0].top) s->u.meter.meter[nm].top[i++] = *p++; if (sscanf(p, "%d", &d) != 1 || d <= 0) return top_err; if (p[-1] == ' ') { if (d > m1) m1 = d; } else { m1 += d; } } break; } if (!in_parenth) wmeasure += m1 * BASE_LEN / m2; nm++; if (*p == ' ') p++; else if (*p == '+') s->u.meter.meter[nm++].top[0] = *p++; } } meter = m1; if (*p == '=') { if (sscanf(++p, "%d/%d", &m1, &m2) != 2 || m1 <= 0 || m2 <= 0) return "Cannot identify meter explicit duration"; wmeasure = m1 * BASE_LEN / m2; s->u.meter.expdur = 1; } s->u.meter.wmeasure = wmeasure; s->u.meter.nmeter = nm; /* in the tune header, change the unit note length */ if (parse.abc_state == ABC_S_HEAD && ulen == 0) { if (wmeasure >= BASE_LEN * 3 / 4 || wmeasure <= 1) ulen = BASE_LEN / 8; else ulen = BASE_LEN / 16; } return 0; } /* -- get a possibly quoted string -- */ char *get_str(char *d, /* destination */ char *s, /* source */ int maxlen) /* max length */ { char c; maxlen--; /* have place for the EOS */ while (isspace((unsigned char) *s)) s++; if (*s == '"') { s++; while ((c = *s) != '\0') { if (c == '"') { s++; break; } if (c == '\\') { if (--maxlen > 0) *d++ = c; c = *++s; } if (--maxlen > 0) *d++ = c; s++; } } else { while ((c = *s) != '\0') { if (isspace((unsigned char) c)) break; if (--maxlen > 0) *d++ = c; s++; } } *d = '\0'; while (isspace((unsigned char) *s)) s++; return s; } /* -- parse a tempo (Q:) -- */ static char *parse_tempo(char *p, struct SYMBOL *s) { char c, str[80]; int i, l, n, top, bot; /* string before */ if (*p == '"') { p = get_str(str, p, sizeof str); s->u.tempo.str1 = getarena(strlen(str) + 1); strcpy(s->u.tempo.str1, str); } /* beat */ if (*p == 'C' || *p == 'c' || *p == 'L' || *p == 'l') { s->u.tempo.beats[0] = ulen; if (parse.abc_vers >= (2 << 16)) syntax("Deprecated Q: value", p); p++; while (isspace((unsigned char) *p)) p++; if (*p != '=') goto inval; c = '='; p--; } else if (isdigit((unsigned char) *p)) { if (strchr(p, '/') != NULL) { i = 0; while (isdigit((unsigned char) *p)) { if (sscanf(p, "%d/%d%n", &top, &bot, &n) != 2 || bot <= 0) goto inval; l = (BASE_LEN * top) / bot; if (l <= 0 || i >= sizeof s->u.tempo.beats / sizeof s->u.tempo.beats[0]) goto inval; s->u.tempo.beats[i++] = l; p += n; while (isspace((unsigned char) *p)) p++; } c = *p; if (c != '=') goto inval; } else { s->u.tempo.beats[0] = ulen; if (parse.abc_vers >= (2 << 16)) syntax("Deprecated Q: value", p); c = '='; p--; } } else { c = '\0'; } /* tempo value */ if (c == '=') { p++; if (strncmp(p, "ca. ", 4) == 0) { s->u.tempo.circa = 1; p += 4; } if (sscanf(p, "%d/%d%n", &top, &bot, &n) == 2) { if (bot <= 0) goto inval; l = (BASE_LEN * top) / bot; if (l <= 0) goto inval; s->u.tempo.new_beat = l; } else { if (sscanf(p, "%d%n", &top, &n) != 1) goto inval; s->u.tempo.tempo = top; } p += n; while (isspace((unsigned char) *p)) p++; } /* string after */ if (*p == '"') { p = get_str(str, p, sizeof str); s->u.tempo.str2 = getarena(strlen(str) + 1); strcpy(s->u.tempo.str2, str); } return 0; inval: return "Invalid tempo"; } /* -- get a user defined symbol (U:) -- */ static char *get_user(char *p, struct SYMBOL *s) { unsigned char c; char *value; c = (unsigned char) *p++; if (c == '\\') { c = (unsigned char) *p++; switch (c) { case 'n': c = '\n'; break; case 't': c = '\t'; break; } } switch (char_tb[c]) { default: return "Bad decoration character"; case CHAR_DECO: break; case CHAR_BAD: case CHAR_IGN: case CHAR_SPAC: case CHAR_DECOS: case CHAR_LINEBREAK: char_tb[c] = CHAR_DECO; break; } s->u.user.symbol = c; /* skip '=' */ while (isspace((unsigned char) *p) || *p == '=') p++; if (char_tb[(unsigned char) *p] == CHAR_DECOS) p++; /*fixme: 'U: <char> = "text"' is not treated */ get_deco(p, &s->u.user.value); if (!s->u.user.value) return 0; /* treat special pseudo decorations */ value = parse.deco_tb[s->u.user.value - 128]; if (strcmp(value, "beambreak") == 0) char_tb[c] = CHAR_SPAC; else if (strcmp(value, "ignore") == 0) char_tb[c] = CHAR_IGN; else if (strcmp(value, "nil") == 0 || strcmp(value, "none") == 0) char_tb[c] = CHAR_BAD; else return 0; s->u.user.value = 0; /* not a decoration */ return 0; } /* -- parse the voice parameters (V:) -- */ static char *parse_voice(char *p, struct SYMBOL *s) { int voice; char *error_txt = NULL; char *clef_name, *clef_middle, *clef_stlines, *clef_scale; char *p_octave, *p_cue, *p_map; signed char *p_stem; static struct kw_s { char *name; short len; short index; } kw_tb[] = { {"name=", 5, 0}, {"nm=", 3, 0}, {"subname=", 8, 1}, {"sname=", 6, 1}, {"snm=", 4, 1}, {"merge", 5, 2}, {"up", 2, 3}, {"down", 4, 4}, {"stem=", 5, 5}, {"gstem=", 6, 6}, {"auto", 4, 7}, {"dyn=", 4, 8}, {"lyrics=", 7, 9}, {"scale=", 6, 10}, {"gchord=", 7, 11}, {0} }; struct kw_s *kw; /* save the parameters of the previous voice */ curvoice->ulen = ulen; curvoice->microscale = microscale; if (voice_tb[0].id[0] == '\0') { switch (s->abc_prev->abc_type) { case ABC_T_EOLN: case ABC_T_NOTE: case ABC_T_REST: case ABC_T_BAR: /* the previous voice was implicit (after K:) */ voice_tb[0].id[0] = '1'; break; } } { char *id, sep; id = p; while (isalnum((unsigned char) *p) || *p == '_') p++; sep = *p; *p = '\0'; if (voice_tb[0].id[0] == '\0') { voice = 0; /* first voice */ } else { for (voice = 0; voice <= nvoice; voice++) { if (strcmp(id, voice_tb[voice].id) == 0) goto found; } if (voice >= MAXVOICE) { syntax("Too many voices", id); voice--; } } nvoice = voice; strncpy(voice_tb[voice].id, id, sizeof voice_tb[voice].id - 1); voice_tb[voice].mvoice = voice; found: strcpy(s->u.voice.id, voice_tb[voice].id); *p = sep; } curvoice = &voice_tb[voice]; s->u.voice.voice = voice; /* if in tune, set the voice parameters */ if (parse.abc_state == ABC_S_TUNE) { ulen = curvoice->ulen; microscale = curvoice->microscale; } /* parse the other parameters */ clef_name = clef_middle = clef_stlines = clef_scale = NULL; p_octave = p_cue = p_map = NULL; p_stem = &s->u.voice.stem; for (;;) { while (isspace((unsigned char) *p)) p++; if (*p == '\0') break; p = parse_extra(p, &clef_name, &clef_middle, &clef_stlines, &clef_scale, &p_octave, &p_cue, &p_map); if (*p == '\0') break; for (kw = kw_tb; kw->name; kw++) { if (strncmp(p, kw->name, kw->len) == 0) break; } if (!kw->name) { while (!isspace((unsigned char) *p) && *p != '\0') p++; /* ignore unknown keywords */ continue; } p += kw->len; switch (kw->index) { case 0: /* name */ p = get_str(tex_buf, p, TEX_BUF_SZ); s->u.voice.fname = getarena(strlen(tex_buf) + 1); strcpy(s->u.voice.fname, tex_buf); break; case 1: /* subname */ p = get_str(tex_buf, p, TEX_BUF_SZ); s->u.voice.nname = getarena(strlen(tex_buf) + 1); strcpy(s->u.voice.nname, tex_buf); break; case 2: /* merge */ s->u.voice.merge = 1; break; case 3: /* up */ *p_stem = 1; break; case 4: /* down */ *p_stem = -1; break; case 5: /* stem= */ p_stem = &s->u.voice.stem; break; case 6: /* gstem= */ p_stem = &s->u.voice.gstem; break; case 7: /* auto */ *p_stem = 2; break; case 8: /* dyn= */ p_stem = &s->u.voice.dyn; break; case 9: /* lyrics= */ p_stem = &s->u.voice.lyrics; break; case 10: { /* scale= */ float sc; sc = atof(p); if (sc >= 0.5 && sc <= 2) s->u.voice.scale = sc; else error_txt = "Bad value for voice scale"; while (!isspace((unsigned char) *p) && *p != '\0') p++; break; } case 11: /* gchord= */ p_stem = &s->u.voice.gchord; break; } } s->u.voice.octave = parse_octave(p_octave); if (p_cue) { if (strncmp(p_cue, "on", 2) == 0) s->u.voice.cue = 1; else s->u.voice.cue = -1; } if (clef_stlines) s->u.voice.stafflines = clef_stlines; // else // s->u.voice.stafflines = "|||||"; if (clef_scale) { float sc; sc = atof(clef_scale); if (sc >= 0.5 && sc <= 3) s->u.voice.staffscale = sc; else syntax("Bad value of staffscale", clef_scale); } if (clef_name || clef_middle) { s = abc_new(ABC_T_CLEF, NULL); parse_clef(s, clef_name, clef_middle); } if (p_map) { strcpy(tex_buf, "%%voicemap "); get_str(&tex_buf[11], p_map, TEX_BUF_SZ - 12); abc_new(ABC_T_PSCOM, tex_buf); } return error_txt; } /* -- parse a bar -- */ static char *parse_bar(char *p) { struct SYMBOL *s; char *q; int bar_type, i; char repeat_value[32]; q = --p; // keep the first char bar_type = 0; for (;;) { switch (*p++) { case '|': bar_type <<= 4; bar_type |= B_BAR; continue; case '[': bar_type <<= 4; bar_type |= B_OBRA; continue; case ']': bar_type <<= 4; bar_type |= B_CBRA; continue; case ':': bar_type <<= 4; bar_type |= B_COL; continue; default: break; } break; } p--; /* if the last element is '[', it may start * a chord, an embedded header or an other bar */ if ((bar_type & 0x0f) == B_OBRA && bar_type != B_OBRA && *p != ' ') { bar_type >>= 4; p--; } if (bar_type == (B_OBRA << 8) + (B_BAR << 4) + B_CBRA) /* [|] */ bar_type = (B_OBRA << 4) + B_CBRA; /* [] */ /* curvoice->last_note = NULL; */ if (vover > 0) { curvoice = &voice_tb[curvoice->mvoice]; vover = 0; } s = abc_new(ABC_T_BAR, gchord); if (gchord) gchord = NULL; /* handle the repeat sequences */ if (bar_type == B_COL) { bar_type = B_BAR; s->u.bar.dotted = 1; } else { if (*q == ']') { /* repeat bar stop */ i = p - q - 1; if (i > 0) /* remove the starting ']' */ s->u.bar.type &= (1 << (i * 4)) - 1; s->flags |= ABC_F_RBSTOP; s->sflags |= S_RBSTOP; } else if ((bar_type & 0x0f) == B_COL /* left or */ || *q == ':') { /* right repeat bar */ s->flags |= ABC_F_RBSTOP; s->sflags |= S_RBSTOP; if (*q == ':') /* right repeat bar */ s->sflags |= S_RRBAR; } } s->u.bar.type = bar_type; if (dc.n > 0) { memcpy(&s->u.bar.dc, &dc, sizeof s->u.bar.dc); dc.n = 0; } if (!lyric_started) { lyric_started = 1; s->flags |= ABC_F_LYRIC_START; } if (!isdigit((unsigned char) *p) /* if not a repeat bar */ && (*p != '"' || p[-1] != '[')) /* ('["' only) */ return p; if (*p == '"') { p = get_str(repeat_value, p, sizeof repeat_value); } else { char *q; q = repeat_value; while (isdigit((unsigned char) *p) || *p == ',' || *p == '-' || (*p == '.' && isdigit((unsigned char) p[1]))) { if (q < &repeat_value[sizeof repeat_value - 1]) *q++ = *p++; else p++; } *q = '\0'; } if (bar_type != B_OBRA || s->text) { s = abc_new(ABC_T_BAR, repeat_value); s->u.bar.type = B_OBRA; } else { s->text = getarena(strlen(repeat_value) + 1); strcpy(s->text, repeat_value); } s->u.bar.repeat_bar = 1; s->flags |= ABC_F_RBSTART | ABC_F_RBSTOP; s->sflags |= S_RBSTART | S_RBSTOP; return p; } // parse the note accidental and pitch char *parse_acc_pit(char *p, int *pit, int *acc) { /* look for accidental sign */ switch (*p) { case '^': p++; if (*p == '^') { p++; *acc = A_DS; } else { *acc = A_SH; } break; case '=': p++; *acc = A_NT; break; case '_': p++; if (*p == '_') { p++; *acc = A_DF; } else { *acc = A_FT; } break; default: *acc = 0; } /* look for microtone value */ if (*acc != 0 && (isdigit((unsigned char) *p) || (*p == '/' && microscale == 0))) { int n, d; char *q; n = d = 1; if (*p != '/') { n = strtol(p, &q, 10); p = q; } if (*p == '/') { p++; if (!isdigit((unsigned char) *p)) { d = 2; } else { d = strtol(p, &q, 10); p = q; } } if (microscale == 0) { d--; d += (n - 1) << 8; /* short [ (n-1) | (d-1) ] */ if (d == 0) { n = MAXMICRO - 1; } else { for (n = 1; n < MAXMICRO; n++) { if (parse.micro_tb[n] == d) break; if (parse.micro_tb[n] == 0) { parse.micro_tb[n] = d; break; } } } if (n == MAXMICRO) { syntax("Too many microtone accidentals", p); n = 0; } } *acc += (n << 3); } /* get the pitch */ { char *p_n; p_n = strchr(all_notes, *p); if (!p_n || *p == '\0') { syntax(*acc ? "Missing note after accidental" : "Not a note", p); *acc = -1; if (*p == '\0') p--; } else { *pit = p_n - all_notes + 16; } p++; } while (*p == '\'') { /* eat up following ' chars */ *pit += 7; p++; } while (*p == ',') { /* eat up following , chars */ *pit -= 7; p++; } return p; } /* -- parse the decorations of notes and bars -- */ static char *parse_deco(char *p, struct decos *deco, int m) /* note index / -1 */ { int n; unsigned char t; n = deco->n; for (;;) { t = (unsigned char) *p++; if (char_tb[t] != CHAR_DECO && char_tb[t] != CHAR_DECOS) break; if (char_tb[t] == CHAR_DECOS) p = get_deco(p, &t); if (n >= MAXDC) { syntax("Too many decorations for the note", p); } else if (t != 0) { deco->tm[n].t = t; deco->tm[n++].m = m; } } deco->n = n; return p - 1; } /* -- parse a decoration line (d: or s:) -- */ static char *parse_decoline(char *p) { struct SYMBOL *is; unsigned char t; int n; if ((is = deco_cont) == NULL) is = deco_start; else deco_cont = NULL; /* scan the decoration line */ while (*p != '\0') { while (isspace((unsigned char) *p)) p++; if (*p == '\0') break; switch (*p) { case '|': while (is && (is->abc_type != ABC_T_BAR || is->u.bar.type == B_OBRA)) is = is->abc_next; if (!is) { syntax("Not enough bar lines for deco line", p); return NULL; } is = is->abc_next; p++; continue; case '*': while (is && is->abc_type != ABC_T_NOTE) is = is->abc_next; if (!is) { syntax("Not enough notes for deco line", p); return NULL; } is = is->abc_next; p++; continue; case '\\': if (p[1] == '\0') { if (!is) return "Not enough notes for deco line"; deco_cont = is; return NULL; } syntax("'\\' ignored", p); p++; continue; case '"': p = parse_gchord(p + 1); break; default: if (char_tb[(unsigned char) *p] == CHAR_DECOS) p = get_deco(p + 1, &t); else t = (unsigned char) *p++; break; } /* store the decoration and gchord/annotation in the next note */ while (is && (is->abc_type != ABC_T_NOTE || (is->flags & ABC_F_GRACE))) is = is->abc_next; if (!is) return "Not enough notes for deco line"; if (gchord) { if (is->text) { char *gch; n = strlen(is->text); gch = getarena(n + strlen(gchord) + 2); strcpy(gch, is->text); gch[n] = '\n'; strcpy(gch + n + 1, gchord); gchord = gch; } is->text = gchord; gchord = NULL; } else { n = is->u.note.dc.n; if (n >= MAXDC) { syntax("Too many decorations for the note", p); } else if (t != 0) { is->u.note.dc.tm[n].t = t; is->u.note.dc.tm[n].m = -1; is->u.note.dc.n = ++n; } } is = is->abc_next; } return NULL; } /* -- parse a guitar chord / annotation -- */ static char *parse_gchord(char *p) { char *q; int l, l2; q = p; while (*p != '"') { if (*p == '\\') p++; if (*p == '\0') { syntax("No end of guitar chord", p); break; } p++; } l = p - q; if (gchord) { char *gch; /* many guitar chords: concatenate with '\n' */ l2 = strlen(gchord); gch = getarena(l2 + 1 + l + 1); strcpy(gch, gchord); gch[l2++] = '\n'; strncpy(&gch[l2], q, l); gch[l2 + l] = '\0'; gchord = gch; } else { gchord = getarena(l + 1); strncpy(gchord, q, l); gchord[l] = '\0'; } if (*p != '\0') p++; return p; } /* -- parse a note length -- */ static char *parse_len(char *p, int dur_u, int *p_len) { int len, fac; int err = 0; char *q; len = dur_u; if (isdigit((unsigned char) *p)) { len *= strtol(p, &q, 10); if (len <= 0 || len > 10000) { syntax("Bad length", p); len = dur_u; } p = q; } if (*p != '/') { *p_len = len; return p; } if (isdigit((unsigned char) p[1])) { fac = strtol(p + 1, &q, 10); p = q; if (fac == 0 || (fac & (fac - 1))) err = 1; else len /= fac; } else { while (*p == '/') { if (len & 1) err = 1; len /= 2; p++; } } if (err || !len) { syntax("Bad length divisor", p - 1); len = dur_u; } *p_len = len; return p; } /* -- parse a ABC line -- */ /* return 1 on end of tune, and 2 on start of new tune */ static int parse_line(char *p) { struct SYMBOL *s; char *q, c; char *dot = NULL; struct SYMBOL *last_note_sav = NULL; struct decos dc_sav; int i, flags, flags_sav = 0, slur; static char qtb[10] = {0, 1, 3, 2, 3, 0, 2, 0, 3, 0}; colnum = 0; switch (*p) { case '\0': /* blank line */ switch (parse.abc_state) { case ABC_S_GLOBAL: if (parse.last_sym && parse.last_sym->abc_type != ABC_T_NULL) abc_new(ABC_T_NULL, NULL); case ABC_S_HEAD: /*fixme: may have blank lines in headers?*/ return 0; } return 1; case '%': if (p[1] == '%') { s = abc_new(ABC_T_PSCOM, p); p += 2; /* skip '%%' */ if (strncasecmp(p, "decoration ", 11) == 0) { p += 11; while (isspace((unsigned char) *p)) p++; switch (*p) { case '!': char_tb['!'] = CHAR_DECOS; char_tb['+'] = CHAR_BAD; break; case '+': char_tb['+'] = CHAR_DECOS; char_tb['!'] = CHAR_BAD; break; } return 0; } if (strncasecmp(p, "linebreak ", 10) == 0) { for (i = 0; i < sizeof char_tb; i++) { if (char_tb[i] == CHAR_LINEBREAK) char_tb[i] = i != '!' ? CHAR_BAD : CHAR_DECOS; } p += 10; for (;;) { while (isspace((unsigned char) *p)) p++; if (*p == '\0') break; switch (*p) { case '!': case '$': case '*': case ';': case '?': case '@': char_tb[(unsigned char) *p++] = CHAR_LINEBREAK; break; case '<': if (strncmp(p, "<none>", 6) == 0) return 0; if (strncmp(p, "<EOL>", 5) == 0) { char_tb['\n'] = CHAR_LINEBREAK; p += 5; break; } /* fall thru */ default: if (strcmp(p, "lock") != 0) syntax("Invalid character in %%%%linebreak", p); return 0; } } return 0; } if (strncasecmp(p, "microscale ", 11) == 0) { int v; p += 11; while (isspace((unsigned char) *p)) p++; sscanf(p, "%d", &v); if (v < 4 || v >= 256 || v & 1) syntax("Invalid value in %%microscale", p); else microscale = v; return 0; } if (strncasecmp(p, "user ", 5) == 0) { p += 5; while (isspace((unsigned char) *p)) p++; get_user(p, s); return 0; } return 0; } /* fall thru */ case '\\': /* abc2mtex specific lines */ return 0; /* skip */ } /* header fields */ if (p[1] == ':' && *p != '|' && *p != ':') { /* not '|:' nor '::' */ int new_tune; new_tune = parse_info(p); /* handle BarFly voice definition */ /* 'V:n <note line ending with a bar>' */ if (*p != 'V' || parse.abc_state != ABC_S_TUNE) return new_tune; /* (normal return) */ c = p[strlen(p) - 1]; if (c != '|' && c != ']') return new_tune; while (!isspace((unsigned char) *p) && *p != '\0') p++; while (isspace((unsigned char) *p)) p++; } if (parse.abc_state != ABC_S_TUNE) return 0; /* music */ flags = 0; if (parse.abc_vers <= (2 << 16)) lyric_started = 0; deco_start = deco_cont = NULL; slur = 0; while (*p != '\0') { colnum = p - abc_line; switch (char_tb[(unsigned char) *p++]) { case CHAR_GCHORD: /* " */ if (flags & ABC_F_GRACE) goto bad_char; p = parse_gchord(p); break; case CHAR_GR_ST: /* '{' */ if (flags & ABC_F_GRACE) goto bad_char; last_note_sav = curvoice->last_note; curvoice->last_note = NULL; memcpy(&dc_sav, &dc, sizeof dc); dc.n = 0; flags_sav = flags; flags = ABC_F_GRACE; if (*p == '/') { flags |= ABC_F_SAPPO; p++; } break; case CHAR_GR_EN: /* '}' */ if (!(flags & ABC_F_GRACE)) goto bad_char; parse.last_sym->flags |= ABC_F_GR_END; if (dc.n != 0) syntax("Decoration ignored", p); curvoice->last_note = last_note_sav; memcpy(&dc, &dc_sav, sizeof dc); flags = flags_sav; break; case CHAR_DECOS: if (p[-1] == '!' && char_tb['\n'] == CHAR_LINEBREAK && check_nl(p)) { s = abc_new(ABC_T_EOLN, NULL); /* abc2win EOL */ s->u.eoln.type = 2; break; } /* fall thru */ case CHAR_DECO: if (p[-1] == '.') { if (*p == '(' || *p == '-') { dot = p; break; } // if (*p == '|') { // p = parse_bar(p + 1); // parse.last_sym->u.bar.dotted = 1; // break; // } } p = parse_deco(p - 1, &dc, -1); break; case CHAR_LINEBREAK: s = abc_new(ABC_T_EOLN, NULL); // s->u.eoln.type = 0; break; case CHAR_NOTE: p = parse_note(p - 1, flags); flags &= ABC_F_GRACE; parse.last_sym->u.note.slur_st = slur; slur = 0; if (parse.last_sym->u.note.notes[0].len > 0) /* if not space */ curvoice->last_note = parse.last_sym; break; case CHAR_SLASH: /* '/' */ if (flags & ABC_F_GRACE) goto bad_char; if (char_tb[(unsigned char) p[-1]] != CHAR_BAR) goto bad_char; q = p; while (*q == '/') q++; if (char_tb[(unsigned char) *q] != CHAR_BAR) goto bad_char; s = abc_new(ABC_T_MREP, NULL); s->u.bar.type = 0; s->u.bar.len = q - p + 1; syntax("Non standard measure repeat syntax", p - 1); p = q; break; case CHAR_BSLASH: /* '\\' */ if (*p == '\0') break; syntax("'\\' ignored", p - 1); break; case CHAR_OBRA: /* '[' */ if (*p == '|' || *p == ']' || *p == ':' || isdigit((unsigned char) *p) || *p == '"' || *p == ' ') { if (flags & ABC_F_GRACE) goto bad_char; p = parse_bar(p); break; } if (p[1] != ':') { p = parse_note(p - 1, flags); /* chord */ flags &= ABC_F_GRACE; parse.last_sym->u.note.slur_st = slur; slur = 0; curvoice->last_note = parse.last_sym; break; } /* embedded information field */ #if 0 /*fixme:OK for [I:staff n], ?? for other headers*/ if (flags & ABC_F_GRACE) goto bad_char; #endif while (p[2] == ' ') { /* remove the spaces */ p[2] = ':'; p[1] = *p; p++; } c = ']'; q = p; while (*p != '\0' && *p != c) p++; if (*p == '\0') { syntax("Escape sequence [..] not closed", q); c = '\0'; } else { *p = '\0'; } parse_info(q); *p = c; if (c != '\0') p++; break; case CHAR_BAR: /* '|', ':' or ']' */ if (flags & ABC_F_GRACE) goto bad_char; p = parse_bar(p); break; case CHAR_OPAR: /* '(' */ if (*p > '0' && *p <= '9') { int pplet, qplet, rplet; pplet = strtol(p, &q, 10); p = q; if ((unsigned) pplet < sizeof qtb / sizeof qtb[0]) qplet = qtb[pplet]; else qplet = qtb[0]; rplet = pplet; if (*p == ':') { p++; if (isdigit((unsigned char) *p)) { qplet = strtol(p, &q, 10); p = q; } if (*p == ':') { p++; if (isdigit((unsigned char) *p)) { rplet = strtol(p, &q, 10); p = q; } } } if (rplet < 1) { syntax("Invalid 'r' in tuplet", p); break; } if (pplet >= 128 || qplet >= 128 || rplet >= 128) { syntax("Invalid 'p:q:r' in tuplet", p); break; } if (qplet == 0) qplet = meter % 3 == 0 ? 3 : 2; s = abc_new(ABC_T_TUPLET, NULL); s->u.tuplet.p_plet = pplet; s->u.tuplet.q_plet = qplet; s->u.tuplet.r_plet = rplet; s->flags |= flags; break; } if (*p == '&') { if (flags & ABC_F_GRACE) goto bad_char; p++; if (vover != 0) { syntax("Nested voice overlay", p - 1); break; } s = abc_new(ABC_T_V_OVER, NULL); s->u.v_over.type = V_OVER_S; s->u.v_over.voice = curvoice - voice_tb; vover = -1; /* multi-bars */ break; } slur <<= 4; if (p == dot + 1 && dc.n == 0) slur |= SL_DOTTED; switch (*p) { case '\'': slur += SL_ABOVE; p++; break; case ',': slur += SL_BELOW; p++; break; default: slur += SL_AUTO; break; } break; case CHAR_CPAR: /* ')' */ switch (parse.last_sym->abc_type) { case ABC_T_NOTE: case ABC_T_REST: break; default: goto bad_char; } parse.last_sym->u.note.slur_end++; break; case CHAR_VOV: /* '&' */ if (flags & ABC_F_GRACE) goto bad_char; if (*p != ')' || vover == 0) { /*??*/ if (!curvoice->last_note) { syntax("Bad start of voice overlay", p); break; } s = abc_new(ABC_T_V_OVER, NULL); /*s->u.v_over.type = V_OVER_V; */ vover_new(); s->u.v_over.voice = curvoice - voice_tb; if (vover == 0) vover = 1; /* single bar */ break; } p++; vover = 0; s = abc_new(ABC_T_V_OVER, NULL); s->u.v_over.type = V_OVER_E; s->u.v_over.voice = curvoice->mvoice; curvoice->last_note = NULL; /* ?? */ curvoice = &voice_tb[curvoice->mvoice]; break; case CHAR_SPAC: /* ' ' and '\t' */ flags |= ABC_F_SPACE; break; case CHAR_MINUS: { /* '-' */ int tie_pos; if (!curvoice->last_note || curvoice->last_note->abc_type != ABC_T_NOTE) goto bad_char; if (p == dot + 1 && dc.n == 0) tie_pos = SL_DOTTED; else tie_pos = 0; switch (*p) { case '\'': tie_pos += SL_ABOVE; p++; break; case ',': tie_pos += SL_BELOW; p++; break; default: tie_pos += SL_AUTO; break; } for (i = 0; i <= curvoice->last_note->nhd; i++) { if (curvoice->last_note->u.note.notes[i].ti1 == 0) curvoice->last_note->u.note.notes[i].ti1 = tie_pos; else if (curvoice->last_note->nhd == 0) syntax("Too many ties", p); } break; } case CHAR_BRHY: /* '>' and '<' */ if (!curvoice->last_note) goto bad_char; i = 1; while (*p == p[-1]) { i++; p++; } if (i > 3) { syntax("Bad broken rhythm", p - 1); i = 3; } if (p[-1] == '<') i = -i; broken_rhythm(curvoice->last_note, i); curvoice->last_note->u.note.brhythm = i; break; case CHAR_IGN: /* '*' & '`' */ break; default: bad_char: syntax((flags & ABC_F_GRACE) ? "Bad character in grace note sequence" : "Bad character", p - 1); break; } } /*fixme: may we have grace notes across lines?*/ if (flags & ABC_F_GRACE) { syntax("EOLN in grace note sequence", p - 1); if (curvoice->last_note) curvoice->last_note->flags |= ABC_F_GR_END; curvoice->last_note = last_note_sav; memcpy(&dc, &dc_sav, sizeof dc); } /* add eoln */ s = abc_new(ABC_T_EOLN, NULL); if (flags & ABC_F_SPACE) s->flags |= ABC_F_SPACE; if (p[-1] == '\\' || char_tb['\n'] != CHAR_LINEBREAK) s->u.eoln.type = 1; /* no break */ return 0; } /* -- parse a note or a rest -- */ static char *parse_note(char *p, int flags) { struct SYMBOL *s; char *q; int pit = 0, len, acc, nostem, chord, j, m, n; if (flags & ABC_F_GRACE) { /* in a grace note sequence */ s = abc_new(ABC_T_NOTE, NULL); } else { s = abc_new(ABC_T_NOTE, gchord); if (gchord) gchord = NULL; } s->flags |= flags; s->u.note.notes[0].color = -1; if (!lyric_started) { lyric_started = 1; s->flags |= ABC_F_LYRIC_START; } if (*p != 'X' && *p != 'Z' && !(flags & ABC_F_GRACE)) { if (!deco_start) deco_start = s; } chord = 0; /* rest */ switch (*p) { case 'X': s->flags |= ABC_F_INVIS; case 'Z': /* multi-rest */ s->abc_type = ABC_T_MREST; p++; len = 1; if (isdigit((unsigned char) *p)) { len = strtol(p, &q, 10); if (len == 0 || len > 100) { syntax("Bad number of measures", p); len = 1; } p = q; } s->u.bar.type = 0; s->u.bar.len = len; goto add_deco; case 'y': /* space (BarFly) */ s->abc_type = ABC_T_REST; s->flags |= ABC_F_INVIS; p++; if (isdigit((unsigned char) *p) /* number of points */ || *p == '-') { /* accept negative offset... */ s->u.note.notes[0].shhd = strtol(p, &q, 10); p = q; } else { s->u.note.notes[0].shhd = 10; // default } goto add_deco; case 'x': /* invisible rest */ s->flags |= ABC_F_INVIS; /* fall thru */ case 'z': s->abc_type = ABC_T_REST; p = parse_len(p + 1, ulen, &len); s->u.note.notes[0].len = len; goto do_brhythm; case '[': /* '[..]' = chord */ chord = 1; p++; break; } q = p; /* get pitch, length and possible accidental */ m = 0; nostem = 0; for (;;) { if (chord) { if (m >= MAXHD) { syntax("Too many notes in chord", p); m--; } n = 0; if (*p == '.') { n = SL_DOTTED; p++; } if (*p == '(') { p++; switch (*p) { case '\'': n += SL_ABOVE; p++; break; case ',': n += SL_BELOW; p++; break; default: n += SL_AUTO; break; } s->u.note.notes[m].sl1 = (s->u.note.notes[m].sl1 << 3) + n; } } p = parse_deco(p, &dc, m); /* note head decorations */ p = parse_acc_pit(p, &pit, &acc); if (*p == '0') { nostem = 1; p++; } p = parse_len(p, (flags & ABC_F_GRACE) ? BASE_LEN / 8 : // for grace note alone ulen, &len); s->u.note.notes[m].pit = pit; s->pits[m] = pit; s->u.note.notes[m].len = len; s->u.note.notes[m].acc = acc; s->u.note.notes[m].color = -1; if (chord) { for (;;) { if (*p == '.') { if (p[1] != '-') break; p++; } if (*p == '-') { switch (p[1]) { case '\'': s->u.note.notes[m].ti1 = SL_ABOVE; p++; break; case ',': s->u.note.notes[m].ti1 = SL_BELOW; p++; break; default: s->u.note.notes[m].ti1 = SL_AUTO; break; } } else if (*p == ')') { s->u.note.notes[m].sl2++; } else { break; } p++; } } if (acc >= 0) /* if no error */ m++; /* normal case */ if (!chord) break; if (*p == ']') { p++; if (*p == '0') { nostem = 1; p++; } if (*p == '/' || isdigit((unsigned char) *p)) { p = parse_len(p, ulen, &len); for (j = 0; j < m; j++) { s->u.note.notes[j].len = len * s->u.note.notes[j].len / ulen; } } break; } if (*p == '\0') { syntax("Chord not closed", q); break; } } if (nostem) s->flags |= ABC_F_STEMLESS; if (m == 0) /* if no note (or error) */ goto err; s->u.note.microscale = microscale; s->nhd = m - 1; do_brhythm: if (curvoice->last_note && curvoice->last_note->u.note.brhythm != 0) broken_rhythm(s, -curvoice->last_note->u.note.brhythm); add_deco: if (dc.n > 0) { memcpy(s->abc_type != ABC_T_MREST ? &s->u.note.dc : &s->u.bar.dc, &dc, sizeof dc); dc.n = 0; } /* forbid rests in grace note sequences */ if (s->abc_type != ABC_T_NOTE && (flags & ABC_F_GRACE)) { syntax("Not a note in grace note sequence", p); goto err; } return p; err: if ((parse.last_sym = s->abc_prev) == NULL) { parse.first_sym = NULL; } else { s->abc_prev->abc_next = NULL; s->abc_prev->flags |= (s->flags & ABC_F_ERROR); } return p; } /* -- parse an information field -- */ /* return 2 on start of new tune */ static int parse_info(char *p) { struct SYMBOL *s; char info_type = *p; char *error_txt = NULL; s = abc_new(ABC_T_INFO, p); p += 2; switch (info_type) { case 'd': case 's': if (parse.abc_state == ABC_S_GLOBAL) break; if (!deco_start) { error_txt = "Erroneous 'd:'/'s:'"; break; } error_txt = parse_decoline(p); break; case 'K': if (parse.abc_state == ABC_S_GLOBAL) break; parse_key(p, s); if (parse.abc_state == ABC_S_HEAD) { int i; parse.abc_state = ABC_S_TUNE; if (ulen == 0) ulen = BASE_LEN / 8; for (i = MAXVOICE; --i >= 0; ) voice_tb[i].ulen = ulen; lyric_started = 0; } break; case 'L': error_txt = get_len(p, s); if (s->u.length.base_length > 0) ulen = s->u.length.base_length; break; case 'M': error_txt = parse_meter(p, s); break; case 'Q': error_txt = parse_tempo(p, s); break; case 'U': error_txt = get_user(p, s); break; case 'V': if (parse.abc_state == ABC_S_GLOBAL) break; error_txt = parse_voice(p, s); break; case 'X': memset(voice_tb, 0, sizeof voice_tb); nvoice = 0; curvoice = voice_tb; parse.abc_state = ABC_S_HEAD; lvlarena(1); return 2; } if (error_txt) syntax(error_txt, p); return 0; } /* -- print a syntax error message -- */ static void syntax(char *msg, char *q) { int n, len, m1, m2, pp; int maxcol = 73; severity = 1; n = q - abc_line; len = strlen(abc_line); if ((unsigned) n > (unsigned) len) n = -1; print_error(msg, n); if (n < 0) { if (q && *q != '\0') fprintf(stderr, " (near '%s')\n", q); return; } m1 = 0; m2 = len; if (m2 > maxcol) { if (n < maxcol) { m2 = maxcol; } else { m1 = n - 20; m2 = m1 + maxcol; if (m2 > len) m2 = len; } } fprintf(stderr, "%4d ", linenum); pp = 6; if (m1 > 0) { fprintf(stderr, "..."); pp += 3; } fprintf(stderr, "%.*s", m2 - m1, &abc_line[m1]); if (m2 < len) fprintf(stderr, "..."); fprintf(stderr, "\n"); if ((unsigned) n < 200) fprintf(stderr, "%*s\n", n + pp - m1, "^"); if (last_sym) last_sym->flags |= ABC_F_ERROR; } /* -- switch to a new voice overlay -- */ static void vover_new(void) { int voice, mvoice; mvoice = curvoice->mvoice; for (voice = curvoice - voice_tb + 1; voice <= nvoice; voice++) if (voice_tb[voice].mvoice == mvoice) break; if (voice > nvoice) { if (nvoice >= MAXVOICE) { syntax("Too many voices", 0); return; } nvoice = voice; voice_tb[voice].id[0] = '&'; voice_tb[voice].mvoice = mvoice; } voice_tb[voice].ulen = curvoice->ulen; voice_tb[voice].microscale = curvoice->microscale; curvoice = &voice_tb[voice]; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������abcm2ps-8.14.11/accordion.abc�����������������������������������������������������������������������0000664�0000000�0000000�00000004450�13762665467�0015656�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������% tablature examples % --- diatonic accordion %%beginps % length x y n accordh - accordion tablature header with 2 or 3 lines /accordh{ % /Times-Roman 14 selectfont /nlines exch def .8 SLW gsave 20 add T /w exch def 0 0 M w 0 RL 0 3 M w 0 RL 0 21 M w 0 RL 0 39 M w 0 RL nlines 2 eq{ 0 42 M w 0 RL stroke /barh 42 def 3 7 M (T:) show 3 25 M (P:) show }{ 0 57 M w 0 RL 0 60 M w 0 RL stroke /barh 60 def 3 7 M (B/a:) show 3 25 M (T:) show 3 43 M (P:) show }ifelse grestore }! % string x y n accordn - accordion tablature /accordn{ -18 mul add barh add 3 add M showc }! /accordb{ 0 eq{20 add M 0 barh RL stroke} {pop pop}ifelse pop}! %%endps X:3 T:Andro à Patrice Corbel N: validé 03/05/01 -- 04/08/02 R:Andro O:Bretagne M:4/4 K:Am %%tablature 70 accordh accordn accordb "A"A2 cA "F" aAce | "G"dGBd "C"e4 | "F"fedc "G"B2 AB |1 "F"cABc "E"B4 :|2 "F"cABc "A"A4 :| w: * * * * * * * 8 6 7 8 7' * * * * * * * * * * * 7 * * * * * w: 7 8 7 11 7 8 9 * * * * * 8' 9 7' 8 6' 7 6' 8 7 6' 8 * 8 7 6' 8 7 "A"A2 GF "E"E3 B | "E"c2 d2 "C"e4 | "F"fedc "G"B2 AB |1 "F"cABc "E"B4 :|2 "F"cABc "A"A4 :| w: * 6 * 4' 7 6' 8 7' * * * * * * * * * * * 7 * * * * * w: 7 * 4' * * * * * 8' 9 7' 8 6' 7 6' 8 7 6' 8 * 8 7 6' 8 7 X:51 T:Jeune fille de quinze ans (50-51-52) T:Une fille de rose N: rev. 01/06/01 R:hanter dro C:trad A:Bretagne O:France M:3/4 L:1/8 Q:100 K:Am %%tablature 90 accordh accordn accordb |: "Am" ee/f/ "Am" ed "Am" c2 | "Am" ed/c/ "G" Bd "Am" cA | "Am" ee/f/ "Am" ed "Am" c2 |1 "Am" ed/c/ "G" BG "Em" E2 :|2 "Am" ed/c/ "G" BG "Am" A2 | w: * * * * * * | * * * 7 8 * * | * * * * * * | * * * 7 6 4'| * * * 7 6 * w: 9 9 8' 9 7' 8 | 9 7' 8 * * 8 7 | 9 9 8' 9 7' 8 | 9 7' 8 * * * | 9 7' 8 * * 7 w: A a * A a A~~a | A a * G g E e | A a * A a A~~a | A a * G g E~~e | A a * G g E~~e | |: "Am" AA/B/ "Am" cd "C" e2 | "G" de/f/ "F" af "Am" e2 | "Am" cd/e/ "F" fe "G" d2 |1 "G" Bc/d/ "Em" cB "Em" cB :|2 "G" Bc/d/ "Em" cB "Am" A2 || w: * * * * * 7' | 8 7' * * * * | * * * * * 8 | 7 6' 8 6' 7 6' 7 | 7 6' 8 6' 7 * w: 7 7 6' 8 7' * | * * 8' 9' 8' 9 | 8 7' 9 8' 9 * | * * * * * * * | * * * * * 7 w: A a * A a C~c | G g * F f A~a | A a * F f G~g | G g * E e E e | G g * E e A~a | ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������abcm2ps-8.14.11/bravura.abc�������������������������������������������������������������������������0000664�0000000�0000000�00000004727�13762665467�0015366�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������% example of musical symbols by Bravura % --- definitions for SVG output --- %%beginsvg <style type="text/css"> @font-face { font-family: 'Bravura'; src: url(/home/jef/abcm2ps-devel/bravura/woff/Bravura.woff); font-weight: normal; font-style: normal; } </style> <defs> <text id="brace" x="-3" y="0" transform="scale(3,-4.2)" font-family="Bravura" font-size="24" font-weight="normal" font-style="normal"></text> <text id="tclef" x="-8" y="-6" font-family="Bravura" font-size="24" font-weight="normal" font-style="normal"></text> <text id="cclef" x="-7" y="-12" font-family="Bravura" font-size="24" font-weight="normal" font-style="normal"></text> <text id="bclef" x="-7" y="-18" font-family="Bravura" font-size="24" font-weight="normal" font-style="normal"></text> <text id="csig" x="-5" y="-12" font-family="Bravura" font-size="24" font-weight="normal" font-style="normal"></text> <text id="ft0" x="-2" y="0" font-family="Bravura" font-size="24" font-weight="normal" font-style="normal"></text> <text id="nt0" x="-2" y="0" font-family="Bravura" font-size="24" font-weight="normal" font-style="normal"></text> <text id="sh0" x="-2" y="0" font-family="Bravura" font-size="24" font-weight="normal" font-style="normal"></text> <text id="dsh0" x="-2" y="0" font-family="Bravura" font-size="24" font-weight="normal" font-style="normal"></text> <text id="dft0" x="-3" y="0" font-family="Bravura" font-size="24" font-weight="normal" font-style="normal"></text> <text id="turn" x="-4" y="0" font-family="Bravura" font-size="24" font-weight="normal" font-style="normal"></text> </defs> %%endsvg % --- definitions for PostScript output --- %%beginps nosvg /brace{/Bravura 24 selectfont gsave T -7.5 0 M -.042 mul 3 exch scale /uniE000 glyphshow grestore}! /tclef{/Bravura 24 selectfont M -8 6 RM/uniE050 glyphshow}! /cclef{/Bravura 24 selectfont M -8 12 RM/uniE05C glyphshow}! /bclef{/Bravura 24 selectfont M -8 18 RM/uniE062 glyphshow}! /csig{/Bravura 24 selectfont M -5 12 RM/uniE08A glyphshow}! /ft0{/Bravura 24 selectfont M -2 0 RM/uniE260 glyphshow}! /nt0{/Bravura 24 selectfont M -2 0 RM/uniE261 glyphshow}! /sh0{/Bravura 24 selectfont M -2 0 RM/uniE262 glyphshow}! /dsh0{/Bravura 24 selectfont M -2 0 RM/uniE263 glyphshow}! /dft0{/Bravura 24 selectfont M -3 0 RM/uniE264 glyphshow}! /turn{/Bravura 24 selectfont M -4 0 RM/uniE567 glyphshow}! %%endps �����������������������������������������abcm2ps-8.14.11/buffer.c����������������������������������������������������������������������������0000664�0000000�0000000�00000055121�13762665467�0014664�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Postscript buffering functions. * * This file is part of abcm2ps. * * Copyright (C) 1998-2019 Jean-François Moine * Adapted from abc2ps, Copyright (C) 1996-1998 Michael Methfessel * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. */ #include <stdarg.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include "abcm2ps.h" #define PPI_96_72 0.75 // convert page format to 72 PPI #define BUFFLN 80 /* max number of lines in output buffer */ static int ln_num; /* number of lines in buffer */ static float ln_pos[BUFFLN]; /* vertical positions of buffered lines */ static char *ln_buf[BUFFLN]; /* buffer location of buffered lines */ static float ln_lmarg[BUFFLN]; /* left margin of buffered lines */ static float ln_scale[BUFFLN]; /* scale of buffered lines */ static signed char ln_font[BUFFLN]; /* font of buffered lines */ static float cur_lmarg = 0; /* current left margin */ static float min_lmarg, max_rmarg; /* margins for -E/-g */ static float cur_scale = 1.0; /* current scale */ static float maxy; /* usable vertical space in page */ static float remy; /* remaining vertical space in page */ static float bposy; /* current position in buffered data */ static int nepsf; /* counter for -E/-g output files */ static int nbpages; /* number of pages in the output file */ int outbufsz; /* size of outbuf */ static char outfnam[FILENAME_MAX]; /* internal file name for open/close */ static struct FORMAT *p_fmt; /* current format while treating a new page */ int (*output)(FILE *out, const char *fmt, ...); int in_page; /* filling a PostScript page */ char *outbuf; /* output buffer.. should hold one tune */ char *mbf; /* where to a2b() */ int use_buffer; /* 1 if lines are being accumulated */ int (*output)(FILE *out, const char *fmt, ...) #ifdef __GNUC__ __attribute__ ((format (printf, 2, 3))) #endif ; /* -- cut off extension on a file identifier -- */ static void cutext(char *fid) { char *p; if ((p = strrchr(fid, DIRSEP)) == NULL) p = fid; if ((p = strrchr(p, '.')) != NULL) *p = '\0'; } /* -- open the output file -- */ void open_fout(void) { int i; char fnm[FILENAME_MAX]; strcpy(fnm, outfn); i = strlen(fnm) - 1; if (i < 0) { strcpy(fnm, svg || epsf > 1 ? "Out.xhtml" : OUTPUTFILE); } else if (i != 0 || fnm[0] != '-') { if (fnm[i] == '=' && in_fname) { char *p; if ((p = strrchr(in_fname, DIRSEP)) == NULL) p = in_fname; else p++; strcpy(&fnm[i], p); strext(fnm, svg || epsf > 1 ? "xhtml" : "ps"); } else if (fnm[i] == DIRSEP) { strcpy(&fnm[i + 1], svg || epsf > 1 ? "Out.xhtml" : OUTPUTFILE); } #if 0 /*fixme: fnm may be a directory*/ else ... #endif } if (svg == 1 && (i != 0 || fnm[0] != '-')) { cutext(fnm); i = strlen(fnm) - 1; if (strncmp(fnm, outfnam, i) != 0) nepsf = 0; sprintf(&fnm[i + 1], "%03d.svg", ++nepsf); } else if (strcmp(fnm, outfnam) == 0) { return; /* same output file */ } close_output_file(); strcpy(outfnam, fnm); if (i != 0 || fnm[0] != '-') { if ((fout = fopen(fnm, "w")) == NULL) { error(1, NULL, "Cannot create output file %s - abort", fnm); exit(EXIT_FAILURE); } } else { fout = stdout; } } /* -- convert a date -- */ static void cnv_date(time_t *ltime) { char buf[TEX_BUF_SZ]; tex_str(cfmt.dateformat); strcpy(buf, tex_buf); strftime(tex_buf, TEX_BUF_SZ, buf, localtime(ltime)); } /* initialize the min/max margin values */ /* (used only with eps -E and svg -g) */ void marg_init(void) { min_lmarg = cfmt.pagewidth; max_rmarg = cfmt.pagewidth; } /* -- initialize the postscript file (PS or EPS) -- */ static void init_ps(char *str) { time_t ltime; unsigned i; char version[] = "/creator [(abcm2ps) " VERSION "] def"; if (epsf) { cur_lmarg = min_lmarg - 10; fprintf(fout, "%%!PS-Adobe-2.0 EPSF-2.0\n" "%%%%BoundingBox: 0 0 %.0f %.0f\n", ((p_fmt->landscape ? p_fmt->pageheight : p_fmt->pagewidth) - cur_lmarg - max_rmarg + 10) * PPI_96_72, -bposy * PPI_96_72); marg_init(); } else { if (!fout) open_fout(); fprintf(fout, "%%!PS-Adobe-2.0\n"); fprintf(fout, "%%%%BoundingBox: 0 0 %.0f %.0f\n", p_fmt->pagewidth * PPI_96_72, p_fmt->pageheight * PPI_96_72); } fprintf(fout, "%%%%Title: %s\n", str); time(<ime); #ifndef WIN32 strftime(tex_buf, TEX_BUF_SZ, "%b %e, %Y %H:%M", localtime(<ime)); #else strftime(tex_buf, TEX_BUF_SZ, "%b %#d, %Y %H:%M", localtime(<ime)); #endif fprintf(fout, "%%%%Creator: abcm2ps-" VERSION "\n" "%%%%CreationDate: %s\n", tex_buf); if (!epsf) fprintf(fout, "%%%%Pages: (atend)\n"); fprintf(fout, "%%%%LanguageLevel: 3\n" "%%%%EndComments\n" "%%CommandLine:"); for (i = 1; i < (unsigned) s_argc; i++) { char *p, *q; int space; p = s_argv[i]; space = strchr(p, ' ') != NULL || strchr(p, '\n') != NULL; fputc(' ', fout); if (space) fputc('\'', fout); for (;;) { q = strchr(p, '\n'); if (!q) break; fprintf(fout, " %.*s\n%%", (int) (q - p), p); p = q + 1; } fprintf(fout, "%s", p); if (space) fputc('\'', fout); } fprintf(fout, "\n\n"); if (epsf) fprintf(fout, "save\n"); for (i = 0; i < strlen(version); i++) { if (version[i] == '.') version[i] = ' '; } fprintf(fout, "%%%%BeginSetup\n" "/!{bind def}bind def\n" "/bdef{bind def}!\n" /* for compatibility */ "/T/translate load def\n" "/M/moveto load def\n" "/RM/rmoveto load def\n" "/L/lineto load def\n" "/RL/rlineto load def\n" "/C/curveto load def\n" "/RC/rcurveto load def\n" "/SLW/setlinewidth load def\n" "/defl 0 def\n" /* decoration flags - see deco.c for values */ "/dlw{0.7 SLW}!\n" "%s\n", version); define_symbols(); output = fprintf; user_ps_write(); define_fonts(); if (!epsf) { fprintf(fout, "/setpagedevice where{pop\n" " <</PageSize[%.0f %.0f]", p_fmt->pagewidth * PPI_96_72, p_fmt->pageheight * PPI_96_72); if (cfmt.gutter) fprintf(fout, "\n /BeginPage{1 and 0 eq{%.1f 0 T}{-%.1f 0 T}ifelse}bind\n ", cfmt.gutter, cfmt.gutter); fprintf(fout, ">>setpagedevice}if\n"); } fprintf(fout, "%%%%EndSetup\n"); file_initialized = 1; } /* -- initialize the svg file (option '-g') -- */ static void init_svg(char *str) { cur_lmarg = min_lmarg - 10; output = svg_output; #if 1 //fixme:test if (file_initialized > 0) fprintf(stderr, "??? init_svg: file_initialized\n"); #endif define_svg_symbols(str, nepsf, (p_fmt->landscape ? p_fmt->pageheight : p_fmt->pagewidth) - cur_lmarg - max_rmarg + 10, -bposy); file_initialized = 1; user_ps_write(); } static void close_fout(void) { long m; if (fout == stdout) goto out2; if (quiet) goto out1; m = ftell(fout); if (epsf || svg == 1) printf("Output written on %s (%ld bytes)\n", outfnam, m); else printf("Output written on %s (%d page%s, %d title%s, %ld bytes)\n", outfnam, nbpages, nbpages == 1 ? "" : "s", tunenum, tunenum == 1 ? "" : "s", m); out1: fclose(fout); out2: fout = NULL; file_initialized = 0; } /* -- close the output file -- */ /* epsf is always null */ void close_output_file(void) { if (!fout) return; if (multicol_start != 0) { /* if no '%%multicol end' */ error(1, NULL, "No \"%%%%multicol end\""); multicol_start = 0; write_buffer(); } if (tunenum == 0) error(0, NULL, "No tunes written to output file"); close_page(); switch (svg) { case 0: /* PS */ if (epsf == 0) fprintf(fout, "%%%%Trailer\n" "%%%%Pages: %d\n" "%%EOF\n", nbpages); close_fout(); break; case 2: /* -X */ fputs("</body>\n" "</html>\n", fout); case 3: /* -z */ close_fout(); break; // default: // case 1: /* -v */ // 'fout' is closed in close_page } nbpages = tunenum = 0; defl = 0; } /* -- close the PS / SVG page -- */ void close_page(void) { if (!in_page) return; in_page = 0; if (svg) { svg_close(); if (svg == 1 && fout != stdout) close_fout(); // else // fputs("</p>\n", fout); } else { fprintf(fout, "grestore\n" "showpage\n" "%%%%EndPage: %d %d\n", nbpages, nbpages); } cur_lmarg = 0; cur_scale = 1.0; outft = -1; use_buffer = 0; } /* -- output a header/footer element -- */ static void format_hf(char *d, char *p) { char *q; time_t ltime; for (;;) { if (*p == '\0') break; if ((q = strchr(p, '$')) != NULL) *q = '\0'; d += sprintf(d, "%s", p); if (!q) break; p = q + 1; switch (*p) { case 'd': ltime = mtime; goto dput; case 'D': time(<ime); dput: cnv_date(<ime); d += sprintf(d, "%s", tex_buf); break; case 'F': /* ABC file name */ #if DIRSEP!='\\' d += sprintf(d, "%s", in_fname); #else { int i; q = in_fname; i = TEX_BUF_SZ; for (;;) { if (--i <= 0 || *q == '\0') break; if ((*d++ = *q++) == '\\') { i--; *d++ = '\\'; } } *d = '\0'; } #endif break; case 'I': /* information field */ p++; if (*p < 'A' || *p > 'Z' || !info[*p - 'A']) break; d += sprintf(d, "%s", &info[*p - 'A']->text[2]); break; case 'P': /* page number */ if (p[1] == '0') { p++; if (pagenum & 1) break; } else if (p[1] == '1') { p++; if ((pagenum & 1) == 0) break; } d += sprintf(d, "%d", pagenum); break; case 'Q': /* non-resetting page number */ if (p[1] == '0') { p++; if (pagenum_nr & 1) break; } else if (p[1] == '1') { p++; if ((pagenum_nr & 1) == 0) break; } d += sprintf(d, "%d", pagenum_nr); break; case 'T': /* tune title */ q = &info['T' - 'A']->text[2]; tex_str(q); d += sprintf(d, "%s", tex_buf); break; case 'V': d += sprintf(d,"abcm2ps-" VERSION); break; default: continue; } p++; } *d = '\0'; /* in case of empty string */ } /* -- output the header or footer -- */ static float headfooter(int header, float pwidth, float pheight) { char tmp[2048], str[TEX_BUF_SZ + 512]; char *p, *q, *r, *outbuf_sav, *mbf_sav; float size, y, wsize; struct FONTSPEC *f, f_sav; int cft_sav, dft_sav, outbufsz_sav; if (header) { p = cfmt.header; f = &cfmt.font_tb[HEADERFONT]; size = f->size; y = -size; } else { p = cfmt.footer; f = &cfmt.font_tb[FOOTERFONT]; size = f->size; y = - (pheight - cfmt.topmargin - cfmt.botmargin) + size; } if (*p == '-') { if (pagenum == 1) return 0; p++; } get_str_font(&cft_sav, &dft_sav); memcpy(&f_sav, &cfmt.font_tb[0], sizeof f_sav); str_font(f - cfmt.font_tb); output(fout, "%.1f F%d ", size, f->fnum); outft = f - cfmt.font_tb; /* may have 2 lines */ wsize = size; if ((r = strstr(p, "\\n")) != NULL) { if (!header) y += size; wsize += size; *r = '\0'; } mbf_sav = mbf; outbuf_sav = outbuf; outbufsz_sav = outbufsz; outbuf = tmp; outbufsz = sizeof tmp; for (;;) { tex_str(p); strcpy(tmp, tex_buf); format_hf(str, tmp); /* left side */ p = str; if ((q = strchr(p, '\t')) != NULL) { if (q != p) { *q = '\0'; output(fout, "%.1f %.1f M ", p_fmt->leftmargin, y); mbf = tmp; str_out(p, A_LEFT); a2b("\n"); if (svg) svg_write(tmp, strlen(tmp)); else fputs(tmp, fout); } p = q + 1; } if ((q = strchr(p, '\t')) != NULL) *q = '\0'; /* center */ if (q != p) { output(fout, "%.1f %.1f M ", pwidth * 0.5, y); mbf = tmp; str_out(p, A_CENTER); a2b("\n"); if (svg) svg_write(tmp, strlen(tmp)); else fputs(tmp, fout); } /* right side */ if (q) { p = q + 1; if (*p != '\0') { output(fout, "%.1f %.1f M ", pwidth - p_fmt->rightmargin, y); mbf = tmp; str_out(p, A_RIGHT); a2b("\n"); if (svg) svg_write(tmp, strlen(tmp)); else fputs(tmp, fout); } } if (!r) break; *r = '\\'; p = r + 2; r = NULL; y -= size; } /* restore the buffer and fonts */ outbuf = outbuf_sav; outbufsz = outbufsz_sav; mbf = mbf_sav; memcpy(&cfmt.font_tb[0], &f_sav, sizeof cfmt.font_tb[0]); set_str_font(cft_sav, dft_sav); return wsize; } /* -- initialize the first page or a new page for svg -- */ /* the flag 'in_page' is always false and epsf is always null */ static void init_page(void) { float pheight, pwidth; p_fmt = !info['X' - 'A'] ? &cfmt : &dfmt; /* global format */ nbpages++; if (svg) { if (file_initialized <= 0) { if (!fout) open_fout(); define_svg_symbols(in_fname, nbpages, cfmt.landscape ? p_fmt->pageheight : p_fmt->pagewidth, cfmt.landscape ? p_fmt->pagewidth : p_fmt->pageheight); user_ps_write(); file_initialized = 1; output = svg_output; } else { define_svg_symbols(in_fname, nbpages, cfmt.landscape ? p_fmt->pageheight : p_fmt->pagewidth, cfmt.landscape ? p_fmt->pagewidth : p_fmt->pageheight); } } else if (file_initialized <= 0) { init_ps(in_fname); } in_page = 1; outft = -1; if (!svg) fprintf(fout, "%%%%Page: %d %d\n", nbpages, nbpages); if (cfmt.landscape) { pheight = p_fmt->pagewidth; pwidth = cfmt.pageheight; if (!svg) fprintf(fout, "%%%%PageOrientation: Landscape\n" "gsave 0.75 dup scale 90 rotate 0 %.1f T\n", -cfmt.topmargin); } else { pheight = cfmt.pageheight; pwidth = p_fmt->pagewidth; if (!svg) fprintf(fout, "gsave 0.75 dup scale 0 %.1f T\n", pheight - cfmt.topmargin); } if (svg) output(fout, "0 %.1f T\n", -cfmt.topmargin); else output(fout, "%% --- width %.1f\n", /* for index */ (pwidth - cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale); remy = maxy = pheight - cfmt.topmargin - cfmt.botmargin; /* output the header and footer */ if (!cfmt.header) { char *p = NULL; switch (pagenumbers) { case 1: p = "$P\t"; break; case 2: p = "\t\t$P"; break; case 3: p = "$P0\t\t$P1"; break; case 4: p = "$P1\t\t$P0"; break; } if (p) cfmt.header = strdup(p); } if (cfmt.header) { float dy; dy = headfooter(1, pwidth, pheight); if (dy != 0) { output(fout, "0 %.1f T\n", -dy); remy -= dy; } } if (cfmt.footer) remy -= headfooter(0, pwidth, pheight); pagenum++; pagenum_nr++; outft = -1; } /* -- adjust the tune title part of the output file name -- */ static void epsf_fn_adj(char *p) { char c; while ((c = *p) != '\0') { if (c == ' ') *p = '_'; else if (c == DIRSEP || (unsigned) c >= 127) *p = '.'; p++; } } /* -- build the title of the eps/svg file and check if correct utf-8 -- */ static void epsf_title(char *p, int sz) { unsigned char c; snprintf(p, sz, "%.72s (%.4s)", in_fname, &info['X' - 'A']->text[2]); while ((c = (unsigned char) *p) != '\0') { if (c >= 0x80) { if ((c & 0xf8) == 0xf0) { if ((p[1] & 0xc0) != 0x80 && (p[2] & 0xc0) != 0x80 && (p[3] & 0xc0) != 0x80) *p = ' '; } else if ((c & 0xf0) == 0xe0) { if ((p[1] & 0xc0) != 0x80 && (p[2] & 0xc0) != 0x80) *p = ' '; } else if ((c & 0xe0) == 0xc0) { if ((p[1] & 0xc0) != 0x80) *p = ' '; } else { *p = ' '; } } p++; } } /* -- output a EPS (-E) or SVG (-g) file -- */ void write_eps(void) { unsigned i; char *p, title[80]; if (mbf == outbuf || !info['X' - 'A']) return; p_fmt = &cfmt; /* tune format */ if (epsf != 3) { /* if not -z */ strcpy(outfnam, outfn); if (outfnam[0] == '\0') strcpy(outfnam, OUTPUTFILE); cutext(outfnam); i = strlen(outfnam) - 1; if (i == 0 && outfnam[0] == '-') { if (epsf == 1) { error(1, NULL, "Cannot use stdout with '-E' - abort"); exit(EXIT_FAILURE); } fout = stdout; } else { if (outfnam[i] == '=') { p = &info['T' - 'A']->text[2]; while (isspace((unsigned char) *p)) p++; strncpy(&outfnam[i], p, sizeof outfnam - i - 4); outfnam[sizeof outfnam - 5] = '\0'; epsf_fn_adj(&outfnam[i]); } else { if (i >= sizeof outfnam - 4 - 3) i = sizeof outfnam - 4 - 3; sprintf(&outfnam[i + 1], "%03d", ++nepsf); } strcat(outfnam, epsf == 1 ? ".eps" : ".svg"); if ((fout = fopen(outfnam, "w")) == NULL) { error(1, NULL, "Cannot open output file %s - abort", outfnam); exit(EXIT_FAILURE); } } } epsf_title(title, sizeof title); if (epsf == 1) { init_ps(title); fprintf(fout, "0.75 dup scale 0 %.1f T\n", -bposy); write_buffer(); fprintf(fout, "showpage\nrestore\n"); } else { if (epsf == 3 && file_initialized == 0) fputs("<br/>\n", fout); /* new image in the same flow */ init_svg(title); write_buffer(); svg_close(); } if (epsf != 3) close_fout(); else file_initialized = 0; cur_lmarg = 0; cur_scale = 1.0; } /* subroutines to handle output buffer */ /* -- update the output buffer pointer -- */ void a2b(char *fmt, ...) { va_list args; if (mbf + BSIZE > outbuf + outbufsz) { if (epsf) { error(1, NULL, "Output buffer overflow - increase outbufsz"); fprintf(stderr, "*** abort\n"); exit(EXIT_FAILURE); } error(0, NULL, "Possible buffer overflow"); write_buffer(); // use_buffer = 0; } va_start(args, fmt); mbf += vsnprintf(mbf, outbuf + outbufsz - mbf, fmt, args); va_end(args); } /* -- translate down by 'h' scaled points in output buffer -- */ void bskip(float h) { if (h == 0) return; bposy -= h * cfmt.scale; a2b("0 %.2f T\n", -h); } /* -- initialize the output buffer -- */ void init_outbuf(int kbsz) { if (outbuf) free(outbuf); outbufsz = kbsz * 1024; // if (outbufsz < 0x10000) // outbufsz = 0x10000; outbuf = malloc(outbufsz); if (!outbuf) { error(1, NULL, "Out of memory for outbuf - abort"); exit(EXIT_FAILURE); } bposy = 0; ln_num = 0; mbf = outbuf; } /* -- write buffer contents, break at full pages -- */ void write_buffer(void) { char *p_buf; int l, np; float p1, dp; int outft_sav; if (mbf == outbuf || multicol_start != 0) return; if (!in_page && !epsf) init_page(); outft_sav = outft; p1 = 0; p_buf = outbuf; for (l = 0; l < ln_num; l++) { if (ln_pos[l] > 0) { /* if in multicol */ int ll; float pos; for (ll = l + 1; ll < ln_num; ll++) { if (ln_pos[ll] <= 0) { pos = ln_pos[ll]; while (--ll >= l) ln_pos[ll] = pos; break; } } } dp = ln_pos[l] - p1; np = remy + dp < 0 && !epsf; if (np) { close_page(); init_page(); if (ln_font[l] >= 0) { struct FONTSPEC *f; f = &cfmt.font_tb[ln_font[l]]; output(fout, "%.1f F%d\n", f->size, f->fnum); } } if (ln_scale[l] != cur_scale) { output(fout, "%.3f dup scale\n", ln_scale[l] / cur_scale); cur_scale = ln_scale[l]; } if (ln_lmarg[l] != cur_lmarg) { output(fout, "%.2f 0 T\n", (ln_lmarg[l] - cur_lmarg) / cur_scale); cur_lmarg = ln_lmarg[l]; } if (np) { output(fout, "0 %.2f T\n", -cfmt.topspace); remy -= cfmt.topspace * cfmt.scale; } if (*p_buf != '\001') { if (epsf > 1 || svg) svg_write(p_buf, ln_buf[l] - p_buf); else fwrite(p_buf, 1, ln_buf[l] - p_buf, fout); } else { /* %%EPS - see parse.c */ FILE *f; char line[BSIZE], *p, *q; p = strchr(p_buf + 1, '\n'); fwrite(p_buf + 1, 1, p - p_buf, fout); p_buf = p + 1; p = strchr(p_buf, '%'); *p++ = '\0'; q = strchr(p, '\n'); *q = '\0'; if ((f = fopen(p, "r")) == NULL) { error(1, NULL, "Cannot open EPS file '%s'", p); } else { if (epsf > 1 || svg) { fprintf(fout, "<!--Begin document %s-->\n", p); svg_output(fout, "gsave\n" "%s T\n", p_buf); while (fgets(line, sizeof line, f)) /* copy the file */ svg_write(line, strlen(line)); svg_output(fout, "grestore\n" "%s T\n", p_buf); fprintf(fout, "<!--End document %s-->\n", p); } else { fprintf(fout, "save\n" "/showpage{}def/setpagedevice{pop}def\n" "%s T\n" "%%%%BeginDocument: %s\n", p_buf, p); while (fgets(line, sizeof line, f)) /* copy the file */ fwrite(line, 1, strlen(line), fout); fprintf(fout, "%%%%EndDocument\n" "restore\n"); } fclose(f); } } p_buf = ln_buf[l]; remy += dp; p1 = ln_pos[l]; } #if 1 //fixme:test if (*p_buf != '\0') { // fprintf(stderr, "??? bug - buffer not empty:\n%s\n", p_buf); memcpy(outbuf, p_buf, strlen(p_buf) + 1); mbf = &outbuf[strlen(outbuf)]; } else { mbf = outbuf; } #endif outft = outft_sav; bposy = 0; ln_num = 0; if (epsf != 3) use_buffer = 0; } /* -- add a block of commmon margins / scale in the output buffer -- */ void block_put(void) { if (mbf == outbuf) return; //fixme: should be done sooner and should be adjusted when cfmt change... if (remy == 0) remy = maxy = (cfmt.landscape ? cfmt.pagewidth : cfmt.pageheight) - cfmt.topmargin - cfmt.botmargin; if (ln_num > 0 && mbf == ln_buf[ln_num - 1]) return; /* no data */ if (ln_num >= BUFFLN) { char c, *p; error(1, NULL, "max number of buffer lines exceeded" " -- check BUFFLN"); multicol_start = 0; p = ln_buf[ln_num - 1]; c = *p; /* (avoid "buffer not empty") */ *p = '\0'; write_buffer(); multicol_start = remy + bposy; *p = c; strcpy(outbuf, p); // use_buffer = 0; } ln_buf[ln_num] = mbf; ln_pos[ln_num] = multicol_start == 0 ? bposy : 1; ln_lmarg[ln_num] = cfmt.leftmargin; if (epsf) { if (cfmt.leftmargin < min_lmarg) min_lmarg = cfmt.leftmargin; if (cfmt.rightmargin < max_rmarg) max_rmarg = cfmt.rightmargin; } ln_scale[ln_num] = cfmt.scale; ln_font[ln_num] = outft; ln_num++; if (!use_buffer) write_buffer(); } /* -- handle completed block in buffer -- */ /* if the added stuff does not fit on current page, write it out after page break and change buffer handling mode to pass though */ void buffer_eob(int eot) { block_put(); if (epsf) { if (epsf == 3) write_eps(); /* close the image */ return; } if (remy + bposy >= 0 || multicol_start != 0) return; // page full if (!in_page) { if ((cfmt.splittune == 2 && !(nbpages & 1)) || (cfmt.splittune == 3 && (nbpages & 1))) { // wrong odd/even page if (remy + maxy + bposy < 0) { // 2nd overflow init_page(); close_page(); write_buffer(); return; } if (eot) write_buffer(); return; } } else { close_page(); } if ((cfmt.splittune == 2 && !(nbpages & 1)) || (cfmt.splittune == 3 && (nbpages & 1))) use_buffer = 1; // if wrong odd/even page else write_buffer(); #if 0 --- old if (use_buffer && !eot && ((cfmt.splittune == 2 && !(nbpages & 1)) || (cfmt.splittune == 3 && (nbpages & 1)))) { init_page(); // 8.7.4 use_buffer = 1; } else { // 8.7.5 - required to avoid buffer overflow write_buffer(); } //8.7.0 // write_buffer(); //8.6.2 // use_buffer = 0; #endif } /* -- dump buffer if not enough place for a music line -- */ void check_buffer(void) { if (mbf + 5000 > outbuf + outbufsz) { /* assume music line < 5000 bytes */ error(0, NULL, "Possibly bad page breaks, outbufsz exceeded"); write_buffer(); // use_buffer = 0; } } /* -- return the current vertical offset in the page -- */ float get_bposy(void) { return remy + bposy; } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������abcm2ps-8.14.11/build.ninja�������������������������������������������������������������������������0000664�0000000�0000000�00000003210�13762665467�0015357�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# rules for ninja (ninja-build) or samurai cc=musl-gcc #cc=gcc #cc=clang #cflags = -O2 -Wall -pipe -I. -fpie cflags = -g -Wall -pipe -I. -fpie ldflags=-static -Wl,--no-dynamic-linker -lm rule cc command = $cc $cflags -c $in -o $out rule ld command = $cc $ldflags -o $out $in build abcm2ps.o: cc abcm2ps.c | config.h abcm2ps.h build abcparse.o: cc abcparse.c | config.h abcm2ps.h build buffer.o: cc buffer.c | config.h abcm2ps.h build deco.o: cc deco.c | config.h abcm2ps.h build draw.o: cc draw.c | config.h abcm2ps.h build format.o: cc format.c | config.h abcm2ps.h build front.o: cc front.c | config.h abcm2ps.h build glyph.o: cc glyph.c | config.h abcm2ps.h build music.o: cc music.c | config.h abcm2ps.h build parse.o: cc parse.c | config.h abcm2ps.h build subs.o: cc subs.c | config.h abcm2ps.h build svg.o: cc svg.c | config.h abcm2ps.h build syms.o: cc syms.c | config.h abcm2ps.h build abcm2ps: ld abcm2ps.o abcparse.o buffer.o deco.o draw.o format.o front.o $ glyph.o music.o parse.o subs.o svg.o syms.o default abcm2ps # GitHub releases rule version command = tag=`grep VERSION= configure|cut -d'=' -f2`;$ if [ $out = minor ]; then$ m=$${tag#*.};$ m=$${m%%.*};$ m=$$((m + 1));$ newtag="$${tag%%.*}.$$m.0";$ else$ p=$${tag##*.};$ p=$$((p + 1));$ newtag="$${tag%.*}.$$p";$ fi;$ p=`grep VDATE= configure|cut -d'=' -f2`;$ m=`date +%F`;$ mv configure configure~;$ sed -e "s/$$tag/$$newtag/;s/$$p/$$m/" configure~ > configure;$ chmod +x configure;$ echo "New release v$$newtag" | git commit -F- configure;$ git tag -a v$$newtag;$ echo "Don't forget: git push --follow-tags; configure" build minor: version build patch: version ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������abcm2ps-8.14.11/chinese.abc�������������������������������������������������������������������������0000664�0000000�0000000�00000004473�13762665467�0015340�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������%abc-2.1 % abcm2ps sample file with chinese characters % this file works with truetype fonts (as UKaiCN) and pango support in abcm2ps X: 1 T: Green Island Serenade T: 绿岛小夜曲 T: 綠島小夜曲 T: Lǜ Dǎo Xiǎoyèqǔ T: Зелёный Остров T: جزيرة اخضرة C: Chen Chang-shou D: Vienna Teng "Warm Strangers" M: 4/4 L: 1/8 K: D A,2 \ | "D"A3 B "(F#m)"A2 F2 | "A7"E2 {FE}DE "Bm"F3 E \ | "G"DD2 B, "D"A,3 B, | "D"A,4 "(A7)"z4 \ | "G"B,3 A, B,2 D2 | "A7"E2 {FE}DE "D"F3 A \ | "Em"EE2 F "A7"A3 B | "D"A4 "(A7)"z4 | | "G"BB2 A "D"F2 FE | "Bm"DE F[AD] "F#m"F4 \ | "Em"EE2 D "G"B,A, B,D | "A7"E4 z4 \ | "D"FF2 E "Bm"FA F2 | "Em"EF ED "G"B,4 \ | "D"A,A FE "A7"AB FE | "D"{E}D4 z4 |] [|"G"B>B BA "D"F F3 | "Em"EE2 D "G"B,A, B,D | "A7"E4 z4 \ | "D"A>B AE "F#m"FF2 F | "Em"EE2 D "G"B,2 D2 | "D"A4 "A7"z3A | | "D"A2 {BA}FA "G"B3 d | "D"AB AF "Em"E4 \ | "Bm"F2 ED "G"B,3 D | "A7"E2 {FE}DE "D"F3A \ | "G"B3 F "A7"E3 F/E/ | "D"D4 z2 |] % W: 这绿岛像一隻船在月夜里摇呀摇 W: 姑娘哟妳也在我的心海裡飘呀飘 W: 让我的歌声随那微风吹開了妳的窗簾 W: 让我的衷情随那流水不断的向妳倾诉 W: W: 椰子树的长影掩不住我的情意 W: 明媚的月光更照亮了我的心 W: 这绿岛的夜已经这样沉静 W: 姑娘哟妳为什么还是默默无语 W: W: zhè lǜ dǎo xiàng yī zhī chuàn, zài yué yè lǐ yáo ya yáo. W: gū niang yo, nǐ ye zài wǒde xīn hǎi lǐ piao ya piao. W: ràng wǒde ge sheng sui na wei feng, chui kai le nǐ de chuang lian. W: ràng wǒde zhong qing sui na liu shui, bu duan de xiang nǐ qing su. W: W: ye zi shu de chang ying yan bu zhu wǒde qing yi; W: mìng mèi de yuè guang geng zhao liang le wǒde xīn. W: zhe lǜ dao de ye yi jing zhe yang de chen jing W: gū niang ya, nǐ wei shen ma hai shì muò muò wú yǚ? W: W: This Green Island is like a boat, floating in the moonlight, W: My darling, you too are floating in the sea of my heart, W: Let the sound of my song follow the breeze, blowing open the curtain of your window, W: Let my love follow the flowing water, endlessly pouring out its feelings for you. W: W: The long shadows of the palm trees cannot conceal my love, W: The bright beauty of the moonlight casts its brilliance into my heart. W: This Green Island night is so calm and serene, W: My darling, why are you silent, saying nothing? �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������abcm2ps-8.14.11/config.h.in�������������������������������������������������������������������������0000664�0000000�0000000�00000000533�13762665467�0015267�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* config.h.in */ /* uncomment to handle the european A4 format. */ //#define A4_FORMAT 1 /* uncomment to have ~ as roll instead of twiddle. */ //#define DECO_IS_ROLL 1 /* comment if you have not mmap() */ #define HAVE_MMAP 1 /* default directory to search for format files */ #define DEFAULT_FDIR xxx #define VERSION xxx #define VDATE xxx ���������������������������������������������������������������������������������������������������������������������������������������������������������������������abcm2ps-8.14.11/configure���������������������������������������������������������������������������0000775�0000000�0000000�00000004175�13762665467�0015161�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#! /bin/sh # (automatic update) VERSION=8.14.11 VDATE=2020-12-05 CC=gcc PKG_CONFIG=pkg-config CFLAGS="-g -O2 -Wall -pipe" srcdir=. prefix=/usr/local INSTALL="/usr/bin/install -c" if test -f ./custom; then . ./custom fi for c in "$@"; do case "$c" in --*=*) c="${c#--}" eval "${c%%=*}='${c#*=}'" ;; esac done case "$srcdir" in *\ *) echo "srcpath cannot contain spaces" exit 1 esac if test "x$INSTALL_DATA" = x; then INSTALL_DATA='${INSTALL} -m 644' fi if test "x$INSTALL_PROGRAM" = x; then INSTALL_PROGRAM='${INSTALL}' fi if test "x$exec_prefix" = x; then exec_prefix='${prefix}' fi if test "x$bindir" = x; then bindir='${exec_prefix}/bin' fi if test "x$datarootdir" = x; then datarootdir='${prefix}/share' fi if test "x$docdir" = x; then docdir='${prefix}/share/doc' fi if test "x$mandir" = x; then mandir='${prefix}/share/man' fi if test "x$DEFAULT_FDIR" = x; then DEFAULT_FDIR="$prefix/share/abcm2ps" fi if which $PKG_CONFIG > /dev/null ; then if $PKG_CONFIG --exists freetype2 ; then if $PKG_CONFIG --exists pangocairo ; then CPPFLAGS="$CPPFLAGS -DHAVE_PANGO=1 `$PKG_CONFIG pango cairo freetype2 --cflags`" LDLIBS="$LDLIBS `$PKG_CONFIG pangocairo pangoft2 freetype2 --libs`" else echo "pangocairo not found - no pango support" fi else echo "freetype2 not found - no pango support" fi else echo "pkg-config not found - no pango support" fi CPPFLAGS="$CPPFLAGS -I." # ./config.h will not be found in srcdir. LDLIBS="$LDLIBS -lm" # Useful on some architectures. sed "s+@CC@+$CC+ s+@CPPFLAGS@+$CPPFLAGS+ s+@CFLAGS@+$CFLAGS+ s+@LDFLAGS@+$LDFLAGS+ s+@LDLIBS@+$LDLIBS+ s+@INSTALL@+$INSTALL+ s+@INSTALL_DATA@+$INSTALL_DATA+ s+@INSTALL_PROGRAM@+$INSTALL_PROGRAM+ s+@prefix@+$prefix+ s+@exec_prefix@+$exec_prefix+ s+@srcdir@+$srcdir+ s+@bindir@+$bindir+ s+@datarootdir@+$datarootdir+ s+@mandir@+$mandir+ s+@docdir@+$docdir+" "$srcdir/Makefile.in" > Makefile echo "Makefile created" sed "s/define VERSION xxx/\define VERSION \"$VERSION\"/ s/define VDATE xxx/define VDATE \"$VDATE\"/ s+define DEFAULT_FDIR xxx+define DEFAULT_FDIR \"$DEFAULT_FDIR\"+ " "$srcdir/config.h.in" > config.h echo "config.h created" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������abcm2ps-8.14.11/deco.abc����������������������������������������������������������������������������0000664�0000000�0000000�00000026054�13762665467�0014633�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������%!! here is an experimental decoration extension !! %!! syntax may change in next releases !! % % this option is required for !trem! % %setdefl 1 % % == postscript definitions - must be before any tune == % %%setfont-1 Times-Italic 14 % dynamics %%setfont-2 Times-BoldItalic 16 %%setfont-3 Times-Roman 11 % optional notes %%setfont-4 Times-Roman 16 % 'al Coda' %%beginsvg <defs> <path id="sfa" class="stroke" stroke-width="1.2" d="m-2.4 -4.8l2.4 -8 2.4 7.4 -1 0 -1.8 -6.4"/> </defs> %%endsvg %%beginps % % -- draw octava indication /octava{ % usage: w x y octava exch -10 add exch 2 copy M 0 10 RM /Times-Roman 16 selectfont(8)show /Times-Roman 12 selectfont(va)show M 0 6 RL currentpoint stroke M [6] 0 setdash 30 add 0 RL currentpoint stroke M [] 0 setdash 0 -6 RL stroke}! /octavab{ % usage: w x y octavab exch -14 add exch 2 copy M 0 2 RM /Times-Roman 16 selectfont(8)show /Times-Roman 12 selectfont(va basso)show 22 add M 0 -6 RL currentpoint stroke M [6] 0 setdash 30 add 0 RL stroke [] 0 setdash}! % % -- write big letters (position marks) above the staff % (from Guido Gonzato) /bigl{ % usage: str x y bigl /Times-Bold 26 selectfont 4 add M showc 1 SLW 1 -2 RM 0 22 RL -22 0 RL 0 -22 RL 22 0 RL stroke}! % (from Jonas Petersson) /biglc{ % usage: str x y biglc 2 copy 5 2 roll /Times-Bold 22 selectfont 6 add M showc 1 SLW 13 add newpath 12 0 360 arc stroke}! % % -- pedal glyph % (from CMN http://ccrma-www.stanford.edu/software/cmn/cmn/cmn.html) /ped{ % usage: str x y ped gsave 4 add exch -10 add exch T 26 dup scale 0.368 0.074 moveto 0.341 0.121 0.335 0.147 0.371 0.203 curveto 0.435 0.289 0.531 0.243 0.488 0.155 curveto 0.472 0.117 0.434 0.096 0.414 0.080 curveto 0.429 0.038 0.494 -0.006 0.541 0.075 curveto 0.559 0.123 0.558 0.224 0.663 0.252 curveto 0.603 0.354 0.449 0.393 0.461 0.405 curveto 0.902 0.262 0.705 -0.124 0.555 0.046 curveto 0.488 -0.032 0.417 0.021 0.389 0.055 curveto 0.303 -0.018 0.303 -0.020 0.248 0.040 curveto 0.218 0.108 0.191 0.062 0.164 0.047 curveto 0.010 -0.056 0.032 0.019 0.124 0.062 curveto 0.229 0.117 0.200 0.091 0.228 0.195 curveto 0.240 0.241 0.149 0.250 0.166 0.311 curveto 0.207 0.493 lineto -0.041 0.441 0.049 0.261 0.126 0.387 curveto 0.138 0.381 lineto -0.020 0.119 -0.100 0.472 0.220 0.507 curveto 0.548 0.486 0.399 0.171 0.254 0.374 curveto 0.264 0.384 lineto 0.338 0.259 0.521 0.449 0.228 0.488 curveto 0.198 0.356 lineto 0.181 0.304 0.273 0.294 0.262 0.241 curveto 0.229 0.101 lineto 0.273 0.070 0.282 -0.038 0.368 0.074 curveto 0.391 0.094 moveto 0.456 0.130 0.476 0.171 0.468 0.213 curveto 0.452 0.276 0.333 0.171 0.391 0.094 curveto 0.627 0.019 moveto 0.533 0.041 0.586 0.228 0.678 0.229 curveto 0.729 0.170 0.712 0.025 0.627 0.019 curveto eofill 0.8 0.04 0.04 0 360 newpath arc fill pop grestore}! % % -- pedal off glyph % (from CMN http://ccrma-www.stanford.edu/software/cmn/cmn/cmn.html) /pedoff{ % usage: str x y pedoff gsave 4 add exch -5 add exch T 26 dup scale 0.219 0.198 moveto 0.231 0.172 0.195 0.138 0.162 0.173 curveto 0.149 0.219 0.206 0.231 0.219 0.198 curveto 0.144 0.242 moveto 0.166 0.223 0.193 0.230 0.181 0.267 curveto 0.178 0.306 0.144 0.302 0.151 0.335 curveto 0.160 0.381 0.225 0.377 0.224 0.330 curveto 0.228 0.302 0.198 0.306 0.197 0.267 curveto 0.194 0.237 0.213 0.222 0.237 0.247 curveto 0.263 0.276 0.234 0.297 0.268 0.322 curveto 0.314 0.347 0.354 0.297 0.316 0.259 curveto 0.296 0.237 0.273 0.266 0.246 0.237 curveto 0.223 0.217 0.232 0.194 0.266 0.197 curveto 0.303 0.202 0.302 0.232 0.332 0.228 curveto 0.381 0.232 0.388 0.156 0.332 0.152 curveto 0.302 0.148 0.302 0.185 0.266 0.183 curveto 0.231 0.186 0.228 0.169 0.245 0.143 curveto 0.273 0.116 0.297 0.141 0.316 0.117 curveto 0.350 0.075 0.303 0.029 0.258 0.062 curveto 0.237 0.082 0.261 0.102 0.233 0.133 curveto 0.212 0.151 0.194 0.147 0.197 0.113 curveto 0.203 0.075 0.232 0.075 0.230 0.043 curveto 0.223 -0.004 0.159 -0.002 0.152 0.042 curveto 0.148 0.075 0.185 0.076 0.183 0.113 curveto 0.183 0.147 0.163 0.150 0.141 0.133 curveto 0.113 0.104 0.140 0.079 0.113 0.059 curveto 0.069 0.037 0.033 0.077 0.063 0.117 curveto 0.082 0.141 0.104 0.117 0.132 0.142 curveto 0.153 0.163 0.144 0.188 0.113 0.182 curveto 0.073 0.182 0.075 0.147 0.046 0.152 curveto -0.003 0.152 -0.003 0.227 0.048 0.227 curveto 0.075 0.231 0.075 0.198 0.113 0.196 curveto 0.141 0.197 0.147 0.207 0.133 0.237 curveto 0.102 0.264 0.082 0.237 0.062 0.261 curveto 0.028 0.302 0.077 0.347 0.118 0.318 curveto 0.138 0.297 0.116 0.275 0.144 0.242 curveto fill pop grestore}! % % -- upper glissendo /glissup{ % usage: x y glissup gsave T 5 0 T 25 rotate 10 0 T 0 0 M 0 8 8{ 2 -1.15 2.30 150 30 arcn 4 0 T 2 1.15 2.30 -150 -30 arc 4 0 T pop }for 1 SLW stroke grestore}! % % -- note decorations % (sorry for I don't know the name of these: there are so many ones) % The convention I use here is: % - t2 or t3: mordent with 2 or 3 peeks % - ta or tb: turn from above or from below % - b, ub or db: middle, upper or lower bar /tr3{ % usage: x y tr3 - mordent with 3 peeks M 2.2 2.2 RL 2.1 -2.9 RL 0.7 0.7 RL 2.2 2.2 RL 2.1 -2.9 RL 0.7 0.7 RL 2.2 2.2 RL 2.1 -2.9 RL 0.7 0.7 RL -2.2 -2.2 RL -2.1 2.9 RL -0.7 -0.7 RL -2.2 -2.2 RL -2.1 2.9 RL -0.7 -0.7 RL -2.2 -2.2 RL -2.1 2.9 RL -0.7 -0.7 RL fill}! /t2ub{ % usage: x y t2ub - mordent ending with an upper bar 2 copy umrd 0.6 SLW M 5 4 RM 0 6 RL stroke}! /t3tab{ % usage: x y t3tab - mordent + upper turn and bar 4 add 2 copy exch 7.5 sub exch tr3 exch 7.5 add exch 2 copy 0.6 SLW M 2 6 14 6 16 0 RC M 8 1 RM 0 6 RL stroke}! /ubt3ta{ % usage: x y ubt3ta - up bar + mordent + upper turn 4 add 2 copy 0.6 SLW M -7.5 0 RM 0 6 RL stroke 2 copy exch 7.5 sub exch tr3 M 7.5 0 RM 2 6 14 6 16 0 RC stroke}! /tbt3{ % usage: x y tbt3 - low turn + long mordent exch 10 sub exch 6 add 2 copy 0.6 SLW M -8 0 RM 2 -6 14 -6 16 0 RC stroke exch 8 add exch tr3}! /t2ta{ % usage: x y t2ta - mordent + upper turn 2 copy umrd M 5 4 RM 1 5 9 5 10 0 RC stroke}! /t3b{ % usage: x y t3b - upper + lower mordent 2 copy exch -7.5 add exch 4 add tr3 0.6 SLW M 2.5 0 RM 0 8 RL stroke}! % % -- latin guitar chords % note: 'Ré' cannot be used /gcshow{ dup 0 get dup dup 65 ge exch 71 le and{ 65 sub[(La)(Si)(Do)(Re)(Mi)(Fa)(Sol)]exch get show dup length 1 sub 1 exch getinterval }if show}! % % -- 'tr' + long trill /trtrill{ % usage: w x y trtrill 2 copy trl 3 1 roll 9 add 3 1 roll 9 sub 3 1 roll 2 add ltr}! % % -- guitar diagrams /guitar{ % usage: x y guitar gsave exch 10 sub exch 8 add T 1.5 SLW -0.3 24.6 M 20.6 0 RL stroke 0.6 SLW 0 0 M 20 0 RL 0 6 M 20 0 RL 0 12 M 20 0 RL 0 18 M 20 0 RL 0 0 M 0 24 RL 4 0 M 0 24 RL 8 0 M 0 24 RL 12 0 M 0 24 RL 16 0 M 0 24 RL 20 0 M 0 24 RL stroke 0.5 SLW}! /gdot{newpath 1.4 0 360 arc fill}! /gx{28 M -1.3 -1.3 RM 2.6 2.6 RL 0 -2.6 RM -2.6 2.6 RL stroke}! /go{28 newpath 1.5 0 360 arc stroke}! /Dm{ % usage: x y Dm guitar 0 gx 4 gx 8 go 20 21 gdot 12 15 gdot 16 9 gdot grestore}! /Bb{ guitar 0 gx 20 gx 4 21 gdot 8 9 gdot 12 9 gdot 16 9 gdot grestore}! /C7{ guitar 0 gx 20 go 16 21 gdot 8 15 gdot 4 9 gdot 12 9 gdot grestore}! % % -- arpeggio variations % arpeggio with arrow 1st version /arpu{ 2 copy 4 index add M -6.5 0 RM 2.5 5 RL 2.5 -5 RL fill arp}! /arpd{ 2 copy M -6.5 0 RM 2.5 -5 RL 2.5 5 RL fill arp}! % arpeggio with arrow other version % /arpu{ 2 copy 4 index add M -7 0 RM 3 5 RL 3 -5 RL % 0.7 SLW stroke arp}! % /arpd{ 2 copy M -7 0 RM 3 -5 RL 3 5 RL % 0.7 SLW stroke arp}! % arpeggio crossing the staves % (! this works because the decoration are drawn sorted by time !) /arps{ % arpeggio start - stack: h x ylow exch /x exch def % memorize 'x' add /y exch def}! % memorize the upper vert offset /arpe{ % arpeggio end - stack: h x ylow 3 -1 roll pop % discard 'h' exch dup x gt {pop x} if exch % have room for accidentals y 1 index sub 3 1 roll % new height arp}def % % -- optional breath /brth{6 add /xbr 2 index def /ybr 1 index def /Times-BoldItalic 30 selectfont M (,) show}def /opbrth{pop pop xbr 10 sub ybr 5 sub /Times-Roman 20 selectfont M (\( \)) show}def % % -- head decorations / replacement % lower mordent /hlmrd{ % usage: x y hlmrd exch 12 sub exch 4 sub lmrd}! % small note head /shd{ % usage: x y shd M 3 1.3 RM -1 2.5 -6.5 0 -5.5 -2.5 RC 1 -2.5 6.5 0 5.5 2.5 RC fill}! % -- measure bar between two staves (1 and 2) /hbar{ % usage: x y hbar dlw pop dup 0 y0 M 24 y1 lineto stroke}def %%endps % % == decoration definitions == % % actual syntax (see http://moinejf.free.fr/abcm2ps-doc/index.html): % %%deco <name> <c_func> <ps_func> <h> <wl> <wr> [<str>] % % -- accent near the note / sforzando %%deco accn 0 accent 8 4 4 %%deco sfa 3 sfa 12 5 5 % % -- dynamic indication below the staff %%deco fp 6 pf 18 5 11 fp %%deco cresc 6 @ 20 10 22 "@-8,4$1Cresc." %%deco decresc 6 @ 20 10 26 "@-8,4$1Decresc." %%deco dimin 6 @ 20 10 22 "@-8,4$1Dimin." %%deco riten 6 @ 20 12 34 "@-8,4$1Poco riten." % % -- dynamic indication below the staff between parenthesis %%deco (p) 6 @ 20 8 16 "@-8,4$1($2p$1)" %%deco (pp) 6 @ 20 8 24 "@-8,4$1($2pp$1)" %%deco (f) 6 @ 20 8 16 "@-8,4$1($2f$1)" %%deco (ff) 6 @ 20 8 24 "@-8,4$1($2ff$1)" % % -- repeat indication above the staff %%deco alcoda 3 @ 20 0 0 "@0,5$4al Coda" % % -- who asked for a Pedal indication ? %%deco ped 6 ped 20 0 0 %%deco ped-up 6 pedoff 20 0 0 % % -- optional note %%deco () 0 @ 0 0 0 "@-8,-3$3( )" %%deco ()l 0 @ 0 0 0 "@-16,-3$3( )" % % -- start / stop of octava indication %%deco 8( 5 - 24 0 0 %%deco 8) 5 octava 24 0 0 %%deco 8b( 7 - 24 0 0 %%deco 8b) 7 octavab 24 0 0 % % -- big letters %%deco biga 3 bigl 20 0 0 A %%deco bigb 3 biglc 20 0 0 B % ... % % -- glissendo %%deco - 1 glissup 0 2 10 % % -- note decorations %%deco t2ub 3 t2ub 12 5 5 %%deco t3tab 3 t3tab 12 8 24 %%deco ubt3ta 3 ubt3ta 12 8 24 %%deco tbt3 3 tbt3 14 14 18 %%deco t2ta 3 t2ta 12 5 15 %%deco t3b 3 t3b 12 5 15 %%deco # 3 @ 8 0 0 "@0,0♯" %%deco b 3 @ 8 0 0 "@0,0♭" %%deco = 3 @ 8 0 0 "@0,0♮" % % -- 'tr' + long trill %%deco tr( 5 - 11 0 0 %%deco tr) 5 trtrill 11 0 0 % % -- guitar chords %%deco Dm 3 Dm 36 0 0 %%deco Bb 3 Bb 36 0 0 %%deco C7 3 C7 36 0 0 % % -- arpeggios %%deco arpu 2 arpu 0 0 0 %%deco arpd 2 arpd 0 0 0 %%deco arps 2 arps 0 0 0 %%deco arpe 2 arpe 0 0 0 % % -- optional breath %%deco opbrth 3 opbrth 0 0 0 % % -- head decorations %%deco m 0 hlmrd 0 0 0 % lower mordent on the left %%deco head-x 0 dsh0 0 0 0 % X head %%deco head-shd 0 shd 0 0 0 % small head % % -- measure bar between two staves %%deco hbar 3 hbar 0 0 0 X:1 T:Customized decorations M:C L:1/8 K:C treble-8 !biga!y20!fp!"C"C!t2ub!C !cresc!"D"D!t3tab!D !decresc!"E"E!ubt3ta!E !dimin!"F"F!tbt3!F|\ !mp!"G"G!t2ta!G !(p)!A!t3b!c T!b!!(pp)!A2 P!#!!(f)!B2|M!=!!(ff)!c8| % K: clef=treble !8(!!riten!EF !-!G2 !ped!GA!ped-up!B!8)!c|!8b(!!bigb!C2[!()l!_E2] !tr(!G3!tr)!!8b)!c!alcoda!|\ CE!-(!G2!-)!C2c2| % "Dm"!Dm!e3/d/ d6 | "Bb"!Bb!z2 d/d3/ "C7"!C7!cB/A/- AG |\ F!accent!G!accn!AB !sfa!c4|| X:2 T:Decorations with abcm2ps 4.x.x M:C L:1/4 %%staves {1 2} K:C V:1 !arpu![!()!C!()!Gc]4 !hbar![] !breath!!opbrth!!arpd![CGc]4 |\ !arps![CGc]4 | z3/[!head-x!B]/ [c!m!eg]2 | c4 || V:2 !arpeggio![C,,G,,C,]4 [] [C,,G,,C,]4 |\ !arpe![C,,G,,C,]4 | [C,,!head-shd!C,][G,,!head-shd!G,]C,2 | C,4 || ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������abcm2ps-8.14.11/deco.c������������������������������������������������������������������������������0000664�0000000�0000000�00000156333�13762665467�0014334�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Decoration handling. * * This file is part of abcm2ps. * * Copyright (C) 2000-2019 Jean-François Moine (http://moinejf.free.fr) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. */ #include <stdlib.h> #include <string.h> #include <ctype.h> #include <math.h> #ifdef WIN32 #define lroundf(x) ((long) ((x) + 0.5)) #endif #include "abcm2ps.h" int defl; /* decoration flags */ char *deco[256]; /* decoration names */ static struct deco_elt { struct deco_elt *next, *prev; /* next/previous decoration */ struct SYMBOL *s; /* symbol */ struct deco_elt *start; /* start a long decoration ending here */ unsigned char t; /* decoration index */ unsigned char staff; /* staff */ unsigned char flags; #define DE_VAL 0x01 /* put extra value if 1 */ #define DE_UP 0x02 /* above the staff */ //#define DE_BELOW 0x08 /* below the staff */ //#define DE_GRACE 0x10 /* in grace note */ #define DE_INV 0x20 /* invert the glyph */ #define DE_LDST 0x40 /* start of long decoration */ #define DE_LDEN 0x80 /* end of long decoration */ unsigned char defl; /* decorations flags - see DEF_xx */ signed char m; /* chord index */ float x, y; /* x, y */ float dy; /* dy for annotation strings */ float val; /* extra value */ // char *str; /* string / 0 */ } *deco_head, *deco_tail; typedef void draw_f(struct deco_elt *de); static draw_f d_arp, d_cresc, d_near, d_slide, d_upstaff, d_pf, d_trill; /* decoration table */ /* !! don't change the order of the numbered items !! */ static struct deco_def_s { char *name; unsigned char func; /* function index */ signed char ps_func; /* postscript function index */ unsigned char h; /* height */ unsigned char wl, wr; /* left and right widths */ unsigned char strx; /* string index - 255=deco name */ unsigned char ld_start; /* index of start of long decoration */ unsigned char ld_end; /* index of end of long decoration */ unsigned char flags; /* only DE_LDST and DE_LDEN */ } deco_def_tb[128]; /* c function table */ static draw_f *func_tb[] = { d_near, /* 0 - near the note */ d_slide, /* 1 */ d_arp, /* 2 */ d_upstaff, /* 3 - tied to note */ d_upstaff, /* 4 (below the staff) */ d_trill, /* 5 */ d_pf, /* 6 - tied to staff (dynamic marks) */ d_cresc, /* 7 */ }; static const short f_near = (1 << 0) | (1 << 1) | (1 << 2); static const short f_note = (1 << 3) | (1 << 4) | (1 << 5); static const short f_staff = (1 << 6) | (1 << 7); /* postscript function table */ static char *ps_func_tb[128]; static char *str_tb[32]; /* standard decorations */ static char *std_deco_tb[] = { "dot 0 stc 5 1 1", "roll 3 cpu 7 6 6", "fermata 3 hld 12 7 7", "emphasis 3 accent 7 4 4", "lowermordent 3 lmrd 10 2 2", "coda 3 coda 24 10 10", "dacoda 3 dacoda 16 10 10", "uppermordent 3 umrd 10 2 2", "segno 3 sgno 20 4 4", "trill 3 trl 11 4 4", "upbow 3 upb 10 5 5", "downbow 3 dnb 9 5 5", "gmark 3 grm 6 5 5", "slide 1 sld 3 7 0", "tenuto 0 emb 5 2 2", "breath 3 brth 0 1 20", "longphrase 3 lphr 0 1 1", "mediumphrase 3 mphr 0 1 1", "shortphrase 3 sphr 0 1 1", "invertedfermata 3 hld 12 7 7", "invertedturn 3 turn 10 0 5", "invertedturnx 3 turnx 10 0 5", "0 3 fng 8 3 3 0", "1 3 fng 8 3 3 1", "2 3 fng 8 3 3 2", "3 3 fng 8 3 3 3", "4 3 fng 8 3 3 4", "5 3 fng 8 3 3 5", "plus 3 dplus 7 3 3", "+ 3 dplus 7 3 3", "accent 3 accent 7 4 4", "> 3 accent 7 4 4", "marcato 3 marcato 9 3 3", "^ 3 marcato 9 3 3", "D.C. 3 dacs 16 10 10 D.C.", "D.C.alcoda 3 dacs 16 10 10 D.C. al Coda", "D.C.alfine 3 dacs 16 10 10 D.C. al Fine", "dacapo 3 dacs 16 10 10 Da Capo", "D.S. 3 dacs 16 10 10 D.S.", "D.S.alcoda 3 dacs 16 10 10 D.S. al Coda", "D.S.alfine 3 dacs 16 10 10 D.S. al Fine", "fine 3 dacs 16 10 10 FINE", "f 6 pf 18 1 7", "ff 6 pf 18 2 10", "fff 6 pf 18 4 13", "ffff 6 pf 18 6 16", "mf 6 pf 18 6 13", "mp 6 pf 18 6 16", "mordent 3 lmrd 10 2 2", "open 3 opend 10 2 2", "p 6 pf 18 2 8", "pp 6 pf 18 5 14", "ppp 6 pf 18 8 20", "pppp 6 pf 18 10 25", "pralltriller 3 umrd 10 2 2", "sfz 6 sfz 18 4 10", "ped 4 ped 20 0 0", "ped-up 4 pedoff 20 0 0", "turn 3 turn 10 0 5", "wedge 3 wedge 8 1 1", "turnx 3 turnx 10 0 5", "trill( 3 ltr 8 0 0", "trill) 3 ltr 8 0 0", "snap 3 snap 14 3 3", "thumb 3 thumb 14 2 2", "arpeggio 2 arp 12 10 0", "crescendo( 6 cresc 18 0 0", "crescendo) 6 cresc 18 0 0", "<( 6 cresc 18 0 0", "<) 6 cresc 18 0 0", "diminuendo( 6 dim 18 0 0", "diminuendo) 6 dim 18 0 0", ">( 6 dim 18 0 0", ">) 6 dim 18 0 0", "-( 8 gliss 0 0 0", "-) 8 gliss 0 0 0", "~( 8 glisq 0 0 0", "~) 8 glisq 0 0 0", "8va( 3 o8va 10 0 0", "8va) 3 o8va 10 0 0", "8vb( 4 o8vb 10 0 0", "8vb) 4 o8vb 10 0 0", "invisible 32 0 0 0 0", "beamon 33 0 0 0 0", "trem1 34 0 0 0 0", "trem2 34 0 0 0 0", "trem3 34 0 0 0 0", "trem4 34 0 0 0 0", "xstem 35 0 0 0 0", "beambr1 36 0 0 0 0", "beambr2 36 0 0 0 0", "rbstop 37 0 0 0 0", "/ 38 0 0 6 6", "// 38 0 0 6 6", "/// 38 0 0 6 6", "beam-accel 39 0 0 0 0", "beam-rall 39 0 0 0 0", "stemless 40 0 0 0 0", "rbend 41 0 0 0 0", 0 }; /* user decorations */ static struct u_deco { struct u_deco *next; char text[256]; // dummy size } *user_deco; //static struct SYMBOL *first_note; /* first note/rest of the line */ static unsigned char deco_define(char *name); static void draw_gchord(struct SYMBOL *s, float gchy_min, float gchy_max); /* -- get the max/min vertical offset -- */ float y_get(int staff, int up, float x, float w) { struct STAFF_S *p_staff; int i, j; float y; p_staff = &staff_tb[staff]; i = (int) (x / realwidth * YSTEP); if (i < 0) { // fprintf(stderr, "y_get i:%d\n", i); i = 0; } j = (int) ((x + w) / realwidth * YSTEP); if (j >= YSTEP) { j = YSTEP - 1; if (i > j) i = j; } if (up) { y = p_staff->top[i++]; while (i <= j) { if (y < p_staff->top[i]) y = p_staff->top[i]; i++; } } else { y = p_staff->bot[i++]; while (i <= j) { if (y > p_staff->bot[i]) y = p_staff->bot[i]; i++; } } return y; } /* -- adjust the vertical offsets -- */ void y_set(int staff, int up, float x, float w, float y) { struct STAFF_S *p_staff; int i, j; p_staff = &staff_tb[staff]; i = (int) (x / realwidth * YSTEP); /* (may occur when annotation on 'y' at start of an empty staff) */ if (i < 0) { // fprintf(stderr, "y_set i:%d\n", i); i = 0; } j = (int) ((x + w) / realwidth * YSTEP); if (j >= YSTEP) { j = YSTEP - 1; if (i > j) i = j; } if (up) { while (i <= j) { if (p_staff->top[i] < y) p_staff->top[i] = y; i++; } } else { while (i <= j) { if (p_staff->bot[i] > y) p_staff->bot[i] = y; i++; } } } //// set the string of a decoration //static char *set_str(struct deco_elt *de, char *str) //{ // float dx, dy; // int n; // // if (sscanf(str, "@%f,%f%n", &dx, &dy, &n) == 2) { // de->x += dx; // de->dy = dy; // return str + n; // } // return str; //} /* -- get the staff position of the dynamic and volume marks -- */ static int up_p(struct SYMBOL *s, int pos) { switch (pos) { case SL_ABOVE: return 1; case SL_BELOW: return 0; } if (s->multi != 0) return s->multi > 0; if (!voice_tb[s->voice].have_ly) return 0; /* above if the lyrics are below the staff */ return s->posit.voc != SL_ABOVE; } /* -- drawing functions -- */ /* 2: special case for arpeggio */ static void d_arp(struct deco_elt *de) { struct SYMBOL *s; struct deco_def_s *dd; int m, h; float xc, dx; s = de->s; dd = &deco_def_tb[de->t]; xc = 0; for (m = 0; m <= s->nhd; m++) { if (s->u.note.notes[m].acc) { dx = 5 + s->u.note.notes[m].shac; } else { dx = 6 - s->u.note.notes[m].shhd; switch (s->head) { case H_SQUARE: case H_OVAL: dx += 2.5; break; } } if (dx > xc) xc = dx; } h = 3 * (s->pits[s->nhd] - s->pits[0]) + 4; m = dd->h; /* minimum height */ if (h < m) h = m; de->flags |= DE_VAL; de->val = h; de->x = s->x - xc; de->y = (float) (3 * (s->pits[0] - 18)) - 3; } /* special case for long dynamics */ static void d_cresc(struct deco_elt *de) { struct SYMBOL *s, *s2; struct deco_def_s *dd, *dd2; struct deco_elt *de1; int up; float x, dx, x2; if (de->flags & DE_LDST) return; s2 = de->s; de1 = de->start; /* start of the deco */ // if (de1) { s = de1->s; x = s->x + 3; // } else { /* end without start */ // if (!first_note) { // dd = &deco_def_tb[de->t]; // error(1, s2, "No start of deco !%s!", dd->name); // de->t = 0; // return; // } // s = first_note; // x = s->x - s->wl - 4; // } de->staff = s2->staff; de->flags &= ~DE_LDEN; /* old behaviour */ de->flags |= DE_VAL; up = up_p(s2, s2->posit.dyn); if (up) de->flags |= DE_UP; /* shift the starting point if any dynamic mark on the left */ if (de1 && de1->prev && de1->prev->s == s && ((de->flags ^ de1->prev->flags) & DE_UP) == 0) { dd2 = &deco_def_tb[de1->prev->t]; if (f_staff & (1 << dd2->func)) { x2 = de1->prev->x + de1->prev->val + 4; if (x2 > x) x = x2; } } if (de->defl & DEF_NOEN) { /* if no decoration end */ dx = de->x - x; if (dx < 20) { x = de->x - 20 - 3; dx = 20; } } else { x2 = s2->x; if (de->next && de->next->s == s && ((de->flags ^ de->next->flags) & DE_UP) == 0) { dd2 = &deco_def_tb[de->next->t]; if (f_staff & (1 << dd2->func)) /* if dynamic mark */ x2 -= 5; } dx = x2 - x - 4; if (dx < 20) { x -= (20 - dx) * 0.5; // if (!de->start) // x -= (20 - dx) * 0.5; dx = 20; } } de->val = dx; de->x = x; de->y = y_get(de->staff, up, x, dx); if (!up) { dd = &deco_def_tb[de->t]; de->y -= dd->h; } /* (y_set is done later in draw_deco_staff) */ } /* special case for glissendo */ static void d_gliss(struct deco_elt *de2) { struct deco_elt *de1; struct SYMBOL *s1, *s2; de1 = de2->start; s1 = de1->s; if (s1->dots) de1->x += 5 + s1->xmx; s2 = de2->s; de2->x -= 2 + s2->u.note.notes[0].shac ? (s2->u.note.notes[0].shac + 3) : hw_tb[s2->head]; } /* 0: near the note (dot, tenuto) */ static void d_near(struct deco_elt *de) { struct SYMBOL *s; struct deco_def_s *dd; int y, up; s = de->s; dd = &deco_def_tb[de->t]; if (s->multi) up = s->multi > 0; else up = s->stem < 0; if (up) y = s->ymx; else y = s->ymn - dd->h; if (y > -6 && y < 24) { if (up) y += 3; y = (y + 6) / 6 * 6 - 6; /* between lines */ } if (up) s->ymx = y + dd->h; else s->ymn = y; de->y = (float) y; de->x = s->x; if (s->type == NOTEREST) de->x += s->u.note.notes[s->stem >= 0 ? 0 : s->nhd].shhd; if (dd->name[0] == 'd' /* if dot decoration */ && s->nflags >= -1) { /* on stem */ if (up) { if (s->stem > 0) de->x += STEM_XOFF; } else { if (s->stem < 0) de->x -= STEM_XOFF; } } if (dd->strx != 0 && dd->strx != 255) { de->x = s->x; de->y = s->y; // de->str = set_str(de, str_tb[dd->strx]); } } /* 6: dynamic marks */ static void d_pf(struct deco_elt *de) { struct SYMBOL *s; struct deco_def_s *dd, *dd2; float x, x2; // char *str; int up; // don't treat here the long decorations if (de->flags & DE_LDST) return; if (de->start) { d_cresc(de); return; } s = de->s; dd = &deco_def_tb[de->t]; de->val = dd->wl + dd->wr; up = up_p(s, s->posit.vol); if (up) de->flags |= DE_UP; x = s->x - dd->wl; if (de->prev && de->prev->s == s && ((de->flags ^ de->prev->flags) & DE_UP) == 0) { dd2 = &deco_def_tb[de->prev->t]; if (f_staff & (1 << dd2->func)) { /* if dynamic mark */ x2 = de->prev->x + de->prev->val + 4; if (x2 > x) x = x2; } #if 0 //fixme:test volume shift // does not work with // cE!p!E !fff!Ceg| } else if (!up && s->stem < 0 && s->ymn < 10) { float y; x2 = x - (STEM_XOFF + dd->wr + 4); y = y_get(s->staff, up, x2, de->val); if (y > s->ymn) { x = x2; } else { x2 -= 3; y = y_get(s->staff, up, x2, de->val); if (y > s->ymn) x = x2; } #endif } de->x = x; de->y = y_get(s->staff, up, x, de->val); if (!up) de->y -= dd->h; // str = dd->name; // if (dd->strx != 0 && dd->strx != 255) // str = set_str(de, str_tb[dd->strx]); // de->str = str; /* (y_set is done later in draw_deco_staff) */ } /* 1: special case for slide */ static void d_slide(struct deco_elt *de) { struct SYMBOL *s; int m, yc; float xc, dx; s = de->s; yc = s->pits[0]; xc = 5; if (s->type == NOTEREST) { for (m = 0; m <= s->nhd; m++) { if (s->u.note.notes[m].acc) { dx = 4 + s->u.note.notes[m].shac; } else { dx = 5 - s->u.note.notes[m].shhd; switch (s->head) { case H_SQUARE: case H_OVAL: dx += 2.5; break; } } if (s->pits[m] <= yc + 3 && dx > xc) xc = dx; } } de->x = s->x - xc; de->y = (float) (3 * (yc - 18)); } /* special case for long decoration */ static void d_trill(struct deco_elt *de) { struct SYMBOL *s, *s2; struct deco_def_s *dd; int staff, up; float x, y, w; if (de->flags & DE_LDST) return; s2 = de->s; // only one ottava per staff if (s2->sflags & S_OTTAVA) { struct deco_elt *de2; for (de2 = de->next; de2; de2 = de2->next) { if (de2->t == de->t && de2->s->time == s2->time && de2->s->staff == s2->staff) { de2->s->sflags &= ~S_OTTAVA; de2->t = 0; } } s2->sflags &= ~S_OTTAVA; } // if (de->start) { /* deco start */ s = de->start->s; x = s->x; if (s->abc_type == ABC_T_NOTE && s->u.note.dc.n > 1) x += 10; // } else { /* end without start */ // s = first_note; // if (!s) { // dd = &deco_def_tb[de->t]; // error(1, s2, "No start of deco !%s!", dd->name); // de->t = 0; // return; // } // x = s->x - s->wl - 4; // } de->staff = staff = s2->staff; dd = &deco_def_tb[de->t]; if (dd->func == 4) // if below up = 0; else if (strcmp(ps_func_tb[dd->ps_func], "o8va") == 0) up = 1; else up = s2->multi >= 0; if (de->defl & DEF_NOEN) { /* if no decoration end */ w = de->x - x; if (w < 20) { x = de->x - 20 - 3; w = 20; } } else { w = s2->x - x - 6; if (s2->abc_type == ABC_T_NOTE) w -= 6; if (w < 20) { x -= (20 - w) * 0.5; // if (!de->start) // x -= (20 - w) * 0.5; w = 20; } } y = y_get(staff, up, x, w); if (up) { float stafft; stafft = staff_tb[s->staff].topbar + 2; if (y < stafft) y = stafft; } else { float staffb; y -= dd->h; staffb = staff_tb[s->staff].botbar - 2; if (y > staffb) y = staffb; } de->flags &= ~DE_LDEN; de->flags |= DE_VAL; de->val = w; de->x = x; de->y = y; if (up) y += dd->h; y_set(staff, up, x, w, y); if (up) s->ymx = s2->ymx = y; else s->ymn = s2->ymn = y; } /* 3, 4: above (or below) the staff */ static void d_upstaff(struct deco_elt *de) { struct SYMBOL *s; struct deco_def_s *dd; char *ps_name; float x, yc, stafft, staffb, w; int up, inv; // don't treat here the start of long decorations if (de->flags & DE_LDST) return; if (de->start) { d_trill(de); return; } s = de->s; dd = &deco_def_tb[de->t]; inv = 0; x = s->x; if (s->type == NOTEREST) x += s->u.note.notes[s->stem >= 0 ? 0 : s->nhd].shhd; w = dd->wl + dd->wr; stafft = staff_tb[s->staff].topbar + 2; staffb = staff_tb[s->staff].botbar - 2; up = -1; // undefined if (dd->func == 4) { // upstaff below up = 0; } else { switch (s->posit.orn) { case SL_ABOVE: up = 1; break; case SL_BELOW: up = 0; break; } } ps_name = ps_func_tb[dd->ps_func]; if (strcmp(ps_name, "accent") == 0 || strcmp(ps_name, "cpu") == 0) { if (!up || (up < 0 && (s->multi < 0 || (s->multi == 0 && s->stem > 0)))) { yc = y_get(s->staff, 0, s->x - dd->wl, w); if (yc > staffb) yc = staffb; yc -= dd->h; y_set(s->staff, 0, s->x, 0, yc); inv = 1; s->ymn = yc; } else { yc = y_get(s->staff, 1, s->x, 0); if (yc < stafft) yc = stafft; y_set(s->staff, 1, s->x - dd->wl, w, yc + dd->h); s->ymx = yc + dd->h; } } else if (strcmp(ps_name, "brth") == 0 || strcmp(ps_name, "lphr") == 0 || strcmp(ps_name, "mphr") == 0 || strcmp(ps_name, "sphr") == 0) { yc = stafft + 1; if (ps_name[0] == 'b') { // if breath if (yc < s->ymx) yc = s->ymx; } for (s = s->ts_next; s; s = s->ts_next) if (s->shrink != 0) break; if (s) x += (s->x - x) * 0.4; else x += (realwidth - x) * 0.4; } else { if (strncmp(dd->name, "invert", 6) == 0) inv = 1; if (strcmp(dd->name, "invertedfermata") != 0 && (up > 0 || (up < 0 && s->multi >= 0))) { yc = y_get(s->staff, 1, s->x - dd->wl, w); if (yc < stafft) yc = stafft; y_set(s->staff, 1, s->x - dd->wl, w, yc + dd->h); s->ymx = yc + dd->h; } else { yc = y_get(s->staff, 0, s->x - dd->wl, w); if (yc > staffb) yc = staffb; yc -= dd->h; y_set(s->staff, 0, s->x - dd->wl, w, yc); if (strcmp(dd->name, "fermata") == 0) // || strcmp(dd->name, "invertedfermata") == 0) inv = 1; s->ymn = yc; } } if (inv) { yc += dd->h; de->flags |= DE_INV; } de->x = x; de->y = yc; // if (dd->strx != 0) { // if (dd->strx == 255) // de->str = dd->name; // else // de->str = set_str(de, str_tb[dd->strx]); // } } /* -- add a decoration - from %%deco -- */ /* syntax: * %%deco <name> <c_func> <ps_func> <h> <wl> <wr> [<str>] */ void deco_add(char *s) { struct u_deco *d; int l; l = strlen(s); d = malloc(sizeof *user_deco - sizeof user_deco->text + l + 1); strcpy(d->text, s); d->next = user_deco; user_deco = d; } static int get_deco(char *name) { struct deco_def_s *dd; int ideco; for (ideco = 1, dd = &deco_def_tb[1]; ideco < 128; ideco++, dd++) { if (!dd->name || strcmp(dd->name, name) == 0) return ideco; } error(1, NULL, "Too many decorations"); return ideco; } static unsigned char deco_build(char *name, char *text) { struct deco_def_s *dd; int c_func, ideco, h, o, wl, wr, n; unsigned l, ps_x, strx; char name2[32]; char ps_func[16]; /* extract the arguments */ if (sscanf(text, "%15s %d %15s %d %d %d%n", name2, &c_func, ps_func, &h, &wl, &wr, &n) != 6) { error(1, NULL, "Invalid %%%%deco %s", text); return 128; } if ((unsigned) c_func > 10 && (c_func < 32 || c_func > 41)) { error(1, NULL, "%%%%deco: bad C function index (%s)", text); return 128; } if (c_func == 5) // old !trill(! c_func = 3; if (c_func == 7) // old !cresc(! c_func = 6; if (h < 0 || wl < 0 || wr < 0) { error(1, NULL, "%%%%deco: cannot have a negative value (%s)", text); return 128; } if (h > 50 || wl > 80 || wr > 80) { error(1, NULL, "%%%%deco: abnormal h/wl/wr value (%s)", text); return 128; } text += n; while (isspace((unsigned char) *text)) text++; /* search the decoration */ ideco = get_deco(name); if (ideco == 128) return ideco; dd = &deco_def_tb[ideco]; /* search the postscript function */ for (ps_x = 0; ps_x < sizeof ps_func_tb / sizeof ps_func_tb[0]; ps_x++) { if (ps_func_tb[ps_x] == 0 || strcmp(ps_func_tb[ps_x], ps_func) == 0) break; } if (ps_x == sizeof ps_func_tb / sizeof ps_func_tb[0]) { error(1, NULL, "Too many postscript functions"); return 128; } /* have an index for the string */ if (strcmp(text, name) == 0) { strx = 255; } else if (*text == '\0') { strx = c_func == 6 ? 255 : 0; } else { for (strx = 1; strx < sizeof str_tb / sizeof str_tb[0]; strx++) { if (!str_tb[strx]) { if (*text == '"') { text++; l = strlen(text); str_tb[strx] = malloc(l); memcpy(str_tb[strx], text, l - 1); str_tb[strx][l - 1] = '\0'; } else { str_tb[strx] = strdup(text); } break; } if (strcmp(str_tb[strx], text) == 0) break; } if (strx == sizeof str_tb / sizeof str_tb[0]) { error(1, NULL, "Too many decoration strings"); return 128; } } /* set the values */ if (!dd->name) dd->name = name; /* new decoration */ dd->func = strncmp(dd->name, "head-", 5) == 0 ? 9 : c_func; if (!ps_func_tb[ps_x]) { if (ps_func[0] == '-' && ps_func[1] == '\0') ps_x = -1; else ps_func_tb[ps_x] = strdup(ps_func); } dd->ps_func = ps_x; dd->h = h; dd->wl = wl; dd->wr = wr; /* link the start and end of long decorations */ l = strlen(name); if (l == 0) return ideco; l--; if (name[l] == '(' || (name[l] == ')' && !strchr(name, '('))) { struct deco_def_s *ddo; strx = 0; // (no string) strcpy(name2, name); if (name[l] == '(') { dd->flags = DE_LDST; name2[l] = ')'; } else { dd->flags = DE_LDEN; name2[l] = '('; } for (o = 1, ddo = &deco_def_tb[1]; o < 128; o++, ddo++) { if (!ddo->name) break; if (strcmp(ddo->name, name2) == 0) { if (name[l] == '(') { ddo->ld_start = ideco; dd->ld_end = o; } else { dd->ld_start = o; ddo->ld_end = ideco; } break; } } if (o >= 128 || !ddo->name) //fixme: memory leak... deco_define(strdup(name2)); } dd->strx = strx; return ideco; } /* -- set the duration of the notes under a feathered beam -- */ static void set_feathered_beam(struct SYMBOL *s1, int accel) { struct SYMBOL *s, *s2; int n, t, tt, d, b, i; float a; /* search the end of the beam */ d = s1->dur; s2 = NULL; n = 1; for (s = (struct SYMBOL *) s1->abc_next; s; s = (struct SYMBOL *) s->abc_next) { if (s->dur != d || (s->flags & ABC_F_SPACE)) break; s2 = s; n++; } if (!s2) return; b = d / 2; /* smallest note duration */ a = (float) d / (n - 1); /* delta duration */ tt = d * n; t = 0; if (accel) { /* !beam-accel! */ for (s = s1, i = n - 1; s != s2; s = (struct SYMBOL *) s->abc_next, i--) { d = (int) lroundf(a * i) + b; s->dur = d; t += d; } } else { /* !beam-rall! */ for (s = s1, i = 0; s != s2; s = (struct SYMBOL *) s->abc_next, i++) { d = (int) lroundf(a * i) + b; s->dur = d; t += d; } } s2->dur = tt - t; } /* -- define a decoration -- */ static unsigned char deco_define(char *name) { struct u_deco *d; unsigned char ideco; int l; l = strlen(name); for (d = user_deco; d; d = d->next) { if (strncmp(d->text, name, l) == 0 && d->text[l] == ' ') return deco_build(name, d->text); } for (ideco = 0; ; ideco++) { if (!std_deco_tb[ideco]) break; if (strncmp(std_deco_tb[ideco], name, l) == 0 && std_deco_tb[ideco][l] == ' ') return deco_build(name, std_deco_tb[ideco]); } return 128; } /* -- convert the external deco number to the internal one -- */ static unsigned char deco_intern(unsigned char ideco, struct SYMBOL *s) { char *name; name = ideco < 128 ? deco[ideco] : parse.deco_tb[ideco - 128]; if (!name) { error(1, s, "Bad character '%c'", ideco); return 0; } for (ideco = 1; ideco < 128; ideco++) { if (!deco_def_tb[ideco].name) { ideco = deco_define(name); break; } if (strcmp(deco_def_tb[ideco].name, name) == 0) break; } if (ideco == 128) { if (cfmt.decoerr) error(1, s, "Decoration !%s! not defined", name); ideco = 0; } return ideco; } /* -- convert the decorations -- */ void deco_cnv(struct decos *dc, struct SYMBOL *s, struct SYMBOL *prev) { int i, j, m, n; struct deco_def_s *dd; unsigned char ideco; static char must_note_fmt[] = "Deco !%s! must be on a note"; for (i = dc->n; --i >= 0; ) { if ((ideco = dc->tm[i].t) == 0) continue; ideco = deco_intern(ideco, s); dc->tm[i].t = ideco; if (ideco == 0) continue; dd = &deco_def_tb[ideco]; m = dc->tm[i].m; /* special decorations */ switch (dd->func) { case 2: // arp if (m >= 0) { error(1, s, "!%s! cannot be on a head (function 2)", dd->name); break; } /* fall thru */ case 0: // near /* special case for dotted bars */ if (dd->func == 0 && s->abc_type == ABC_T_BAR && strcmp(dd->name, "dot") == 0) { s->u.bar.dotted = 1; break; } // fall thru case 1: // slide if (s->abc_type != ABC_T_NOTE && s->abc_type != ABC_T_REST) { error(1, s, "!%s! must be on a note or a rest", dd->name); break; } continue; case 8: // gliss: move to the upper note of the chord if (s->abc_type != ABC_T_NOTE) { error(1, s, "!%s! must be on a note", dd->name); break; } if (m < 0) dc->tm[i].m = s->nhd; continue; case 9: // move the alternate head of the chord to the notes if (s->abc_type != ABC_T_NOTE && s->abc_type != ABC_T_REST) { error(1, s, "!%s! must be on a note or a rest", dd->name); break; } if (m >= 0) { s->u.note.notes[m].invisible = 1; continue; } // apply !head-xx! to each head dc->tm[i].m = 0; s->u.note.notes[0].invisible = 1; n = dc->n; for (m = 1; m <= s->nhd; m++) { if (n >= MAXDC) { error(1, s, "Too many decorations"); break; } dc->tm[n].t = ideco; dc->tm[n++].m = m; s->u.note.notes[m].invisible = 1; } dc->n = n; continue; default: if (strlen(dd->name) >= 4 && dd->name[0] == '8' && dd->name[1] == 'v' && dd->name[4] == '\0') { if (dd->name[3] == '(') { if (dd->name[2] == 'a') curvoice->ottava = -7; else if (dd->name[2] == 'b') curvoice->ottava = 7; } else if (dd->name[3] == ')') { if (dd->name[2] == 'a' || dd->name[2] == 'b') curvoice->ottava = 0; } s->sflags |= S_OTTAVA; } continue; case 32: /* invisible */ if (m < 0) s->flags |= ABC_F_INVIS; else s->u.note.notes[m].invisible = 1; break; case 33: /* beamon */ s->sflags |= S_BEAM_ON; break; case 34: /* trem1..trem4 */ if (s->abc_type != ABC_T_NOTE || !prev || prev->abc_type != ABC_T_NOTE) { error(1, s, "!%s! must be on the last of a couple of notes", dd->name); break; } if (strlen(dd->name) != 5) n = 0; else n = dd->name[4] - '0'; if (n <= 0 || n > 4) { error(1, s, "bad definition of !%s!", dd->name); break; } s->sflags |= (S_TREM2 | S_BEAM_END); s->sflags &= ~S_BEAM_ST; prev->sflags |= (S_TREM2 | S_BEAM_ST); prev->sflags &= ~S_BEAM_END; s->aux = prev->aux = n; for (j = 0; j <= s->nhd; j++) s->u.note.notes[j].len *= 2; for (j = 0; j <= prev->nhd; j++) prev->u.note.notes[j].len *= 2; break; case 35: /* xstem */ if (s->abc_type != ABC_T_NOTE) { error(1, s, must_note_fmt, dd->name); break; } s->sflags |= S_XSTEM; break; case 36: /* beambr1 / beambr2 */ if (s->abc_type != ABC_T_NOTE) { error(1, s, must_note_fmt, dd->name); break; } if (strlen(dd->name) != 7 || (dd->name[6] != '1' && dd->name[6] != '2')) { error(1, s, "bad definition of !%s!", dd->name); break; } s->sflags |= dd->name[6] == '1' ? S_BEAM_BR1 : S_BEAM_BR2; break; case 37: /* rbstop */ s->sflags |= S_RBSTOP; break; case 38: /* /, // and /// = tremolo */ if (s->abc_type != ABC_T_NOTE) { error(1, s, must_note_fmt, dd->name); break; } n = strlen(dd->name); if (n > 3) { error(1, s, "bad definition of !%s!", dd->name); break; } s->sflags |= S_TREM1; s->aux = n; /* 1, 2 or 3 */ break; case 39: /* beam-accel/beam-rall */ if (s->abc_type != ABC_T_NOTE) { error(1, s, must_note_fmt, dd->name); break; } if (strlen(dd->name) < 6) { error(1, s, "bad definition of !%s!", dd->name); break; } s->sflags |= S_FEATHERED_BEAM; set_feathered_beam(s, dd->name[5] == 'a'); break; case 40: /* stemless */ if (s->abc_type != ABC_T_NOTE) { error(1, s, must_note_fmt, dd->name); break; } s->flags |= ABC_F_STEMLESS; break; case 41: /* rbend */ s->flags |= ABC_F_RBSTOP; /* with bracket end */ s->sflags |= S_RBSTOP; break; } dc->tm[i].t = 0; /* already treated */ } } /* -- update the x position of a decoration -- */ void deco_update(struct SYMBOL *s, float dx) { struct deco_elt *de; for (de = deco_head; de; de = de->next) { if (de->s == s) { while (de && de->s == s) { de->x += dx; de = de->next; } break; } } } /* -- adjust the symbol width -- */ float deco_width(struct SYMBOL *s) { struct decos *dc; int i; float wl; wl = 0; if (s->type == BAR) dc = &s->u.bar.dc; else dc = &s->u.note.dc; for (i = dc->n; --i >= 0; ) { struct deco_def_s *dd; dd = &deco_def_tb[dc->tm[i].t]; switch (dd->func) { case 1: /* slide */ if (wl < 7) wl = 7; break; case 2: /* arpeggio */ if (wl < 14) wl = 14; break; } } if (wl != 0 && s->prev && s->prev->type == BAR) wl -= 3; return wl; } /* -- draw the decorations -- */ /* (the staves are defined) */ void draw_all_deco(void) { struct deco_elt *de; struct deco_def_s *dd; struct SYMBOL *s; int f, staff, l; char *gl, *p; float x, y, y2, ym; float ymid[MAXSTAFF]; if (!cfmt.dynalign) { staff = nstaff; y = staff_tb[staff].y; while (--staff >= 0) { y2 = staff_tb[staff].y; ymid[staff] = (y + 24 + y2) * 0.5; y = y2; } } for (de = deco_head; de; de = de->next) { if (de->t == 0) // deleted continue; dd = &deco_def_tb[de->t]; // if ((dd->flags & DE_LDST) && dd->ld_end != 0) if (dd->ld_end != 0) continue; // start of long decoration if ((f = dd->ps_func) < 0) continue; // old behaviour // handle the stem direction gl = ps_func_tb[f]; // glyph name(s) p = strchr(gl, '/'); if (p) { if (de->s->stem >= 0) { l = (int) (p - gl); } else { gl = p + 1; l = strlen(gl); } } else { l = strlen(gl); } s = de->s; // David Lacroix - 16-12-28 // set_color(s->color); // no scale if staff decoration if (f_staff & (1 << dd->func)) set_sscale(-1); else set_scale(s); staff = de->staff; x = de->x; // y = de->y + staff_tb[staff].y / staff_tb[staff].staffscale; y = de->y + staff_tb[staff].y; /* update the coordinates if head decoration */ if (de->m >= 0) { x += s->u.note.notes[de->m].shhd * staff_tb[staff].staffscale; /* center the dynamic marks between two staves */ /*fixme: KO when deco on other voice and same direction*/ } else if ((f_staff & (1 << dd->func)) && !cfmt.dynalign && (((de->flags & DE_UP) && staff > 0) || (!(de->flags & DE_UP) && staff < nstaff))) { if (de->flags & DE_UP) ym = ymid[--staff]; else ym = ymid[staff++]; ym -= dd->h * 0.5; if (((de->flags & DE_UP) && y < ym) || (!(de->flags & DE_UP) && y > ym)) { // if (s->staff > staff) { // while (s->staff != staff) // s = s->ts_prev; // } else if (s->staff < staff) { // while (s->staff != staff) // s = s->ts_next; // } y2 = y_get(staff, !(de->flags & DE_UP), de->x, de->val) + staff_tb[staff].y; if (de->flags & DE_UP) y2 -= dd->h; if (((de->flags & DE_UP) && y2 > ym) || (!(de->flags & DE_UP) && y2 < ym)) { y = ym; y_set(staff, de->flags & DE_UP, de->x, de->val, ((de->flags & DE_UP) ? y + dd->h : y) - staff_tb[staff].y); } } } set_defl(de->defl); if (de->flags & DE_VAL) { if (dd->func != 2 || voice_tb[s->voice].scale != 1) putx(de->val); else putf(de->val); } if (dd->strx != 0) { char *p, *q; y += dd->h * 0.2; // font descent if (dd->strx == 255) { p = dd->name; } else { p = str_tb[dd->strx]; if (*p == '@') { float dx, dy; int n; if (sscanf(p, "@%f,%f%n", &dx, &dy, &n) == 2) { x += dx; y += dy; p += n; } str_font(ANNOTATIONFONT); outft = -1; // force font selection putxy(x, y); a2b("M"); put_str(p, A_LEFT); continue; } } a2b("("); q = p; while (*p != '\0') { if (*p == '(' || *p == ')') { if (p != q) a2b("%.*s", (int) (p - q), q); a2b("\\"); q = p; } p++; } if (p != q) a2b("%.*s", (int) (p - q), q); a2b(")"); } putxy(x, y); if (de->flags & DE_LDEN) { // if (de->start) { x = de->start->x; y = de->start->y + staff_tb[de->start->staff].y; // } else { // x = first_note->x - first_note->wl - 4; // } if (x > de->x - 20) x = de->x - 20; putxy(x, y); } if (de->flags & DE_INV) a2b("gsave 1 -1 scale neg %.*s grestore\n", l, gl); else a2b("%.*s\n", l, gl); } set_sscale(-1); /* restore the scale */ set_color(0); } /* -- create the deco elements, and treat the near ones -- */ static void deco_create(struct SYMBOL *s, struct decos *dc) { int k, m, posit; unsigned char ideco; struct deco_def_s *dd; struct deco_elt *de; /*fixme:pb with decorations above the staff*/ for (k = 0; k < dc->n; k++) { m = dc->tm[k].m; if ((ideco = dc->tm[k].t) == 0) continue; dd = &deco_def_tb[ideco]; /* check if hidden */ switch (dd->func) { default: posit = 0; break; case 3: /* d_upstaff */ case 4: posit = s->posit.orn; break; case 6: /* d_pf */ posit = s->posit.vol; break; case 7: /* d_cresc */ posit = s->posit.dyn; break; } if (posit == SL_HIDDEN) { dc->tm[k].t = 0; continue; } de = (struct deco_elt *) getarena(sizeof *de); memset(de, 0, sizeof *de); de->prev = deco_tail; if (!deco_tail) deco_head = de; else deco_tail->next = de; deco_tail = de; de->s = s; de->t = dd - deco_def_tb; de->staff = s->staff; de->m = m; // if (s->flags & ABC_F_GRACE) // de->flags = DE_GRACE; if (dd->flags & DE_LDST) { de->flags |= DE_LDST; } else if (dd->flags & DE_LDEN) { de->flags |= DE_LDEN; de->defl = DEF_NOST; } if (cfmt.setdefl && s->stem >= 0) de->defl |= DEF_STEMUP; /* set the coordinates of the decoration */ if (m >= 0) { /* head decoration */ de->x = s->x; de->y = 3 * (s->pits[m] - 18); // if (dd->func == 9) /* alternate note head */ // s->u.note.notes[m].invisible = 1; continue; } if (!(f_near & (1 << dd->func))) /* if not near the note */ continue; func_tb[dd->func](de); } } // link the long decorations static void ll_deco(void) { struct deco_elt *de, *de2, *tail; struct deco_def_s *dd; int t, voice, staff; // add ending decorations tail = deco_tail; if (!tail) return; for (de = deco_head; ; de = de->next) { t = de->t; dd = &deco_def_tb[t]; if (!(de->flags & DE_LDST)) { if (de == tail) break; continue; } t = dd->ld_end; #if 0 if (t == 0) { // if long deco has no end int l; // create one char *name; l = strlen(dd->name); name = getarena(l + 1); strcpy(name, dd->name); name[l - 1] = ')'; t = get_deco(name); if (t != 128) { struct deco_def_s *dd2; dd2 = &deco_def_tb[t]; dd2->name = name; dd2->func = dd->func; dd2->ps_func = dd->ps_func; dd2->h = dd->h; dd->ld_end = t; } else { t = 0; } } #endif voice = de->s->voice; /* search later in the voice */ for (de2 = de->next; de2; de2 = de2->next) if (!de2->start && de2->t == t && de2->s->voice == voice) break; if (!de2) { /* no end, search in the staff */ staff = de->s->staff; for (de2 = de->next; de2; de2 = de2->next) if (!de2->start && de2->t == t && de2->s->staff == staff) break; } if (!de2) { /* no end, insert one */ de2 = (struct deco_elt *) getarena(sizeof *de2); memset(de2, 0, sizeof *de2); de2->prev = deco_tail; deco_tail->next = de2; deco_tail = de2; de2->s = de->s; de2->t = t; de2->defl = DEF_NOEN; de2->flags = DE_LDEN; de2->x = realwidth - 6; de2->y = de->s->y; de2->m = de->m; } de2->start = de; de2->defl &= ~DEF_NOST; if (dd->func == 8) d_gliss(de2); if (de == tail) break; } // add starting decorations for (de2 = deco_head; ; de2 = de2->next) { if (!(de2->flags & DE_LDEN) // not the end of long decoration || de2->start) { // start already found if (de2 == tail) break; continue; } t = de2->t; dd = &deco_def_tb[t]; de = (struct deco_elt *) getarena(sizeof *de); memset(de, 0, sizeof *de); de->prev = deco_tail; deco_tail->next = de; deco_tail = de; de->s = prev_scut(de2->s); de->t = dd->ld_start; de->flags = DE_LDST; de->defl = DEF_NOST; de->x = de->s->x; //de2->s->x - de2->s->wl - 4; de->y = de2->s->y; de->m = de2->m; de2->start = de; // de2->defl &= ~DEF_NOST; if (de2 == tail) break; } } /* -- create the decorations and treat the ones near the notes -- */ /* (the staves are not yet defined) */ /* this function must be called first as it builds the deco element table */ void draw_deco_near(void) { struct SYMBOL *s, *g; struct decos *dc; // struct SYMBOL *first; deco_head = deco_tail = NULL; // first = NULL; for (s = tsfirst; s; s = s->ts_next) { switch (s->type) { case BAR: case MREST: if (s->u.bar.dc.n == 0) continue; dc = &s->u.bar.dc; break; case NOTEREST: case SPACE: // if (!first) // first = s; if (s->u.note.dc.n == 0) continue; dc = &s->u.note.dc; break; case GRACE: for (g = s->extra; g; g = g->next) { if (g->abc_type != ABC_T_NOTE || g->u.note.dc.n == 0) continue; dc = &g->u.note.dc; deco_create(g, dc); } /* fall thru */ default: continue; } deco_create(s, dc); } // first_note = first; ll_deco(); // link the long decorations } /* -- draw the decorations tied to a note -- */ /* (the staves are not yet defined) */ void draw_deco_note(void) { struct deco_elt *de; struct deco_def_s *dd; int f, t; for (de = deco_head; de; de = de->next) { t = de->t; dd = &deco_def_tb[t]; f = dd->func; if (!(f_note & (1 << f)) /* if not tied to the note */ || de->m >= 0) /* or head decoration */ continue; // if (f == 4) // de->flags |= DE_BELOW; func_tb[f](de); } } /* draw the repeat brackets */ static void draw_repbra(struct VOICE_S *p_voice) { struct SYMBOL *s, *s1, *s2, *first_repeat; int i; float x, y, y2, w; /* search the max y offset */ y = staff_tb[p_voice->staff].topbar + 6 + 20; first_repeat = 0; for (s = p_voice->sym->next; s; s = s->next) { if (s->type != BAR) continue; if (!(s->sflags & S_RBSTART) || (s->sflags & S_NOREPBRA)) continue; /*fixme: line cut on repeat!*/ if (!s->next) break; if (!first_repeat) first_repeat = s; s1 = s; for (;;) { if (!s->next) break; s = s->next; if (s->sflags & S_RBSTOP) break; } y2 = y_get(p_voice->staff, 1, s1->x, s->x - s1->x); if (y < y2) y = y2; /* have room for the repeat numbers */ if (s1->gch) { w = s1->gch->w; y2 = y_get(p_voice->staff, 1, s1->x + 4, w); y2 += cfmt.font_tb[REPEATFONT].size + 2; if (y < y2) y = y2; } if (s->sflags & S_RBSTART) s = s->prev; } /* draw the repeat indications */ s = first_repeat; if (!s) return; // set_sscale(p_voice->staff); //temporary set_sscale(-1); set_font(REPEATFONT); for ( ; s; s = s->next) { char *p; if (!(s->sflags & S_RBSTART) || (s->sflags & S_NOREPBRA)) continue; s1 = s; for (;;) { if (!s->next) break; s = s->next; if (s->sflags & S_RBSTOP) break; } s2 = s; if (s1 == s2) break; x = s1->x; if ((s1->u.bar.type & 0x0f) == B_COL) x -= 4; if (s2->type != BAR) { if (s2->sflags & S_RBSTOP) w = 0; else w = s2->x - realwidth + 4; } else if (((s2->u.bar.type & 0xf0) /* if complex bar */ && s2->u.bar.type != (B_OBRA | B_CBRA)) || s2->u.bar.type == B_CBRA) { if (s2->u.bar.type == B_CBRA) s2->flags |= ABC_F_INVIS; /*fixme:%%staves: cursys moved?*/ if (s1->staff > 0 && !(cursys->staff[s1->staff - 1].flags & STOP_BAR)) w = s2->wl; else if ((s2->u.bar.type & 0x0f) == B_COL) w = 12; else if (!(s2->sflags & S_RRBAR)) // || s2->u.bar.type == B_CBRA) w = 0; /* explicit repeat end */ else w = 8; } else { w = (s2->sflags & S_RBSTOP) ? 0 : 8; } w = s2->x - x - w; p = s1->text; if (!p) p = ""; if (!s2->next /* 2nd ending at end of line */ && !(s2->sflags & S_RBSTOP) && (p_voice->bar_start == 0)) { p_voice->bar_start = B_OBRA | 0x1000; /* S_RBSTART */ p_voice->bar_repeat = 1; /* continue on next line */ } if (s1->flags & ABC_F_RBSTART) i = (s2->flags & ABC_F_RBSTOP) ? 3 : 1; else i = (s2->flags & ABC_F_RBSTOP) ? 2 : 0; a2b("(%s)-%.1f %d ", p, cfmt.font_tb[REPEATFONT].size * 0.8 + 1, i); putx(w); putxy(x, y * staff_tb[s1->staff].staffscale); a2b("yns%d repbra\n", s1->staff); y_set(s1->staff, 1, x, w, y + 2); if (s->u.bar.repeat_bar) s = s->prev; } } /* -- draw the music elements tied to the staff -- */ /* (the staves are not yet defined) */ void draw_deco_staff(void) { struct SYMBOL *s, *first_gchord; struct VOICE_S *p_voice; float y, w; struct deco_elt *de; struct { float ymin, ymax; } minmax[MAXSTAFF]; // outft = -1; /* force font output */ /* search the vertical offset for the guitar chords */ memset(minmax, 0, sizeof minmax); first_gchord = 0; for (s = tsfirst; s; s = s->ts_next) { struct gch *gch, *gch2; int ix; gch = s->gch; if (!gch) continue; if (!first_gchord) first_gchord = s; gch2 = NULL; for (ix = 0; ix < MAXGCH; ix++, gch++) { if (gch->type == '\0') break; if (gch->type != 'g') continue; gch2 = gch; /* guitar chord closest to the staff */ if (gch->y < 0) break; } if (gch2) { w = gch2->w; if (gch2->y >= 0) { y = y_get(s->staff, 1, s->x, w); if (y > minmax[s->staff].ymax) minmax[s->staff].ymax = y; } else { y = y_get(s->staff, 0, s->x, w); if (y < minmax[s->staff].ymin) minmax[s->staff].ymin = y; } } } /* draw the guitar chords if any */ if (first_gchord) { int i; for (i = 0; i <= nstaff; i++) { int top, bot; bot = staff_tb[i].botbar; if (minmax[i].ymin > bot - 6) minmax[i].ymin = bot - 6; top = staff_tb[i].topbar; if (minmax[i].ymax < top + 6) minmax[i].ymax = top + 6; } set_sscale(-1); /* restore the scale parameters */ for (s = first_gchord; s; s = s->ts_next) { if (!s->gch) continue; switch (s->type) { case NOTEREST: case SPACE: case MREST: break; case BAR: if (!s->u.bar.repeat_bar) break; default: continue; } draw_gchord(s, minmax[s->staff].ymin, minmax[s->staff].ymax); } } /* draw the repeat brackets */ for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { if (p_voice->second || !p_voice->sym) continue; draw_repbra(p_voice); } /* create the decorations tied to the staves */ memset(minmax, 0, sizeof minmax); for (de = deco_head; de; de = de->next) { struct deco_def_s *dd; dd = &deco_def_tb[de->t]; if (!(f_staff & (1 << dd->func)) /* if not tied to the staff */ || de->m >= 0) /* or chord decoration */ continue; func_tb[dd->func](de); if (dd->ps_func < 0) continue; if (cfmt.dynalign) { if (de->flags & DE_UP) { if (de->y > minmax[de->staff].ymax) minmax[de->staff].ymax = de->y; } else { if (de->y < minmax[de->staff].ymin) minmax[de->staff].ymin = de->y; } } } /* and, if wanted, set them at a same vertical offset */ for (de = deco_head; de; de = de->next) { struct deco_def_s *dd; dd = &deco_def_tb[de->t]; if (dd->ps_func < 0 || !(f_staff & (1 << dd->func))) continue; if (cfmt.dynalign) { if (de->flags & DE_UP) y = minmax[de->staff].ymax; else y = minmax[de->staff].ymin; de->y = y; } else { y = de->y; } if (de->flags & DE_UP) y += dd->h; y_set(de->staff, de->flags & DE_UP, de->x, de->val, y); } } /* -- draw the guitar chords and annotations -- */ /* (the staves are not yet defined) */ static void draw_gchord(struct SYMBOL *s, float gchy_min, float gchy_max) { struct gch *gch, *gch2; int action, ix, box, yav; float x, y, w, h, y_above, y_below; float hbox, xboxl, yboxh, yboxl, expdx; /* adjust the vertical offset according to the guitar chords */ //fixme: w may be too small w = s->gch->w; #if 1 y_above = y_get(s->staff, 1, s->x - 2, w); y_below = y_get(s->staff, 0, s->x - 2, w); #else y_above = y_get(s->staff, 1, s->x - 2, w) + 2; y_below = y_get(s->staff, 0, s->x - 2, w) - 2; #endif gch2 = NULL; yav = ((s->pits[s->nhd] + s->pits[0]) / 2 - 18) * 3; for (ix = 0, gch = s->gch; ix < MAXGCH; ix++, gch++) { if (gch->type == '\0') break; if (gch->type != 'g') continue; gch2 = gch; /* guitar chord closest to the staff */ if (gch->y < 0) break; } if (gch2) { if (gch2->y >= 0) { if (y_above < gchy_max) y_above = gchy_max; } else { if (y_below > gchy_min) y_below = gchy_min; } } str_font(s->gch->font); set_font(s->gch->font); /* needed if scaled staff */ // set_sscale(s->staff); //temporary set_sscale(-1); // action = A_GCHORD; xboxl = s->x; yboxh = -100; yboxl = 100; box = 0; expdx = 0; for (ix = 0, gch = s->gch; ix < MAXGCH; ix++, gch++) { if (gch->type == '\0') break; h = cfmt.font_tb[gch->font].size; str_font(gch->font); tex_str(s->text + gch->idx); w = gch->w; if (gch->type == 'g') { /* guitar chord */ if (!strchr(tex_buf, '\t')) { action = A_GCHORD; } else { struct SYMBOL *next; char *r; int n; /* some TAB: expand the guitar chord */ x = realwidth; next = s->next; while (next) { switch (next->type) { default: next = next->next; continue; case NOTEREST: case BAR: x = next->x; break; } break; } n = 0; r = tex_buf; for (;;) { n++; r = strchr(r, '\t'); if (!r) break; r++; } expdx = (x - s->x - w) / n; action = A_GCHEXP; } } else { action = A_ANNOT; } x = s->x + gch->x; switch (gch->type) { case '_': /* below */ y = gch->y + y_below; y_set(s->staff, 0, x, w, y - h * 0.2 - 2); break; case '^': /* above */ y = gch->y + y_above; y_set(s->staff, 1, x, w, y + h * 0.8 + 2); break; default: /* guitar chord */ hbox = gch->box ? 3 : 2; if (gch->y >= 0) { y = gch->y + y_above; y_set(s->staff, 1, x, w, y + h + hbox); } else { y = gch->y + y_below; y_set(s->staff, 0, x, w, y - hbox); } if (gch->box) { if (xboxl > x) xboxl = x; if (yboxl > y) yboxl = y; if (yboxh < y + h) yboxh = y + h; box++; } break; case '<': /* left */ /*fixme: what symbol space?*/ if (s->u.note.notes[0].acc) x -= s->u.note.notes[0].shac; y = yav + gch->y; break; case '>': /* right */ x += s->xmx; if (s->dots > 0) x += 1.5 + 3.5 * s->dots; y = yav + gch->y; break; case '@': /* absolute */ y = yav + gch->y; break; } putxy(x, (y + h * 0.2) * /* (descent) */ staff_tb[s->staff].staffscale); a2b("yns%d M ", s->staff); if (action == A_GCHEXP) a2b("%.2f ", expdx); str_out(tex_buf, action); if (gch->type == 'g' && box > 0) { if (box == 1) a2b(" boxend"); else a2b(" boxmark"); } a2b("\n"); } /* draw the box around the guitar chords */ if (box) { xboxl -= 2; putxy(xboxl, (yboxl - 1) * staff_tb[s->staff].staffscale); a2b("yns%d %.1f boxdraw\n", s->staff, yboxh - yboxl + 3); } } /* -- draw the measure bar numbers -- */ void draw_measnb(void) { struct SYMBOL *s; struct SYSTEM *sy; char *showm; int any_nb, staff, bar_num; float x, y, w, font_size; showm = cfmt.measurebox ? "showb" : "show"; any_nb = 0; /* search the first staff */ sy = cursys; for (staff = 0; staff <= nstaff; staff++) { if (!sy->staff[staff].empty) break; } if (staff > nstaff) return; /* no visible staff */ //fixme: must use the scale, otherwise bad y offset (y0 empty) set_sscale(staff); /* leave the measure numbers as unscaled */ font_size = cfmt.font_tb[MEASUREFONT].size; cfmt.font_tb[MEASUREFONT].size /= staff_tb[staff].staffscale; s = tsfirst; /* clef */ bar_num = nbar; if (bar_num > 1) { if (cfmt.measurenb == 0) { set_font(MEASUREFONT); any_nb = 1; x = 0; w = 20; y = y_get(staff, 1, x, w); if (y < staff_tb[staff].topbar + 14) y = staff_tb[staff].topbar + 14; a2b("0 "); puty(y); a2b("y%d M(%d)%s", staff, bar_num, showm); y_set(staff, 1, x, w, y + cfmt.font_tb[MEASUREFONT].size + 2); } else if (bar_num % cfmt.measurenb == 0) { for ( ; ; s = s->ts_next) { switch (s->type) { case TIMESIG: case CLEF: case KEYSIG: case FMTCHG: case STBRK: continue; } break; } if (s->prev && s->prev->type != CLEF) s = s->prev; x = s->x - s->wl; set_font(MEASUREFONT); any_nb = 1; w = cwid('0') * cfmt.font_tb[MEASUREFONT].swfac; if (bar_num >= 10) { if (bar_num >= 100) w *= 3; else w *= 2; } if (cfmt.measurebox) w += 4; y = y_get(staff, 1, x, w); if (y < staff_tb[staff].topbar + 6) y = staff_tb[staff].topbar + 6; y += 2; putxy(x, y); a2b("y%d M(%d)%s", staff, bar_num, showm); y += cfmt.font_tb[MEASUREFONT].size; y_set(staff, 1, x, w, y); s->ymx = y; } } for ( ; s; s = s->ts_next) { if (s->sflags & S_NEW_SY) { sy = sy->next; for (staff = 0; staff < nstaff; staff++) { if (!sy->staff[staff].empty) break; } set_sscale(staff); } if (s->type != BAR || s->aux <= 0) continue; bar_num = s->aux; if (cfmt.measurenb == 0 || (bar_num % cfmt.measurenb) != 0 || !s->next) continue; if (!any_nb) { any_nb = 1; set_font(MEASUREFONT); } w = cwid('0') * cfmt.font_tb[MEASUREFONT].swfac; if (bar_num >= 10) { if (bar_num >= 100) w *= 3; else w *= 2; } if (cfmt.measurebox) w += 4; x = s->x - w * 0.4; y = y_get(staff, 1, x, w); if (y < staff_tb[staff].topbar + 6) y = staff_tb[staff].topbar + 6; if (s->next->abc_type == ABC_T_NOTE) { if (s->next->stem > 0) { if (y < s->next->ys - cfmt.font_tb[MEASUREFONT].size) y = s->next->ys - cfmt.font_tb[MEASUREFONT].size; } else { if (y < s->next->y) y = s->next->y; } } y += 2; a2b(" "); putxy(x, y); a2b("y%d M(%d)%s", staff, bar_num, showm); y += cfmt.font_tb[MEASUREFONT].size; y_set(staff, 1, x, w, y); s->ymx = y; } if (any_nb) a2b("\n"); nbar = bar_num; cfmt.font_tb[MEASUREFONT].size = font_size; } /* -- get the beat from a time signature -- */ static int get_beat(struct meter_s *m) { int top, bot; if (m->meter[0].top[0] == 'C') { if (m->meter[0].top[0] == '|') return BASE_LEN / 2; return BASE_LEN / 4; } if (m->meter[0].bot[0] == '\0') return BASE_LEN / 4; sscanf(m->meter[0].top, "%d", &top); sscanf(m->meter[0].bot, "%d", &bot); if (bot >= 8 && top >= 6 && top % 3 == 0) return BASE_LEN * 3 / 8; return BASE_LEN / bot; } /* -- draw the note of the tempo -- */ static void draw_notempo(struct SYMBOL *s, int len, float sc) { int head, dots, flags; float dx; a2b("gsave %.2f dup scale 8 3 RM currentpoint ", sc); identify_note(s, len, &head, &dots, &flags); switch (head) { case H_OVAL: a2b("HD"); break; case H_EMPTY: a2b("Hd"); break; default: a2b("hd"); break; } dx = 4; if (dots) { float dotx; dotx = 8; if (flags > 0) dotx += 4; switch (head) { case H_SQUARE: case H_OVAL: dotx += 2; break; case H_EMPTY: dotx += 1; break; } while (--dots >= 0) { a2b(" %.1f 0 dt", dotx); dx = dotx; dotx += 3.5; } } if (len < SEMIBREVE) { if (flags <= 0) { a2b(" %d su", STEM); } else { a2b(" %d %d sfu", flags, STEM); if (dx < 6) dx = 6; } } a2b(" grestore %.1f 0 RM\n", (dx + 15) * sc); } /* -- return the tempo width -- */ float tempo_width(struct SYMBOL *s) { unsigned i; float w; w = 0; if (s->u.tempo.str1) w += tex_str(s->u.tempo.str1); if (s->u.tempo.beats[0] != 0) { if (s->u.tempo.circa) w += tex_str("ca. "); i = 1; while (i < sizeof s->u.tempo.beats / sizeof s->u.tempo.beats[0] && s->u.tempo.beats[i] != 0) { w += 10; i++; } w += 6 + cwid(' ') * cfmt.font_tb[TEMPOFONT].swfac * 6 + 10 + 10; } if (s->u.tempo.str2) w += tex_str(s->u.tempo.str2); return w; } /* - output a tempo --*/ void write_tempo(struct SYMBOL *s, int beat, float sc) { unsigned i; char tmp[16]; if (s->u.tempo.str1) put_str(s->u.tempo.str1, A_LEFT); if (s->u.tempo.beats[0] != 0) { sc *= 0.7 * cfmt.font_tb[TEMPOFONT].size / 15.0; /*fixme: 15.0 = initial tempofont*/ for (i = 0; i < sizeof s->u.tempo.beats / sizeof s->u.tempo.beats[0] && s->u.tempo.beats[i] != 0; i++) { draw_notempo(s, s->u.tempo.beats[i], sc); } put_str("= ", A_LEFT); if (s->u.tempo.tempo != 0) { if (s->u.tempo.circa) put_str("ca. ", A_LEFT); snprintf(tmp, sizeof tmp, "%d", s->u.tempo.tempo); put_str(tmp, A_LEFT); } else { draw_notempo(s, s->u.tempo.new_beat, sc); } } if (s->u.tempo.str2) put_str(s->u.tempo.str2, A_LEFT); } /* -- draw the parts and the tempo information -- */ /* (the staves are being defined) */ float draw_partempo(int staff, float top) { struct SYMBOL *s, *g; int beat, dosh, shift; int some_part, some_tempo; float h, ht, w, x, y, ymin, dy; /* put the tempo indication at top */ dy = 0; ht = 0; some_part = some_tempo = 0; /* get the minimal y offset */ ymin = staff_tb[staff].topbar + 12; dosh = 0; shift = 1; x = 0; for (s = tsfirst; s; s = s->ts_next) { g = s->extra; if (!g) continue; for ( ; g; g = g->next) if (g->type == TEMPO) break; if (!g) continue; if (!some_tempo) { some_tempo = 1; str_font(TEMPOFONT); } w = tempo_width(g); y = y_get(staff, 1, s->x - 5, w) + 2; if (y > ymin) ymin = y; if (x >= s->x - 5 && !(dosh & (shift >> 1))) dosh |= shift; shift <<= 1; x = s->x - 5 + w; } if (some_tempo) { ht = cfmt.font_tb[TEMPOFONT].size + 2 + 2; y = 2 - ht; h = y - ht; if (dosh != 0) ht *= 2; if (top < ymin + ht) dy = ymin + ht - top; /* draw the tempo indications */ str_font(TEMPOFONT); beat = 0; for (s = tsfirst; s; s = s->ts_next) { if (!(s->sflags & S_SEQST)) continue; if (s->type == TIMESIG) beat = get_beat(&s->u.meter); g = s->extra; // if (!g) // continue; for ( ; g; g = g->next) if (g->type == TEMPO) break; if (!g) continue; /*fixme: cf left shift (-5)*/ a2b("%.1f %.1f M ", s->x - 5, (dosh & 1) ? h : y); dosh >>= 1; write_tempo(g, beat, 1); } } /* then, put the parts */ /*fixme: should reduce if parts don't overlap tempo...*/ ymin = staff_tb[staff].topbar + 14; for (s = tsfirst; s; s = s->ts_next) { g = s->extra; if (!g) continue; for (; g; g = g->next) if (g->type == PART) break; if (!g) continue; if (!some_part) { some_part = 1; str_font(PARTSFONT); } w = tex_str(&g->text[2]); y = y_get(staff, 1, s->x - 10, w + 3) + 5; if (ymin < y) ymin = y; } if (!some_part) goto out; h = cfmt.font_tb[PARTSFONT].size + 2 + 2; /* + cfmt.partsspace; ?? */ if (top < ymin + h + ht) dy = ymin + h + ht - top; set_font(PARTSFONT); for (s = tsfirst; s; s = s->ts_next) { g = s->extra; if (!g) continue; for (; g; g = g->next) if (g->type == PART) break; if (!g) continue; // w = tex_str(&g->text[2]); a2b("%.1f %.1f M", s->x - 10, 2 - ht - h); tex_str(&g->text[2]); str_out(tex_buf, A_LEFT); if (cfmt.partsbox) a2b(" %.1f %.1f %.1f boxend boxdraw", s->x - 10 - 2, 2 - ht - h - 4, h); // a2b(" %.1f %.1f %.1f %.1f box", // s->x - 10 - 2, 2 - ht - h - 4, // w + 4, h); a2b("\n"); } out: return dy; } /* -- initialize the default decorations -- */ void init_deco(void) { memset(&deco, 0, sizeof deco); /* standard */ deco['.'] = "dot"; #ifdef DECO_IS_ROLL deco['~'] = "roll"; #endif deco['H'] = "fermata"; deco['L'] = "emphasis"; deco['M'] = "lowermordent"; deco['O'] = "coda"; deco['P'] = "uppermordent"; deco['S'] = "segno"; deco['T'] = "trill"; deco['u'] = "upbow"; deco['v'] = "downbow"; /* non-standard */ #ifndef DECO_IS_ROLL deco['~'] = "gmark"; #endif deco['J'] = "slide"; deco['R'] = "roll"; } /* reset the decoration table at start of a new tune */ void reset_deco(void) { // struct deco_def_s *dd; // int ideco; // // for (ideco = 1, dd = &deco_def_tb[1]; ideco < 128; ideco++, dd++) { // if (!dd->name) // break; // free(dd->name); // } memset(deco_def_tb, 0, sizeof deco_def_tb); } /* -- set the decoration flags -- */ void set_defl(int new_defl) { if (defl == new_defl) return; defl = new_defl; a2b("/defl %d def ", new_defl); } �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������abcm2ps-8.14.11/draw.c������������������������������������������������������������������������������0000664�0000000�0000000�00000333713�13762665467�0014356�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Drawing functions. * * This file is part of abcm2ps. * * Copyright (C) 1998-2020 Jean-François Moine (http://moinejf.free.fr) * Adapted from abc2ps, Copyright (C) 1996-1998 Michael Methfessel * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. */ #include <stdlib.h> #include <string.h> #include <ctype.h> #include "abcm2ps.h" struct BEAM { /* packages info on one beam */ struct SYMBOL *s1, *s2; float a, b; short nflags; }; static char *acc_tb[] = { "", "sh", "nt", "ft", "dsh", "dft" }; /* scaling stuff */ static int scale_voice; /* staff (0) or voice(1) scaling */ static float cur_scale = 1; /* voice or staff scale */ static float cur_trans = 0; /* != 0 when scaled staff */ static float cur_staff = 1; /* current scaled staff */ static int cur_color = 0; /* current color */ static void draw_note(float x, struct SYMBOL *s, int fl); static void set_tie_room(void); // set the symbol color /* set the voice color */ void set_color(int new_color) { if (new_color == cur_color) return; cur_color = new_color; a2b("%.2f %.2f %.2f setrgbcolor ", (float) (cur_color >> 16) / 255, (float) ((cur_color >> 8) & 0xff) / 255, (float) (cur_color & 0xff) / 255); } /* output debug annotations */ static void anno_out(struct SYMBOL *s, char type) { if (s->linenum == 0) return; if (mbf[-1] != '\n') *mbf++ = '\n'; a2b("%%A %c %d %d ", type, s->linenum, s->colnum); putxy(s->x - s->wl - 2, staff_tb[s->staff].y + s->ymn - 2); if (type != 'b' && type != 'e') /* if not beam */ a2b("%.1f %d", s->wl + s->wr + 4, s->ymx - s->ymn + 4); a2b("\n"); } /* -- up/down shift needed to get k*6 -- */ static float rnd6(float y) { int iy; iy = ((int) (y + 2.999) + 12) / 6 * 6 - 12; return iy - y; } /* -- compute the best vertical offset for the beams -- */ static float b_pos(int grace, int stem, int flags, float b) { float d1, d2, shift, depth; float top, bot; shift = !grace ? BEAM_SHIFT : 3.5; depth = !grace ? BEAM_DEPTH : 1.8; if (stem > 0) { bot = b - (flags - 1) * shift - depth; if (bot > 26) return 0; top = b; } else { top = b + (flags - 1) * shift + depth; if (top < -2) return 0; bot = b; } d1 = rnd6(top - BEAM_OFFSET); d2 = rnd6(bot + BEAM_OFFSET); if (d1 * d1 > d2 * d2) return d2; return d1; } /* duplicate a note for beaming continuation */ static struct SYMBOL *sym_dup(struct SYMBOL *s_orig) { struct SYMBOL *s; int m; s = (struct SYMBOL *) getarena(sizeof *s); memcpy(s, s_orig, sizeof *s); s->flags |= ABC_F_INVIS; s->text = NULL; for (m = 0; m <= s->nhd; m++) s->u.note.notes[m].sl1 = 0; memset(&s->u.note.dc, 0, sizeof s->u.note.dc); s->gch = NULL; s->ly = NULL; s->extra = NULL; return s; } /* -- calculate a beam -- */ /* (the staves may be defined or not) */ static int calculate_beam(struct BEAM *bm, struct SYMBOL *s1) { struct SYMBOL *s, *s2; int notes, nflags, staff, voice, two_staves, two_dir, visible = 0; float x, y, ys, a, b, max_stem_err; float sx, sy, sxx, sxy, syy, a0, stem_xoff, scale; static float min_tb[2][6] = { {STEM_MIN, STEM_MIN, STEM_MIN2, STEM_MIN3, STEM_MIN4, STEM_MIN4}, {STEM_CH_MIN, STEM_CH_MIN, STEM_CH_MIN2, STEM_CH_MIN3, STEM_CH_MIN4, STEM_CH_MIN4} }; if (!(s1->sflags & S_BEAM_ST)) { /* beam from previous music line */ s = sym_dup(s1); s->prev = s1->prev; if (s->prev) s->prev->next = s; else voice_tb[s->voice].sym = s; s1->prev = s; s->next = s1; s->ts_prev = s1->ts_prev; // if (s->ts_prev) s->ts_prev->ts_next = s; s1->ts_prev = s; s->ts_next = s1; for (s2 = s->ts_prev; /*s2*/; s2 = s2->ts_prev) { switch (s2->type) { default: continue; case CLEF: case KEYSIG: case TIMESIG: break; } break; } s->x -= 12; if (s->x > s2->x + 12) s->x = s2->x + 12; s->sflags &= S_SEQST; s->sflags |= S_BEAM_ST | S_TEMP; s->u.note.slur_st = 0; s->u.note.slur_end = 0; s1 = s; } /* search last note in beam */ notes = nflags = 0; /* set x positions, count notes and flags */ two_staves = two_dir = 0; staff = s1->staff; voice = s1->voice; stem_xoff = (s1->flags & ABC_F_GRACE) ? GSTEM_XOFF : s1->u.note.sdx; for (s2 = s1; ; s2 = s2->next) { if (s2->abc_type == ABC_T_NOTE) { if (s2->nflags > nflags) nflags = s2->nflags; notes++; if (s2->staff != staff) two_staves = 1; if (s2->stem != s1->stem) two_dir = 1; if (!visible && !(s2->flags & ABC_F_INVIS) && (!(s2->flags & ABC_F_STEMLESS) || (s2->sflags & S_TREM2))) visible = 1; if (s2->sflags & S_BEAM_END) break; } if (!s2->next) { /* beam towards next music line */ for (; ; s2 = s2->prev) { if (s2->abc_type == ABC_T_NOTE) break; } s = sym_dup(s2); s->next = s2->next; if (s->next) s->next->prev = s; s2->next = s; s->prev = s2; s->ts_next = s2->ts_next; if (s->ts_next) s->ts_next->ts_prev = s; s2->ts_next = s; s->ts_prev = s2; s->sflags &= S_SEQST; s->sflags |= S_BEAM_END | S_TEMP; s->u.note.slur_st = 0; s->u.note.slur_end = 0; s->x += 12; if (s->x < realwidth - 12) s->x = realwidth - 12; s2 = s; notes++; break; } } if (!visible) return 0; bm->s2 = s2; /* (don't display the flags) */ if (staff_tb[staff].y == 0) { /* staves not defined */ if (two_staves) return 0; } else { /* staves defined */ if (!two_staves) { bm->s1 = s1; /* beam already calculated */ if (s1->xs == s2->xs) bug("beam with null length", 1); bm->a = (s1->ys- s2->ys) / (s1->xs - s2->xs); bm->b = s1->ys - s1->xs * bm->a + staff_tb[staff].y; bm->nflags = nflags; return 1; } } sx = sy = sxx = sxy = syy = 0; /* linear fit through stem ends */ for (s = s1; ; s = s->next) { if (s->abc_type != ABC_T_NOTE) continue; if ((scale = voice_tb[s->voice].scale) == 1) scale = staff_tb[s->staff].staffscale; if (s->stem >= 0) x = stem_xoff + s->u.note.notes[0].shhd; else x = -stem_xoff + s->u.note.notes[s->nhd].shhd; x *= scale; x += s->x; s->xs = x; y = s->ys + staff_tb[s->staff].y; sx += x; sy += y; sxx += x * x; sxy += x * y; syy += y * y; if (s == s2) break; } /* beam fct: y=ax+b */ a = (sxy * notes - sx * sy) / (sxx * notes - sx * sx); b = (sy - a * sx) / notes; /* the next few lines modify the slope of the beam */ if (!(s1->flags & ABC_F_GRACE)) { if (notes >= 3) { float hh; hh = syy - a * sxy - b * sy; /* flatten if notes not in line */ if (hh > 0 && hh / (notes - 2) > .5) a *= BEAM_FLATFAC; } if (a >= 0) a = BEAM_SLOPE * a / (BEAM_SLOPE + a); /* max steepness for beam */ else a = BEAM_SLOPE * a / (BEAM_SLOPE - a); } else { if (a > BEAM_SLOPE) a = BEAM_SLOPE; else if (a < -BEAM_SLOPE) a = -BEAM_SLOPE; } /* to decide if to draw flat etc. use normalized slope a0 */ a0 = a * (s2->xs - s1->xs) / (20 * (notes - 1)); if (a0 * a0 < BEAM_THRESH * BEAM_THRESH) a = 0; /* flat below threshhold */ // b = (sy - a * sx) / notes; /* recalculate b for new slope */ /* if (nflags>1) b=b+2*stem;*/ /* leave a bit more room if several beams */ /* have flat beams when asked */ if ( cfmt.flatbeamgracing && s1->flags & ABC_F_GRACE) a = 0; if (cfmt.flatbeams) { // if (!(s1->flags & ABC_F_GRACE)) // b = -11 + staff_tb[staff].y; // else // b = 35 + staff_tb[staff].y; a = 0; } b = (sy - a * sx) / notes; /* recalculate b for new slope */ /*fixme: have a look again*/ /* have room for the symbols in the staff */ max_stem_err = 0; /* check stem lengths */ s = s1; if (two_dir) { /* 2 directions */ /*fixme: more to do*/ if (!(s1->flags & ABC_F_GRACE)) ys = BEAM_SHIFT; else ys = 3.5; ys *= (nflags - 1); ys += BEAM_DEPTH; ys *= .5; if (s1->stem != s2->stem && s1->nflags < s2->nflags) ys *= s2->stem; else ys *= s1->stem; b += ys; } else if (!(s1->flags & ABC_F_GRACE)) { /* normal notes */ float stem_err, beam_h; beam_h = BEAM_DEPTH + BEAM_SHIFT * (nflags - 1); while (s->ts_prev->abc_type == ABC_T_NOTE && s->ts_prev->time == s->time && s->ts_prev->x > s1->xs) s = s->ts_prev; for (; s && s->time <= s2->time; s = s->ts_next) { if (s->abc_type != ABC_T_NOTE || (s->flags & ABC_F_INVIS) || (s->staff != staff && s->voice != voice)) { continue; } x = s->voice == voice ? s->xs : s->x; ys = a * x + b - staff_tb[s->staff].y; if (s->voice == voice) { if (s->nhd == 0) stem_err = min_tb[0][(unsigned) s->nflags]; else stem_err = min_tb[1][(unsigned) s->nflags]; if (s->stem > 0) { if (s->pits[s->nhd] > 26) { stem_err -= 2; if (s->pits[s->nhd] > 28) stem_err -= 2; } stem_err -= ys - (float) (3 * (s->pits[s->nhd] - 18)); } else { if (s->pits[0] < 18) { stem_err -= 2; if (s->pits[0] < 16) stem_err -= 2; } stem_err -= (float) (3 * (s->pits[0] - 18)) - ys; } stem_err += BEAM_DEPTH + BEAM_SHIFT * (s->nflags - 1); } else { /*fixme: KO when two_staves*/ if (s1->stem > 0) { if (s->stem > 0) { /*fixme: KO when the voice numbers are inverted*/ if (s->ymn > ys + 4 || s->ymx < ys - beam_h - 2) continue; if (s->voice > voice) stem_err = s->ymx - ys; else stem_err = s->ymn + 8 - ys; } else { stem_err = s->ymx - ys; } } else { if (s->stem < 0) { if (s->ymx < ys - 4 || s->ymn > ys - beam_h - 2) continue; if (s->voice < voice) stem_err = ys - s->ymn; else stem_err = ys - s->ymx + 8; } else { stem_err = ys - s->ymn; } } stem_err += 2 + beam_h; } if (stem_err > max_stem_err) max_stem_err = stem_err; } } else { /* grace notes */ for ( ; ; s = s->next) { float stem_err; ys = a * s->xs + b - staff_tb[s->staff].y; stem_err = GSTEM - 2; if (s->stem > 0) stem_err -= ys - (float) (3 * (s->pits[s->nhd] - 18)); else stem_err += ys - (float) (3 * (s->pits[0] - 18)); stem_err += 3 * (s->nflags - 1); if (stem_err > max_stem_err) max_stem_err = stem_err; if (s == s2) break; } } if (max_stem_err > 0) /* shift beam if stems too short */ b += s1->stem * max_stem_err; /* have room for the gracenotes, bars and clefs */ /*fixme: test*/ if (!two_staves && !two_dir) for (s = s1->next; ; s = s->next) { struct SYMBOL *g; switch (s->type) { case NOTEREST: /* cannot move rests in multi-voices */ if (s->abc_type != ABC_T_REST) break; g = s->ts_next; if (!g || g->staff != staff || g->type != NOTEREST) break; //fixme:too much vertical shift if some space above the note //fixme:this does not fix rest under beam in second voice (ts_prev) /*fall thru*/ case BAR: #if 1 if (s->flags & ABC_F_INVIS) #else //?? if (!(s->flags & ABC_F_INVIS)) #endif break; /*fall thru*/ case CLEF: y = a * s->x + b; if (s1->stem > 0) { y = s->ymx - y + BEAM_DEPTH + BEAM_SHIFT * (nflags - 1) + 2; if (y > 0) b += y; } else { y = s->ymn - y - BEAM_DEPTH - BEAM_SHIFT * (nflags - 1) - 2; if (y < 0) b += y; } break; case GRACE: g = s->extra; for ( ; g; g = g->next) { if (g->type != NOTEREST) continue; y = a * g->x + b; if (s1->stem > 0) { y = g->ymx - y + BEAM_DEPTH + BEAM_SHIFT * (nflags - 1) + 2; if (y > 0) b += y; } else { y = g->ymn - y - BEAM_DEPTH - BEAM_SHIFT * (nflags - 1) - 2; if (y < 0) b += y; } } break; } if (s == s2) break; } if (a == 0) /* shift flat beams onto staff lines */ b += b_pos(s1->flags & ABC_F_GRACE, s1->stem, nflags, b - staff_tb[staff].y); /* adjust final stems and rests under beam */ for (s = s1; ; s = s->next) { float dy; switch (s->abc_type) { case ABC_T_NOTE: s->ys = a * s->xs + b - staff_tb[s->staff].y; if (s->stem > 0) { s->ymx = s->ys + 2.5; #if 0 //fixme: hack if (s->ts_prev && s->ts_prev->stem > 0 && s->ts_prev->staff == s->staff && s->ts_prev->ymn < s->ymx && s->ts_prev->x == s->x && s->u.note.notes[0].shhd == 0) { s->ts_prev->x -= 5; /* fix stem clash */ s->ts_prev->xs -= 5; } #endif } else { s->ymn = s->ys - 2.5; } break; case ABC_T_REST: y = a * s->x + b - staff_tb[s->staff].y; dy = BEAM_DEPTH + BEAM_SHIFT * (nflags - 1) + (s->head != H_FULL ? 4 : 9); if (s1->stem > 0) { y -= dy; if (s1->multi == 0 && y > 12) y = 12; if (s->y <= y) break; } else { y += dy; if (s1->multi == 0 && y < 12) y = 12; if (s->y >= y) break; } if (s->head != H_FULL) { int iy; iy = ((int) y + 3 + 12) / 6 * 6 - 12; y = iy; } s->y = y; break; } if (s == s2) break; } /* save beam parameters */ if (staff_tb[staff].y == 0) /* if staves not defined */ return 0; bm->s1 = s1; bm->a = a; bm->b = b; bm->nflags = nflags; return 1; } /* -- draw a single beam -- */ /* (the staves are defined) */ static void draw_beam(float x1, float x2, float dy, float h, struct BEAM *bm, int n) /* beam number (1..n) */ { struct SYMBOL *s; float y1, dy2; s = bm->s1; if ((s->sflags & S_TREM2) && n > s->nflags - s->aux && s->head != H_EMPTY) { if (s->head >= H_OVAL) { x1 = s->x + 6; x2 = bm->s2->x - 6; } else { x1 += 5; x2 -= 6; } } y1 = bm->a * x1 + bm->b - dy; x2 -= x1; dy2 = bm->a * x2; putf(h); putx(x2); putf(dy2); putxy(x1, y1); a2b("bm\n"); } /* -- draw the beams for one word -- */ /* (the staves are defined) */ static void draw_beams(struct BEAM *bm) { struct SYMBOL *s, *s1, *s2; int i, beam_dir; float shift, bshift, bstub, bh, da; s1 = bm->s1; /*fixme: KO if many staves with different scales*/ //fixme: useless? // set_scale(s1); s2 = bm->s2; if (!(s1->flags & ABC_F_GRACE)) { bshift = BEAM_SHIFT; bstub = BEAM_STUB; shift = .34; /* (half width of the stem) */ bh = BEAM_DEPTH; } else { bshift = 3.5; bstub = 3.2; shift = .29; bh = 1.8; } /*fixme: quick hack for stubs at end of beam and different stem directions*/ beam_dir = s1->stem; if (s1->stem != s2->stem && s1->nflags < s2->nflags) beam_dir = s2->stem; if (beam_dir < 0) bh = -bh; if (cur_trans == 0 && cur_scale != 1) { bm->a /= cur_scale; bm->b = s1->ys - s1->xs * bm->a + staff_tb[s1->staff].y; bshift *= cur_scale; } /* make first beam over whole word and adjust the stem lengths */ draw_beam(s1->xs - shift, s2->xs + shift, 0., bh, bm, 1); da = 0; for (s = s1; ; s = s->next) { if (s->abc_type == ABC_T_NOTE && s->stem != beam_dir) s->ys = bm->a * s->xs + bm->b - staff_tb[s->staff].y + bshift * (s->nflags - 1) * s->stem - bh; if (s == s2) break; } if (s1->sflags & S_FEATHERED_BEAM) { da = bshift / (s2->xs - s1->xs); if (s1->dur > s2->dur) { da = -da; bshift = da * s1->xs; } else { bshift = da * s2->xs; } da = da * beam_dir; } /* other beams with two or more flags */ shift = 0; for (i = 2; i <= bm->nflags; i++) { shift += bshift; if (da != 0) bm->a += da; for (s = s1; ; s = s->next) { struct SYMBOL *k1, *k2; float x1; if (s->abc_type != ABC_T_NOTE || s->nflags < i) { if (s == s2) break; continue; } if ((s->sflags & S_TREM1) && i > s->nflags - s->aux) { if (s->head >= H_OVAL) x1 = s->x; else x1 = s->xs; draw_beam(x1 - 5, x1 + 5, (shift + 2.5) * beam_dir, bh, bm, i); if (s == s2) break; continue; } k1 = s; for (;;) { if (s == s2) break; if (s->next->type == NOTEREST) { if (s->next->sflags & S_TREM1) { if (s->next->nflags - s->next->aux < i) break; } else if (s->next->nflags < i) { break; } } if ((s->next->sflags & S_BEAM_BR1) || ((s->next->sflags & S_BEAM_BR2) && i > 2)) break; s = s->next; } k2 = s; while (k2->abc_type != ABC_T_NOTE) k2 = k2->prev; x1 = k1->xs; if (k1 == k2) { if (k1 == s1) { x1 += bstub; } else if (k1 == s2) { x1 -= bstub; } else if ((k1->sflags & S_BEAM_BR1) || ((k1->sflags & S_BEAM_BR2) && i > 2)) { x1 += bstub; } else { struct SYMBOL *k; k = k1->next; while (k->abc_type != ABC_T_NOTE) k = k->next; if ((k->sflags & S_BEAM_BR1) || ((k->sflags & S_BEAM_BR2) && i > 2)) { x1 -= bstub; } else { k1 = k1->prev; while (k1->abc_type != ABC_T_NOTE) k1 = k1->prev; if (k1->nflags < k->nflags || (k1->nflags == k->nflags && k1->dots < k->dots)) x1 += bstub; else x1 -= bstub; } } } draw_beam(x1, k2->xs, #if 1 shift * beam_dir, #else shift * k1->stem, /*fixme: more complicated */ #endif bh, bm, i); if (s == s2) break; } } if (s1->sflags & S_TEMP) unlksym(s1); else if (s2->sflags & S_TEMP) unlksym(s2); } /* -- draw a system brace or bracket -- */ static void draw_sysbra(float x, int staff, int flag) { int i, end; float yt, yb; while (cursys->staff[staff].empty) { // || staff_tb[staff].stafflines == 0) { if (cursys->staff[staff].flags & flag) return; staff++; } i = end = staff; for (;;) { if (!cursys->staff[i].empty) // && staff_tb[i].stafflines != 0) end = i; if (cursys->staff[i].flags & flag) break; i++; } yt = staff_tb[staff].y + staff_tb[staff].topbar * staff_tb[staff].staffscale; yb = staff_tb[end].y + staff_tb[end].botbar * staff_tb[end].staffscale; a2b("%.1f %.1f %.1f %s\n", yt - yb, x, yt, (flag & (CLOSE_BRACE | CLOSE_BRACE2)) ? "brace" : "bracket"); } /* -- draw the left side of the staves -- */ static void draw_lstaff(float x) { int i, j, l, nst; float yb; if (cfmt.alignbars) return; nst = cursys->nstaff; l = 0; for (i = 0; ; i++) { if (cursys->staff[i].flags & (OPEN_BRACE | OPEN_BRACKET)) l++; if (!cursys->staff[i].empty) // && staff_tb[i].stafflines != 0) break; if (cursys->staff[i].flags & (CLOSE_BRACE | CLOSE_BRACKET)) l--; if (i == nst) break; } for (j = nst; j > i; j--) { if (!cursys->staff[j].empty) // && staff_tb[j].stafflines != 0) break; } if (i == j && l == 0) return; set_sscale(-1); yb = staff_tb[j].y + staff_tb[j].botbar * staff_tb[j].staffscale; a2b("%.1f %.1f %.1f bar\n", staff_tb[i].y + staff_tb[i].topbar * staff_tb[i].staffscale - yb, x, yb); for (i = 0; i <= nst; i++) { if (cursys->staff[i].flags & OPEN_BRACE) draw_sysbra(x, i, CLOSE_BRACE); if (cursys->staff[i].flags & OPEN_BRACKET) draw_sysbra(x, i, CLOSE_BRACKET); if (cursys->staff[i].flags & OPEN_BRACE2) draw_sysbra(x - 6, i, CLOSE_BRACE2); if (cursys->staff[i].flags & OPEN_BRACKET2) draw_sysbra(x - 6, i, CLOSE_BRACKET2); } } /* -- draw a staff -- */ static void draw_staff(int staff, float x1, float x2) { char *stafflines; int i, l, thick = -1; float y, w; /* draw the staff */ set_sscale(staff); y = staff_tb[staff].y; stafflines = staff_tb[staff].stafflines; l = strlen(stafflines); for (i = 0; i < l; i++) { if (stafflines[i] != '.') { w = x2 - x1; for ( ; i < l; i++) { if (stafflines[i] != '.') { if (stafflines[i] != '|') { if (thick != 1) { if (thick >= 0) a2b("stroke\n"); a2b("1.5 SLW "); thick = 1; } } else { if (thick != 0) { if (thick >= 0) a2b("stroke\n"); a2b("dlw "); thick = 0; } } putx(w); putxy(x1, y); a2b("M 0 RL "); } y += 6; } a2b("stroke\n"); break; } y += 6; } } /* -- draw the time signature -- */ static void draw_timesig(float x, struct SYMBOL *s) { unsigned i, staff, l, l2; char *f, meter[64]; float dx, y; if (s->u.meter.nmeter == 0) return; staff = s->staff; x -= s->wl; y = staff_tb[staff].y; for (i = 0; i < s->u.meter.nmeter; i++) { l = strlen(s->u.meter.meter[i].top); if (l > sizeof s->u.meter.meter[i].top) l = sizeof s->u.meter.meter[i].top; if (s->u.meter.meter[i].bot[0] != '\0') { sprintf(meter, "(%.8s)(%.2s)", s->u.meter.meter[i].top, s->u.meter.meter[i].bot); f = "tsig"; l2 = strlen(s->u.meter.meter[i].bot); if (l2 > sizeof s->u.meter.meter[i].bot) l2 = sizeof s->u.meter.meter[i].bot; if (l2 > l) l = l2; } else switch (s->u.meter.meter[i].top[0]) { case 'C': if (s->u.meter.meter[i].top[1] != '|') { f = "csig"; } else { f = "ctsig"; l--; } dx = (float) (13 * l); putxy(x - 5 + dx * .5, y + 12); a2b("%s\n", f); x += dx; continue; case 'c': if (s->u.meter.meter[i].top[1] != '.') { f = "imsig"; } else { f = "iMsig"; l--; } meter[0] = '\0'; break; case 'o': if (s->u.meter.meter[i].top[1] != '.') { f = "pmsig"; } else { f = "pMsig"; l--; } meter[0] = '\0'; break; case '(': case ')': sprintf(meter, "(\\%s)", s->u.meter.meter[i].top); f = "stsig"; break; default: sprintf(meter, "(%.8s)", s->u.meter.meter[i].top); f = "stsig"; break; } if (meter[0] != '\0') a2b("%s ", meter); dx = (float) (13 * l); putxy(x + dx * .5, y); a2b("%s\n", f); x += dx; } } /* -- draw an accidental -- */ static void draw_acc(int acc, int microscale) { int n, d; n = parse.micro_tb[acc >> 3]; if (acc >> 3 != 0 && microscale) { if (microscale) { d = microscale; n = acc >> 3; } else { d = ((n & 0xff) + 1) * 2; n = (n >> 8) + 1; } a2b("%d %s%d ", n, acc_tb[acc & 0x07], d); } else { if (acc >> 3 != 0 && cfmt.nedo) // %%MIDI temperamentequal <nedo> n = ((((n >> 8) + 1) * 12) - 1) * 256 + cfmt.nedo - 1; a2b("%s%d ", acc_tb[acc & 0x07], n); } } // draw helper lines static void draw_hl(float x, float staffb, int up, int y, char *stafflines, char *hltype) { int i, l; l = strlen(stafflines); // lower ledger lines if (!up) { for (i = 0; i < l - 1; i++) { if (stafflines[i] != '.') break; } i = i * 6 - 6; for ( ; i >= y; i -= 6) { putxy(x, staffb + i); a2b("%s ", hltype); } return; } // upper ledger lines i = l * 6; for ( ; i <= y; i += 6) { putxy(x, staffb + i); a2b("%s ", hltype); } } /* -- draw a key signature -- */ static void draw_keysig(struct VOICE_S *p_voice, float x, struct SYMBOL *s) { int old_sf = s->aux; int staff = p_voice->staff; float staffb = staff_tb[staff].y; int i, clef_ix, shift; const signed char *p_seq; static const char sharp_cl[] = {24, 9, 15, 21, 6, 12, 18}; static const char flat_cl[] = {12, 18, 24, 9, 15, 21, 6}; // (the ending 0 is needed to avoid array overflow) static const signed char sharp1[] = {-9, 12, -9, -9, 12, -9, 0}; static const signed char sharp2[] = {12, -9, 12, -9, 12, -9, 0}; static const signed char flat1[] = {9, -12, 9, -12, 9, -12, 0}; static const signed char flat2[] = {-12, 9, -12, 9, -12, 9, 0}; clef_ix = s->u.key.clef_delta; if (clef_ix & 1) clef_ix += 7; clef_ix /= 2; while (clef_ix < 0) clef_ix += 7; clef_ix %= 7; /* normal accidentals */ if (s->u.key.nacc == 0 && !s->u.key.empty) { /* put neutrals if 'accidental cancel' */ if (cfmt.cancelkey || s->u.key.sf == 0) { /* when flats to sharps, or sharps to flats, */ if (s->u.key.sf == 0 || old_sf * s->u.key.sf < 0) { /* old sharps */ shift = sharp_cl[clef_ix]; p_seq = shift > 9 ? sharp1 : sharp2; for (i = 0; i < old_sf; i++) { putxy(x, staffb + shift); a2b("nt0 "); shift += *p_seq++; x += 5.5; } /* old flats */ shift = flat_cl[clef_ix]; p_seq = shift < 18 ? flat1 : flat2; for (i = 0; i > old_sf; i--) { putxy(x, staffb + shift); a2b("nt0 "); shift += *p_seq++; x += 5.5; } if (s->u.key.sf != 0) x += 3; /* extra space */ } } /* new sharps */ if (s->u.key.sf > 0) { shift = sharp_cl[clef_ix]; p_seq = shift > 9 ? sharp1 : sharp2; for (i = 0; i < s->u.key.sf; i++) { putxy(x, staffb + shift); a2b("sh0 "); shift += *p_seq++; x += 5.5; } if (cfmt.cancelkey && s->u.key.sf < old_sf) { x += 2; for (; i < old_sf; i++) { putxy(x, staffb + shift); a2b("nt0 "); shift += *p_seq++; x += 5.5; } } } /* new flats */ if (s->u.key.sf < 0) { shift = flat_cl[clef_ix]; p_seq = shift < 18 ? flat1 : flat2; for (i = 0; i > s->u.key.sf; i--) { putxy(x, staffb + shift); a2b("ft0 "); shift += *p_seq++; x += 5.5; } if (cfmt.cancelkey && s->u.key.sf > old_sf) { x += 2; for (; i > old_sf; i--) { putxy(x, staffb + shift); a2b("nt0 "); shift += *p_seq++; x += 5.5; } } } } else { int acc, last_acc, last_shift; /* explicit accidentals */ last_acc = s->u.key.accs[0]; last_shift = 100; for (i = 0; i < s->u.key.nacc; i++) { acc = s->u.key.accs[i]; shift = s->u.key.clef_delta * 3 // clef shift + 3 * (s->u.key.pits[i] - 18); if (i != 0 && (shift > last_shift + 18 || shift < last_shift - 18)) x -= 5.5; // no clash else if (acc != last_acc) x += 3; last_acc = acc; if (shift < 0) draw_hl(x, staffb, 0, shift, /* lower ledger line */ staff_tb[s->staff].stafflines, "hl"); else if (shift > 24) draw_hl(x, staffb, 1, shift, /* upper ledger line */ staff_tb[s->staff].stafflines, "hl"); last_shift = shift; putxy(x, staffb + shift); draw_acc(acc, s->u.key.microscale); x += 5.5; } } if (old_sf != 0 || s->u.key.sf != 0 || s->u.key.nacc != 0) a2b("\n"); } /* -- convert the standard measure bars -- */ static int bar_cnv(int bar_type) { switch (bar_type) { case B_OBRA: /* case B_CBRA: */ case (B_OBRA << 4) + B_CBRA: return 0; /* invisible */ // case B_COL: // return B_BAR; /* dotted */ #if 0 case (B_CBRA << 4) + B_BAR: return B_BAR; #endif case (B_SINGLE << 8) | B_LREP: case (B_BAR << 4) + B_COL: bar_type |= (B_OBRA << 8); /* ||: and |: -> [|: */ break; case (B_BAR << 8) + (B_COL << 4) + B_COL: bar_type |= (B_OBRA << 12); /* |:: -> [|:: */ break; case (B_BAR << 12) + (B_COL << 8) + (B_COL << 4) + B_COL: bar_type |= (B_OBRA << 16); /* |::: -> [|::: */ break; case (B_COL << 4) + B_BAR: case (B_COL << 8) + (B_COL << 4) + B_BAR: case (B_COL << 12) + (B_COL << 8) + (B_COL << 4) + B_BAR: bar_type <<= 4; bar_type |= B_CBRA; /* :..| -> :..|] */ break; case (B_COL << 4) + B_COL: bar_type = cfmt.dblrepbar; /* :: -> dble repeat bar */ break; } return bar_type; } /* -- draw a measure bar -- */ static void draw_bar(struct SYMBOL *s, float bot, float h) { int staff, bar_type; float x, yb; char *psf; staff = s->staff; yb = staff_tb[staff].y; x = s->x; /* if measure repeat, draw the '%' like glyphs */ if (s->u.bar.len != 0) { struct SYMBOL *s2; set_scale(s); if (s->u.bar.len == 1) { for (s2 = s->prev; s2->abc_type != ABC_T_REST; s2 = s2->prev) ; putxy(s2->x, yb + 12); a2b("mrep\n"); } else { putxy(x, yb + 12); a2b("mrep2\n"); if (s->voice == cursys->top_voice) { /*fixme set_font(s->gcf); */ set_font(cfmt.anf); putxy(x, yb + staff_tb[staff].topbar + 4); a2b("M(%d)showc\n", s->u.bar.len); } } } /* don't put a line between the staves if there is no bar above */ if (staff != 0 && s->ts_prev // (a staff may not be displayed) // && (s->ts_prev->type != BAR || s->ts_prev->staff != staff - 1)) && s->ts_prev->type != BAR) h = staff_tb[staff].topbar * staff_tb[staff].staffscale; bar_type = bar_cnv(s->u.bar.type); if (bar_type == 0) return; /* invisible */ for (;;) { psf = "bar"; switch (bar_type & 0x07) { case B_BAR: if (s->u.bar.dotted) psf = "dotbar"; break; case B_OBRA: case B_CBRA: psf = "thbar"; x -= 3; break; case B_COL: x -= 2; break; } switch (bar_type & 0x07) { default: set_sscale(-1); a2b("%.1f %.1f %.1f %s ", h, x, bot, psf); break; case B_COL: set_sscale(staff); putxy(x + 1, staff_tb[staff].y); a2b("rdots "); break; } bar_type >>= 4; if (bar_type == 0) break; x -= 3; } a2b("\n"); } /* -- draw a rest -- */ /* (the staves are defined) */ static void draw_rest(struct SYMBOL *s) { int i, j, y, l; // int no_head; char *stafflines; float x, dotx, staffb; static char *rest_tb[NFLAGS_SZ] = { "r128", "r64", "r32", "r16", "r8", "r4", "r2", "r1", "r0", "r00" }; /* don't display the rests of invisible staves */ /* (must do this here for voices out of their normal staff) */ if (staff_tb[s->staff].empty) return; /* if rest alone in the measure or measure repeat, center */ if (s->dur == voice_tb[s->voice].meter.wmeasure || ((s->sflags & S_REPEAT) && s->doty >= 0)) { struct SYMBOL *s2; /* don't use next/prev: there is no bar in voice averlay */ s2 = s->ts_next; while (s2 && s2->time != s->time + s->dur) s2 = s2->ts_next; if (s2) x = s2->x; else x = realwidth; s2 = s; while (!(s2->sflags & S_SEQST)) s2 = s2->ts_prev; s2 = s2->ts_prev; x = (x + s2->x) * .5; /* center the associated decorations */ if (s->u.note.dc.n > 0) deco_update(s, x - s->x); s->x = x; } else { x = s->x + s->u.note.notes[0].shhd * cur_scale; } if (s->flags & ABC_F_INVIS) // && !(s->sflags & S_OTHER_HEAD)) //fixme: before new deco struct return; staffb = staff_tb[s->staff].y; /* bottom of staff */ if (s->sflags & S_REPEAT) { putxy(x, staffb + 12); if (s->doty < 0) { a2b("srep\n"); } else { a2b("mrep\n"); if (s->doty > 2 && s->voice == cursys->top_voice) { /*fixme set_font(s->gcf); */ set_font(cfmt.anf); putxy(x, staffb + 24 + 4); a2b("M(%d)showc\n", s->doty); } } return; } y = s->y; i = C_XFLAGS - s->nflags; /* rest_tb index */ stafflines = staff_tb[s->staff].stafflines; l = strlen(stafflines); if (i == 7 && y == 12 && l <= 2) y -= 6; /* semibreve a bit lower */ putxy(x, y + staffb); /* rest */ a2b("%s ", s->u.note.notes[0].head ? s->u.note.notes[0].head : rest_tb[i]); /* output ledger line(s) when greater than minim */ if (i >= 6) { j = y / 6; switch (i) { default: if (j >= l - 1 || stafflines[j + 1] != '|') { putxy(x, y + staffb); a2b("hl1 "); } if (i == 9) { // longa y -= 6; j--; } break; case 7: y += 6; j++; case 6: break; } if (j >= l || stafflines[j] != '|') { putxy(x, y + staffb); a2b("hl1 "); } } dotx = 8; for (i = 0; i < s->dots; i++) { a2b("%.1f 3 dt ", dotx); dotx += 3.5; } a2b("\n"); } /* -- draw grace notes -- */ /* (the staves are defined) */ static void draw_gracenotes(struct SYMBOL *s) { int yy; float x0, y0, x1, y1, x2, y2, x3, y3, bet1, bet2, dy1, dy2; struct SYMBOL *g, *last; struct BEAM bm; /* draw the notes */ bm.s2 = NULL; /* (draw flags) */ for (g = s->extra; g; g = g->next) { if (g->type != NOTEREST) continue; if ((g->sflags & (S_BEAM_ST | S_BEAM_END)) == S_BEAM_ST) { if (annotate) anno_out(g, 'b'); if (calculate_beam(&bm, g)) draw_beams(&bm); } draw_note(g->x, g, bm.s2 == NULL); if (annotate) anno_out(s, 'g'); if (g == bm.s2) bm.s2 = NULL; /* (draw flags again) */ if (g->flags & ABC_F_SAPPO) { /* (on 1st note only) */ if (!g->next) { /* if one note */ x1 = 9; y1 = g->stem > 0 ? 5 : -5; } else { /* many notes */ x1 = (g->next->x - g->x) * .5 + 4; y1 = (g->ys + g->next->ys) * .5 - g->y; if (g->stem > 0) y1 -= 1; else y1 += 1; } putxy(x1, y1); a2b("g%ca\n", g->stem > 0 ? 'u' : 'd'); } if (annotate && (g->sflags & (S_BEAM_ST | S_BEAM_END)) == S_BEAM_END) anno_out(g, 'e'); if (!g->next) break; /* (keep the last note) */ } /* slur */ if (voice_tb[s->voice].key.instr == K_HP /* no slur when bagpipe */ || voice_tb[s->voice].key.instr == K_Hp || pipeformat || !cfmt.graceslurs || s->u.note.slur_st /* explicit slur */ || !s->next || s->next->abc_type != ABC_T_NOTE) return; last = g; if (last->stem >= 0) { yy = 127; for (g = s->extra; g; g = g->next) { if (g->type != NOTEREST) continue; if (g->y < yy) { yy = g->y; last = g; } } x0 = last->x; y0 = last->y - 5; if (s->extra != last) { x0 -= 4; y0 += 1; } s = s->next; x3 = s->x - 1; if (s->stem < 0) x3 -= 4; y3 = 3 * (s->pits[0] - 18) - 5; dy1 = (x3 - x0) * .4; if (dy1 > 3) dy1 = 3; dy2 = dy1; bet1 = .2; bet2 = .8; if (y0 > y3 + 7) { x0 = last->x - 1; y0 += .5; y3 += 6.5; x3 = s->x - 5.5; dy1 = (y0 - y3) * .8; dy2 = (y0 - y3) * .2; bet1 = 0; } else if (y3 > y0 + 4) { y3 = y0 + 4; x0 = last->x + 2; y0 = last->y - 4; } } else { yy = -127; for (g = s->extra; g; g = g->next) { if (g->type != NOTEREST) continue; if (g->y > yy) { yy = g->y; last = g; } } x0 = last->x; y0 = last->y + 5; if (s->extra != last) { x0 -= 4; y0 -= 1; } s = s->next; x3 = s->x - 1; if (s->stem >= 0) x3 -= 2; y3 = 3 * (s->pits[s->nhd] - 18) + 5; dy1 = (x0 - x3) * .4; if (dy1 < -3) dy1 = -3; dy2 = dy1; bet1 = .2; bet2 = .8; if (y0 < y3 - 7) { x0 = last->x - 1; y0 -= .5; y3 -= 6.5; x3 = s->x - 5.5; dy1 = (y0 - y3) * .8; dy2 = (y0 - y3) * .2; bet1 = 0; } else if (y3 < y0 - 4) { y3 = y0 - 4; x0 = last->x + 2; y0 = last->y + 4; } } x1 = bet1 * x3 + (1 - bet1) * x0; y1 = bet1 * y3 + (1 - bet1) * y0 - dy1; x2 = bet2 * x3 + (1 - bet2) * x0; y2 = bet2 * y3 + (1 - bet2) * y0 - dy2; a2b("%.2f %.2f %.2f %.2f %.2f %.2f ", x1 - x0, y1 - y0, x2 - x0, y2 - y0, x3 - x0, y3 - y0); putxy(x0, y0 + staff_tb[s->staff].y); a2b("gsl\n"); } /* -- set the y offset of the dots -- */ static void setdoty(struct SYMBOL *s, signed char *y_tb) { int m, m1, y, doty; /* set the normal offsets */ doty = s->doty; for (m = 0; m <= s->nhd; m++) { y = 3 * (s->pits[m] - 18); /* note height on staff */ if ((y % 6) == 0) { if (doty != 0) y -= 3; else y += 3; } y_tb[m] = y; } /* dispatch and recenter the dots in the staff space */ for (m = 0; m < s->nhd; m++) { if (y_tb[m + 1] > y_tb[m]) continue; m1 = m; while (m1 > 0) { if (y_tb[m1] > y_tb[m1 - 1] + 6) break; m1--; } if (3 * (s->pits[m1] - 18) - y_tb[m1] < y_tb[m + 1] - 3 * (s->pits[m + 1] - 18)) { while (m1 <= m) y_tb[m1++] -= 6; } else { y_tb[m + 1] = y_tb[m] + 6; } } } /* -- draw m-th head with accidentals and dots -- */ /* (the staves are defined) */ static void draw_basic_note(float x, struct SYMBOL *s, int m, signed char *y_tb) { struct note *note = &s->u.note.notes[m]; int y, head, dots, nflags, acc; // int no_head; int old_color = -1; float staffb, shhd; char *p; char hd[32]; staffb = staff_tb[s->staff].y; /* bottom of staff */ y = 3 * (s->pits[m] - 18); /* note height on staff */ shhd = note->shhd * cur_scale; if (s->flags & ABC_F_INVIS) return; putxy(x + shhd, y + staffb); /* output x and y */ // /* special case when no head */ // if (s->nohdi1 >= 0 // && m >= s->nohdi1 && m < s->nohdi2) { // a2b("xydef"); /* set x y */ // return; // } identify_note(s, note->len, &head, &dots, &nflags); acc = note->acc; /* output a ledger line if horizontal shift / chord * and note on a line */ if (y % 6 == 0 && shhd != (s->stem > 0 ? s->u.note.notes[0].shhd : s->u.note.notes[s->nhd].shhd)) { int yy; yy = 0; if (y >= 30) { yy = y; if (yy % 6) yy -= 3; } else if (y <= -6) { yy = y; if (yy % 6) yy += 3; } if (yy) { putxy(x + shhd, yy + staffb); a2b("hl "); } } /* draw the head */ if (note->invisible) { p = "xydef"; } else if ((p = note->head) != NULL) { snprintf(hd, sizeof hd, "%.*s", note->hlen, p); p = hd; a2b("2 copy xydef "); /* set x y */ } else if (s->flags & ABC_F_GRACE) { p = "ghd"; } else if (s->type == CUSTOS) { p = "custos"; } else if ((s->sflags & S_PERC) && acc != 0) { sprintf(hd, "p%shd", acc_tb[acc & 0x07]); acc = 0; p = hd; } else { switch (head) { case H_OVAL: if (note->len < BREVE) { p = "HD"; break; } if (s->head != H_SQUARE) { p = "HDD"; break; } /* fall thru */ case H_SQUARE: p = note->len < BREVE * 2 ? "breve" : "longa"; /* don't display dots on last note of the tune */ if (!tsnext && s->next && s->next->type == BAR && !s->next->next) dots = 0; break; case H_EMPTY: p = "Hd"; break; default: p = "hd"; break; } } if (note->color >= 0) { old_color = cur_color; set_color(note->color); } a2b("%s", p); /* draw the dots */ /*fixme: to see for grace notes*/ if (dots) { float dotx; int doty; dotx = 7.7 + s->xmx - note->shhd; doty = y_tb[m] - y; if (scale_voice) doty /= cur_scale; while (--dots >= 0) { a2b(" %.1f %d dt", dotx, doty); dotx += 3.5; } } /* draw the accidental */ if (acc != 0) { x -= note->shac * cur_scale; a2b(" "); putx(x); a2b(s->flags & ABC_F_GRACE ? "gsc " : "y "); draw_acc(acc, s->u.note.microscale); if (s->flags & ABC_F_GRACE) a2b(" grestore"); } if (old_color >= 0) { a2b("\n"); set_color(old_color); } } /* -- draw a note or a chord -- */ /* (the staves are defined) */ static void draw_note(float x, struct SYMBOL *s, int fl) { int m, ma; float staffb, slen, shhd; char c, *hltype; signed char y_tb[MAXHD]; if (s->dots) setdoty(s, y_tb); if (s->head >= H_OVAL) x += 1; staffb = staff_tb[s->staff].y; /* output the ledger lines */ if (!(s->flags & ABC_F_INVIS)) { if (s->flags & ABC_F_GRACE) { hltype = "ghl"; } else { switch (s->head) { default: hltype = "hl"; break; case H_OVAL: hltype = "hl1"; break; case H_SQUARE: hltype = "hl2"; break; } } shhd = (s->stem > 0 ? s->u.note.notes[0].shhd : s->u.note.notes[s->nhd].shhd) * cur_scale; if (s->pits[0] < 22) draw_hl(x + shhd, staffb, 0, 3 * (s->pits[0] - 18), /* lower ledger lines */ staff_tb[s->staff].stafflines, hltype); if (s->pits[s->nhd] > 22) draw_hl(x + shhd, staffb, 1, 3 * (s->pits[s->nhd] - 18), /* upper ledger lines */ staff_tb[s->staff].stafflines, hltype); } /* draw the master note, first or last one */ if (cfmt.setdefl) set_defl(s->stem >= 0 ? DEF_STEMUP : 0); ma = s->stem >= 0 ? 0 : s->nhd; draw_basic_note(x, s, ma, y_tb); /* draw the stem and flags */ if (!(s->flags & (ABC_F_INVIS | ABC_F_STEMLESS))) { char c2; c = s->stem >= 0 ? 'u' : 'd'; slen = (s->ys - s->y) / voice_tb[s->voice].scale; if (!fl || s->nflags - s->aux <= 0) { /* stem only */ c2 = (s->flags & ABC_F_GRACE) ? 'g' : 's'; if (s->nflags > 0) { /* (fix for PS low resolution) */ if (s->stem >= 0) slen -= 1; else slen += 1; } a2b(" %.1f %c%c", slen, c2, c); } else { /* stem and flags */ if (cfmt.straightflags) c = 's'; /* straight flag */ c2 = (s->flags & ABC_F_GRACE) ? 'g' : 'f'; a2b(" %d %.1f s%c%c", s->nflags - s->aux, slen, c2, c); } } else if (s->sflags & S_XSTEM) { /* cross-staff stem */ struct SYMBOL *s2; s2 = s->ts_prev; slen = (s2->stem > 0 ? s2->y : s2->ys) - s->y; slen += staff_tb[s2->staff].y - staffb; /*fixme:KO when different scales*/ slen /= voice_tb[s->voice].scale; a2b(" %.1f su", slen); } /* draw the tremolo bars */ if (!(s->flags & ABC_F_INVIS) && fl && (s->sflags & S_TREM1)) { float x1; x1 = x + (s->stem > 0 ? s->u.note.notes[0].shhd : s->u.note.notes[s->nhd].shhd) * cur_scale; slen = 3 * (s->pits[s->stem > 0 ? s->nhd : 0] - 18); if (s->head >= H_OVAL) { if (s->stem > 0) slen += 5 + 5.4 * s->aux; else slen -= 5 + 5.4; } else { x1 += ((s->flags & ABC_F_GRACE) ? GSTEM_XOFF : STEM_XOFF) * s->stem; if (s->stem > 0) slen += 6 + 5.4 * s->aux; else slen -= 6 + 5.4; } slen /= voice_tb[s->voice].scale; a2b(" %d ", s->aux); putxy(x1, staffb + slen); a2b("trem"); } /* draw the other note heads */ for (m = 0; m <= s->nhd; m++) { if (m == ma) continue; a2b(" "); draw_basic_note(x, s, m, y_tb); } a2b("\n"); } /* -- find where to terminate/start a slur -- */ static struct SYMBOL *next_scut(struct SYMBOL *s) { struct SYMBOL *prev; prev = s; for (s = s->next; s; s = s->next) { if (s->type == BAR && ((s->sflags & S_RRBAR) || s->u.bar.type == B_THIN_THICK || s->u.bar.type == B_THICK_THIN || (s->u.bar.repeat_bar && s->text && s->text[0] != '1'))) return s; prev = s; } /*fixme: KO when no note for this voice at end of staff */ return prev; } struct SYMBOL *prev_scut(struct SYMBOL *s) { while (s->prev) { s = s->prev; if (s->type == BAR && ((s->sflags & S_RRBAR) || s->u.bar.type == B_THIN_THICK || s->u.bar.type == B_THICK_THIN || (s->u.bar.repeat_bar && s->text && s->text[0] != '1'))) return s; } /* return a symbol of any voice starting before the start of the voice */ s = voice_tb[s->voice].sym; while (s->type != CLEF) s = s->ts_prev; /* search a main voice */ if (s->next && s->next->type == KEYSIG) s = s->next; if (s->next && s->next->type == TIMESIG) s = s->next; return s; } /* -- decide whether a slur goes up or down -- */ static int slur_direction(struct SYMBOL *k1, struct SYMBOL *k2) { struct SYMBOL *s; int some_upstem, low; if ((k1->flags & ABC_F_GRACE) && k1->stem > 0) return -1; some_upstem = low = 0; for (s = k1; ; s = s->next) { if (s->abc_type == ABC_T_NOTE) { if (!(s->flags & ABC_F_STEMLESS)) { if (s->stem < 0) return 1; some_upstem = 1; } if (s->pits[0] < 22) /* if under middle staff */ low = 1; } if (s == k2) break; } if (!some_upstem && !low) return 1; return -1; } /* -- output a slur / tie -- */ static void slur_out(float x1, float y1, float x2, float y2, int s, float height, int dotted, int staff) /* if < 0, the staves are defined */ { float alfa, beta, mx, my, xx1, yy1, xx2, yy2, dx, dy, dz; float scale_y; alfa = .3; beta = .45; /* for wide flat slurs, make shape more square */ dy = y2 - y1; if (dy < 0) dy = -dy; dx = x2 - x1; if (dx > 40. && dy / dx < .7) { alfa = .3 + .002 * (dx - 40.); if (alfa > .7) alfa = .7; } /* alfa, beta, and height determine Bezier control points pp1,pp2 * * X====alfa===|===alfa=====X * / | \ * pp1 | pp2 * / height \ * beta | beta * / | \ * p1 m p2 * */ mx = .5 * (x1 + x2); my = .5 * (y1 + y2); xx1 = mx + alfa * (x1 - mx); yy1 = my + alfa * (y1 - my) + height; xx1 = x1 + beta * (xx1 - x1); yy1 = y1 + beta * (yy1 - y1); xx2 = mx + alfa * (x2 - mx); yy2 = my + alfa * (y2 - my) + height; xx2 = x2 + beta * (xx2 - x2); yy2 = y2 + beta * (yy2 - y2); dx = .03 * (x2 - x1); // if (dx > 10.) // dx = 10.; // dy = 1.6 * s; dy = 2 * s; dz = .2 + .001 * (x2 - x1); if (dz > .6) dz = .6; dz *= s; scale_y = scale_voice ? cur_scale : 1; if (!dotted) a2b("%.2f %.2f %.2f %.2f %.2f %.2f 0 %.2f ", (xx2 - dx - x2) / cur_scale, (yy2 + dy - y2 - dz) / scale_y, (xx1 + dx - x2) / cur_scale, (yy1 + dy - y2 - dz) / scale_y, (x1 - x2) / cur_scale, (y1 - y2 - dz) / scale_y, dz); a2b("%.2f %.2f %.2f %.2f %.2f %.2f ", (xx1 - x1) / cur_scale, (yy1 - y1) / scale_y, (xx2 - x1) / cur_scale, (yy2 - y1) / scale_y, (x2 - x1) / cur_scale, (y2 - y1) / scale_y); putxy(x1, y1); if (staff >= 0) a2b("y%d ", staff); a2b(dotted ? "dSL\n" : "SL\n"); } /* -- check if slur sequence in a multi-voice staff -- */ static int slur_multi(struct SYMBOL *k1, struct SYMBOL *k2) { for (;;) { if (k1->multi != 0) /* if multi voice */ /*fixme: may change*/ return k1->multi; if (k1 == k2) break; k1 = k1->next; } return 0; } /* -- draw a phrasing slur between two symbols -- */ /* (the staves are not yet defined) */ /* (not a pretty routine, this) */ static int draw_slur(struct SYMBOL *k1_orig, struct SYMBOL *k2, int m1, int m2, int slur_type) { struct SYMBOL *k1, *k; float x1, y1, x2, y2, height, addy; float a, y, z, h, dx, dy; int s, nn, upstaff, two_staves; k1 = k1_orig; while (k1->voice != k2->voice) k1 = k1->ts_next; /*fixme: if two staves, may have upper or lower slur*/ switch (slur_type & 0x07) { /* (ignore dot bit) */ case SL_ABOVE: s = 1; break; case SL_BELOW: s = -1; break; default: if ((s = slur_multi(k1, k2)) == 0) s = slur_direction(k1, k2); break; } nn = 1; upstaff = k1->staff; two_staves = 0; if (k1 != k2) for (k = k1->next; k; k = k->next) { if (k->type == NOTEREST) { nn++; if (k->staff != upstaff) { two_staves = 1; if (k->staff < upstaff) upstaff = k->staff; } } if (k == k2) break; } /*fixme: KO when two staves*/ if (two_staves) error(0, k1, "*** multi-staves slurs not treated yet"); /* fix endpoints */ // x1 = k1->x + k1->xmx; /* take the max right side */ x1 = k1_orig->x + k1_orig->u.note.notes[0].shhd; if (k1_orig != k2) { // x2 = k2->x; x2 = k2->x + k2->u.note.notes[0].shhd; } else { /* (the slur starts on last note of the line) */ for (k = k2->ts_next; k; k = k->ts_next) if (k->sflags & S_NEW_SY) break; if (!k) x2 = realwidth; else x2 = k->x; } if (m1 >= 0) { y1 = (float) (3 * (k1->pits[m1] - 18) + 5 * s); } else { y1 = (float) (s > 0 ? k1->ymx + 2 : k1->ymn - 2); if (k1->abc_type == ABC_T_NOTE) { if (s > 0) { if (k1->stem > 0) { x1 += 5; if ((k1->sflags & S_BEAM_END) && k1->nflags >= -1 /* if with a stem */ //fixme: check if at end of tuplet && (!(k1->sflags & S_IN_TUPLET))) { // || k1->ys > y1 - 3)) { if (k1->nflags > 0) { x1 += 2; y1 = k1->ys - 3; } else { y1 = k1->ys - 6; } // don't clash with decorations // } else { // y1 = k1->ys + 3; } // } else { // y1 = k1->y + 8; } } else { if (k1->stem < 0) { x1 -= 1; if ((k1->sflags & S_BEAM_END) && k1->nflags >= -1 && (!(k1->sflags & S_IN_TUPLET) || k1->ys < y1 + 3)) { if (k1->nflags > 0) { x1 += 2; y1 = k1->ys + 3; } else { y1 = k1->ys + 6; } // } else { // y1 = k1->ys - 3; } // } else { // y1 = k1->y - 8; } } } } if (m2 >= 0) { y2 = (float) (3 * (k2->pits[m2] - 18) + 5 * s); } else { y2 = (float) (s > 0 ? k2->ymx + 2 : k2->ymn - 2); if (k2->abc_type == ABC_T_NOTE) { if (s > 0) { if (k2->stem > 0) { x2 += 1; if ((k2->sflags & S_BEAM_ST) && k2->nflags >= -1 && (!(k2->sflags & S_IN_TUPLET))) // || k2->ys > y2 - 3)) y2 = k2->ys - 6; // else // y2 = k2->ys + 3; // } else { // y2 = k2->y + 8; } } else { if (k2->stem < 0) { x2 -= 5; if ((k2->sflags & S_BEAM_ST) && k2->nflags >= -1 && (!(k2->sflags & S_IN_TUPLET))) // || k2->ys < y2 + 3)) y2 = k2->ys + 6; // else // y2 = k2->ys - 3; // } else { // y2 = k2->y - 8; } } } } if (k1->abc_type != ABC_T_NOTE) { y1 = y2 + 1.2 * s; x1 = k1->x + k1->wr * .5; if (x1 > x2 - 12) x1 = x2 - 12; } if (k2->abc_type != ABC_T_NOTE) { if (k1->abc_type == ABC_T_NOTE) y2 = y1 + 1.2 * s; else y2 = y1; if (k1 != k2) x2 = k2->x - k2->wl * .3; } if (nn >= 3) { if (k1->next->type != BAR && k1->next->x < x1 + 48) { if (s > 0) { y = k1->next->ymx - 2; if (y1 < y) y1 = y; } else { y = k1->next->ymn + 2; if (y1 > y) y1 = y; } } if (k2->prev && k2->prev->type != BAR && k2->prev->x > x2 - 48) { if (s > 0) { y = k2->prev->ymx - 2; if (y2 < y) y2 = y; } else { y = k2->prev->ymn + 2; if (y2 > y) y2 = y; } } } a = (y2 - y1) / (x2 - x1); /* slur steepness */ if (a > SLUR_SLOPE || a < -SLUR_SLOPE) { if (a > SLUR_SLOPE) a = SLUR_SLOPE; else a = -SLUR_SLOPE; if (a * s > 0) y1 = y2 - a * (x2 - x1); else y2 = y1 + a * (x2 - x1); } /* for big vertical jump, shift endpoints */ y = y2 - y1; if (y > 8) y = 8; else if (y < -8) y = -8; z = y; if (z < 0) z = -z; dx = .5 * z; dy = .3 * y; if (y * s > 0) { x2 -= dx; y2 -= dy; } else { x1 += dx; y1 += dy; } /* special case for grace notes */ if (k1->flags & ABC_F_GRACE) x1 = k1->x - GSTEM_XOFF * .5; if (k2->flags & ABC_F_GRACE) x2 = k2->x + GSTEM_XOFF * 1.5; h = 0; a = (y2 - y1) / (x2 - x1); if (k1 != k2 && k1->voice == k2->voice) { addy = y1 - a * x1; for (k = k1->next; k != k2 ; k = k->next) { if (k->staff != upstaff) continue; switch (k->type) { case NOTEREST: if (s > 0) { y = 3 * (k->pits[k->nhd] - 18) + 6; if (y < k->ymx) y = k->ymx; y -= a * k->x + addy; if (y > h) h = y; } else { y = 3 * (k->pits[0] - 18) - 6; if (y > k->ymn) y = k->ymn; y -= a * k->x + addy; if (y < h) h = y; } break; case GRACE: { struct SYMBOL *g; for (g = k->extra; g; g = g->next) { #if 1 if (g->type != NOTEREST) continue; if (s > 0) { y = 3 * (g->pits[g->nhd] - 18) + 6; if (y < g->ymx) y = g->ymx; y -= a * g->x + addy; if (y > h) h = y; } else { y = 3 * (g->pits[0] - 18) - 6; if (y > g->ymn) y = g->ymn; y -= a * g->x + addy; if (y < h) h = y; } #else y = g->y - a * k->x - addy; if (s > 0) { y += GSTEM + 2; if (y > h) h = y; } else { y -= 2; if (y < h) h = y; } #endif } break; } } } y1 += .45 * h; y2 += .45 * h; h *= .65; } if (nn > 3) height = (.08 * (x2 - x1) + 12) * s; else height = (.03 * (x2 - x1) + 8) * s; if (s > 0) { if (height < 3 * h) height = 3 * h; if (height > 40) height = 40; } else { if (height > 3 * h) height = 3 * h; if (height < -40) height = -40; } y = y2 - y1; if (y < 0) y = -y; if (s > 0) { if (height < .8 * y) height = .8 * y; } else { if (height > -.8 * y) height = -.8 * y; } height *= cfmt.slurheight; slur_out(x1, y1, x2, y2, s, height, slur_type & SL_DOTTED, upstaff); /* have room for other symbols */ dx = x2 - x1; a = (y2 - y1) / dx; /*fixme: it seems to work with .4, but why?*/ addy = y1 - a * x1 + .4 * height; if (k1->voice == k2->voice) for (k = k1; k != k2; k = k->next) { if (k->staff != upstaff) continue; y = a * k->x + addy; if (k->ymx < y) k->ymx = y; else if (k->ymn > y) k->ymn = y; if (k->next == k2) { dx = x2; if (k2->sflags & S_SL1) dx -= 5; } else { dx = k->next->x; } if (k != k1) x1 = k->x; dx -= x1; y_set(upstaff, s > 0, x1, dx, y); } return (s > 0 ? SL_ABOVE : SL_BELOW) | (slur_type & SL_DOTTED); } /* -- draw the slurs between 2 symbols --*/ static void draw_slurs(struct SYMBOL *first, struct SYMBOL *last) { struct SYMBOL *s, *s1, *k, *gr1, *gr2; int i, m1, m2, gr1_out, slur_type, cont; gr1 = gr2 = NULL; s = first; for (;;) { if (!s || s == last) { if (!gr1 || !(s = gr1->next) || s == last) break; gr1 = NULL; } if (s->type == GRACE) { gr1 = s; s = s->extra; continue; } if ((s->type != NOTEREST && s->type != SPACE) || (s->u.note.slur_st == 0 && !(s->sflags & S_SL1))) { s = s->next; continue; } k = NULL; /* find matching slur end */ s1 = s->next; gr1_out = 0; for (;;) { if (!s1) { if (gr2) { s1 = gr2->next; gr2 = NULL; continue; } if (!gr1 || gr1_out) break; s1 = gr1->next; gr1_out = 1; continue; } if (s1->type == GRACE) { gr2 = s1; s1 = s1->extra; continue; } if (s1->type == BAR && ((s1->sflags & S_RRBAR) || s1->u.bar.type == B_THIN_THICK || s1->u.bar.type == B_THICK_THIN || (s1->u.bar.repeat_bar && s1->text && s1->text[0] != '1'))) { k = s1; break; } if (s1->type != NOTEREST && s1->type != SPACE) { s1 = s1->next; continue; } if (s1->u.note.slur_end || (s1->sflags & S_SL2)) { k = s1; break; } if (s1->u.note.slur_st || (s1->sflags & S_SL1)) { if (gr2) { /* if in grace note sequence */ for (k = s1; k->next; k = k->next) ; k->next = gr2->next; if (gr2->next) gr2->next->prev = k; // gr2->u.note.slur_st = SL_AUTO; k = NULL; } draw_slurs(s1, last); if (gr2 && gr2->next) { gr2->next->prev->next = NULL; gr2->next->prev = gr2; } } if (s1 == last) break; s1 = s1->next; } if (!s1) { k = next_scut(s); } else if (!k) { s = s1; if (s == last) break; continue; } /* if slur in grace note sequence, change the linkages */ if (gr1) { for (s1 = s; s1->next; s1 = s1->next) ; s1->next = gr1->next; if (gr1->next) gr1->next->prev = s1; gr1->u.note.slur_st = SL_AUTO; } if (gr2) { gr2->prev->next = gr2->extra; gr2->extra->prev = gr2->prev; gr2->u.note.slur_st = SL_AUTO; } if (s->u.note.slur_st) { slur_type = s->u.note.slur_st & 0x0f; s->u.note.slur_st >>= 4; m1 = -1; } else { for (m1 = 0; m1 <= s->nhd; m1++) if (s->u.note.notes[m1].sl1) break; slur_type = s->u.note.notes[m1].sl1 & 0x0f; s->u.note.notes[m1].sl1 >>= 4; if (s->u.note.notes[m1].sl1 == 0) { for (i = m1 + 1; i <= s->nhd; i++) if (s->u.note.notes[i].sl1) break; if (i > s->nhd) s->sflags &= ~S_SL1; } } m2 = -1; cont = 0; if ((k->type == NOTEREST || k->type == SPACE) && (k->u.note.slur_end || (k->sflags & S_SL2))) { if (k->u.note.slur_end) { k->u.note.slur_end--; } else { for (m2 = 0; m2 <= k->nhd; m2++) if (k->u.note.notes[m2].sl2) break; k->u.note.notes[m2].sl2--; if (k->u.note.notes[m2].sl2 == 0) { for (i = m2 + 1; i <= k->nhd; i++) if (k->u.note.notes[i].sl2) break; if (i > k->nhd) k->sflags &= ~S_SL2; } } } else { if (k->type != BAR || (!(k->sflags & S_RRBAR) && k->u.bar.type != B_THIN_THICK && k->u.bar.type != B_THICK_THIN && (!k->u.bar.repeat_bar || !k->text || k->text[0] == '1'))) cont = 1; } slur_type = draw_slur(s, k, m1, m2, slur_type); if (cont) { /*fixme: the slur types are inverted*/ voice_tb[k->voice].slur_st <<= 4; voice_tb[k->voice].slur_st += slur_type; } /* if slur in grace note sequence, restore the linkages */ if (gr1 && gr1->next) { gr1->next->prev->next = NULL; gr1->next->prev = gr1; } if (gr2) { gr2->prev->next = gr2; gr2->extra->prev = NULL; gr2 = NULL; } if (s->u.note.slur_st || (s->sflags & S_SL1)) continue; if (s == last) break; s = s->next; } } /* -- draw a tuplet -- */ /* (the staves are not yet defined) */ /* See http://moinejf.free.fr/abcm2ps-doc/tuplets.xhtml * about the value of 'aux' */ static struct SYMBOL *draw_tuplet(struct SYMBOL *t, /* tuplet in extra */ struct SYMBOL *s) /* main note */ { struct SYMBOL *s1, *s2, *sy, *next, *g; int r, upstaff, nb_only, some_slur, dir; float x1, x2, y1, y2, xm, ym, a, s0, yy, yx, dy; next = s; if ((t->aux & 0xf000) == 0x1000) /* if 'when' == never */ goto done; /* treat the nested tuplets starting on this symbol */ for (g = t->next; g; g = g->next) { if (g->type == TUPLET) { sy = draw_tuplet(g, s); if (sy->time > next->time) next = sy; } } /* search the first and last notes/rests of the tuplet */ r = t->u.tuplet.r_plet; s1 = NULL; some_slur = 0; upstaff = s->staff; for (s2 = s; s2; s2 = s2->next) { if (s2 != s && (s2->sflags & S_IN_TUPLET)) { for (g = s2->extra; g; g = g->next) { if (g->type == TUPLET) { sy = draw_tuplet(g, s2); if (sy->time > next->time) next = sy; } } } if (s2->type != NOTEREST) { if (s2->type == GRACE) { for (g = s2->extra; g; g = g->next) { if (g->type != NOTEREST) continue; if (g->u.note.slur_st || (g->sflags & S_SL1)) some_slur = 1; } } continue; } if (s2->u.note.slur_st /* if slur start/end */ || s2->u.note.slur_end || (s2->sflags & (S_SL1 | S_SL2))) some_slur = 1; if (s2->staff < upstaff) upstaff = s2->staff; if (!s1) s1 = s2; if (--r <= 0) break; } if (!s2) goto done; /* no solution... */ if (s2->time > next->time) next = s2; dir = t->aux & 0x000f; if (!dir) dir = s1->stem > 0 ? SL_ABOVE : SL_BELOW; if (s1 == s2) { /* tuplet with 1 note (!) */ nb_only = 1; } else if ((t->aux & 0x0f00) == 0x0100) { /* 'what' == slur */ nb_only = 1; draw_slur(s1, s2, -1, -1, dir); } else { /* search if a bracket is needed */ if ((t->aux & 0xf000) == 0x2000 /* if 'when' == always */ || s1->abc_type != ABC_T_NOTE || s2->abc_type != ABC_T_NOTE) { nb_only = 0; } else { nb_only = 1; for (sy = s1; ; sy = sy->next) { if (sy->type != NOTEREST) { if (sy->type == GRACE || sy->type == SPACE) continue; nb_only = 0; break; } if (sy == s2) break; if (sy->sflags & S_BEAM_END) { nb_only = 0; break; } } if (nb_only && !(s1->sflags & (S_BEAM_ST | S_BEAM_BR1 | S_BEAM_BR2))) { for (sy = s1->prev; sy; sy = sy->prev) { if (sy->type == NOTEREST) { if (sy->nflags >= s1->nflags) nb_only = 0; break; } } } if (nb_only && !(s2->sflags & S_BEAM_END)) { for (sy = s2->next; sy; sy = sy->next) { if (sy->type == NOTEREST) { if (!(sy->sflags & (S_BEAM_BR1 | S_BEAM_BR2)) && sy->nflags >= s2->nflags) nb_only = 0; break; } } } } } /* if number only, draw it */ if (nb_only) { float a, b; if ((t->aux & 0x00f0) == 0x0010) /* if 'which' == none */ goto done; xm = (s2->x + s1->x) * .5; if (s1 == s2) /* tuplet with 1 note */ a = 0; else a = (s2->ys - s1->ys) / (s2->x - s1->x); b = s1->ys - a * s1->x; yy = a * xm + b; if (dir == SL_ABOVE) { ym = y_get(s1->staff, 1, xm - 3, 6); if (ym > yy) b += ym - yy; b += 2; } else { ym = y_get(s1->staff, 0, xm - 3, 6); if (ym < yy) b += ym - yy; b -= 10; } for (sy = s1; ; sy = sy->next) { if (sy->x >= xm) break; } if (s1->stem * s2->stem > 0) { if (s1->stem > 0) xm += GSTEM_XOFF; else xm -= GSTEM_XOFF; } ym = a * xm + b; if ((t->aux & 0x00f0) == 0) /* if 'which' == number */ a2b("(%d)", t->u.tuplet.p_plet); else a2b("(%d:%d)", t->u.tuplet.p_plet, t->u.tuplet.q_plet); putxy(xm, ym); a2b("y%d bnum\n", s1->staff); if (dir == SL_ABOVE) { ym += 8; if (sy->ymx < ym) sy->ymx = (short) ym; y_set(s1->staff, 1, xm - 3, 6, ym); } else { if (sy->ymn > ym) sy->ymn = (short) ym; y_set(s1->staff, 0, xm - 3, 6, ym); } goto done; } /* draw the slurs when inside the tuplet */ if (some_slur) { draw_slurs(s1, s2); if (s1->u.note.slur_st || (s1->sflags & S_SL1)) return next; for (sy = s1->next; sy != s2; sy = sy->next) { if (sy->u.note.slur_st /* if slur start/end */ || sy->u.note.slur_end || (sy->sflags & (S_SL1 | S_SL2))) return next; /* don't draw now */ } /* don't draw the tuplet when a slur ends on the last note */ if (s2->u.note.slur_end || (s2->sflags & S_SL2)) return next; } if ((t->aux & 0x0f00) != 0) /* if 'what' != square */ fprintf(stderr, "'what' value of %%%%tuplets not yet coded\n"); /*fixme: two staves not treated*/ /*fixme: to optimize*/ dir = t->aux & 0x000f; /* 'where' */ if (!dir) dir = s1->multi >= 0 ? SL_ABOVE : SL_BELOW; if (dir == SL_ABOVE) { /* sole or upper voice: the bracket is above the staff */ x1 = s1->x - 4; y1 = 24; if (s1->staff == upstaff) { sy = s1; if (sy->abc_type != ABC_T_NOTE) { for (sy = sy->next; sy != s2; sy = sy->next) if (sy->abc_type == ABC_T_NOTE) break; } ym = y_get(upstaff, 1, sy->x, 0); if (ym > y1) y1 = ym; if (s1->stem > 0) x1 += 3; } y2 = 24; if (s2->staff == upstaff) { sy = s2; if (sy->abc_type != ABC_T_NOTE) { for (sy = sy->prev; sy != s1; sy = sy->prev) if (sy->abc_type == ABC_T_NOTE) break; } ym = y_get(upstaff, 1, sy->x, 0); if (ym > y2) y2 = ym; } /* end the backet according to the last note duration */ if (s2->dur > s2->prev->dur) { if (s2->next) x2 = s2->next->x - s2->next->wl - 5; else x2 = realwidth - 6; } else { x2 = s2->x + 4; r = s2->stem >= 0 ? 0 : s2->nhd; if (s2->u.note.notes[r].shhd > 0) x2 += s2->u.note.notes[r].shhd; if (s2->staff == upstaff && s2->stem > 0) x2 += 3.5; } xm = .5 * (x1 + x2); ym = .5 * (y1 + y2); a = (y2 - y1) / (x2 - x1); s0 = 3 * (s2->pits[s2->nhd] - s1->pits[s1->nhd]) / (x2 - x1); if (s0 > 0) { if (a < 0) a = 0; else if (a > s0) a = s0; } else { if (a > 0) a = 0; else if (a < s0) a = s0; } if (a * a < .1 * .1) a = 0; /* shift up bracket if needed */ dy = 0; for (sy = s1; ; sy = sy->next) { if (sy->dur == 0 /* not a note or a rest */ || sy->staff != upstaff) { if (sy == s2) break; continue; } yy = ym + (sy->x - xm) * a; yx = y_get(upstaff, 1, sy->x, 0); if (yx - yy > dy) dy = yx - yy; if (sy == s2) break; } ym += dy; y1 = ym + a * (x1 - xm); y2 = ym + a * (x2 - xm); putxy(x2 - x1, y2 - y1); putxy(x1, y1 + 4); a2b("y%d tubr", upstaff); /* shift the slurs / decorations */ ym += 8; for (sy = s1; ; sy = sy->next) { if (sy->staff == upstaff) { yy = ym + (sy->x - xm) * a; if (sy->ymx < yy) sy->ymx = yy; if (sy == s2) break; y_set(upstaff, 1, sy->x, sy->next->x - sy->x, yy); } else if (sy == s2) { break; } } } else { /* lower voice of the staff: the bracket is below the staff */ /*fixme: think to all that again..*/ x1 = s1->x - 7; if (s2->dur > s2->prev->dur) { sy = s2->next; if (!sy // maybe a note in an overlay voice || sy->time != s2->time + s2->dur) { for (sy = s2->ts_next; sy; sy = sy->ts_next) { if ((sy->sflags & S_SEQST) && sy->time >= s2->time + s2->dur) break; } } x2 = sy ? sy->x - sy->wl - 5 : realwidth - 6; } else { x2 = s2->x + 2; if (s2->u.note.notes[s2->nhd].shhd > 0) x2 += s2->u.note.notes[s2->nhd].shhd; } if (s1->staff == upstaff) { sy = s1; if (sy->abc_type != ABC_T_NOTE) { for (sy = sy->next; sy != s2; sy = sy->next) if (sy->abc_type == ABC_T_NOTE) break; } y1 = y_get(upstaff, 0, sy->x, 0); } else { y1 = 0; } if (s2->staff == upstaff) { sy = s2; if (sy->abc_type != ABC_T_NOTE) { for (sy = sy->prev; sy != s1; sy = sy->prev) if (sy->abc_type == ABC_T_NOTE) break; } y2 = y_get(upstaff, 0, sy->x, 0); } else { y2 = 0; } xm = .5 * (x1 + x2); ym = .5 * (y1 + y2); a = (y2 - y1) / (x2 - x1); s0 = 3 * (s2->pits[0] - s1->pits[0]) / (x2 - x1); if (s0 > 0) { if (a < 0) a = 0; else if (a > s0) a = s0; } else { if (a > 0) a = 0; else if (a < s0) a = s0; } if (a * a < .1 * .1) a = 0; /* shift down bracket if needed */ dy = 0; for (sy = s1; ; sy = sy->next) { if (sy->dur == 0 /* not a note nor a rest */ || sy->staff != upstaff) { if (sy == s2) break; continue; } yy = ym + (sy->x - xm) * a; yx = y_get(upstaff, 0, sy->x, 0); if (yx - yy < dy) dy = yx - yy; if (sy == s2) break; } ym += dy - 10; y1 = ym + a * (x1 - xm); y2 = ym + a * (x2 - xm); putxy(x2 - x1, y2 - y1); putxy(x1, y1 + 4); a2b("y%d tubrl",upstaff); /* shift the slurs / decorations */ ym -= 2; for (sy = s1; ; sy = sy->next) { if (sy->staff == upstaff) { if (sy == s2) break; yy = ym + (sy->x - xm) * a; if (sy->ymn > yy) sy->ymn = (short) yy; y_set(upstaff, 0, sy->x, sy->next->x - sy->x, yy); } if (sy == s2) break; } } /* lower voice */ if ((t->aux & 0x00f0) == 0x10) { /* if 'which' == none */ a2b("\n"); goto done; } yy = .5 * (y1 + y2); if ((t->aux & 0x00f0) == 0) /* if 'which' == number */ a2b("(%d)", t->u.tuplet.p_plet); else a2b("(%d:%d)", t->u.tuplet.p_plet, t->u.tuplet.q_plet); putxy(xm, yy); a2b("y%d bnumb\n", upstaff); // let some room for the number if (dir == SL_ABOVE) y_set(upstaff, 1, xm - 3, 6, ym + 3); else y_set(upstaff, 0, xm - 3, 6, ym); done: s->sflags &= ~S_IN_TUPLET; return next; } /* -- draw the ties between two notes/chords -- */ static void draw_note_ties(struct SYMBOL *k1, struct SYMBOL *k2, int ntie, unsigned char *mhead1, unsigned char *mhead2, int job) { int i, s, m1, m2, p, p1, p2, y, staff; float x1, x2, h, sh; for (i = 0; i < ntie; i++) { m1 = mhead1[i]; p1 = k1->pits[m1]; m2 = mhead2[i]; p2 = job != 2 ? k2->pits[m2] : p1; s = (k1->u.note.notes[m1].ti1 & 0x07) == SL_ABOVE ? 1 : -1; x1 = k1->x; sh = k1->u.note.notes[m1].shhd; /* head shift */ if (s > 0) { if (m1 < k1->nhd && p1 + 1 == k1->pits[m1 + 1]) if (k1->u.note.notes[m1 + 1].shhd > sh) sh = k1->u.note.notes[m1 + 1].shhd; } else { if (m1 > 0 && p1 == k1->pits[m1 - 1] + 1) if (k1->u.note.notes[m1 - 1].shhd > sh) sh = k1->u.note.notes[m1 - 1].shhd; } // x1 += sh; x1 += sh * 0.6; x2 = k2->x; if (job != 2) { sh = k2->u.note.notes[m2].shhd; if (s > 0) { if (m2 < k2->nhd && p2 + 1 == k2->pits[m2 + 1]) if (k2->u.note.notes[m2 + 1].shhd < sh) sh = k2->u.note.notes[m2 + 1].shhd; } else { if (m2 > 0 && p2 == k2->pits[m2 - 1] + 1) if (k2->u.note.notes[m2 - 1].shhd < sh) sh = k2->u.note.notes[m2 - 1].shhd; } x2 += sh * 0.6; } staff = k1->staff; switch (job) { case 0: if (p1 == p2 || (p1 & 1)) p = p1; else p = p2; break; case 3: /* clef or staff change */ s = -s; // fall thru case 1: /* no starting note */ x1 = k1->x; if (x1 > x2 - 20) x1 = x2 - 20; p = p2; staff = k2->staff; break; /* case 2: * no ending note */ default: if (k1 != k2) { if (k2->type == BAR) x2 -= 2; else x2 -= k2->wl; } else { struct SYMBOL *k; int time; time = k1->time + k1->dur; for (k = k1->ts_next; k; k = k->ts_next) if (k->time > time) break; if (!k) x2 = realwidth; else x2 = k->x; } if (x2 < x1 + 16) x2 = x1 + 16; p = p1; break; } if (x2 - x1 > 20) { x1 += 3.5; x2 -= 3.5; } else { x1 += 1.5; x2 -= 1.5; } if (k1->dots && !(p1 & 1) && ((s > 0 && k1->doty >= 0) || (s < 0 && k1->doty < 0))) x1 += 6; // avoid clash with dots y = 3 * (p - 18); //fixme: clash when 2 ties on second interval chord // if (p & 1) // y += 2 * s; #if 0 if (job != 1 && job != 3) { if (s > 0) { // if (k1->nflags > -2 && k1->stem > 0 // && k1->nhd == 0) // x1 += 4.5; if (!(p & 1) && k1->dots > 0) y = 3 * (p - 18) + 6; } // } else { // if (s < 0) { // if (k2->nflags > -2 && k2->stem < 0 // && k2->nhd == 0) // x2 -= 4.5; // } } #endif h = (.04 * (x2 - x1) + 10) * s; h *= cfmt.tieheight; slur_out(x1, staff_tb[staff].y + y, x2, staff_tb[staff].y + y, s, h, k1->u.note.notes[m1].ti1 & SL_DOTTED, -1); } } /* -- draw ties between neighboring notes/chords -- */ static void draw_ties(struct SYMBOL *k1, struct SYMBOL *k2, int job) /* 0: normal * 1: no starting note * 2: no ending note * 3: no start for clef or staff change */ { struct SYMBOL *k3; int i, m1, nh1, pit, ntie, tie2, ntie3, time; unsigned char mhead1[MAXHD], mhead2[MAXHD], mhead3[MAXHD]; time = k1->time + k1->dur; ntie = ntie3 = 0; nh1 = k1->nhd; /* half ties from last note in line or before new repeat */ if (job == 2) { for (i = 0; i <= nh1; i++) { if (k1->u.note.notes[i].ti1) mhead3[ntie3++] = i; } draw_note_ties(k1, k2 ? k2 : k1, ntie3, mhead3, mhead3, job); return; } /* set up list of ties to draw */ for (i = 0; i <= nh1; i++) { if (k1->u.note.notes[i].ti1 == 0) continue; tie2 = -1; pit = k1->u.note.notes[i].pit; for (m1 = k2->nhd; m1 >= 0; m1--) { switch (k2->u.note.notes[m1].pit - pit) { case 1: /* maybe ^c - _d */ case -1: /* _d - ^c */ if (k1->u.note.notes[i].acc != k2->u.note.notes[m1].acc) tie2 = m1; default: continue; case 0: tie2 = m1; break; } break; } if (tie2 >= 0) { /* 1st or 2nd choice */ mhead1[ntie] = i; mhead2[ntie++] = tie2; } else { mhead3[ntie3++] = i; /* no match */ } } /* draw the ties */ draw_note_ties(k1, k2, ntie, mhead1, mhead2, job); /* if any bad tie, try an other voice of the same staff */ if (ntie3 == 0) return; /* no bad tie */ k3 = k1->ts_next; while (k3 && k3->time < time) k3 = k3->ts_next; while (k3 && k3->time == time) { if (k3->abc_type != ABC_T_NOTE || k3->staff != k1->staff) { k3 = k3->ts_next; continue; } ntie = 0; for (i = ntie3; --i >= 0; ) { pit = k1->u.note.notes[mhead3[i]].pit; for (m1 = k3->nhd; m1 >= 0; m1--) { if (k3->u.note.notes[m1].pit == pit) { mhead1[ntie] = mhead3[i]; mhead2[ntie++] = m1; ntie3--; // mhead3[i] = mhead3[--ntie3]; break; } } } if (ntie > 0) { draw_note_ties(k1, k3, ntie, mhead1, mhead2, job == 1 ? 1 : 0); if (ntie3 == 0) return; } k3 = k3->ts_next; } // if (ntie3 != 0) error(1, k1, "Bad tie"); } static void draw_ties_g(struct SYMBOL *s1, struct SYMBOL *s2, int job) { struct SYMBOL *g; if (s1->type == GRACE) { for (g = s1->extra; g; g = g->next) { if (g->sflags & S_TI1) draw_ties(g, s2, job); } } else { draw_ties(s1, s2, job); } } /* -- try to get the symbol of a ending tie when combined voices -- */ static struct SYMBOL *tie_comb(struct SYMBOL *s) { struct SYMBOL *s1; int time, st; time = s->time + s->dur; st = s->staff; for (s1 = s->ts_next; s1; s1 = s1->ts_next) { if (s1->staff != st) continue; if (s1->time == time) { if (s1->abc_type == ABC_T_NOTE) return s1; continue; } if (s1->time > time) return s; // bad tie } return NULL; // no ending tie } /* -- draw all ties between neighboring notes -- */ static void draw_all_ties(struct VOICE_S *p_voice) { struct SYMBOL *s1, *s2, *s3, *s4, *rtie; struct SYMBOL tie; int clef_chg, time; for (s1 = p_voice->sym; s1; s1 = s1->next) { switch (s1->type) { case CLEF: case KEYSIG: case TIMESIG: continue; } break; } rtie = p_voice->rtie; /* tie from 1st repeat bar */ for (s2 = s1; s2; s2 = s2->next) { // if (s2->abc_type == ABC_T_NOTE if (s2->dur || s2->type == GRACE) break; if (s2->type != BAR || !s2->u.bar.repeat_bar || !s2->text) continue; if (s2->text[0] == '1') /* 1st repeat bar */ rtie = p_voice->tie; else p_voice->tie = rtie; } if (!s2) return; if (p_voice->tie) { /* tie from previous line */ p_voice->tie->x = s1->x + s1->wr; s1 = p_voice->tie; p_voice->tie = NULL; s1->staff = s2->staff; s1->ts_next = s2->ts_next; /* (for tie to other voice) */ s1->time = s2->time - s1->dur; /* (if after repeat sequence) */ draw_ties(s1, s2, 1); /* tie to 1st note */ } /* search the start of ties */ clef_chg = 0; for (;;) { for (s1 = s2; s1; s1 = s1->next) { if (s1->sflags & S_TI1) break; if (!rtie) continue; if (s1->type != BAR || !s1->u.bar.repeat_bar || !s1->text) continue; if (s1->text[0] == '1') { /* 1st repeat bar */ rtie = NULL; continue; } if (s1->u.bar.type == B_BAR) continue; // not a repeat for (s2 = s1->next; s2; s2 = s2->next) if (s2->abc_type == ABC_T_NOTE) break; if (!s2) { s1 = NULL; break; } memcpy(&tie, rtie, sizeof tie); // tie.x = s1->x + s1->wr; tie.x = s1->x; tie.next = s2; tie.staff = s2->staff; tie.time = s2->time - tie.dur; draw_ties(&tie, s2, 1); } if (!s1) break; /* search the end of the tie * and notice the clef changes (may occur in an other voice) */ time = s1->time + s1->dur; for (s2 = s1->next; s2; s2 = s2->next) { if (s2->dur != 0) break; if (s2->type == BAR && s2->u.bar.repeat_bar && s2->text) { if (s2->text[0] != '1') break; rtie = s1; /* 1st repeat bar */ } } if (!s2) { for (s4 = s1->ts_next; s4; s4 = s4->ts_next) { if (s4->staff != s1->staff) continue; if (s4->time < time) continue; if (s4->time > time) { s4 = NULL; break; } if (s4->dur != 0) break; } if (!s4) { draw_ties_g(s1, NULL, 2); p_voice->tie = s1; break; } } else { if (s2->abc_type != ABC_T_NOTE && s2->abc_type != ABC_T_BAR) { error(1, s1, "Bad tie"); continue; } if (s2->time == time) { s4 = s2; } else { s4 = tie_comb(s1); if (s4 == s1) { error(1, s1, "Bad tie"); continue; } } } for (s3 = s1->ts_next; s3; s3 = s3->ts_next) { if (s3->staff != s1->staff) continue; if (s3->time > time) break; if (s3->type == CLEF) { clef_chg = 1; continue; } #if 0 if (s3->type == BAR) { // if ((s3->sflags & S_RRBAR) // || s3->u.bar.type == B_THIN_THICK // || s3->u.bar.type == B_THICK_THIN) { // s4 = s3; // break; // } if (!s3->u.bar.repeat_bar || !s3->text) continue; if (s3->text[0] != '1') { s4 = s3; break; } rtie = s1; /* 1st repeat bar */ } #endif } /* ties with clef or staff change */ if (clef_chg || s1->staff != s4->staff) { float x, dx; clef_chg = 0; dx = (s4->x - s1->x) * 0.4; x = s4->x; s4->x -= dx; if (s4->x > s1->x + 32.) s4->x = s1->x + 32.; draw_ties_g(s1, s4, 2); s4->x = x; x = s1->x; s1->x += dx; if (s1->x < s4->x - 24.) s1->x = s4->x - 24.; draw_ties(s1, s4, 3); s1->x = x; continue; } draw_ties_g(s1, s4, s4->abc_type == ABC_T_NOTE ? 0 : 2); } p_voice->rtie = rtie; } /* -- draw all phrasing slurs for one staff -- */ /* (the staves are not yet defined) */ static void draw_all_slurs(struct VOICE_S *p_voice) { struct SYMBOL *s, *k; int i, m2, slur_type; unsigned char slur_st; s = p_voice->sym; if (!s) return; slur_type = p_voice->slur_st; p_voice->slur_st = 0; /* the starting slur types are inverted */ slur_st = 0; while (slur_type != 0) { slur_st <<= 4; slur_st |= (slur_type & 0x0f); slur_type >>= 4; } /* draw the slurs inside the music line */ draw_slurs(s, NULL); /* do unbalanced slurs still left over */ for ( ; s; s = s->next) { if (s->type != NOTEREST && s->type != SPACE) continue; while (s->u.note.slur_end || (s->sflags & S_SL2)) { if (s->u.note.slur_end) { s->u.note.slur_end--; m2 = -1; } else { for (m2 = 0; m2 <= s->nhd; m2++) if (s->u.note.notes[m2].sl2) break; s->u.note.notes[m2].sl2--; if (s->u.note.notes[m2].sl2 == 0) { for (i = m2 + 1; i <= s->nhd; i++) if (s->u.note.notes[i].sl2) break; if (i > s->nhd) s->sflags &= ~S_SL2; } } slur_type = slur_st & 0x0f; k = prev_scut(s); draw_slur(k, s, -1, m2, slur_type); if (k->type != BAR || (!(k->sflags & S_RRBAR) && k->u.bar.type != B_THIN_THICK && k->u.bar.type != B_THICK_THIN && (!k->u.bar.repeat_bar || !k->text || k->text[0] == '1'))) slur_st >>= 4; } } s = p_voice->sym; while (slur_st != 0) { slur_type = slur_st & 0x0f; slur_st >>= 4; k = next_scut(s); draw_slur(s, k, -1, -1, slur_type); if (k->type != BAR || (!(k->sflags & S_RRBAR) && k->u.bar.type != B_THIN_THICK && k->u.bar.type != B_THICK_THIN && (!k->u.bar.repeat_bar || !k->text || k->text[0] == '1'))) { /*fixme: the slur types are inverted again*/ p_voice->slur_st <<= 4; p_voice->slur_st += slur_type; } } } /* -- work out accidentals to be applied to each note -- */ static void setmap(int sf, /* number of sharps/flats in key sig (-7 to +7) */ unsigned char *map) /* for 7 notes only */ { int j; for (j = 7; --j >= 0; ) map[j] = A_NULL; switch (sf) { case 7: map[6] = A_SH; case 6: map[2] = A_SH; case 5: map[5] = A_SH; case 4: map[1] = A_SH; case 3: map[4] = A_SH; case 2: map[0] = A_SH; case 1: map[3] = A_SH; break; case -7: map[3] = A_FT; case -6: map[0] = A_FT; case -5: map[4] = A_FT; case -4: map[1] = A_FT; case -3: map[5] = A_FT; case -2: map[2] = A_FT; case -1: map[6] = A_FT; break; } } /* output a tablature string escaping the parenthesis */ static void tbl_out(char *s, float x, int j, char *f) { char *p; a2b("("); p = s; for (;;) { while (*p != '\0' && *p != '(' && *p != ')' ) p++; if (p != s) { a2b("%.*s", (int) (p - s), s); s = p; } if (*p == '\0') break; a2b("\\"); p++; } a2b(")%.1f y %d %s ", x, j, f); } /* -- draw the tablature with w: -- */ static void draw_tblt_w(struct VOICE_S *p_voice, int nly, float y, struct tblt_s *tblt) { struct SYMBOL *s; struct lyrics *ly; struct lyl *lyl; char *p; int j, l; a2b("/y{%.1f yns%d}def ", y, p_voice->staff); set_font(VOCALFONT); a2b("%.1f 0 y %d %s\n", realwidth, nly, tblt->head); for (j = 0; j < nly ; j++) { for (s = p_voice->sym; s; s = s->next) { ly = s->ly; if (!ly || (lyl = ly->lyl[j]) == NULL) { if (s->type == BAR) { if (!tblt->bar) continue; p = &tex_buf[16]; *p-- = '\0'; l = bar_cnv(s->u.bar.type); while (l != 0) { *p-- = "?|[]:???"[l & 0x07]; l >>= 4; } p++; tbl_out(p, s->x, j, tblt->bar); } continue; } tbl_out(lyl->t, s->x, j, tblt->note); } a2b("\n"); } } /* -- draw the tablature with automatic pitch -- */ static void draw_tblt_p(struct VOICE_S *p_voice, float y, struct tblt_s *tblt) { struct SYMBOL *s; int j, pitch, octave, sf, tied, acc; unsigned char workmap[70]; /* sharps/flats - base: lowest 'C' */ unsigned char basemap[7]; static int scale[7] = {0, 2, 4, 5, 7, 9, 11}; /* index = natural note */ static int acc_pitch[6] = {0, 1, 0, -1, 2, -2}; /* index = enum accidentals */ sf = p_voice->key.sf; setmap(sf, basemap); for (j = 0; j < 10; j++) memcpy(&workmap[7 * j], basemap, 7); a2b("gsave 0 %.1f yns%d T(%.2s)%s\n", y, p_voice->staff, tblt->instr, tblt->head); tied = 0; for (s = p_voice->sym; s; s = s->next) { switch (s->type) { case NOTEREST: if (s->abc_type == ABC_T_REST) continue; if (tied) { tied = s->u.note.notes[0].ti1; continue; } break; case KEYSIG: sf = s->u.key.sf; setmap(sf, basemap); for (j = 0; j < 10; j++) memcpy(&workmap[7 * j], basemap, 7); continue; case BAR: if (s->flags & ABC_F_INVIS) continue; for (j = 0; j < 10; j++) memcpy(&workmap[7 * j], basemap, 7); continue; default: continue; } pitch = s->u.note.notes[0].pit + 19; acc = s->u.note.notes[0].acc; if (acc != 0) { workmap[pitch] = acc == A_NT ? A_NULL : (acc & 0x07); } pitch = scale[pitch % 7] + acc_pitch[workmap[pitch]] + 12 * (pitch / 7) - tblt->pitch; octave = 0; while (pitch < 0) { pitch += 12; octave--; } while (pitch >= 36) { pitch -= 12; octave++; } if ((acc & 0xf8) == 0) { a2b("%d %d %.2f %s\n", octave, pitch, s->x, tblt->note); } else { int n, d; float micro_p; n = parse.micro_tb[acc >> 3]; d = (n & 0xff) + 1; n = (n >> 8) + 1; switch (acc & 0x07) { case A_FT: case A_DF: n = -n; break; } micro_p = (float) pitch + (float) n / d; a2b("%d %.3f %.2f %s\n", octave, micro_p, s->x, tblt->note); } tied = s->u.note.notes[0].ti1; } a2b("grestore\n"); } /* -- draw the lyrics under (or above) notes -- */ /* !! this routine is tied to set_width() !! */ static void draw_lyric_line(struct VOICE_S *p_voice, int j) { struct SYMBOL *s; struct lyrics *ly; struct lyl *lyl; int hyflag, l, lflag; int ft, curft, defft; char *p; float lastx, w; float x0, shift; hyflag = lflag = 0; if (p_voice->hy_st & (1 << j)) { hyflag = 1; p_voice->hy_st &= ~(1 << j); } for (s = p_voice->sym; /*s*/; s = s->next) if (s->type != CLEF && s->type != KEYSIG && s->type != TIMESIG) break; if (s->prev) lastx = s->prev->x; else lastx = tsfirst->x; x0 = 0; for ( ; s; s = s->next) { ly = s->ly; if (!ly || (lyl = ly->lyl[j]) == NULL) { switch (s->type) { case NOTEREST: if (s->abc_type == ABC_T_NOTE) break; /* fall thru */ case MREST: if (lflag) { putx(x0 - lastx); putx(lastx + 3); a2b("y wln "); lflag = 0; lastx = s->x + s->wr; } } continue; } #if 1 ft = lyl->f - cfmt.font_tb; get_str_font(&curft, &defft); if (ft != curft) { set_str_font(ft, defft); } #else if (lyl->f != f) { /* font change */ f = lyl->f; str_font(f - cfmt.font_tb); if (lskip < f->size * 1.1) lskip = f->size * 1.1; } #endif p = lyl->t; w = lyl->w; shift = lyl->s; if (hyflag) { if (*p == LY_UNDER) { /* '_' */ *p = LY_HYPH; } else if (*p != LY_HYPH) { /* not '-' */ putx(s->x - shift - lastx); putx(lastx); a2b("y hyph "); hyflag = 0; lastx = s->x + s->wr; } } if (lflag && *p != LY_UNDER) { /* not '_' */ putx(x0 - lastx + 3); putx(lastx + 3); a2b("y wln "); lflag = 0; lastx = s->x + s->wr; } if (*p == LY_HYPH /* '-' */ || *p == LY_UNDER) { /* '_' */ if (x0 == 0 && lastx > s->x - 18) lastx = s->x - 18; if (*p == LY_HYPH) hyflag = 1; else lflag = 1; x0 = s->x - shift; continue; } x0 = s->x - shift; l = strlen(p) - 1; if (p[l] == LY_HYPH) { /* '-' at end */ p[l] = '\0'; hyflag = 1; } putx(x0); a2b("y M "); put_str(p, A_LYRIC); lastx = x0 + w; } if (hyflag) { x0 = realwidth - 10; if (x0 < lastx + 10) x0 = lastx + 10; putx(x0 - lastx); putx(lastx); a2b("y hyph "); if (cfmt.hyphencont) p_voice->hy_st |= (1 << j); } /* see if any underscore in the next line */ for (s = tsnext; s; s = s->ts_next) if (s->voice == p_voice - voice_tb) break; for ( ; s; s = s->next) { if (s->abc_type == ABC_T_NOTE) { if (s->ly && s->ly->lyl[j] && s->ly->lyl[j]->t[0] == LY_UNDER) { lflag = 1; x0 = realwidth - 15; if (x0 < lastx + 12) x0 = lastx + 12; } break; } } if (lflag) { putx(x0 - lastx + 3); putx(lastx + 3); a2b("y wln"); } a2b("\n"); } static float draw_lyrics(struct VOICE_S *p_voice, int nly, float *h, float y, int incr) /* 1: below, -1: above */ { int j, top; float sc; /* check if the lyrics contain tablatures */ if (p_voice->tblts[0]) { if (p_voice->tblts[0]->pitch == 0) return y; /* yes */ if (p_voice->tblts[1] && p_voice->tblts[1]->pitch == 0) return y; /* yes */ } str_font(VOCALFONT); outft = -1; /* force font output */ sc = staff_tb[p_voice->staff].staffscale; /* under the staff */ if (incr > 0) { if (y > -cfmt.vocalspace) y = -cfmt.vocalspace; y += h[0] / 6; // descent y *= sc; for (j = 0; j < nly; j++) { y -= h[j] * 1.1; a2b("/y{%.1f yns%d}! ", y, p_voice->staff); draw_lyric_line(p_voice, j); } return (y - h[j - 1] / 6) / sc; } /* above the staff */ top = staff_tb[p_voice->staff].topbar + cfmt.vocalspace; if (y < top) y = top; y += h[nly - 1] / 6; // descent y *= sc; for (j = nly; --j >= 0; ) { a2b("/y{%.1f yns%d}! ", y, p_voice->staff); draw_lyric_line(p_voice, j); y += h[j] * 1.1; } return y / sc; } /* -- draw all the lyrics and the tablatures -- */ /* (the staves are not yet defined) */ static void draw_all_lyrics(void) { struct VOICE_S *p_voice; struct SYMBOL *s; int staff, voice, nly, i; struct { short a, b; float top, bot; } lyst_tb[MAXSTAFF]; struct { int nly; float h[MAXLY]; } lyvo_tb[MAXVOICE]; char above_tb[MAXVOICE]; char rv_tb[MAXVOICE]; float top, bot, y, sc; /* check if any lyric */ for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { if (p_voice->have_ly || p_voice->tblts[0]) break; } if (!p_voice) return; /* compute the number of lyrics per voice - staff * and their y offset on the staff */ memset(above_tb, 0, sizeof above_tb); memset(lyvo_tb, 0, sizeof lyvo_tb); memset(lyst_tb, 0, sizeof lyst_tb); staff = -1; top = bot = 0; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { if (!p_voice->sym) continue; voice = p_voice - voice_tb; if (p_voice->staff != staff) { top = 0; bot = 0; staff = p_voice->staff; } nly = -1; if (p_voice->have_ly) { for (s = p_voice->sym; s; s = s->next) { struct lyrics *ly; float x, w; ly = s->ly; if (!ly) continue; /*fixme:should get the real width*/ x = s->x; if (ly->lyl[0]) { x -= ly->lyl[0]->s; w = ly->lyl[0]->w; } else { w = 10; } y = y_get(p_voice->staff, 1, x, w); if (top < y) top = y; y = y_get(p_voice->staff, 0, x, w); if (bot > y) bot = y; for (i = 0; i < MAXLY; i++) { if (!ly->lyl[i]) continue; if (i > nly) nly = i; if (lyvo_tb[voice].h[i] < ly->lyl[i]->f->size) lyvo_tb[voice].h[i] = ly->lyl[i]->f->size; } } } else { y = y_get(p_voice->staff, 1, 0, realwidth); if (top < y) top = y; y = y_get(p_voice->staff, 0, 0, realwidth); if (bot > y) bot = y; } lyst_tb[staff].top = top; lyst_tb[staff].bot = bot; if (nly < 0) continue; nly++; lyvo_tb[voice].nly = nly; if (p_voice->posit.voc != 0) above_tb[voice] = p_voice->posit.voc == SL_ABOVE; else if (p_voice->next /*fixme:%%staves:KO - find an other way..*/ && p_voice->next->staff == staff && p_voice->next->have_ly) above_tb[voice] = 1; if (above_tb[voice]) lyst_tb[staff].a = 1; else lyst_tb[staff].b = 1; } /* draw the lyrics under the staves */ i = 0; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { struct tblt_s *tblt; if (!p_voice->sym) continue; if (!p_voice->have_ly && !p_voice->tblts[0]) continue; voice = p_voice - voice_tb; if (above_tb[voice]) { rv_tb[i++] = voice; continue; } staff = p_voice->staff; if (lyvo_tb[voice].nly > 0) lyst_tb[staff].bot = draw_lyrics(p_voice, lyvo_tb[voice].nly, lyvo_tb[voice].h, lyst_tb[staff].bot, 1); sc = staff_tb[p_voice->staff].staffscale; for (nly = 0; nly < 2; nly++) { if ((tblt = p_voice->tblts[nly]) == NULL) break; // if (tblt->hu > 0) { // lyst_tb[staff].bot -= tblt->hu; // lyst_tb[staff].b = 1; // } if (tblt->pitch == 0) draw_tblt_w(p_voice, lyvo_tb[voice].nly, lyst_tb[staff].bot * sc - tblt->hu, tblt); else draw_tblt_p(p_voice, // lyst_tb[staff].bot, lyst_tb[staff].bot * sc - tblt->hu, tblt); if (tblt->hu > 0) { lyst_tb[staff].bot -= tblt->hu / sc; lyst_tb[staff].b = 1; } if (tblt->ha != 0) { lyst_tb[staff].top += tblt->ha / sc; lyst_tb[staff].a = 1; } } } /* draw the lyrics above the staff */ while (--i >= 0) { voice = rv_tb[i]; p_voice = &voice_tb[voice]; staff = p_voice->staff; lyst_tb[staff].top = draw_lyrics(p_voice, lyvo_tb[voice].nly, lyvo_tb[voice].h, lyst_tb[staff].top, -1); } /* set the max y offsets of all symbols */ for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { if (!p_voice->sym) continue; staff = p_voice->staff; if (lyst_tb[staff].a) { top = lyst_tb[staff].top + 2; for (s = p_voice->sym; s; s = s->next) { /*fixme: may have lyrics crossing a next symbol*/ if (s->ly) { /*fixme:should set the real width*/ y_set(staff, 1, s->x - 2, 10, top); } } } if (lyst_tb[staff].b) { bot = lyst_tb[staff].bot - 2; if (lyvo_tb[p_voice - voice_tb].nly > 0) { for (s = p_voice->sym; s; s = s->next) { if (s->ly) { /*fixme:should set the real width*/ y_set(staff, 0, s->x - 2, 10, bot); } } } else { y_set(staff, 0, 0, realwidth, bot); } } } } /* -- draw the symbols near the notes -- */ /* (the staves are not yet defined) */ /* order: * - beams * - decorations near the notes * - measure bar numbers * - n-plets * - decorations tied to the notes * - slurs * - guitar chords * - then remaining decorations * The buffer output is delayed until the definition of the staff system, * so, global variables must be saved (see music.c delayed_output()). */ void draw_sym_near(void) { struct VOICE_S *p_voice; struct SYMBOL *s, *g; int i, staff; /* calculate the beams but don't draw them (the staves are not yet defined) */ for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { struct BEAM bm; int first_note = 1; for (s = p_voice->sym; s; s = s->next) { for (g = s->extra; g; g = g->next) { if (g->type != NOTEREST) continue; if ((g->sflags & (S_BEAM_ST | S_BEAM_END)) == S_BEAM_ST) calculate_beam(&bm, g); } if (s->abc_type != ABC_T_NOTE) continue; if (((s->sflags & S_BEAM_ST) && !(s->sflags & S_BEAM_END)) || (first_note && !(s->sflags & S_BEAM_ST))) { first_note = 0; calculate_beam(&bm, s); } } } /* initialize the y offsets */ for (staff = 0; staff <= nstaff; staff++) { for (i = 0; i < YSTEP; i++) { staff_tb[staff].top[i] = 0; staff_tb[staff].bot[i] = 24; } } set_tie_room(); draw_deco_near(); /* set the min/max vertical offsets */ for (s = tsfirst; s; s = s->ts_next) { int y; if (s->flags & ABC_F_INVIS) continue; if (s->type == GRACE) { for (g = s->extra; g; g = g->next) { y_set(s->staff, 1, g->x - 2, 4, g->ymx + 1); y_set(s->staff, 0, g->x - 2, 4, g->ymn - 1); } continue; } if (s->type != MREST) { y_set(s->staff, 1, s->x - s->wl, s->wl + s->wr, s->ymx + 2); y_set(s->staff, 0, s->x - s->wl, s->wl + s->wr, s->ymn - 2); } else { y_set(s->staff, 1, s->x - 16, 32, s->ymx + 2); } if (s->abc_type != ABC_T_NOTE) continue; /* have room for the accidentals */ if (s->u.note.notes[s->nhd].acc) { y = s->y + 8; if (s->ymx < y) s->ymx = y; y_set(s->staff, 1, s->x, 0., y); } if (s->u.note.notes[0].acc) { y = s->y; if ((s->u.note.notes[0].acc & 0x07) == A_SH || s->u.note.notes[0].acc == A_NT) y -= 7; else y -= 5; if (s->ymn > y) s->ymn = y; y_set(s->staff, 0, s->x, 0., y); } } if (cfmt.measurenb >= 0) draw_measnb(); draw_deco_note(); for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { s = p_voice->sym; if (!s) continue; //color set_color(s->color); set_sscale(p_voice->staff); /* draw the tuplets near the notes */ for ( ; s; s = s->next) { if (s->sflags & S_IN_TUPLET) { for (g = s->extra; g; g = g->next) { if (g->type == TUPLET) { s = draw_tuplet(g, s); break; } } } } draw_all_slurs(p_voice); /* draw the tuplets over the slurs */ for (s = p_voice->sym; s; s = s->next) { if (s->sflags & S_IN_TUPLET) { for (g = s->extra ; g; g = g->next) { if (g->type == TUPLET) { s = draw_tuplet(g, s); break; } } } } } /* set the top and bottom for all symbols to be out of the staves */ { int top, bot; for (staff = 0; staff <= nstaff; staff++) { top = staff_tb[staff].topbar + 2; bot = staff_tb[staff].botbar - 2; for (i = 0; i < YSTEP; i++) { if (top > staff_tb[staff].top[i]) staff_tb[staff].top[i] = (float) top; if (bot < staff_tb[staff].bot[i]) staff_tb[staff].bot[i] = (float) bot; } } } set_color(0); // draw_deco_note(); draw_deco_staff(); set_sscale(-1); /* restore the scale parameters */ draw_all_lyrics(); } /* -- draw the name/subname of the voices -- */ static void draw_vname(float indent) { struct VOICE_S *p_voice; int n, staff; struct { int nl; char *v[8]; } staff_d[MAXSTAFF], *staff_p; char *p, *q; float y; for (staff = cursys->nstaff; staff >= 0; staff--) { if (!cursys->staff[staff].empty) break; } if (staff < 0) return; memset(staff_d, 0, sizeof staff_d); n = 0; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { if (!p_voice->sym) continue; staff = cursys->voice[p_voice - voice_tb].staff; if (cursys->staff[staff].empty) continue; if (p_voice->new_name) { p_voice->new_name = 0; p = p_voice->nm; } else { p = p_voice->snm; } if (!p) continue; if (cursys->staff[staff].flags & CLOSE_BRACE2) { while (!(cursys->staff[staff].flags & OPEN_BRACE2)) staff--; } else if (cursys->staff[staff].flags & CLOSE_BRACE) { while (!(cursys->staff[staff].flags & OPEN_BRACE)) staff--; } staff_p = &staff_d[staff]; for (;;) { staff_p->v[staff_p->nl++] = p; p = strstr(p, "\\n"); if (!p || staff_p->nl >= MAXSTAFF) break; p += 2; } n++; } if (n == 0) return; str_font(VOICEFONT); indent = -indent * .5; /* center */ for (staff = nstaff; staff >= 0; staff--) { staff_p = &staff_d[staff]; if (staff_p->nl == 0) continue; y = staff_tb[staff].y + staff_tb[staff].topbar * .5 * staff_tb[staff].staffscale + 9 * (staff_p->nl - 1) - cfmt.font_tb[VOICEFONT].size * .3; n = staff; if (cursys->staff[staff].flags & OPEN_BRACE2) { while (!(cursys->staff[n].flags & CLOSE_BRACE2)) n++; } else if (cursys->staff[staff].flags & OPEN_BRACE) { while (!(cursys->staff[n].flags & CLOSE_BRACE)) n++; } if (n != staff) y -= (staff_tb[staff].y - staff_tb[n].y) * .5; for (n = 0; n < staff_p->nl; n++) { p = staff_p->v[n]; q = strstr(p, "\\n"); if (q) *q = '\0'; a2b("%.1f %.1f M ", indent, y); put_str(p, A_CENTER); y -= 18.; if (q) *q = '\\'; } } } /* -- set the y offset of the staves and return the whole height -- */ static float set_staff(void) { struct SYSTEM *sy; struct SYMBOL *s; int i, staff, prev_staff; float y, staffsep, dy, maxsep, mbot, v; char empty[MAXSTAFF]; /* search the empty staves in each parts */ memset(empty, 1, sizeof empty); for (staff = 0; staff <= nstaff; staff++) staff_tb[staff].empty = 0; sy = cursys; for (staff = 0; staff <= nstaff; staff++) { if (!sy->staff[staff].empty) empty[staff] = 0; } for (s = tsfirst; s; s = s->ts_next) { if (!(s->sflags & S_NEW_SY)) continue; sy = sy->next; for (staff = 0; staff <= nstaff; staff++) { if (!sy->staff[staff].empty) empty[staff] = 0; } } /* output the scale of the voices * and flag as non empty the staves with tablatures */ { struct VOICE_S *p_voice; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { if (p_voice->scale != 1) a2b("/scvo%d{gsave %.2f dup scale}!\n", (int) (p_voice - voice_tb), p_voice->scale); if (p_voice->tblts[0]) empty[p_voice->staff] = 0; } } /* set the vertical offset of the 1st staff */ for (staff = 0; staff <= nstaff; staff++) { if (!empty[staff]) break; staff_tb[staff].empty = 1; } y = 0; if (staff > nstaff) { staff--; /* one staff, empty */ } else { for (i = 0; i < YSTEP; i++) { v = staff_tb[staff].top[i]; if (y < v) y = v; } } /* draw the parts and tempo indications if any */ y += draw_partempo(staff, y); if (empty[staff]) return y; y *= staff_tb[staff].staffscale; staffsep = cfmt.staffsep * 0.5 + staff_tb[staff].topbar * staff_tb[staff].staffscale; if (y < staffsep) y = staffsep; staff_tb[staff].y = -y; /* set the offset of the other staves */ prev_staff = staff; for (staff++; staff <= nstaff; staff++) { if (empty[staff]) { staff_tb[staff].empty = 1; continue; } if (sy->staff[prev_staff].sep != 0) staffsep = sy->staff[prev_staff].sep; else staffsep = cfmt.sysstaffsep; if (sy->staff[prev_staff].maxsep != 0) maxsep = sy->staff[prev_staff].maxsep; else maxsep = cfmt.maxsysstaffsep; dy = 0; if (staff_tb[staff].staffscale == staff_tb[prev_staff].staffscale) { for (i = 0; i < YSTEP; i++) { v = staff_tb[staff].top[i] - staff_tb[prev_staff].bot[i]; if (dy < v) dy = v; } dy *= staff_tb[staff].staffscale; } else { for (i = 0; i < YSTEP; i++) { v = staff_tb[staff].top[i] * staff_tb[staff].staffscale - staff_tb[prev_staff].bot[i] * staff_tb[prev_staff].staffscale; if (dy < v) dy = v; } } staffsep += staff_tb[staff].topbar * staff_tb[staff].staffscale; if (dy < staffsep) dy = staffsep; maxsep += staff_tb[staff].topbar * staff_tb[staff].staffscale; if (dy > maxsep) dy = maxsep; y += dy; staff_tb[staff].y = -y; prev_staff = staff; } mbot = 0; for (i = 0; i < YSTEP; i++) { v = staff_tb[prev_staff].bot[i]; if (mbot > v) mbot = v; } mbot *= staff_tb[prev_staff].staffscale; /* output the staff offsets */ for (staff = nstaff; staff >= 0; staff--) { dy = staff_tb[staff].y; if (staff_tb[staff].staffscale != 1 && staff_tb[staff].staffscale != 0) { a2b("/scst%d{gsave 0 %.2f T %.2f dup scale}!\n", staff, dy, staff_tb[staff].staffscale); a2b("/y%d{}!\n", staff); } else { a2b("/y%d{%.1f add}!\n", staff, dy); } a2b("/yns%d{%.1f add}!\n", staff, dy); } if (mbot == 0) { for (staff = nstaff; staff >= 0; staff--) { if (!empty[staff]) break; } if (staff < 0) /* no symbol in this system ! */ return y; } dy = -mbot; staffsep = cfmt.staffsep * 0.5; if (dy < staffsep) dy = staffsep; maxsep = cfmt.maxstaffsep * 0.5; if (dy > maxsep) dy = maxsep; /* return the whole staff system height */ return y + dy; } /* -- set the bottom and height of the measure bars -- */ static void bar_set(float *bar_bot, float *bar_height, float *xstaff) { int staff; float dy, staffscale, top, bot; dy = 0; for (staff = 0; staff <= cursys->nstaff; staff++) { // if (cursys->staff[staff].empty) { if (xstaff[staff] < 0) { bar_bot[staff] = bar_height[staff] = 0; continue; } staffscale = staff_tb[staff].staffscale; top = staff_tb[staff].topbar * staffscale; bot = staff_tb[staff].botbar * staffscale; if (dy == 0) dy = staff_tb[staff].y + top; bar_bot[staff] = staff_tb[staff].y + bot; bar_height[staff] = dy - bar_bot[staff]; if (cursys->staff[staff].flags & STOP_BAR) dy = 0; else dy = bar_bot[staff]; } } /* -- draw the staff systems and the measure bars -- */ float draw_systems(float indent) { struct SYMBOL *s, *s2; int staff, bar_force; float xstaff[MAXSTAFF], bar_bot[MAXSTAFF], bar_height[MAXSTAFF]; float staves_bar, x, x2, line_height; line_height = set_staff(); draw_vname(indent); /* draw the staff, skipping the staff breaks */ for (staff = 0; staff <= nstaff; staff++) xstaff[staff] = cursys->staff[staff].empty ? -1 : 0; bar_set(bar_bot, bar_height, xstaff); draw_lstaff(0); bar_force = 0; for (s = tsfirst; s; s = s->ts_next) { if (bar_force && s->time != bar_force) { bar_force = 0; for (staff = 0; staff <= nstaff; staff++) { if (cursys->staff[staff].empty) xstaff[staff] = -1; } bar_set(bar_bot, bar_height, xstaff); } if (s->sflags & S_NEW_SY) { staves_bar = 0; for (s2 = s; s2; s2 = s2->ts_next) { if (s2->time != s->time) break; if (s2->type == BAR) { staves_bar = s2->x; break; } } cursys = cursys->next; for (staff = 0; staff <= nstaff; staff++) { x = xstaff[staff]; if (x < 0) { // no staff yet if (!cursys->staff[staff].empty) { if (staves_bar != 0) xstaff[staff] = staves_bar; else xstaff[staff] = s->x - s->wl - 2; } continue; } if (!cursys->staff[staff].empty) // if not staff stop continue; if (staves_bar != 0) { x2 = staves_bar; bar_force = s->time; } else { x2 = s->x - s->wl - 2; xstaff[staff] = -1; } draw_staff(staff, x, x2); } bar_set(bar_bot, bar_height, xstaff); } staff = s->staff; switch (s->type) { case BAR: if ((s->sflags & S_SECOND) // || cursys->staff[staff].empty) || xstaff[staff] < 0) s->flags |= ABC_F_INVIS; if (s->flags & ABC_F_INVIS) break; draw_bar(s, bar_bot[staff], bar_height[staff]); if (annotate) anno_out(s, 'B'); break; case STBRK: if (cursys->voice[s->voice].range == 0) { if (s->xmx > .5 CM) { int i, nvoice; /* draw the left system if stbrk in all voices */ nvoice = 0; for (i = 0; i < MAXVOICE; i++) { if (cursys->voice[i].range > 0) nvoice++; } for (s2 = s->ts_next; s2; s2 = s2->ts_next) { if (s2->type != STBRK) break; nvoice--; } if (nvoice == 0) draw_lstaff(s->x); } } s2 = s->prev; if (!s2) break; if (s2->type == BAR) x2 = s2->x; else x2 = s->x - s->xmx; staff = s->staff; x = xstaff[staff]; if (x >= 0) { if (x >= x2) continue; draw_staff(staff, x, x2); xstaff[staff] = s->x; } break; default: //fixme:does not work for "%%staves K: M: $" */ //removed for K:/M: in empty staves // if (cursys->staff[staff].empty) // s->flags |= ABC_F_INVIS; break; } } // draw the end of the staves for (staff = 0; staff <= nstaff; staff++) { if (bar_force && cursys->staff[staff].empty) continue; if ((x = xstaff[staff]) < 0) continue; draw_staff(staff, x, realwidth); } set_sscale(-1); return line_height; } /* -- output PostScript sequences and set the staff and voice colors -- */ void output_ps(struct SYMBOL *s, int color) { struct SYMBOL *g, *g2; g = s->extra; g2 = NULL; for ( ; g; g = g->next) { if (g->type == FMTCHG) { switch (g->aux) { case PSSEQ: // case SVGSEQ: // if (g->aux == SVGSEQ) // a2b("%%svg %s\n", g->text); // else a2b("%s\n", g->text); if (!g2) s->extra = g->next; else g2->next = g->next; continue; } } g2 = g; } } /* -- draw remaining symbols when the staves are defined -- */ static void draw_symbols(struct VOICE_S *p_voice) { struct BEAM bm; struct SYMBOL *s; float x, y; int staff, first_note; #if 0 /* output the PostScript code at start of line */ for (s = p_voice->sym; s; s = s->next) { if (s->extra) output_ps(s, 1); switch (s->type) { case CLEF: case KEYSIG: case TIMESIG: case BAR: continue; /* skip the symbols added by init_music_line() */ } break; } #endif bm.s2 = NULL; first_note = 1; for (s = p_voice->sym; s; s = s->next) { if (s->extra) output_ps(s, 1); if (s->flags & ABC_F_INVIS) { switch (s->type) { case KEYSIG: memcpy(&p_voice->key, &s->u.key, sizeof p_voice->key); default: continue; case NOTEREST: case GRACE: break; } } set_color(s->color); x = s->x; switch (s->type) { case NOTEREST: set_scale(s); if (s->abc_type == ABC_T_NOTE) { if ((s->sflags & (S_BEAM_ST | S_BEAM_END)) == S_BEAM_ST || (first_note && !(s->sflags & S_BEAM_ST))) { first_note = 0; if (calculate_beam(&bm, s)) { if (annotate) anno_out(s, 'b'); draw_beams(&bm); } } draw_note(x, s, bm.s2 == NULL); if (annotate) anno_out(s, 'N'); if (s == bm.s2) bm.s2 = NULL; if (annotate && (s->sflags & (S_BEAM_ST | S_BEAM_END)) == S_BEAM_END) anno_out(s, 'e'); break; } draw_rest(s); if (annotate) anno_out(s, 'R'); break; case BAR: break; /* drawn in draw_systems */ case CLEF: staff = s->staff; if (s->sflags & S_SECOND) /* || p_voice->staff != staff) */ break; /* only one clef per staff */ if ((s->flags & ABC_F_INVIS) || staff_tb[staff].empty) break; set_color(0); set_sscale(staff); y = staff_tb[staff].y; x -= 10; /* clef shift - see set_width() */ putxy(x, y + s->y); if (s->u.clef.name) a2b("%s\n", s->u.clef.name); else a2b("%c%cclef\n", s->aux ? 's' : ' ', "tcbp"[(unsigned) s->u.clef.type]); if (s->u.clef.octave != 0) { /*fixme:break the compatibility and avoid strange numbers*/ if (s->u.clef.octave > 0) y += s->ymx - 9; else y += s->ymn; putxy(x - 2, y); a2b("oct\n"); } if (annotate) anno_out(s, 'c'); break; case TIMESIG: //fixme: set staff color memcpy(&p_voice->meter, &s->u.meter, sizeof p_voice->meter); if ((s->sflags & S_SECOND) || staff_tb[s->staff].empty) break; if (cfmt.alignbars && s->staff != 0) break; set_color(0); set_sscale(s->staff); draw_timesig(x, s); if (annotate) anno_out(s, 'M'); break; case KEYSIG: //fixme: set staff color memcpy(&p_voice->key, &s->u.key, sizeof p_voice->key); if ((s->sflags & S_SECOND) || staff_tb[s->staff].empty) break; set_color(0); set_sscale(s->staff); draw_keysig(p_voice, x, s); if (annotate) anno_out(s, 'K'); break; case MREST: set_scale(s); putxy(x, staff_tb[s->staff].y + 12); a2b("mrest(%d)/Times-Bold 15 selectfont ", s->u.bar.len); putxy(x, staff_tb[s->staff].y + 28); a2b("M showc\n"); if (annotate) anno_out(s, 'Z'); break; case GRACE: set_scale(s); draw_gracenotes(s); break; case SPACE: case STBRK: case FMTCHG: break; /* nothing */ case CUSTOS: set_scale(s); s->sflags |= ABC_F_STEMLESS; draw_note(x, s, 0); break; default: bug("Symbol not drawn", 1); } } set_scale(p_voice->sym); draw_all_ties(p_voice); set_sscale(-1); set_color(0); } /* -- draw all symbols -- */ void draw_all_symb(void) { struct VOICE_S *p_voice; struct SYMBOL *s; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { #if 1 /* pb about rest display when "%%staffnonote 0" fixed in draw_rest */ if (p_voice->sym) #else if (p_voice->sym && !staff_tb[p_voice->staff].empty) #endif draw_symbols(p_voice); } // update the clefs for (s = tsfirst; s; s = s->ts_next) { if (s->type == CLEF) staff_tb[s->staff].s_clef = s; } } /* -- output a floating value, and x and y according to the current scale -- */ void putf(float v) { a2b("%.1f ", v); } void putx(float x) { putf(x / cur_scale); } void puty(float y) { putf(scale_voice ? y / cur_scale : /* scaled voice */ y - cur_trans); /* scaled staff */ } void putxy(float x, float y) { if (scale_voice) a2b("%.1f %.1f ", x / cur_scale, y / cur_scale); /* scaled voice */ else a2b("%.1f %.1f ", x / cur_scale, y - cur_trans); /* scaled staff */ } /* -- set the voice or staff scale -- */ void set_scale(struct SYMBOL *s) { float scale, trans; scale = voice_tb[s->voice].scale; if (scale == 1) { set_sscale(s->staff); return; } /*fixme: KO when both staff and voice are scaled */ trans = 0; scale_voice = 1; if (scale == cur_scale && trans == cur_trans) return; if (cur_scale != 1) a2b("grestore "); cur_scale = scale; cur_trans = trans; if (scale != 1) a2b("scvo%d ", s->voice); } /* -- set the staff scale (only) -- */ void set_sscale(int staff) { float scale, trans; scale_voice = 0; if (staff != cur_staff && cur_scale != 1) cur_scale = 0; if (staff >= 0) scale = staff_tb[staff].staffscale; else scale = 1; if (staff >= 0 && scale != 1) trans = staff_tb[staff].y; else trans = 0; if (scale == cur_scale && trans == cur_trans) return; if (cur_scale != 1) a2b("grestore "); cur_scale = scale; cur_trans = trans; if (scale != 1) { a2b("scst%d ", staff); cur_staff = staff; } } /* -- set the tie directions for one voice -- */ static void set_tie_dir(struct SYMBOL *sym) { struct SYMBOL *s; int i, ntie, dir, sec, pit, ti; for (s = sym; s; s = s->next) { if (!(s->sflags & S_TI1)) continue; /* if other voice, set the ties in opposite direction */ if (s->multi != 0) { /* struct SYMBOL *s2; s2 = s->ts_next; if (s2->time == s->time && s2->staff == s->staff) { */ dir = s->multi > 0 ? SL_ABOVE : SL_BELOW; for (i = 0; i <= s->nhd; i++) { ti = s->u.note.notes[i].ti1; if (!((ti & 0x07) == SL_AUTO)) continue; s->u.note.notes[i].ti1 = (ti & SL_DOTTED) | dir; } continue; /* } */ } /* if one note, set the direction according to the stem */ sec = ntie = 0; pit = 128; for (i = 0; i <= s->nhd; i++) { if (s->u.note.notes[i].ti1) { ntie++; if (pit < 128 && s->u.note.notes[i].pit <= pit + 1) sec++; pit = s->u.note.notes[i].pit; } } if (ntie <= 1) { dir = s->stem < 0 ? SL_ABOVE : SL_BELOW; for (i = 0; i <= s->nhd; i++) { ti = s->u.note.notes[i].ti1; if (ti != 0) { if ((ti & 0x07) == SL_AUTO) s->u.note.notes[i].ti1 = (ti & SL_DOTTED) | dir; break; } } continue; } if (sec == 0) { if (ntie & 1) { /* in chords with an odd number of notes, the outer noteheads are paired off * center notes are tied according to their position in relation to the * center line */ ntie /= 2; dir = SL_BELOW; for (i = 0; i <= s->nhd; i++) { ti = s->u.note.notes[i].ti1; if (ti == 0) continue; if (ntie == 0) { /* central tie */ if (s->pits[i] >= 22) dir = SL_ABOVE; } if ((ti & 0x07) == SL_AUTO) s->u.note.notes[i].ti1 = (ti & SL_DOTTED) | dir; if (ntie-- == 0) dir = SL_ABOVE; } continue; } /* even number of notes, ties divided in opposite directions */ ntie /= 2; dir = SL_BELOW; for (i = 0; i <= s->nhd; i++) { ti = s->u.note.notes[i].ti1; if (ti == 0) continue; if ((ti & 0x07) == SL_AUTO) s->u.note.notes[i].ti1 = (ti & SL_DOTTED) | dir; if (--ntie == 0) dir = SL_ABOVE; } continue; } /*fixme: treat more than one second */ /* if (nsec == 1) { */ /* When a chord contains the interval of a second, tie those two notes in * opposition; then fill in the remaining notes of the chord accordingly */ pit = 128; for (i = 0; i <= s->nhd; i++) { if (s->u.note.notes[i].ti1) { if (pit < 128 && s->u.note.notes[i].pit <= pit + 1) { ntie = i; break; } pit = s->u.note.notes[i].pit; } } dir = SL_BELOW; for (i = 0; i <= s->nhd; i++) { ti = s->u.note.notes[i].ti1; if (ti == 0) continue; if (ntie == i) dir = SL_ABOVE; if ((ti & 0x07) == SL_AUTO) s->u.note.notes[i].ti1 = (ti & SL_DOTTED) | dir; } /*fixme.. continue; } ..*/ /* if a chord contains more than one pair of seconds, the pair farthest * from the center line receives the ties drawn in opposition */ } } /* -- have room for the ties out of the staves -- */ static void set_tie_room(void) { struct VOICE_S *p_voice; struct SYMBOL *s, *s2; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { s = p_voice->sym; if (!s) continue; s = s->next; if (!s) continue; set_tie_dir(s); for ( ; s; s = s->next) { float dx, y, dy; if (!(s->sflags & S_TI1)) continue; if (s->pits[0] < 20 && (s->u.note.notes[0].ti1 & 0x07) == SL_BELOW) ; else if (s->pits[s->nhd] > 24 && (s->u.note.notes[s->nhd].ti1 & 0x07) == SL_ABOVE) ; else continue; s2 = s->next; while (s2 && s2->abc_type != ABC_T_NOTE) s2 = s2->next; if (s2) { if (s2->staff != s->staff) continue; dx = s2->x - s->x - 10; } else { dx = realwidth - s->x - 10; } if (dx < 100) dy = 9; else if (dx < 300) dy = 12; else dy = 16; if (s->pits[s->nhd] > 24) { y = 3 * (s->pits[s->nhd] - 18) + dy; if (s->ymx < y) s->ymx = y; if (s2 && s2->ymx < y) s2->ymx = y; y_set(s->staff, 1, s->x + 5, dx, y); } if (s->pits[0] < 20) { y = 3 * (s->pits[0] - 18) - dy; if (s->ymn > y) s->ymn = y; if (s2 && s2->ymn > y) s2->ymn = y; y_set(s->staff, 0, s->x + 5, dx, y); } } } } �����������������������������������������������������abcm2ps-8.14.11/flute.fmt���������������������������������������������������������������������������0000664�0000000�0000000�00000020323�13762665467�0015072�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������% format file for flute tablatures - this file is part of abcm2ps % % Tin whistle by Guido Gonzato % Galoubet and Pipe-Tabor by Michel Bellon % % tw_ : tin whistle % gbl_ : galoubet (3 holes pipe from Provence) % gblc_ : galoubet (digit only ) % pt_ : pipe and tabor (3 holes pipe from England) % xi_ : txistu (3 holes pipe from Euskadi) % % you may try it running: % abcm2ps sample -e1 -F flute.fmt -T1 -T3 beginps % -- whistle drawing functions -- % % start of line % string tw_head /tw_head{/Helvetica 8 selectfont 0 15 M 90 rotate(WHISTLE)show -90 rotate /Helvetica-Bold 36 selectfont 0 15 M show .5 SLW newpath}! /tw_under{ -2.5 6.5 M 2.5 -2.5 RL 2.5 2.5 RL -2.5 -2.5 RM 0 6 RL stroke}! /tw_over{ -2.5 7.5 M 2.5 2.5 RL 2.5 -2.5 RL -2.5 2.5 RM 0 -6 RL stroke}! /tw_p{-3 0 M 6 0 RL -3 -3 RM 0 6 RL stroke}! /tw_pp{-3 0 M 6 0 RL -1.5 -1.5 RM 0 3 RL -3 0 RM 0 -3 RL stroke}! % holes /tw_0{0 0 3 0 360 arc stroke}! /tw_1{0 0 3 90 270 arc fill 0 0 3 270 90 arc stroke}! /tw_2{0 0 3 0 360 arc fill}! % hole table - from C to B /tw_holes [8#222222 8#222221 8#222220 8#222210 8#222200 8#222000 8#221000 8#220000 8#210000 8#200000 8#022000 8#000000] def % draw a note % octave pitch x tw_note /tw_note{gsave 7 T % oct pit cvi % (if microtone) dup 12 idiv % oct pit pit/12 dup 0 eq{pop} {1 eq{tw_p} {tw_pp}ifelse }ifelse dup 12 eq{ pop 8#022222 % (special case) }{ 12 mod tw_holes exch get }ifelse % oct holes 0 1 5{pop 0 7 T dup 8#3 and dup 0 eq{pop tw_0} {1 eq{tw_1} {tw_2}ifelse }ifelse -3 bitshift }for pop % oct dup 0 gt{pop tw_over} {0 lt{tw_under}if }ifelse grestore }! % -- galoubet en Do -- % % début de ligne % string gbl_head /gbl_tete{/Helvetica 7 selectfont 0 10 M 90 rotate(GALOUBET)show -90 rotate /Helvetica-Bold 24 selectfont 0 10 M show .5 SLW newpath}! /gbl_c{/Helvetica-utf8 14 selectfont -5 10 M show .5 SLW newpath}! /gbl_doux{-3.5 0 M 7 0 RL stroke}! /gbl_fort{-3.5 0 M 7 0 RL -3.5 -3.5 RM 0 7 RL stroke}! /gbl_tres_fort{-5 0 M 10 0 RL -2.5 -3 RM 0 6 RL -5 0 RM 0 -6 RL stroke}! % trous /gbl_0{0 0 3.5 0 360 arc stroke}! /gbl_1{0 0 3.5 90 270 arc fill 0 0 3.5 270 90 arc stroke}! /gbl_2{0 0 3.5 0 360 arc fill}! % table des trous - de E à d' /gbl_trous [8#22210 % F.. B 8#22200 8#22100 8#22000 8#21000 8#20000 8#10000 8#00000 % c .. e 8#22201 8#22101 8#22001 8#21001 8#20001 % f ..b 8#22202 8#00002 8#22002 8#21002 8#20002 8#10202 8#00202 % c' .. d' 8#22203 8#22103 8#22003] def % table des doigtés chiffrés /gbl_dgt [(3Þ) (3) (2Þ) (2) (1Þ) (1) (Þ) (0) (3) (2Þ) (2) (1Þ) (1) (3) (0) (2) (1Þ) (1) (ÞF) (F) (3) (2Þ) (2)] def % dessin d'une note % octave pitch x gbl_note /gbl_note{gsave 8 T % oct pit cvi % (if microtone) 4 sub % oct pit/'E' exch % pit/E oct 0 eq % (1ere octave seulement) 1 index 0 ge and % (pitch = 0 .. 22 = E .. d') 1 index 23 lt and{ dup gbl_dgt exch get 0 30 T gbl_c 0 -35 T gbl_trous exch get dup 8#3 and dup 0 eq{pop gbl_doux} {dup 1 eq{pop} {2 eq{gbl_fort} {gbl_tres_fort}ifelse }ifelse }ifelse -3 bitshift 0 9 T dup 8#3 and 1 eq{gbl_1}if -3 bitshift 0 1 2{pop 0 9 T dup 8#3 and dup 0 eq{pop gbl_0} {1 eq{gbl_1} {gbl_2}ifelse }ifelse -3 bitshift }for }if pop grestore }! % -- galoubet en Do Tablature chiffrée-- % % début de ligne % string gbl_head /gblc_tete{/Helvetica 7 selectfont 0 10 M 90 rotate(GALOUBET)show -90 rotate /Helvetica-Bold 24 selectfont 0 10 M show .5 SLW newpath}! /gblc_c{/Helvetica-utf8 18 selectfont -5 10 M show .5 SLW newpath}! /gblc_doux{-3.5 0 M 7 0 RL stroke}! /gblc_fort{-3.5 0 M 7 0 RL -3.5 -3.5 RM 0 7 RL stroke}! /gblc_tres_fort{-5 0 M 10 0 RL -2.5 -3 RM 0 6 RL -5 0 RM 0 -6 RL stroke}! % trous /gblc_0{0 0 3.5 0 360 arc stroke}! /gblc_1{0 0 3.5 90 270 arc fill 0 0 3.5 270 90 arc stroke}! /gblc_2{0 0 3.5 0 360 arc fill}! % table des trous - de E à d' /gblc_trous [8#22210 % F.. B 8#22200 8#22100 8#22000 8#21000 8#20000 8#10000 8#00000 % c .. e 8#22201 8#22101 8#22001 8#21001 8#20001 % f ..b 8#22202 8#00002 8#22002 8#21002 8#20002 8#10202 8#00202 % c' .. d' 8#22203 8#22103 8#22003] def % table des doigtés chiffrés /gblc_dgt [(3Þ) (3) (2Þ) (2) (1Þ) (1) (Þ) (0) (3) (2Þ) (2) (1Þ) (1) (3) (0) (2) (1Þ) (1) (ÞF) (F) (3) (2Þ) (2)] def % dessin d'une note % octave pitch x gblc_note /gblc_note{gsave 8 T % oct pit cvi % (if microtone) 4 sub % oct pit/'E' exch % pit/E oct 0 eq % (1ere octave seulement) 1 index 0 ge and % (pitch = 0 .. 22 = E .. d') 1 index 23 lt and{ dup gblc_dgt exch get 0 10 T gblc_c 0 0 T gblc_trous exch get dup 8#3 and dup 0 eq{pop gblc_doux} {dup 1 eq{pop} {2 eq{gblc_fort} {gblc_tres_fort}ifelse }ifelse }ifelse -3 bitshift % 0 9 T % dup 8#3 and 1 eq{gblc_1}if % -3 bitshift % 0 1 2{pop % 0 9 T % dup 8#3 and dup 0 eq{pop gblc_0} % {1 eq{gblc_1} % {gblc_2}ifelse % }ifelse % -3 bitshift % }for }if pop grestore }! % -- Pipe-Tabor en Do -- % % début de ligne % string pt_head /pt_tete{/Helvetica 7 selectfont 0 10 M 90 rotate(PIPE-TABOR)show -90 rotate /Helvetica-Bold 24 selectfont 0 10 M show .5 SLW newpath}! /pt_doux{-3.5 0 M 7 0 RL stroke}! /pt_fort{-3.5 0 M 7 0 RL -3.5 -3.5 RM 0 7 RL stroke}! /pt_tres_fort{-5 0 M 10 0 RL -2.5 -3 RM 0 6 RL -5 0 RM 0 -6 RL stroke}! % trous /pt_0{0 0 3.5 0 360 arc stroke}! /pt_1{0 0 3.5 90 270 arc fill 0 0 3.5 270 90 arc stroke}! /pt_2{0 0 3.5 0 360 arc fill}! % table des trous - de E à d' /pt_trous [8#22210 % F.. B 8#22200 8#22100 8#22000 8#20200 8#20000 8#00000 8#22211 % c .. e 8#22201 8#22101 8#22001 8#21001 8#20001 % f ..b 8#22202 8#22102 8#22002 8#21002 8#20202 8#00202 8#22002 % c' .. d' 8#22203 8#22103 8#00203] def % dessin d'une note % octave pitch x pt_note /pt_note{gsave 8 T % oct pit cvi % (if microtone) 11 sub % oct pit/'E' exch % pit/E oct 0 eq % (1ere octave seulement) 1 index 0 ge and % (pitch = 0 .. 22 = E .. d') 1 index 23 lt and{ pt_trous exch get dup 8#3 and dup 0 eq{pop pt_doux} {dup 1 eq{pop} {2 eq{pt_fort} {pt_tres_fort}ifelse }ifelse }ifelse -3 bitshift 0 9 T dup 8#3 and 1 eq{pt_1}if -3 bitshift 0 1 2{pop 0 9 T dup 8#3 and dup 0 eq{pop pt_0} {1 eq{pt_1} {pt_2}ifelse }ifelse -3 bitshift }for }if pop grestore }! % -- Galoubet en dièze ou Txistu -- % % début de ligne % string xi_head /xi_tete{/Helvetica 7 selectfont 0 10 M 90 rotate(TXISTU)show -90 rotate /Helvetica-Bold 24 selectfont 0 10 M show .5 SLW newpath}! /xi_doux{-3.5 0 M 7 0 RL stroke}! /xi_fort{-3.5 0 M 7 0 RL -3.5 -3.5 RM 0 7 RL stroke}! /xi_tres_fort{-5 0 M 10 0 RL -2.5 -3 RM 0 6 RL -5 0 RM 0 -6 RL stroke}! % trous /xi_0{0 0 3.5 0 360 arc stroke}! /xi_1{0 0 3.5 90 270 arc fill 0 0 3.5 270 90 arc stroke}! /xi_2{0 0 3.5 0 360 arc fill}! % table des trous - de E à d' /xi_trous [8#22210 % E.. _B 8#22200 8#22100 8#22000 8#20000 8#02200 8#00200 8#22210 % B .. ^d 8#22201 8#22101 8#22001 8#20001 8#02001 % e ..g 8#22202 8#22102 8#22002 8#20002 % c' .. d' 8#22203 8#00203 8#22213 8#22203 8#22103 8#22003] def % dessin d'une note % octave pitch xi tw_note /xi_note{gsave 8 T % oct pit cvi % (if microtone) 11 sub % oct pit/'E' exch % pit/E oct 0 eq % (1ere octave seulement) 1 index 0 ge and % (pitch = 0 .. 22 = E .. d') 1 index 23 lt and{ xi_trous exch get dup 8#3 and dup 0 eq{pop xi_doux} {dup 1 eq{pop} {2 eq{xi_fort} {xi_tres_fort}ifelse }ifelse }ifelse -3 bitshift 0 9 T dup 8#3 and 1 eq{xi_1}if -3 bitshift 0 1 2{pop 0 9 T dup 8#3 and dup 0 eq{pop xi_0} {1 eq{xi_1} {xi_2}ifelse }ifelse -3 bitshift }for }if pop grestore }! endps % all tin whistle transpositions tablature #1 pitch=D 35 0 63 tw_head tw_note tablature #2 pitch=C 35 0 63 tw_head tw_note tablature #3 pitch=Eb 60 0 63 tw_head tw_note %tablature #4 pitch=Bb, 60 0 63 tw_head tw_note %tablature #5 pitch=F, 35 0 63 tw_head tw_note %tablature #6 pitch=G, 35 0 63 tw_head tw_note %tablature #7 pitch=A, 35 0 63 tw_head tw_note % galoubet %tablature #3 pitch=C 30 0 60 gbl_tete gbl_note tablature #4 pitch=B,b 50 0 60 gbl_tete gbl_note tablature #5 pitch=G, 30 0 54 pt_tete pt_note tablature #6 pitch=C 30 0 46 gblc_tete gblc_note �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������abcm2ps-8.14.11/format.c����������������������������������������������������������������������������0000664�0000000�0000000�00000106645�13762665467�0014713�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Formatting functions. * * This file is part of abcm2ps. * * Copyright (C) 1998-2019 Jean-François Moine (http://moinejf.free.fr) * Adapted from abc2ps, Copyright (C) 1996-1998 Michael Methfessel * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. */ #include <stdlib.h> #include <string.h> #include <ctype.h> #include "abcm2ps.h" struct FORMAT cfmt; /* current format for output */ char *fontnames[MAXFONTS]; /* list of font names */ static char font_enc[MAXFONTS]; /* font encoding */ static char def_font_enc[MAXFONTS]; /* default font encoding */ static char used_font[MAXFONTS]; /* used fonts */ static float swfac_font[MAXFONTS]; /* width scale */ static int nfontnames; static float staffwidth; /* format table */ static struct format { char *name; void *v; char type; #define FORMAT_I 0 /* int */ #define FORMAT_R 1 /* float */ #define FORMAT_F 2 /* font spec */ #define FORMAT_U 3 /* float with unit */ #define FORMAT_B 4 /* boolean */ #define FORMAT_S 5 /* string */ char subtype; /* special cases - see code */ short lock; } format_tb[] = { {"abc2pscompat", &cfmt.abc2pscompat, FORMAT_B, 3}, {"alignbars", &cfmt.alignbars, FORMAT_I, 0}, {"aligncomposer", &cfmt.aligncomposer, FORMAT_I, 0}, {"annotationfont", &cfmt.font_tb[ANNOTATIONFONT], FORMAT_F, 0}, {"autoclef", &cfmt.autoclef, FORMAT_B, 0}, {"barsperstaff", &cfmt.barsperstaff, FORMAT_I, 0}, {"bgcolor", &cfmt.bgcolor, FORMAT_S, 0}, {"botmargin", &cfmt.botmargin, FORMAT_U, 1}, {"breaklimit", &cfmt.breaklimit, FORMAT_R, 3}, {"breakoneoln", &cfmt.breakoneoln, FORMAT_B, 0}, {"bstemdown", &cfmt.bstemdown, FORMAT_B, 0}, {"cancelkey", &cfmt.cancelkey, FORMAT_B, 0}, {"capo", &cfmt.capo, FORMAT_I, 0}, {"combinevoices", &cfmt.combinevoices, FORMAT_I, 0}, {"composerfont", &cfmt.font_tb[COMPOSERFONT], FORMAT_F, 0}, {"composerspace", &cfmt.composerspace, FORMAT_U, 0}, {"contbarnb", &cfmt.contbarnb, FORMAT_B, 0}, {"continueall", &cfmt.continueall, FORMAT_B, 0}, {"custos", &cfmt.custos, FORMAT_B, 0}, {"dateformat", &cfmt.dateformat, FORMAT_S, 0}, {"dblrepbar", &cfmt.dblrepbar, FORMAT_I, 2}, {"decoerr", &cfmt.decoerr, FORMAT_B, 0}, {"dynalign", &cfmt.dynalign, FORMAT_B, 0}, {"footer", &cfmt.footer, FORMAT_S, 0}, {"footerfont", &cfmt.font_tb[FOOTERFONT], FORMAT_F, 0}, {"flatbeams", &cfmt.flatbeams, FORMAT_B, 0}, {"flatbeamgracing", &cfmt.flatbeamgracing, FORMAT_B, 0}, {"gchordbox", &cfmt.gchordbox, FORMAT_B, 0}, {"gchordfont", &cfmt.font_tb[GCHORDFONT], FORMAT_F, 3}, {"graceslurs", &cfmt.graceslurs, FORMAT_B, 0}, {"graceword", &cfmt.graceword, FORMAT_B, 0}, {"gracespace", &cfmt.gracespace, FORMAT_I, 5}, {"gutter", &cfmt.gutter, FORMAT_U, 0}, {"header", &cfmt.header, FORMAT_S, 0}, {"headerfont", &cfmt.font_tb[HEADERFONT], FORMAT_F, 0}, {"historyfont", &cfmt.font_tb[HISTORYFONT], FORMAT_F, 0}, {"hyphencont", &cfmt.hyphencont, FORMAT_B, 0}, {"indent", &cfmt.indent, FORMAT_U, 0}, {"infofont", &cfmt.font_tb[INFOFONT], FORMAT_F, 0}, {"infoline", &cfmt.infoline, FORMAT_B, 0}, {"infospace", &cfmt.infospace, FORMAT_U, 0}, {"keywarn", &cfmt.keywarn, FORMAT_B, 0}, {"landscape", &cfmt.landscape, FORMAT_B, 0}, {"leftmargin", &cfmt.leftmargin, FORMAT_U, 1}, {"lineskipfac", &cfmt.lineskipfac, FORMAT_R, 0}, {"linewarn", &cfmt.linewarn, FORMAT_B, 0}, {"maxshrink", &cfmt.maxshrink, FORMAT_R, 2}, {"maxstaffsep", &cfmt.maxstaffsep, FORMAT_U, 0}, {"maxsysstaffsep", &cfmt.maxsysstaffsep, FORMAT_U, 0}, {"measurebox", &cfmt.measurebox, FORMAT_B, 0}, {"measurefirst", &cfmt.measurefirst, FORMAT_I, 0}, {"measurefont", &cfmt.font_tb[MEASUREFONT], FORMAT_F, 2}, {"measurenb", &cfmt.measurenb, FORMAT_I, 0}, {"musicfont", &cfmt.musicfont, FORMAT_S, 1}, {"musicspace", &cfmt.musicspace, FORMAT_U, 0}, {"notespacingfactor", &cfmt.notespacingfactor, FORMAT_R, 1}, {"oneperpage", &cfmt.oneperpage, FORMAT_B, 0}, {"pageheight", &cfmt.pageheight, FORMAT_U, 1}, {"pagewidth", &cfmt.pagewidth, FORMAT_U, 1}, {"pagescale", &cfmt.scale, FORMAT_R, 0}, #ifdef HAVE_PANGO {"pango", &cfmt.pango, FORMAT_B, 2}, #endif {"parskipfac", &cfmt.parskipfac, FORMAT_R, 0}, {"partsbox", &cfmt.partsbox, FORMAT_B, 0}, {"partsfont", &cfmt.font_tb[PARTSFONT], FORMAT_F, 1}, {"partsspace", &cfmt.partsspace, FORMAT_U, 0}, {"pdfmark", &cfmt.pdfmark, FORMAT_I, 0}, {"rbdbstop", &cfmt.rbdbstop, FORMAT_B, 0}, {"rbmax", &cfmt.rbmax, FORMAT_I, 0}, {"rbmin", &cfmt.rbmin, FORMAT_I, 0}, {"repeatfont", &cfmt.font_tb[REPEATFONT], FORMAT_F, 0}, {"rightmargin", &cfmt.rightmargin, FORMAT_U, 1}, // {"scale", &cfmt.scale, FORMAT_R, 0}, {"setdefl", &cfmt.setdefl, FORMAT_B, 0}, // {"shifthnote", &cfmt.shiftunison, FORMAT_B, 0}, /*to remove*/ {"shiftunison", &cfmt.shiftunison, FORMAT_I, 0}, {"slurheight", &cfmt.slurheight, FORMAT_R, 0}, {"splittune", &cfmt.splittune, FORMAT_I, 1}, {"squarebreve", &cfmt.squarebreve, FORMAT_B, 0}, {"staffnonote", &cfmt.staffnonote, FORMAT_I, 0}, {"staffsep", &cfmt.staffsep, FORMAT_U, 0}, {"staffwidth", &staffwidth, FORMAT_U, 2}, {"stemheight", &cfmt.stemheight, FORMAT_R, 0}, {"straightflags", &cfmt.straightflags, FORMAT_B, 0}, {"stretchlast", &cfmt.stretchlast, FORMAT_R, 2}, {"stretchstaff", &cfmt.stretchstaff, FORMAT_B, 0}, {"subtitlefont", &cfmt.font_tb[SUBTITLEFONT], FORMAT_F, 0}, {"subtitlespace", &cfmt.subtitlespace, FORMAT_U, 0}, {"sysstaffsep", &cfmt.sysstaffsep, FORMAT_U, 0}, {"tempofont", &cfmt.font_tb[TEMPOFONT], FORMAT_F, 0}, {"textfont", &cfmt.font_tb[TEXTFONT], FORMAT_F, 0}, {"textoption", &cfmt.textoption, FORMAT_I, 4}, {"textspace", &cfmt.textspace, FORMAT_U, 0}, {"tieheight", &cfmt.tieheight, FORMAT_R, 0}, {"titlecaps", &cfmt.titlecaps, FORMAT_B, 0}, {"titlefont", &cfmt.font_tb[TITLEFONT], FORMAT_F, 0}, {"titleformat", &cfmt.titleformat, FORMAT_S, 0}, {"titleleft", &cfmt.titleleft, FORMAT_B, 0}, {"titlespace", &cfmt.titlespace, FORMAT_U, 0}, {"titletrim", &cfmt.titletrim, FORMAT_I, 0}, {"timewarn", &cfmt.timewarn, FORMAT_B, 0}, {"topmargin", &cfmt.topmargin, FORMAT_U, 1}, {"topspace", &cfmt.topspace, FORMAT_U, 0}, {"tuplets", &cfmt.tuplets, FORMAT_I, 3}, {"vocalfont", &cfmt.font_tb[VOCALFONT], FORMAT_F, 0}, {"vocalspace", &cfmt.vocalspace, FORMAT_U, 0}, {"voicefont", &cfmt.font_tb[VOICEFONT], FORMAT_F, 0}, {"wordsfont", &cfmt.font_tb[WORDSFONT], FORMAT_F, 0}, {"wordsspace", &cfmt.wordsspace, FORMAT_U, 0}, {"writefields", &cfmt.fields, FORMAT_B, 1}, {0, 0, 0, 0} /* end of table */ }; static const char helvetica[] = "Helvetica"; static const char times[] = "Times-Roman"; static const char times_bold[] = "Times-Bold"; static const char times_italic[] = "Times-Italic"; static const char sans[] = "sans-serif"; static const char serif[] = "serif"; static const char serif_italic[] = "serif-Italic"; static const char serif_bold[] = "serif-Bold"; /* -- search a font and add it if not yet defined -- */ static int get_font(const char *fname, int encoding) { int fnum; /* get or set the default encoding */ for (fnum = nfontnames; --fnum >= 0; ) if (strcmp(fname, fontnames[fnum]) == 0) { if (encoding < 0) encoding = def_font_enc[fnum]; if (encoding == font_enc[fnum]) return fnum; /* font found */ break; } while (--fnum >= 0) { if (strcmp(fname, fontnames[fnum]) == 0 && encoding == font_enc[fnum]) return fnum; } /* add the font */ if (nfontnames >= MAXFONTS) { error(1, NULL, "Too many fonts"); return 0; } if (epsf <= 1 && !svg) { if (file_initialized > 0) error(1, NULL, "Cannot have a new font when the output file is opened"); if (strchr(fname, ' ') != NULL) { error(1, NULL, "PostScript fonts cannot have names with spaces"); return 0; } } fnum = nfontnames++; fontnames[fnum] = strdup(fname); if (encoding < 0) encoding = 0; font_enc[fnum] = encoding; return fnum; } /* -- set a dynamic font -- */ static int dfont_set(struct FONTSPEC *f) { int i; for (i = FONT_DYN; i < cfmt.ndfont; i++) { if (cfmt.font_tb[i].fnum == f->fnum && cfmt.font_tb[i].size == f->size) return i; } if (i >= FONT_MAX - 1) { error(1, NULL, "Too many dynamic fonts"); return FONT_MAX - 1; } memcpy(&cfmt.font_tb[i], f, sizeof cfmt.font_tb[0]); cfmt.ndfont = i + 1; return i; } /* -- define a font -- */ static void fontspec(struct FONTSPEC *f, const char *name, int encoding, float size) { if (name) f->fnum = get_font(name, encoding); else name = fontnames[f->fnum]; f->size = size; f->swfac = size; if (swfac_font[f->fnum] != 0) { f->swfac *= swfac_font[f->fnum]; } else if (strncmp(name, times, 5) == 0 || strncmp(name, serif, 5) == 0) { if (strcmp(name, times_bold) == 0 || strcmp(name, serif_bold) == 0) f->swfac *= 1.05; } else if (strcmp(name, "Helvetica-Bold") == 0) { f->swfac *= 1.15; } else if (strncmp(name, helvetica, 9) == 0 || strncmp(name, "Palatino", 8) == 0) { f->swfac *= 1.1; } else if (strncmp(name, "Courier", 7) == 0) { f->swfac *= 1.35; } else { f->swfac *= 1.1; /* unknown font */ } if (f == &cfmt.font_tb[GCHORDFONT]) cfmt.gcf = dfont_set(f); else if (f == &cfmt.font_tb[ANNOTATIONFONT]) cfmt.anf = dfont_set(f); else if (f == &cfmt.font_tb[VOCALFONT]) cfmt.vof = dfont_set(f); } /* -- output the font definitions with their encodings -- */ /* This output must occurs after user PostScript definitions because * these ones may change the default behaviour */ void define_fonts(void) { int i; static char *mkfont = "/mkfont{findfont dup length 1 add dict begin\n" " {1 index/FID ne{def}{pop pop}ifelse}forall\n" " CharStrings/double_sharp known not{\n" " /CharStrings CharStrings dup length dict copy def\n" " FontMatrix 0 get 1 eq{\n" " CharStrings/sharp{pop .46 0 setcharwidth .001 dup scale usharp ufill}bind put\n" " CharStrings/flat{pop .46 0 setcharwidth .001 dup scale uflat ufill}bind put\n" " CharStrings/natural{pop .40 0 setcharwidth .001 dup scale unat ufill}bind put\n" " CharStrings/double_sharp{pop .46 0 setcharwidth .001 dup scale udblesharp ufill}bind put\n" " CharStrings/double_flat{pop .50 0 setcharwidth .001 dup scale udbleflat ufill}bind put\n" " }{\n" " CharStrings/sharp{pop 460 0 setcharwidth usharp ufill}bind put\n" " CharStrings/flat{pop 460 0 setcharwidth uflat ufill}bind put\n" " CharStrings/natural{pop 400 0 setcharwidth unat ufill}bind put\n" " CharStrings/double_sharp{pop 460 0 setcharwidth udblesharp ufill}bind put\n" " CharStrings/double_flat{pop 500 0 setcharwidth udbleflat ufill}bind put\n" " }ifelse\n" " }if currentdict definefont pop end}!\n"; fputs(mkfont, fout); make_font_list(); for (i = 0; i < nfontnames; i++) { if (used_font[i]) define_font(fontnames[i], i, font_enc[i]); } } /* -- mark the used fonts -- */ void make_font_list(void) { struct FORMAT *f; int i; f = &cfmt; for (i = FONT_UMAX; i < FONT_DYN; i++) used_font[f->font_tb[i].fnum] = 1; } /* -- set the name of an information header type -- */ /* the argument is * <letter> [ <possibly quoted string> ] * this information is kept in the 'I' information */ static void set_infoname(char *p) { struct SYMBOL *s, *prev; if (*p == 'I') return; s = info['I' - 'A']; prev = NULL; while (s) { if (s->text[0] == *p) break; prev = s; s = s->next; } if (p[1] == '\0') { /* if delete */ if (s) { if (!prev) info['I' - 'A'] = s->next; else if ((prev->next = s->next) != 0) prev->next->prev = prev; } return; } if (!s) { s = (struct SYMBOL *) getarena(sizeof *s); memset(s, 0, sizeof *s); if (!prev) info['I' - 'A'] = s; else { prev->next = s; s->prev = prev; } } s->text = (char *) getarena(strlen(p) + 1); strcpy(s->text, p); } /* -- set the default format -- */ /* this function is called only once, at abcm2ps startup time */ void set_format(void) { struct FORMAT *f; f = &cfmt; memset(f, 0, sizeof *f); f->pageheight = PAGEHEIGHT; f->pagewidth = PAGEWIDTH; f->leftmargin = MARGIN; f->rightmargin = MARGIN; f->topmargin = 1.0 CM; f->botmargin = 1.0 CM; f->topspace = 22.0 PT; f->titlespace = 6.0 PT; f->subtitlespace = 3.0 PT; f->composerspace = 6.0 PT; f->musicspace = 6.0 PT; f->partsspace = 8.0 PT; f->staffsep = 46.0 PT; f->sysstaffsep = 34.0 PT; f->maxstaffsep = 2000.0 PT; f->maxsysstaffsep = 2000.0 PT; f->vocalspace = 10 PT; f->textspace = 14 PT; f->scale = 1.0; f->slurheight = 1.0; f->tieheight = 1.0; f->maxshrink = 0.65; f->breaklimit = 0.7; f->stretchlast = 0.25; f->stretchstaff = 1; f->graceslurs = 1; f->hyphencont = 1; f->lineskipfac = 1.1; f->parskipfac = 0.4; f->measurenb = -1; f->measurefirst = 1; f->autoclef = 1; f->breakoneoln = 1; f->cancelkey = 1; f->dblrepbar = (B_COL << 12) + (B_CBRA << 8) + (B_OBRA << 4) + B_COL; f->decoerr = 1; f->dynalign = 1; f->keywarn = 1; f->linewarn = 1; #ifdef HAVE_PANGO if (!svg && epsf <= 1) f->pango = 1; else lock_fmt(&cfmt.pango); /* SVG output does not use pango */ #endif f->rbdbstop = 1; f->rbmax = 4; f->rbmin = 2; f->staffnonote = 1; f->titletrim = 1; f->aligncomposer = A_RIGHT; f->notespacingfactor = 1.414; f->stemheight = STEM; #ifndef WIN32 f->dateformat = strdup("%b %e, %Y %H:%M"); #else f->dateformat = strdup("%b %#d, %Y %H:%M"); #endif f->gracespace = (65 << 16) | (80 << 8) | 120; /* left-inside-right - unit 1/10 pt */ f->textoption = T_LEFT; f->ndfont = FONT_DYN; if (svg || epsf > 2) { // SVG output fontspec(&f->font_tb[ANNOTATIONFONT], sans, 0, 12.0); fontspec(&f->font_tb[COMPOSERFONT], serif_italic, 0, 14.0); fontspec(&f->font_tb[FOOTERFONT], serif, 0, 16.0); fontspec(&f->font_tb[GCHORDFONT], sans, 0, 12.0); fontspec(&f->font_tb[HEADERFONT], serif, 0, 16.0); fontspec(&f->font_tb[HISTORYFONT], serif, 0, 16.0); fontspec(&f->font_tb[INFOFONT], serif_italic, 0, 14.0); /* same as composer by default */ fontspec(&f->font_tb[MEASUREFONT], serif_italic, 0, 14.0); fontspec(&f->font_tb[PARTSFONT], serif, 0, 15.0); fontspec(&f->font_tb[REPEATFONT], serif, 0, 13.0); fontspec(&f->font_tb[SUBTITLEFONT], serif, 0, 16.0); fontspec(&f->font_tb[TEMPOFONT], serif_bold, 0, 15.0); fontspec(&f->font_tb[TEXTFONT], serif, 0, 16.0); fontspec(&f->font_tb[TITLEFONT], serif, 0, 20.0); fontspec(&f->font_tb[VOCALFONT], serif_bold, 0, 13.0); fontspec(&f->font_tb[VOICEFONT], serif_bold, 0, 13.0); fontspec(&f->font_tb[WORDSFONT], serif, 0, 16.0); } else { // PS output fontspec(&f->font_tb[ANNOTATIONFONT], helvetica, 0, 12.0); fontspec(&f->font_tb[COMPOSERFONT], times_italic, 0, 14.0); fontspec(&f->font_tb[FOOTERFONT], times, 0, 16.0); fontspec(&f->font_tb[GCHORDFONT], helvetica, 0, 12.0); fontspec(&f->font_tb[HEADERFONT], times, 0, 16.0); fontspec(&f->font_tb[HISTORYFONT], times, 0, 16.0); fontspec(&f->font_tb[INFOFONT], times_italic, 0, 14.0); /* same as composer by default */ fontspec(&f->font_tb[MEASUREFONT], times_italic, 0, 14.0); fontspec(&f->font_tb[PARTSFONT], times, 0, 15.0); fontspec(&f->font_tb[REPEATFONT], times, 0, 13.0); fontspec(&f->font_tb[SUBTITLEFONT], times, 0, 16.0); fontspec(&f->font_tb[TEMPOFONT], times_bold, 0, 15.0); fontspec(&f->font_tb[TEXTFONT], times, 0, 16.0); fontspec(&f->font_tb[TITLEFONT], times, 0, 20.0); fontspec(&f->font_tb[VOCALFONT], times_bold, 0, 13.0); fontspec(&f->font_tb[VOICEFONT], times_bold, 0, 13.0); fontspec(&f->font_tb[WORDSFONT], times, 0, 16.0); } f->fields[0] = (1 << ('C' - 'A')) | (1 << ('M' - 'A')) | (1 << ('O' - 'A')) | (1 << ('P' - 'A')) | (1 << ('Q' - 'A')) | (1 << ('T' - 'A')) | (1 << ('W' - 'A')); f->fields[1] = (1 << ('w' - 'a')); set_infoname("R \"Rhythm: \""); set_infoname("B \"Book: \""); set_infoname("S \"Source: \""); set_infoname("D \"Discography: \""); set_infoname("N \"Notes: \""); set_infoname("Z \"Transcription: \""); set_infoname("H \"History: \""); } /* -- print the current format -- */ void print_format(void) { struct format *fd; static char *yn[2] = {"no","yes"}; for (fd = format_tb; fd->name; fd++) { printf("%-15s ", fd->name); switch (fd->type) { case FORMAT_B: switch (fd->subtype) { #ifdef HAVE_PANGO case 2: /* pango = 0, 1 or 2 */ if (cfmt.pango == 2) { printf("2\n"); break; } /* fall thru */ #endif default: case 0: printf("%s\n", yn[*((int *) fd->v)]); break; case 1: { /* writefields */ int i; for (i = 0; i < 32; i++) { if (cfmt.fields[0] & (1 << i)) printf("%c", (char) ('A' + i)); if (cfmt.fields[1] & (1 << i)) printf("%c", (char) ('a' + i)); } printf("\n"); break; } } break; case FORMAT_I: switch (fd->subtype) { default: printf("%d\n", *((int *) fd->v)); break; case 2: { /* dblrepbar */ int v; char tmp[16], *p; p = &tmp[sizeof tmp - 1]; *p = '\0'; for (v = cfmt.dblrepbar; v != 0; v >>= 4) { switch (v & 0x0f) { case B_BAR: *--p = '|'; break; case B_OBRA: *--p = '['; break; case B_CBRA: *--p = ']'; break; default: // case B_COL: *--p = ':'; break; } } printf("%s\n", p); break; } case 3: /* tuplets */ printf("%d %d %d %d\n", cfmt.tuplets >> 12, (cfmt.tuplets >> 8) & 0x0f, (cfmt.tuplets >> 4) & 0x0f, cfmt.tuplets & 0x0f); break; // case 4: /* textoption */ // break; case 5: /* gracespace */ printf("%d.%d %d.%d %d.%d\n", (cfmt.gracespace >> 16) / 10, (cfmt.gracespace >> 16) % 10, ((cfmt.gracespace >> 8) & 0xff) / 10, ((cfmt.gracespace >> 8) & 0xff) % 10, (cfmt.gracespace & 0xff) / 10, (cfmt.gracespace & 0xff) % 10); break; } break; case FORMAT_R: printf("%.2f\n", *((float *) fd->v)); break; case FORMAT_F: { struct FONTSPEC *s; s = (struct FONTSPEC *) fd->v; printf("%s", fontnames[s->fnum]); printf(" %s", font_enc[s->fnum] ? "native" : "utf-8"); printf(" %.1f", s->size); if ((fd->subtype == 1 && cfmt.partsbox) || (fd->subtype == 2 && cfmt.measurebox) || (fd->subtype == 3 && cfmt.gchordbox)) printf(" box"); printf("\n"); break; } case FORMAT_U: if (fd->subtype == 0) printf("%.2f\n", *((float *) fd->v)); else if (fd->subtype == 1) printf("%.2fcm\n", *((float *) fd->v) / (1 CM)); else //if (fd->subtype == 2) printf("%.2fcm\n", (cfmt.pagewidth - cfmt.leftmargin - cfmt.rightmargin) / (1 CM)); break; case FORMAT_S: printf("\"%s\"\n", *((char **) fd->v) != 0 ? *((char **) fd->v) : ""); break; } } } /* -- get a number with a unit -- */ /* The type may be * = 0: internal space - convert 'cm' and 'in' to 72 PPI * != 0: page dimensions and margins */ float scan_u(char *str, int type) { float a; int nch; if (sscanf(str, "%f%n", &a, &nch) == 1) { str += nch; if (a == 0) return 0; if (*str == '\0' || *str == ' ') { if (type != 0) error(0, NULL, "No unit \"%s\"", str - nch); return a PT; } if (!strncasecmp(str, "pt", 2)) return a PT; if (!strncasecmp(str, "cm", 2)) return type ? a CM : a * 28.35; if (!strncasecmp(str, "in", 2)) return type ? a IN : a * 72; } error(1, NULL, "Unknown unit value \"%s\"", str); return 20 PT; } /* -- get an encoding -- */ static int parse_encoding(char *p) { return strncasecmp(p, "native", 6) == 0; } /* -- get a position -- */ static int get_posit(char *p) { if (strcmp(p, "up") == 0 || strcmp(p, "above") == 0) return SL_ABOVE; if (strcmp(p, "down") == 0 || strcmp(p, "below") == 0 || strcmp(p, "under") == 0) return SL_BELOW; if (strcmp(p, "hidden") == 0 || strcmp(p, "opposite") == 0) return SL_HIDDEN; if (strcmp(p, "auto") == 0) return 0; /* auto (!= SL_AUTO) */ return -1; } /* -- get the option for text -- */ int get_textopt(char *p) { if (strncmp(p, "align", 5) == 0 || strncmp(p, "justify", 7) == 0) return T_JUSTIFY; if (strncmp(p, "ragged", 6) == 0 || strncmp(p, "fill", 4) == 0) return T_FILL; if (strncmp(p, "center", 6) == 0) return T_CENTER; if (strncmp(p, "skip", 4) == 0) return T_SKIP; if (strncmp(p, "right", 5) == 0) return T_RIGHT; return T_LEFT; } /* -- get the double repeat bar -- */ static int get_dblrepbar(char *p) { int bar_type; bar_type = 0; for (;;) { switch (*p++) { case '|': bar_type <<= 4; bar_type |= B_BAR; continue; case '[': bar_type <<= 4; bar_type |= B_OBRA; continue; case ']': bar_type <<= 4; bar_type |= B_CBRA; continue; case ':': bar_type <<= 4; bar_type |= B_COL; continue; default: break; } break; } return bar_type; } /* -- get a boolean value -- */ int get_bool(char *p) { switch (*p) { case '\0': case '1': case 'y': case 'Y': case 't': case 'T': return 1; case '0': case 'n': case 'N': case 'f': case 'F': break; default: error(0, NULL, "Unknown logical '%s' - false assumed", p); break; } return 0; } /* -- get a font specifier -- */ static void g_fspc(char *p, struct FONTSPEC *f) { char fname[80]; int encoding; float fsize; p = get_str(fname, p, sizeof fname); if (isalpha((unsigned char) *p) || *p == '*') { if (*p == '*') encoding = font_enc[f->fnum]; else encoding = parse_encoding(p); while (*p != '\0' && !isspace((unsigned char) *p)) p++; while (isspace((unsigned char) *p)) p++; } else { encoding = -1; } fsize = f->size; if (*p != '\0' && *p != '*') { char *q; float v; v = strtod(p, &q); if (v <= 0 || (*q != '\0' && *q != ' ')) error(1, NULL, "Bad font size '%s'", p); else fsize = v; } fontspec(f, strcmp(fname, "*") != 0 ? fname : NULL, encoding, fsize); if (file_initialized <= 0) used_font[f->fnum] = 1; if (f - cfmt.font_tb == outft) outft = -1; #ifdef HAVE_PANGO pg_reset_font(); #endif } /* -- parse a 'tablature' definition -- */ /* %%tablature * [#<nunmber (1..MAXTBLT)>] * [pitch=<instrument pitch (<note> # | b)>] * [[<head width>] * <height above>] * <height under> * <head function> * <note function> * [<bar function>] */ struct tblt_s *tblt_parse(char *p) { struct tblt_s *tblt; int n; char *q; static const char notes_tb[] = "CDEFGABcdefgab"; static const char pitch_tb[14] = {60, 62, 64, 65, 67, 69, 71, 72, 74, 76, 77, 79, 81, 83}; /* number */ if (*p == '#') { p++; n = *p++ - '0' - 1; if ((unsigned) n >= MAXTBLT || (*p != '\0' && *p != ' ')) { error(1, NULL, "Invalid number in %%%%tablature"); return 0; } if (*p == '\0') return tblts[n]; while (isspace((unsigned char) *p)) p++; } else { n = -1; } /* pitch */ tblt = malloc(sizeof *tblt); memset(tblt, 0, sizeof *tblt); if (strncmp(p, "pitch=", 6) == 0) { p += 6; if (*p == '^' || *p == '_') { if (*p == '^') { tblt->pitch++; tblt->instr[1] = '#'; } else { tblt->pitch--; tblt->instr[1] = 'b'; } p++; } if (*p == '\0' || (q = strchr(notes_tb, *p)) == NULL) { error(1, NULL, "Invalid pitch in %%%%tablature"); return 0; } tblt->pitch += pitch_tb[q - notes_tb]; tblt->instr[0] = toupper(*p++); while (*p == '\'' || *p == ',') { if (*p++ == '\'') tblt->pitch += 12; else tblt->pitch -= 12; } if (*p == '#' || *p == 'b') { if (*p == '#') tblt->pitch++; else tblt->pitch--; tblt->instr[1] = *p++; } while (*p == '\'' || *p == ',') { if (*p++ == '\'') tblt->pitch += 12; else tblt->pitch -= 12; } while (isspace((unsigned char) *p)) p++; } /* width and heights */ if (!isdigit(*p)) { error(1, NULL, "Invalid width/height in %%%%tablature"); return 0; } tblt->hu = scan_u(p, 0); while (*p != '\0' && !isspace((unsigned char) *p)) p++; while (isspace((unsigned char) *p)) p++; if (isdigit(*p)) { tblt->ha = tblt->hu; tblt->hu = scan_u(p, 0); while (*p != '\0' && !isspace((unsigned char) *p)) p++; while (isspace((unsigned char) *p)) p++; if (isdigit(*p)) { tblt->wh = tblt->ha; tblt->ha = tblt->hu; tblt->hu = scan_u(p, 0); while (*p != '\0' && !isspace((unsigned char) *p)) p++; while (isspace((unsigned char) *p)) p++; } } if (*p == '\0') goto err; /* PS functions */ p = strdup(p); tblt->head = p; while (*p != '\0' && !isspace((unsigned char) *p)) p++; if (*p == '\0') goto err; *p++ = '\0'; while (isspace((unsigned char) *p)) p++; tblt->note = p; while (*p != '\0' && !isspace((unsigned char) *p)) p++; if (*p != '\0') { *p++ = '\0'; while (isspace((unsigned char) *p)) p++; tblt->bar = p; while (*p != '\0' && !isspace((unsigned char) *p)) p++; if (*p != '\0') goto err; } /* memorize the definition */ if (n >= 0) tblts[n] = tblt; return tblt; err: error(1, NULL, "Wrong values in %%%%tablature"); return 0; } /* functions to set a voice parameter */ #define F_SET_PAR(param) \ static void set_ ## param(struct VOICE_S *p_voice, int val)\ {\ p_voice->posit.param = val;\ } F_SET_PAR(dyn) F_SET_PAR(gch) F_SET_PAR(orn) F_SET_PAR(voc) F_SET_PAR(vol) F_SET_PAR(std) F_SET_PAR(gsd) struct vpar { char *name; void (*f)(struct VOICE_S *p_voice, int val); }; static const struct vpar vpar_tb[] = { {"dynamic", set_dyn}, /* 0 */ {"gchord", set_gch}, /* 1 */ {"gstemdir", set_gsd}, /* 2 */ {"ornament", set_orn}, /* 3 */ {"stemdir", set_std}, /* 4 */ {"vocal", set_voc}, /* 5 */ {"volume", set_vol}, /* 6 */ #ifndef WIN32 {} #else {NULL, NULL, 0} #endif }; /* -- set a voice parameter -- */ void set_voice_param(struct VOICE_S *p_voice, /* current voice */ int state, /* tune state */ char *w, /* keyword */ char *p) /* argument */ { const struct vpar *vpar, *vpar2 = NULL; int i, l, val; l = strlen(w); for (vpar = vpar_tb; vpar->name; vpar++) { if (strncmp(w, vpar->name, l)) continue; // if (!isdigit(*p)) val = get_posit(p); // else // val = strtol(p, NULL, 10); // if ((unsigned) val > vpar->max) // goto err; break; } if (!vpar->name) { /* compatibility with previous versions */ val = -1; switch (*w) { case 'e': if (strcmp(w, "exprabove") == 0) { vpar = &vpar[0]; /* dyn */ vpar2 = &vpar[6]; /* vol */ if (get_bool(p)) val = SL_ABOVE; else val = SL_BELOW; break; } if (strcmp(w, "exprbelow") == 0) { vpar = &vpar[0]; /* dyn */ vpar2 = &vpar[6]; /* vol */ if (get_bool(p)) val = SL_BELOW; else val = SL_ABOVE; break; } break; case 'v': if (strcmp(w, "vocalabove") == 0) { /* compatibility */ vpar = &vpar[5]; /* voc */ if (get_bool(p)) val = SL_ABOVE; else val = SL_BELOW; break; } break; } if (val < 0) goto err; } if (state == ABC_S_TUNE) { vpar->f(p_voice, val); if (vpar2) vpar2->f(p_voice, val); return; } for (i = MAXVOICE, p_voice = voice_tb; /* global */ --i >= 0; p_voice++) { vpar->f(p_voice, val); if (vpar2) vpar2->f(p_voice, val); } cfmt.posit = voice_tb[0].posit; return; err: error(1, NULL, "Bad value %%%%%s %s", w, p); } /* -- parse a format line -- */ void interpret_fmt_line(char *w, /* keyword */ char *p, /* value */ int lock) { struct format *fd; int i; char *q; float f; switch (*w) { case 'b': if (strcmp(w, "barnumbers") == 0) /* compatibility */ w = "measurenb"; break; case 'c': if (strcmp(w, "comball") == 0) { /* compatibility */ cfmt.combinevoices = 2; return; } break; case 'f': if (strcmp(w, "font") == 0) { int fnum, encoding; float swfac; char fname[80]; if (file_initialized > 0 && !svg && epsf <= 1) { /* PS */ error(1, NULL, "Cannot define a font when the output file is opened"); return; } p = get_str(fname, p, sizeof fname); swfac = 0; /* defaults to 1.2 */ encoding = 0; if (*p != '\0') { if (isalpha((unsigned char) *p)) { encoding = parse_encoding(p); while (*p != '\0' && !isspace((unsigned char) *p)) p++; while (isspace((unsigned char) *p)) p++; } if (isdigit((unsigned char) *p)) { f = strtod(p, &q); if (f > 2 || (*q != '\0' && *q != '\0')) goto bad; swfac = f; } } fnum = get_font(fname, encoding); def_font_enc[fnum] = encoding; swfac_font[fnum] = swfac; used_font[fnum] = 1; for (i = FONT_UMAX; i < FONT_MAX; i++) { if (cfmt.font_tb[i].fnum == fnum) cfmt.font_tb[i].swfac = cfmt.font_tb[i].size * swfac; } return; } break; case 'i': if (strcmp(w, "infoname") == 0) { if (*p < 'A' || *p > 'Z') goto bad; set_infoname(p); return; } break; case 'm': if (strcmp(w, "musiconly") == 0) { /* compatibility */ if (get_bool(p)) cfmt.fields[1] &= ~(1 << ('w' - 'a')); else cfmt.fields[1] |= (1 << ('w' - 'a')); return; } break; case 'p': if (strcmp(w, "printparts") == 0) { /* compatibility */ if (get_bool(p)) cfmt.fields[0] |= (1 << ('P' - 'A')); else cfmt.fields[0] &= ~(1 << ('P' - 'A')); return; } if (strcmp(w, "printtempo") == 0) { /* compatibility */ if (get_bool(p)) cfmt.fields[0] |= (1 << ('Q' - 'A')); else cfmt.fields[0] &= ~(1 << ('Q' - 'A')); return; } break; case 's': if (strncmp(w, "setfont-", 8) == 0) { i = w[8] - '0'; if (i < 0 || i >= FONT_UMAX) return; g_fspc(p, &cfmt.font_tb[i]); return; } if (strcmp(w, "scale") == 0) { for (fd = format_tb; fd->name; fd++) if (strcmp("pagescale", fd->name) == 0) break; if (fd->lock) return; fd->lock = lock; f = strtod(p, &q); if (*q != '\0' && *q != ' ') goto bad; cfmt.scale = f / 0.75; // old -> new scale return; } break; case 'w': if (strcmp(w, "withxrefs") == 0) { /* compatibility */ if (get_bool(p)) cfmt.fields[0] |= (1 << ('X' - 'A')); else cfmt.fields[0] &= ~(1 << ('X' - 'A')); return; } if (strcmp(w, "writehistory") == 0) { /* compatibility */ struct SYMBOL *s; int bool; unsigned u; bool = get_bool(p); for (s = info['I' - 'A']; s != 0; s = s->next) { u = s->text[0] - 'A'; if (bool) cfmt.fields[0] |= (1 << u); else cfmt.fields[0] &= ~(1 << u); } return; } break; } for (fd = format_tb; fd->name; fd++) if (strcmp(w, fd->name) == 0) break; if (!fd->name) return; i = strlen(p); if (strcmp(p + i - 5, " lock") == 0) { p[i - 5] = '\0'; lock = 1; } if (lock) fd->lock = 1; else if (fd->lock) return; switch (fd->type) { case FORMAT_B: switch (fd->subtype) { #ifdef HAVE_PANGO case 2: /* %%pango = 0, 1 or 2 */ if (svg || epsf > 1) // if SVG output break; if (*p == '2') { cfmt.pango = 2; break; } /* fall thru */ #endif default: case 0: case 3: /* %%abc2pscompat */ *((int *) fd->v) = get_bool(p); if (fd->subtype == 3) { if (cfmt.abc2pscompat) deco['M'] = "tenuto"; else deco['M'] = "lowermordent"; } break; case 1: { /* %%writefields */ int bool; unsigned u; q = p; while (*p != '\0' && !isspace((unsigned char) *p)) p++; while (isspace((unsigned char) *p)) p++; bool = get_bool(p); while (*q != '\0' && !isspace((unsigned char) *q)) { u = *q - 'A'; if (u < 26) { i = 0; } else { u = *q - 'a'; if (u < 26) i = 1; else break; /*fixme: error */ } if (bool) cfmt.fields[i] |= (1 << u); else cfmt.fields[i] &= ~(1 << u); q++; } break; } } break; case FORMAT_I: if (fd->subtype == 3) { /* tuplets */ unsigned i1, i2, i3, i4 = 0; if (sscanf(p, "%d %d %d %d", &i1, &i2, &i3, &i4) != 4 && sscanf(p, "%d %d %d", &i1, &i2, &i3) != 3) goto bad; if (i1 > 2 || i2 > 2 || i3 > 2 || i4 > 2) goto bad; cfmt.tuplets = (i1 << 12) | (i2 << 8) | (i3 << 4) | i4; break; } if (fd->subtype == 5) { /* gracespace */ unsigned i1, i2, i3; float f1, f2, f3; if (sscanf(p, "%f %f %f", &f1, &f2, &f3) != 3 || f1 > 25 || f2 > 25 || f3 > 25) goto bad; i1 = f1 * 10; i2 = f2 * 10; i3 = f3 * 10; cfmt.gracespace = (i1 << 16) | (i2 << 8) | i3; break; } if (fd->subtype == 1 /* splittune */ && (strcmp(p, "odd") == 0 || strcmp(p, "even") == 0)) cfmt.splittune = p[0] == 'e' ? 2 : 3; else if (fd->subtype == 2) /* dblrepbar */ cfmt.dblrepbar = get_dblrepbar(p); else if (fd->subtype == 4 && !isdigit(*p)) /* textoption */ cfmt.textoption = get_textopt(p); else if (isdigit(*p) || *p == '-' || *p == '+') sscanf(p, "%d", (int *) fd->v); else *((int *) fd->v) = get_bool(p); if (fd->subtype == 4) { /* textoption */ if (cfmt.textoption < 0) { cfmt.textoption = 0; goto bad; } } break; case FORMAT_R: f = strtod(p, &q); if (*q != '\0' && *q != ' ') goto bad; switch (fd->subtype) { default: if (f <= 0) goto bad; break; case 1: { /* note spacing factor */ float v2; if (f < 1 || f > 2) goto bad; i = C_XFLAGS; /* crotchet index */ v2 = space_tb[i]; for ( ; --i >= 0; ) { v2 /= f; space_tb[i] = v2; } i = C_XFLAGS; v2 = space_tb[i]; for ( ; ++i < NFLAGS_SZ; ) { v2 *= f; space_tb[i] = v2; } break; } case 2: /* maxshrink / stretchlast */ if (f < 0 || f > 1) goto bad; break; case 3: /* breaklimit */ if (f < 0.5 || f > 1) goto bad; break; } *((float *) fd->v) = f; break; case FORMAT_F: { int b; g_fspc(p, (struct FONTSPEC *) fd->v); b = strstr(p, "box") != NULL; switch (fd->subtype) { case 1: cfmt.partsbox = b; break; case 2: cfmt.measurebox = b; break; case 3: cfmt.gchordbox = b; break; } break; } case FORMAT_U: *((float *) fd->v) = scan_u(p, fd->subtype); switch (fd->subtype) { case 1: if (strcmp(fd->name, "rightmargin") != 0 && strcmp(fd->name, "leftmargin") != 0) break; staffwidth = cfmt.pagewidth - cfmt.leftmargin - cfmt.rightmargin; if (staffwidth > 100) break; error(1, NULL, "'staffwidth' too small\n"); staffwidth = 100; if (fd->name[0] == 'r') cfmt.rightmargin = cfmt.pagewidth - cfmt.leftmargin - staffwidth; else cfmt.leftmargin = cfmt.pagewidth - cfmt.rightmargin - staffwidth; break; case 2: /* staffwidth */ if (staffwidth < 100) { error(1, NULL, "'staffwidth' too small\n"); break; } f = (cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth) - staffwidth - cfmt.leftmargin; if (f < 0) error(1, NULL, "'staffwidth' too big\n"); else cfmt.rightmargin = f; break; } break; case FORMAT_S: i = strlen(p) + 1; *((char **) fd->v) = getarena(i); if (*p == '"') get_str(*((char **) fd->v), p, i); else strcpy(*((char **) fd->v), p); if (fd->subtype == 1) { // musicfont // (no control) svg_font_switch(); } break; } return; bad: error(1, NULL, "Bad value '%s' for '%s' - ignored", p, w); } /* -- lock a format -- */ void lock_fmt(void *fmt) { struct format *fd; for (fd = format_tb; fd->name; fd++) if (fd->v == fmt) break; if (fd->name == 0) return; fd->lock = 1; } /* -- start a new font -- */ void set_font(int ft) { int fnum; struct FONTSPEC *f, *f2; if (ft == outft) return; f = &cfmt.font_tb[ft]; if (outft >= 0) { f2 = &cfmt.font_tb[outft]; outft = ft; fnum = f->fnum; if (fnum == f2->fnum && f->size == f2->size) return; } else { outft = ft; fnum = f->fnum; } if (!used_font[fnum] && epsf <= 1 && !svg) { /* (not usefull for svg output) */ if (file_initialized <= 0) { used_font[fnum] = 1; } else { error(1, NULL, "Font '%s' not predefined; using first in list", fontnames[fnum]); fnum = 0; } } if (f->size == 0) { error(0, NULL, "Font '%s' with a null size - set to 8", fontnames[fnum]); f->size = 8; } a2b("%.1f F%d ", f->size, fnum); } /* -- get the encoding of a font -- */ int get_font_encoding(int ft) { return font_enc[cfmt.font_tb[ft].fnum]; } �������������������������������������������������������������������������������������������abcm2ps-8.14.11/free.abc����������������������������������������������������������������������������0000664�0000000�0000000�00000002032�13762665467�0014630�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������% example of musical symbols with FreeFont % --- definitions for SVG output --- %%beginsvg <defs> <text id="tclef" x="-12" y="0" font-family="FreeSerif" font-size="36" font-weight="normal" font-style="normal">𝄞</text> <text id="bclef" x="-10" y="1.5" font-family="FreeSerif" font-size="34" font-weight="normal" font-style="normal">𝄢</text> <text id="csig" x="-7.5" y="-1" font-family="FreeSerif" font-size="31" font-weight="normal" font-style="normal">𝄴</text> <text id="brace" x="-9" y="-.5" transform="scale(1.2,-4.2)" font-family="FreeSerif" font-size="31" font-weight="normal" font-style="normal">𝄔</text> </defs> %%endsvg % --- definitions for PostScript output --- %%beginps nosvg /tclef{/FreeSerif 36 selectfont M -12 0 RM/g_clef glyphshow}! /bclef{/FreeSerif 34 selectfont M -10 -1.5 RM/f_clef glyphshow}! /csig{/FreeSerif 31 selectfont M -7.5 1 RM/common_time glyphshow}! /brace{/FreeSerif 31 selectfont gsave T -9 -1 M -.042 mul 1.1 exch scale /brace glyphshow grestore}! %%endps ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������abcm2ps-8.14.11/front.c�����������������������������������������������������������������������������0000664�0000000�0000000�00000046307�13762665467�0014551�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * ABC front-end parser * * This file is part of abcm2ps. * * Copyright (C) 2011-2019 Jean-François Moine (http://moinejf.free.fr) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <regex.h> #ifdef WIN32 #define strncasecmp _strnicmp #define strdup _strdup #endif #include "abcm2ps.h" static unsigned char *dst; static int offset, size; static unsigned char *selection; static int latin, skip; static char prefix[4] = {'%'}; static int state; /* * translation table from the ABC draft version 2 * ` grave * ' acute * ^ circumflex * , cedilla * " umlaut * ~ tilde * o ring * = macron or stroke * / slash * ; ogonek * v caron * u breve * : long Hungarian umlaut * . dot / dotless * else, ligatures * ae ss ng */ /* !! each table item is 3 bytes: 1 char, 1 UTF-8 char on 2 bytes */ static unsigned char grave[] = "AÀEÈIÌOÒUÙaàeèiìoòuù"; static unsigned char acute[] = "AÁEÉIÍOÓUÚYÝaáeéiíoóuúyýSŚZŹsśzźRŔLĹCĆNŃrŕlĺcćnń"; static unsigned char circumflex[] = "AÂEÊIÎOÔUÛaâeêiîoôuûHĤJĴhĥjĵCĈGĜSŜcĉgĝsŝ"; static unsigned char cedilla[] = "CÇcçSŞsşTŢtţRŖLĻGĢrŗlļgģNŅKĶnņkķ"; static unsigned char umlaut[] = "AÄEËIÏOÖUÜYŸaäeëiïoöuüyÿ"; static unsigned char tilde[] = "AÃNÑOÕaãnñoõIĨiĩUŨuũ"; static unsigned char ring[] = "AÅaåUŮuůeœ"; static unsigned char macron[] = "AĀDĐEĒHĦIĪOŌTŦUŪaādđeēhħiīoōtŧuū"; /* and stroke! */ static unsigned char slash[] = "OØoøDĐdđLŁlł"; static unsigned char ogonek[] = "AĄEĘIĮUŲaąeęiįuų"; static unsigned char caron[] = "LĽSŠTŤZŽlľsštťzžCČEĚDĎNŇRŘcčeědďnňrř"; static unsigned char breve[] = "AĂaăEĔeĕGĞgğIĬiĭOŎoŏUŬuŭ"; static unsigned char hungumlaut[] = "OŐUŰoőuű"; static unsigned char dot[] = "ZŻzżIİiıCĊcċGĠgġEĖeė"; /* the items of this table are 4 bytes long */ static unsigned char ligature[] = "AAÅaaåAEÆaeæccçcCÇDHÐdhðngŋOEŒssßTHÞthþ"; /* latin conversion tables - range 0xa0 .. 0xff */ static unsigned char latin2[] = { " Ą˘Ł¤ĽŚ§¨ŠŞŤŹ­ŽŻ°ą˛ł´ľśˇ¸šşťź˝žż" "ŔÁÂĂÄĹĆÇČÉĘËĚÍÎĎĐŃŇÓÔŐÖ×ŘŮÚŰÜÝŢß" "ŕáâăäĺćçčéęëěíîďđńňóôőö÷řůúűüýţ˙" }; static unsigned char latin3[] = { " Ħ˘£¤ Ĥ§¨İŞĞĴ­ ݰħ²³´µĥ·¸ışğĵ½ ż" "ÀÁ ÄĊĈÇÈÉÊËÌÍÎÏ ÑÒÓÔĠÖ×ĜÙÚÛÜŬŜß" "àáâ äċĉçèéêëìíîï ñòóôġö÷ĝùúûüŭŝ˙" }; static unsigned char latin4[] = { " ĄĸŖ¤Ĩϧ¨ŠĒĢŦ­Ž¯°ą˛ŗ´ĩšēģŧŊžŋ" "ĀÁÂÃÄÅÆĮČÉĘËĖÍÎĪĐŅŌĶÔÕÖרŲÚÛÜŨŪß" "āáâãäåæįčéęëėíîīđņōķôõö÷øųúûüũū˙" }; static unsigned char latin5[] = { " ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿" "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏĞÑÒÓÔÕÖרÙÚÛÜİŞß" "àáâãäåæçèéêëìíîïğñòóôõö÷øùúûüışÿ" }; static unsigned char latin6[] = { " ĄĒĢĪĨͧĻĐŠŦŽ­ŪŊ°ąēģīĩķ·ļđšŧž―ūŋ" "ĀÁÂÃÄÅÆĮČÉĘËĖÍÎÏÐŅŌÓÔÕÖŨØŲÚÛÜÝÞß" "āáâãäåæįčéęëėíîïðņōóôõöũøųúûüýþĸ" }; static unsigned char *latin_tb[5] = { latin2, latin3, latin4, latin5, latin6 }; /* add text to the output buffer */ static void txt_add(unsigned char *s, int sz) { if (skip) return; if (offset + sz > size) { size = (offset + sz + 8191) / 8192 * 8192; if (!dst) dst = malloc(size); else dst = realloc(dst, size); if (!dst) { fprintf(stderr, "Out of memory - abort\n"); exit(EXIT_FAILURE); } } memcpy(dst + offset, s, sz); offset += sz; } /* add text to the output buffer translating * the escape sequences and the non utf-8 characters */ static void txt_add_cnv(unsigned char *s, int sz, int comment) { unsigned char *p, c, tmp[4]; int in_string = 0; p = s; while (sz > 0) { switch (*p) { case '"': if (comment) in_string = !in_string; break; case '%': if (in_string || !comment) break; while (--p >= s) { // start of comment if (*p != ' ' && *p != '\t') break; } p++; goto done; case '\\': if (sz >= 4 /* \ooo */ && p[1] >= '0' && p[1] <= '3' && p[2] >= '0' && p[2] <= '7' && p[3] >= '0' && p[3] <= '7') { c = ((p[1] - '0') << 6) + ((p[2] - '0') << 3) + p[3] - '0'; if (p != s) txt_add(s, (int) (p - s)); p += 4; s = p; sz -= 4; switch (c) { /* convert the accidentals */ case 0x01: case 0x81: tmp[0] = 0xe2; tmp[1] = 0x99; tmp[2] = 0xaf; txt_add(tmp, 3); continue; case 0x02: case 0x82: tmp[0] = 0xe2; tmp[1] = 0x99; tmp[2] = 0xad; txt_add(tmp, 3); continue; case 0x03: case 0x83: tmp[0] = 0xe2; tmp[1] = 0x99; tmp[2] = 0xae; txt_add(tmp, 3); continue; case 0x04: case 0x84: tmp[0] = 0xf0; tmp[1] = 0x9d; tmp[2] = 0x84; tmp[3] = 0xaa; txt_add(tmp, 4); continue; case 0x05: case 0x85: tmp[0] = 0xf0; tmp[1] = 0x9d; tmp[2] = 0x84; tmp[3] = 0xab; txt_add(tmp, 4); continue; } if (c >= 0x80 && latin > 0) goto latin; tmp[0] = c; txt_add(tmp, 1); continue; } if (sz >= 6 /* \uxxxx */ && p[1] == 'u' && isxdigit(p[2]) && isxdigit(p[3]) && isxdigit(p[4]) && isxdigit(p[5])) { int i, v; v = 0; for (i = 2; i < 6; i++) { v <<= 4; c = p[i]; if (c <= '9') v += c - '0'; else if (c <= 'F') v += c - 'A' + 10; else v += c - 'a' + 10; } if (p != s) txt_add(s, (int) (p - s)); p += 6; sz -= 6; if ((v & 0xdc00) == 0xd800 /* surrogates */ && sz >= 6 && *p == '\\' && p[1] == 'u' && isxdigit(p[2]) && isxdigit(p[3]) && isxdigit(p[4]) && isxdigit(p[5])) { int v2; v = (v - 0xd7c0) << 10; v2 = 0; for (i = 2; i < 6; i++) { v2 <<= 4; c = p[i]; if (c <= '9') v2 += c - '0'; else if (c <= 'F') v2 += c - 'A' + 10; else v2 += c - 'a' + 10; } v2 -= 0xdc00; v += v2; p += 6; sz -= 6; } //fixme: else error s = p; if (v < 0x80) { /* convert to UTF-8 */ tmp[0] = v; i = 1; } else if (v < 0x800) { tmp[0] = 0xc0 | (v >> 6); tmp[1] = 0x80 | (v & 0x3f); i = 2; } else if (v < 0x10000) { tmp[0] = 0xe0 | (v >> 12); tmp[1] = 0x80 | ((v >> 6) & 0x3f); tmp[2] = 0x80 | (v & 0x3f); i = 3; } else { tmp[0] = 0xf0 | (v >> 18); tmp[1] = 0x80 | ((v >> 12) & 0x3f); tmp[2] = 0x80 | ((v >> 6) & 0x3f); tmp[3] = 0x80 | (v & 0x3f); i = 4; } txt_add(tmp, i); continue; } if (sz >= 3) { unsigned char *q; switch (p[1]) { case '`': q = grave; break; case '\'': q = acute; break; case '^': q = circumflex; break; case ',': q = cedilla; break; case '"': q = umlaut; break; case '~': q = tilde; break; case 'o': q = ring; break; case '=': q = macron; break; case '/': q = slash; break; case ';': q = ogonek; break; case 'v': q = caron; break; case 'u': q = breve; break; case 'H': case ':': q = hungumlaut; break; case '.': q = dot; break; default: q = ligature; do { if (*q == p[1] && q[1] == p[2]) break; q += 4; } while (*q != '\0'); if (*q != '\0') { if (p != s) txt_add(s, (int) (p - s)); txt_add(q + 2, 2); p += 3; sz -= 3; s = p; continue; } q = 0; break; } if (q != 0) { do { if (*q == p[2]) break; q += 3; } while (*q != '\0'); if (*q != '\0') { if (p != s) txt_add(s, (int) (p - s)); txt_add(q + 1, 2); p += 3; sz -= 3; s = p; continue; } } } p++; sz--; if (sz > 0) { p++; sz--; } continue; } if (*p >= 0x80 && latin > 0) { if (p != s) txt_add(s, (int) (p - s)); c = *p++; s = p; sz--; latin: if (c < 0xa0 || latin == 1) { tmp[0] = 0xc0 | ((c >> 6) & 0x03); tmp[1] = 0x80 | (c & 0x3f); txt_add(tmp, 2); } else { unsigned char *q; q = latin_tb[latin - 2]; txt_add(q + (c - 0xa0) * 2, 2); } continue; } p++; sz--; } done: if (p != s) txt_add(s, (int) (p - s)); } static void txt_add_eos(char *fname, int linenum) { static unsigned char eos = '\0'; /* special case for continuation lines in ABC version 2.0 */ if (parse.abc_vers == (2 << 16) && offset > 0 && dst[offset - 1] == '\\') { offset--; return; } txt_add(&eos, 1); abc_parse((char *) dst, fname, linenum); offset = 0; } /* get the ABC version */ static void get_vers(char *p) { int i, j, k; i = j = k = 0; if (sscanf(p, "%d.%d.%d", &i, &j, &k) != 3) if (sscanf(p, "%d.%d", &i, &j) != 2) sscanf(p, "%d", &i); parse.abc_vers = (i << 16) + (j << 8) + k; } /* check if the current tune is to be selected */ static int tune_select(unsigned char *s) { regex_t r; unsigned char *p, *sel; int ret; /* if there is a list of tune indexes, * check the tune index */ sel = selection; if (isdigit(*sel)) { int tune_number, cur_sel, end_sel, n; /* get the tune number ('s' points to X:) */ tune_number = strtod((char *) s + 2, 0); /* search it in the number list */ for (;;) { if (sscanf((char *) sel, "%d%n", &cur_sel, &n) != 1) break; sel += n; if (*sel == '-') { sel++; if (sscanf((char *) sel, "%d%n", &end_sel, &n) != 1) end_sel = ~0u >> 1; else sel += n; } else { end_sel = cur_sel; } if (tune_number >= cur_sel && tune_number <= end_sel) return 1; if (*sel != ',') break; sel++; } if (*sel == '\0') return 0; } for (p = s + 2; ; p++) { switch (*p) { case '\0': return 0; default: continue; case '\n': case '\r': break; } if (p[1] != 'K' || p[2] != ':') continue; p += 3; while (*p != '\n' && *p != '\r' && *p != '\0') p++; if (*p != '\0') p++; /* keep the EOL for RE with '\s' */ break; } ret = p - s; if (ret >= TEX_BUF_SZ - 1) { fprintf(stderr, "Tune header too big for %%%%select\n"); return 0; } memcpy(tex_buf, s, ret); tex_buf[ret] = '\0'; ret = regcomp(&r, (char *) sel, REG_EXTENDED | REG_NEWLINE | REG_NOSUB); if (ret) return 0; ret = regexec(&r, tex_buf, 0, NULL, 0); regfree(&r); return !ret; } /* -- front end parser -- */ void frontend(unsigned char *s, int ftype, char *fname, int linenum) { unsigned char *p, *q, c, *begin_end, sep; int i, l, str_cnv_p, histo, end_len; char prefix_sav[4]; int latin_sav = 0; /* have C compiler happy */ begin_end = NULL; end_len = 0; histo = 0; // state = 0; if (ftype == FE_ABC && strncmp((char *) s, "%abc-", 5) == 0) { get_vers((char *) s + 5); while (*s != '\0' && *s != '\r' && *s != '\n') s++; if (*s != '\0') { s++; if (s[-1] == '\r' && *s == '\n') s++; } linenum++; } /* if unknown encoding, check if latin1 or utf-8 */ if (ftype == FE_ABC && parse.abc_vers >= ((2 << 16) | (1 << 8))) { // if ABC version >= 2.1 latin = 0; // always UTF-8 } else { for (p = s; *p != '\0'; p++) { c = *p; if (c == '\\') { if (!isdigit(p[1])) continue; if ((p[1] == '0' || p[1] == '2') && p[2] == '0') /* accidental */ continue; latin = 1; break; } if (c < 0x80) continue; if (c >= 0xc2) { if ((p[1] & 0xc0) == 0x80) { latin = 0; break; } } latin = 1; break; } } latin_sav = latin; /* (have gcc happy) */ /* scan the file */ skip = 0; while (*s != '\0') { /* get a line */ str_cnv_p = 0; p = s; while (*p != '\0' && *p != '\r' && *p != '\n') { if (*p == '\\' || *p == '%' || (latin > 0 && *p >= 0x80)) str_cnv_p = 1; p++; } l = p - s; if (*p != '\0') { p++; if (p[-1] == '\r' && *p == '\n') /* (DOS) */ p++; } linenum++; if (skip) { if (l != 0) goto ignore; skip = 0; } if (begin_end) { if (ftype == FE_FMT) { if (strncmp((char *) s, "end", 3) == 0 && strncmp((char *) s + 3, (char *) begin_end, end_len) == 0) { begin_end = NULL; goto next_eol; } if (*s == '%') goto ignore; /* comment */ goto next; } if (*s == '%' && strchr(prefix, s[1])) { q = s + 2; while (*q == ' ' || *q == '\t') q++; if (strncmp((char *) q, "end", 3) == 0 && strncmp((char *) q + 3, (char *) begin_end, end_len) == 0) { begin_end = NULL; goto next_eol; } } if (strncmp("ps", (char *) begin_end, end_len) == 0) { if (*s == '%') goto ignore; /* comment */ } else { if (*s == '%' && strchr(prefix, s[1])) { s += 2; l -= 2; } } goto next; } while (l > 0 && isspace(s[l - 1])) l--; if (l == 0) { /* empty line */ if (ftype == FE_FMT) goto next_eol; switch (state) { default: goto ignore; case 1: fprintf(stderr, "Line %d: Empty line in tune header - K:C added\n", linenum); txt_add((unsigned char *) "K:C", 3); txt_add_eos(fname, linenum); /* fall thru */ case 2: state = 0; strcpy(prefix, prefix_sav); latin = latin_sav; break; } goto next_eol; } if (histo) { /* H: continuation */ if ((s[1] == ':' && isalpha(*s)) || (*s == '%' && strchr(prefix, s[1]))) { histo = 0; } else { if (*s != '+' || s[1] != ':') txt_add((unsigned char *) "+:", 2); goto next; } } /* special case 'space* "%" ' */ if (*s == ' ' || *s == '\t') { q = s; do { q++; } while (*q == ' ' || *q == '\t'); if (*q == '%') goto ignore; } if (ftype == FE_PS) { if (*s == '%') goto ignore; goto next; } /* treat the pseudo-comments */ if (ftype == FE_FMT) { if (*s == '%') goto ignore; goto pscom; } if (*s == 'I' && s[1] == ':') { s += 2; l -= 2; while (*s == ' ' || *s == '\t') { s++; l--; } if (l <= 0) goto ignore; txt_add((unsigned char *) "%%", 2); goto pcinfo; } if (*s == '%') { if (!strchr(prefix, s[1])) /* pure comment */ goto ignore; s += 2; l -= 2; if (strncmp((char *) s, "abc ", 4) == 0) { s += 4; l -= 4; goto info; } if (strncmp((char *) s, "abcm2ps ", 8) == 0 || strncmp((char *) s, "ss-pref ", 8) == 0) { s += 8; l -= 8; while (*s == ' ' || *s == '\t') { s++; l--; } for (i = 0; i < sizeof prefix - 1; i++) { if (*s == ' ' || *s == '\t' || --l < 0) break; prefix[i] = *s++; } if (i == 0) prefix[i++] = '%'; prefix[i] = '\0'; goto ignore; } if (strncmp((char *) s, "abc-version ", 12) == 0) { get_vers((char *) s + 12); goto ignore; } pscom: while (*s == ' ' || *s == '\t') { s++; l--; } if (l <= 0) goto ignore; txt_add((unsigned char *) "%%", 2); if (strncmp((char *) s, "begin", 5) == 0) { q = begin_end = s + 5; while (!isspace(*q)) q++; end_len = q - begin_end; goto next; } pcinfo: if (strncmp((char *) s, "encoding ", 9) == 0 || strncmp((char *) s, "abc-charset ", 12) == 0) { if (*s == 'e') q = s + 9; else q = s + 12; while (*q == ' ' || *q == '\t') q++; if (strncasecmp((char *) q, "latin", 5) == 0) { q += 5; } else if (strncasecmp((char *) q, "iso-8859-", 9) == 0) { q += 9; } else if (strncasecmp((char *) q, "utf-8", 5) == 0 || strncasecmp((char *) q, "native", 6) == 0) { latin = 0; goto next; } else if (!isdigit(*q)) { goto next; /* unknown charset */ } switch (*q) { case '1': if (q[1] == '0') latin = 6; else latin = 1; break; case '2': latin = 2; break; case '3': latin = 3; break; case '4': latin = 4; break; case '5': if (q[-1] != '-') latin = 5; break; case '6': if (q[-1] != '-') latin = 6; break; /*fixme: iso-8859 5..8 not treated */ case '9': latin = 5; break; } goto next; } if (strncmp((char *) s, "format ", 7) == 0 || strncmp((char *) s, "abc-include ", 12) == 0) { int skip_sav; if (*s == 'f') s += 7; else s += 12; while (*s == ' ' || *s == '\t') s++; q = s; while (*q != '\0' && *q != '%' && *q != '\n' && *q != '\r') q++; while (q[-1] == ' ') q--; sep = *q; *q = '\0'; skip_sav = skip; //fixme: pb when different encoding in included file: != behaviour .fmt or .abc... // latin_sav = latin; offset = 0; include_file(s); // latin = latin_sav; skip = skip_sav; *q = sep; goto ignore; } if (strncmp((char *) s, "select", 6) == 0) { s += 6; if (*s == '\n') { /* select clear */ q = s; } else if (*s != ' ' && *s != '\t') { goto next; } else { while (*s == ' ' || *s == '\t') s++; q = s; while (*q != '\0' && *q != '%' && *q != '\n' && *q != '\r') q++; while (q[-1] == ' ' || q[-1] == '\t') q--; if (strncmp((char *) q - 5, " lock", 5) == 0) q -= 5; } if (selection) { free(selection); selection = NULL; } if (q != s) { sep = *q; *q = '\0'; selection = (unsigned char *) strdup((char *) s); *q = sep; } offset = 0; goto ignore; } goto next; } /* treat the information fields */ info: if (s[1] == ':' && (isalpha(*s) || *s == '+')) { c = *s; switch (c) { case 'I': /* treat as a pseudo-comment */ s += 2; l -= 2; goto pscom; case 'X': switch (state) { case 1: fprintf(stderr, "Line %d: X: found in tune header - K:C added\n", linenum); txt_add((unsigned char *) "K:C", 3); txt_add_eos(fname, linenum); txt_add_eos(fname, linenum); /* empty line */ break; case 2: txt_add_eos(fname, linenum); /* no empty line - minor error */ break; } if (selection) { skip = !tune_select(s); if (skip) goto ignore; } state = 1; strcpy(prefix_sav, prefix); latin_sav = latin; break; case 'U': break; case 'H': histo = 1; break; default: if (state == 0 /* if global */ && strchr("dKPQsVWw", *s) != NULL) goto ignore; if (*s == 'K') state = 2; break; } txt_add(s, 2); s += 2; l -= 2; while (*s == ' ' || *s == '\t') { s++; l--; } str_cnv_p = 1; goto next; } /* treat the music lines */ if (state == 0) /* if not in tune */ goto ignore; next: if (str_cnv_p) txt_add_cnv(s, l, !begin_end); else txt_add(s, l); if (begin_end) txt_add((unsigned char *) "\n", 1); else next_eol: txt_add_eos(fname, linenum); ignore: s = p; } if (begin_end) fprintf(stderr, "Line %d: No %%%%end after %%%%begin\n", linenum); if (ftype == FE_FMT) return; if (state == 1) fprintf(stderr, "Line %d: Unexpected EOF in header definition\n", linenum); abc_eof(); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������abcm2ps-8.14.11/glyph.c�����������������������������������������������������������������������������0000664�0000000�0000000�00000020134�13762665467�0014532�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * utf-8 to glyph translation * * This file is part of abcm2ps. * * Copyright (C) 2011-2019 Jean-François Moine (http://moinejf.free.fr) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include "abcm2ps.h" static char *c2[64] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "nbspace", "exclamdown", "cent", "sterling", "currency", "yen", "brokenbar", "section", "dieresis", "copyright", "ordfeminine", "guillemotleft", "logicalnot", "sfthyphen", "registered", "macron", "degree", "plusminus", "twosuperior", "threesuperior", "acute", "mu", "paragraph", "periodcentered", "cedilla", "onesuperior", "ordmasculine", "guillemotright", "onequarter", "onehalf", "threequarters","questiondown" }; static char *c3[64] = { "Agrave", "Aacute", "Acircumflex", "Atilde", "Adieresis", "Aring", "AE", "Ccedilla", "Egrave", "Eacute", "Ecircumflex", "Edieresis", "Igrave", "Iacute", "Icircumflex", "Idieresis", "Eth", "Ntilde", "Ograve", "Oacute", "Ocircumflex", "Otilde", "Odieresis", "multiply", "Oslash", "Ugrave", "Uacute", "Ucircumflex", "Udieresis", "Yacute", "Thorn", "germandbls", "agrave", "aacute", "acircumflex", "atilde", "adieresis", "aring", "ae", "ccedilla", "egrave", "eacute", "ecircumflex", "edieresis", "igrave", "iacute", "icircumflex", "idieresis", "eth", "ntilde", "ograve", "oacute", "ocircumflex", "otilde", "odieresis", "divide", "oslash", "ugrave", "uacute", "ucircumflex", "udieresis", "yacute", "thorn", "ydieresis" }; static char *c4[64] = { "Amacron", "amacron", "Abreve", "abreve", "Aogonek", "aogonek", "Cacute", "cacute", "Ccircumflex", "ccircumflex", "Cdotaccent", "cdotaccent", "Ccaron", "ccaron", "Dcaron", "dcaron", "Dcroat", "dcroat", "Emacron", "emacron", "Ebreve", "ebreve", "Edotaccent", "edotaccent", "Eogonek", "eogonek", "Ecaron", "ecaron", "Gcircumflex", "gcircumflex", "Gbreve", "gbreve", "Gdotaccent", "gdotaccent", "Gcommaaccent", "gcommaaccent", "Hcircumflex", "hcircumflex", "Hbar", "hbar", "Itilde", "itilde", "Imacron", "imacron", "Ibreve", "ibreve", "Iogonek", "iogonek", "Idotaccent", "dotlessi", "IJ", "ij", "Jcircumflex", "jcircumflex", "Kcedilla", "kcedilla", "kgreenlandic", "Lacute", "lacute", "Lcedilla", "lcedilla", "Lcaron", "lcaron", "Ldot" }; static char *c5[64] = { "ldot", "Lslash", "lslash", "Nacute", "nacute", "Ncedilla", "ncedilla", "tmacron", "ncaron", "napostrophe", "Eng", "eng", "Omacron", "omacron", "Obreve", "obreve", "Ohungarumlaut","ohungarumlaut","OE", "oe", "Racute", "racute", "Rcommaaccent", "rcommaaccent", "Rcaron", "rcaron", "Sacute", "sacute", "Scircumflex", "scircumflex", "Scedilla", "scedilla", "Scaron", "scaron", "Tcedilla", "tcedilla", "Tcaron", "tcaron", "Tbar", "tbar", "Utilde", "utilde", "Umacron", "umacron", "Ubreve", "ubreve", "Uring", "uring", "Uhungarumlaut","uhungarumlaut","Uogonek", "uogonek", "Wcircumflex", "wcircumflex", "Ycircumflex", "ycircumflex", "Ydieresis", "Zacute", "zacute", "Zdotaccent", "zdotaccent", "Zcaron", "zcaron", "longs" }; static char *ce[64] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "Delta", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static char *e299[64] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // NULL, NULL, NULL, NULL, NULL, "uni266D", "uni266E", "uni266F", NULL, NULL, NULL, NULL, NULL, "flat", "natural", "sharp", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static char **e2[64] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, e299, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static char *f09d84[64] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // NULL, NULL, "u1D12A", "u1D12B", NULL, NULL, "double_sharp", "double_flat", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static char **f09d[64] = { NULL, NULL, NULL, NULL, f09d84, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static char ***f0[64] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, f09d, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; /* 1st character - c2..ff */ static char **utf_1[62] = { c2, c3, c4, c5, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ce, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, (char **) e2, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, (char **) f0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }; /* output the glyph of a utf-8 character */ /* return the next character */ char *glyph_out(char *p) { int i1, i2, i3, i4; char **g, *q; g = NULL; i1 = (unsigned char) *p++ - 0xc2; i2 = (unsigned char) *p++ - 0x80; if (i1 >= 0xe0 - 0xc2) { i3 = (unsigned char) *p++ - 0x80; if (i1 >= 0xf0 - 0xc2) i4 = (unsigned char) *p++ - 0x80; else i4 = -1; } else { i3 = -1; i4 = -1; } if (i1 >= 0 && i2 >= 0) { g = (char **) utf_1[i1]; if (g) { g = (char **) g[i2]; if (i3 >= 0 && g) { g = (char **) g[i3]; if (i4 >= 0 && g) g = (char **) g[i4]; } } q = (char *) g; } else { q = NULL; } if (!q) q = ".notdef"; a2b("/%s", q); return p; } /* -- add a glyph -- */ /* %%glyph hex_value glyph_name */ void glyph_add(char *p) { int val, i1, i2, i3, i4; char **g, **g1, *q; val = strtoul(p, &q, 16); /* unicode value */ if (val < 0x80 || val >= 0x100000) { error(1, 0, "Bad unicode value '%s'", p); return; } p = q; while (isspace(*p)) p++; i3 = i4 = -1; if (val < 0x0400) { i1 = (val >> 6) - 2; i2 = val & 0x3f; } else if (val < 0x10000) { i1 = (val >> 12) + 0x20 - 2; i2 = (val >> 6) & 0x3f; i3 = val & 0x3f; } else { i1 = (val >> 18) + 0x30 - 2; i2 = (val >> 12) & 0x3f; i3 = (val >> 6) & 0x3f; i4 = val & 0x3f; } g1 = utf_1[i1]; if (!g1) { g1 = calloc(64, sizeof(char *)); utf_1[i1] = g1; } if (i3 < 0) { g1[i2] = strdup(p); return; } g = (char **) g1[i2]; if (!g) { g = calloc(64, sizeof(char *)); g1[i2] = (char *) g; } if (i4 < 0) { g[i3] = strdup(p); return; } g1 = (char **) g[i3]; if (!g1) { g1 = calloc(64, sizeof(char *)); g[i3] = (char *) g1; } g1[i4] = strdup(p); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������abcm2ps-8.14.11/glyphs.abc��������������������������������������������������������������������������0000664�0000000�0000000�00000003411�13762665467�0015217�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������% Definition of the glyph names of some musical unicode characters % for use in PostScript output (without pango) with abcm2ps. % These values were extracted from FreeSerif.ttf. % %%glyph 2669 quarternote %%glyph 266a musicalnote %%glyph 266b musicalnotedbl %%glyph 266c beamedsixteenthnotes %%glyph 266d flat %%glyph 266e natural %%glyph 266f sharp %%glyph 1d109 dal_segno %%glyph 1d10a dal_capo %%glyph 1d10b segno %%glyph 1d10c coda %%glyph 1d110 fermata %%glyph 1d112 breath_mark %%glyph 1d113 caesura %%glyph 1d114 brace %%glyph 1d115 bracket %%glyph 1d11e g_clef %%glyph 1d11f g_clef_ottava_alta %%glyph 1d120 g_clef_ottava_bassa %%glyph 1d121 c_clef %%glyph 1d122 f_clef %%glyph 1d123 f_clef_ottava_alta %%glyph 1d124 f_clef_ottava_bassa %%glyph 1d125 drum_clef_1 %%glyph 1d126 drum_clef_2 %%glyph 1d12a double_sharp %%glyph 1d12b double_flat %%glyph 1d134 common_time %%glyph 1d135 cut_time %%glyph 1d136 ottava_alta %%glyph 1d137 ottava_bassa %%glyph 1d13d quarter_rest %%glyph 1d143 x_notehead %%glyph 1d144 plus_notehead %%glyph 1d145 circle_x_notehead %%glyph 1d15d whole_note %%glyph 1d15e half_note %%glyph 1d15f quarter_note %%glyph 1d160 eighth_note %%glyph 1d18d subito %%glyph 1d18e z_mus %%glyph 1d18f piano %%glyph 1d190 mezzo %%glyph 1d191 forte %%glyph 1d196 tr % Trill %%glyph 1d197 turn % Turn %%glyph 1d19b ornament_stroke_1 % Ornament left vertical stroke %%glyph 1d19c ornament_stroke_2 % Ornament zig-zag line without right-hand end %%glyph 1d19d ornament_stroke_3 % Ornament zig-zag line with right-hand end %%glyph 1d19e ornament_stroke_4 % Ornament top right convex stroke %%glyph 1d1a0 ornament_stroke_6 % Ornament middle vertical stroke %%glyph 1d1a3 ornament_stroke_9 % Ornament low right concave stroke %%glyph 1d1ae pedal_mark %%glyph 1d1af pedal_up_mark �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������abcm2ps-8.14.11/landscape.fmt�����������������������������������������������������������������������0000664�0000000�0000000�00000000223�13762665467�0015702�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ % format for wide output in landscape mode landscape 1 titleleft yes titlefont Palatino-Bold 22 composerspace 0.6cm staffsep 60 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������abcm2ps-8.14.11/music.c�����������������������������������������������������������������������������0000664�0000000�0000000�00000335721�13762665467�0014542�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Music generator. * * This file is part of abcm2ps. * * Copyright (C) 1998-2019 Jean-François Moine (http://moinejf.free.fr) * Adapted from abc2ps, Copyright (C) 1996-1998 Michael Methfessel * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. */ #include <string.h> #include <stdlib.h> #include <ctype.h> #include "abcm2ps.h" struct STAFF_S staff_tb[MAXSTAFF]; /* staff table */ struct SYMBOL *tsnext; /* next line when cut */ float realwidth; /* real staff width while generating */ static int insert_meter; /* insert time signature (1) and indent 1st line (2) */ static float beta_last; /* for last short short line.. */ #define AT_LEAST(a,b) do { float tmp = b; if(a<tmp) a=tmp; } while (0) /* width of notes indexed by log2(note_length) */ float space_tb[NFLAGS_SZ] = { 7, 10, 14.15, 20, 28.3, 40, /* crotchet */ 56.6, 80, 113, 150 }; // width of note heads indexed by s->head float hw_tb[] = {4.5, 5, 6, 8}; static int smallest_duration; /* upper and lower space needed by rests */ static char rest_sp[NFLAGS_SZ][2] = { {18, 18}, {12, 18}, {12, 12}, {8, 12}, {6, 8}, {10, 10}, /* crotchet */ {6, 4}, {10, 0}, {10, 4}, {10, 10} }; /* set the head of the notes */ static float set_heads(struct SYMBOL *s) { struct note *note; int i, m, n; char *p, *q, *r; float w, wmax; static float dx_tb[4] = { 7, 8, 10, 13.3 }; n = s->nhd; wmax = -1; for (m = 0; m <= n; m++) { note = &s->u.note.notes[m]; p = note->head; /* list of heads from parsing */ if (!p) continue; i = s->head; for (;;) { // search the head for the duration q = strchr(p, ','); if (!q) break; if (--i < 0) break; p = q + 1; } if (!q) q = p + strlen(p); r = strchr(p, '/'); if (r && r < q) { // search the head for the stem direction if (s->stem >= 0) q = r; else p = r + 1; } r = strchr(p, ':'); // width separator if (r && r < q) { q = r; sscanf(r, ":%f", &w); if (w > wmax) wmax = w; } note->head = p; note->hlen = q - p; } if (wmax < 0) { wmax = dx_tb[s->head]; if (s->dur >= BASE_LEN * 2 && s->head == H_OVAL) wmax = 13.8; } s->u.note.sdx = wmax / 2; // stem offset return wmax; } /* -- decide whether to shift heads to other side of stem on chords -- */ /* and set the head of the notes */ /* this routine is called only once per tune for normal notes * it is called on setting symbol width for grace notes */ static void set_head_shift(struct SYMBOL *s) { int i, i1, i2, n, sig, d, shift, ps; float dx, dx_max, dx_head; // unsigned char ax_tb[MAXHD], ac_tb[MAXHD]; /* distance for no overlap - index: [prev acc][cur acc] */ // static char dt_tb[4][4] = { // {5, 5, 5, 5}, /* dble sharp */ // {5, 6, 6, 6}, /* sharp */ // {5, 6, 5, 6}, /* natural */ // {5, 5, 5, 5} /* flat */ // }; /* set the heads of the notes with mapping */ dx_head = set_heads(s) + 2; n = s->nhd; if (n == 0) return; // single note /* set the head shifts */ dx = dx_head * 0.78; if (s->flags & ABC_F_GRACE) dx *= 0.5; sig = s->stem; if (sig >= 0) { i1 = 1; i2 = n + 1; ps = s->pits[0]; } else { dx = -dx; i1 = n - 1; i2 = -1; ps = s->pits[n]; } shift = 0; dx_max = 0; for (i = i1; i != i2; i += sig) { d = s->pits[i] - ps; ps = s->pits[i]; if (d == 0) { if (shift) { /* unison on shifted note */ float new_dx = s->u.note.notes[i].shhd = s->u.note.notes[i - sig].shhd + dx; if (dx_max < new_dx) dx_max = new_dx; continue; } if (i + sig != i2 /* second after unison */ //fixme: should handle many unisons after second && ps + sig == s->pits[i + sig]) { s->u.note.notes[i].shhd = -dx; if (dx_max < -dx) dx_max = -dx; continue; } } if (d < 0) d = -d; if (d > 3 || (d >= 2 && s->head < H_SQUARE)) { shift = 0; } else { shift = !shift; if (shift) { s->u.note.notes[i].shhd = dx; if (dx_max < dx) dx_max = dx; } } } s->xmx = dx_max; /* shift the dots */ } // set the accidental shifts for a set of chords static void acc_shift(struct note *notes[], int n, float dx_head) { int i, i1, ps, p1, acc; float dx, dx1; // set the shifts from the head shifts for (i = n - 1; --i >= 0; ) { // (no shift on top) dx = notes[i]->shhd; if (dx == 0 || dx > 0) continue; dx = dx_head - dx; ps = notes[i]->pit; for (i1 = n; --i1 >= 0; ) { if (!notes[i1]->acc) continue; p1 = notes[i1]->pit; if (p1 < ps - 3) break; if (p1 > ps + 3) continue; if (notes[i1]->shac < dx) notes[i1]->shac = dx; } } for (i = n; --i >= 0; ) { // from top to bottom acc = notes[i]->acc; if (!acc) continue; dx = notes[i]->shac; if (dx == 0) { dx = notes[i]->shhd; if (dx < 0) dx = dx_head - dx; else dx = dx_head; } ps = notes[i]->pit; for (i1 = n; --i1 > i; ) { if (!notes[i1]->acc) continue; p1 = notes[i1]->pit; if (p1 >= ps + 4) { // pitch far enough if (p1 > ps + 4) // if more than a fifth continue; switch (acc) { case A_NULL: case A_FT: case A_DF: continue; } switch (notes[i1]->acc) { case A_NULL: case A_FT: case A_DF: continue; } } if (dx > notes[i1]->shac - 6) { dx1 = notes[i1]->shac + 7; if (dx1 > dx) dx = dx1; } } notes[i]->shac = dx; } } /* set the horizontal shift of accidentals */ /* this routine is called only once per tune */ static void set_acc_shft(void) { struct SYMBOL *s, *s2; int i, staff, t, acc, n, nx; float dx_head; struct note *notes[MAXHD * 4]; // (max = 4 voices per staff) struct note *nt; s = tsfirst; while (s) { if (s->abc_type != ABC_T_NOTE || (s->flags & ABC_F_INVIS)) { s = s->ts_next; continue; } staff = s->staff; t = s->time; acc = 0; for (s2 = s; s2; s2 = s2->ts_next) { if (s2->time != t || s2->abc_type != ABC_T_NOTE || s2->staff != staff) break; if (acc) continue; for (i = 0; i <= s2->nhd; i++) { if (s2->u.note.notes[i].acc) { acc = 1; continue; } } } if (!acc) { s = s2; continue; } dx_head = set_heads(s) + 2; n = 0; for ( ; s != s2; s = s->ts_next) { for (i = 0; i <= s->nhd; i++) notes[n++] = &s->u.note.notes[i]; } // sort the notes for (;;) { nx = 0; for (i = 1; i < n; i++) { if (notes[i]->pit >= notes[i - 1]->pit) continue; nt = notes[i]; notes[i] = notes[i - 1]; notes[i - 1] = nt; nx++; } if (nx == 0) break; } acc_shift(notes, n, dx_head); } } /* -- unlink a symbol -- */ void unlksym(struct SYMBOL *s) { // if (!s->next) { // if (s->extra) { // s->type = FMTCHG; // s->aux = -1; // return; // } // } else { if (s->next) { s->next->prev = s->prev; // if (s->extra) { // struct SYMBOL *g; // // g = s->next->extra; // if (!g) { // s->next->extra = s->extra; // } else { // for (; g->next; g = g->next) // ; // g->next = s->extra; // } // } } if (s->prev) s->prev->next = s->next; else voice_tb[s->voice].sym = s->next; if (s->ts_next) { if (s->extra) { struct SYMBOL *g; g = s->ts_next->extra; if (!g) { s->ts_next->extra = s->extra; } else { for (; g->next; g = g->next) ; g->next = s->extra; } } if ((s->sflags & S_SEQST) && !(s->ts_next->sflags & S_SEQST)) { s->ts_next->sflags |= S_SEQST; s->ts_next->shrink = s->shrink; s->ts_next->space = s->space; } if (s->sflags & S_NEW_SY) s->ts_next->sflags |= S_NEW_SY; s->ts_next->ts_prev = s->ts_prev; } if (s->ts_prev) s->ts_prev->ts_next = s->ts_next; if (tsfirst == s) tsfirst = s->ts_next; if (tsnext == s) tsnext = s->ts_next; } /* -- check if voice combine may occur -- */ static int may_combine(struct SYMBOL *s) { struct SYMBOL *s2; int nhd2; s2 = s->ts_next; if (!s2 || s2->type != NOTEREST) return 0; if (s2->voice == s->voice || s2->staff != s->staff || s2->time != s->time || s2->dur != s->dur) return 0; if (s->combine <= 0 && s2->abc_type != s->abc_type) return 0; if (s->u.note.dc.n + s2->u.note.dc.n >= MAXDC) return 0; //fixme: should check the double decorations if (s->gch && s2->gch) return 0; if (s->abc_type == ABC_T_REST) { if (s2->abc_type == ABC_T_REST && (s->flags & ABC_F_INVIS) && !(s2->flags & ABC_F_INVIS)) return 0; return 1; } if (s2->ly || (s2->sflags & (S_SL1 | S_SL2)) || s2->u.note.slur_st != 0 || s2->u.note.slur_end != 0) return 0; if ((s2->sflags ^ s->sflags) & (S_BEAM_ST | S_BEAM_END)) return 0; nhd2 = s2->nhd; if (s->nhd + nhd2 + 1 >= MAXHD) return 0; if (s->combine <= 1 && s->pits[0] <= s2->pits[nhd2] + 1) return 0; return 1; } /* -- combine 2 voices -- */ static void do_combine(struct SYMBOL *s) { struct SYMBOL *s2; int i, m, nhd, nhd2, type; again: nhd = s->nhd; s2 = s->ts_next; s2->extra = NULL; if (s->abc_type != s2->abc_type) { /* if note and rest */ if (s2->abc_type != ABC_T_REST) { s2 = s; s = s2->ts_next; } goto delsym2; } if (s->abc_type == ABC_T_REST) { if ((s->flags & ABC_F_INVIS) && !(s2->flags & ABC_F_INVIS)) s->flags &= ~ABC_F_INVIS; goto delsym2; } /* combine the voices */ nhd2 = s2->nhd + 1; memcpy(&s->u.note.notes[nhd + 1], s2->u.note.notes, sizeof s->u.note.notes[0] * nhd2); memcpy(&s->pits[nhd + 1], s2->pits, sizeof s->pits[0] * (nhd2 + 1)); s->sflags |= s2->sflags & (S_SL1 | S_SL2 | S_TI1); nhd += nhd2; s->nhd = nhd; sort_pitch(s); /* sort the notes by pitch */ if (s->combine >= 3) { // remove unison heads for (m = nhd; m > 0; m--) { if (s->u.note.notes[m].pit == s->u.note.notes[m - 1].pit && s->u.note.notes[m].acc == s->u.note.notes[m - 1].acc) { i = nhd - m; if (i > 0) { memmove(&s->u.note.notes[m], &s->u.note.notes[m + 1], sizeof s->u.note.notes[0] * i); memmove(&s->pits[m], &s->pits[m + 1], sizeof s->pits[0] * i); } s->nhd = --nhd; } } } s->ymx = 3 * (s->pits[nhd] - 18) + 4; s->ymn = 3 * (s->pits[0] - 18) - 4; /* force the tie directions */ type = s->u.note.notes[0].ti1; if ((type & 0x0f) == SL_AUTO) s->u.note.notes[0].ti1 = SL_BELOW | (type & ~SL_DOTTED); type = s->u.note.notes[nhd].ti1; if ((type & 0x0f) == SL_AUTO) s->u.note.notes[nhd].ti1 = SL_ABOVE | (type & ~SL_DOTTED); delsym2: if (s2->text && !s->text) { s->text = s2->text; s->gch = s2->gch; } if (s2->u.note.dc.n > 0) { // update the added decorations int n; for (i = 0; i < s2->u.note.dc.n; i++) { if (s2->u.note.dc.tm[i].m >= 0) s2->u.note.dc.tm[i].m += nhd + 1; } n = s->u.note.dc.n; memcpy(&s->u.note.dc.tm[n], s2->u.note.dc.tm, sizeof s->u.note.dc.tm[0] * s2->u.note.dc.n); s->u.note.dc.n += s2->u.note.dc.n; } unlksym(s2); /* remove the next symbol */ /* there may be more voices */ if (!(s->sflags & S_IN_TUPLET) && may_combine(s)) goto again; } /* -- try to combine voices */ static void combine_voices(void) { struct SYMBOL *s, *s2, *g; int i, r; for (s = tsfirst; s->ts_next; s = s->ts_next) { if (s->combine < 0) continue; if (s->combine == 0 && s->abc_type != ABC_T_REST) continue; if (s->sflags & S_IN_TUPLET) { g = s->extra; if (!g) continue; /* tuplet already treated */ r = 0; for ( ; g; g = g->next) { if (g->type == TUPLET && g->u.tuplet.r_plet > r) r = g->u.tuplet.r_plet; } if (r == 0) continue; i = r; for (s2 = s; s2; s2 = s2->next) { if (!s2->ts_next) break; if (s2->type != NOTEREST) continue; if (!may_combine(s2)) break; if (--i <= 0) break; } if (i > 0) continue; for (s2 = s; /*s2*/; s2 = s2->next) { if (s2->type != NOTEREST) continue; do_combine(s2); if (--r <= 0) break; } continue; } if (s->type != NOTEREST) continue; if (s->abc_type == ABC_T_NOTE) { if (!(s->sflags & S_BEAM_ST)) continue; if (s->sflags & S_BEAM_END) { if (may_combine(s)) do_combine(s); continue; } } else { if (may_combine(s)) do_combine(s); continue; } s2 = s; for (;;) { if (!may_combine(s2)) { s2 = NULL; break; } //fixme: may have rests in beam if (s2->sflags & S_BEAM_END) break; do { s2 = s2->next; } while (s2->type != NOTEREST); } if (!s2) continue; s2 = s; for (;;) { do_combine(s2); //fixme: may have rests in beam if (s2->sflags & S_BEAM_END) break; do { s2 = s2->next; } while (s2->type != NOTEREST); } } } /* -- insert a clef change (treble or bass) before a symbol -- */ static struct SYMBOL *insert_clef(struct SYMBOL *s, int clef_type, int clef_line) { struct VOICE_S *p_voice; struct SYMBOL *new_s; int staff; staff = s->staff; /* don't insert the clef between two bars */ if (s->type == BAR && s->prev && s->prev->type == BAR /* && s->time == s->prev->time */ ) s = s->prev; /* create the symbol */ p_voice = &voice_tb[s->voice]; p_voice->last_sym = s->prev; if (!p_voice->last_sym) p_voice->sym = NULL; p_voice->time = s->time; new_s = sym_add(p_voice, CLEF); new_s->next = s; s->prev = new_s; new_s->u.clef.type = clef_type; new_s->u.clef.line = clef_line; new_s->staff = staff; new_s->aux = 1; /* small clef */ new_s->sflags &= ~S_SECOND; /* link in time */ while (!(s->sflags & S_SEQST)) s = s->ts_prev; // if (!s->ts_prev || s->ts_prev->type != CLEF) if (s->ts_prev->type != CLEF) new_s->sflags |= S_SEQST; new_s->ts_prev = s->ts_prev; // if (new_s->ts_prev) new_s->ts_prev->ts_next = new_s; // else // tsfirst = new_s; new_s->ts_next = s; s->ts_prev = new_s; return new_s; } /* -- set the staff of the floating voices -- */ /* this function is called only once per tune */ static void set_float(void) { struct VOICE_S *p_voice; int staff, staff_chg; struct SYMBOL *s, *s1; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { if (!p_voice->floating) continue; staff_chg = 0; staff = p_voice->staff; for (s = p_voice->sym; s; s = s->next) { signed char up, down; if (!s->dur) { if (staff_chg) s->staff++; continue; } if (!(s->sflags & S_FLOATING)) { staff_chg = 0; continue; } if (s->pits[0] >= 19) { /* F */ staff_chg = 0; continue; } if (s->pits[s->nhd] <= 12) { /* F, */ staff_chg = 1; s->staff++; continue; } up = 127; for (s1 = s->ts_prev; s1; s1 = s1->ts_prev) { if (s1->staff != staff || s1->voice == s->voice) break; #if 1 /*fixme:test again*/ if (s1->abc_type == ABC_T_NOTE) #endif if (s1->pits[0] < up) up = s1->pits[0]; } if (up == 127) { if (staff_chg) s->staff++; continue; } if (s->pits[s->nhd] > up - 3) { staff_chg = 0; continue; } down = -127; for (s1 = s->ts_next; s1; s1 = s1->ts_next) { if (s1->staff != staff + 1 || s1->voice == s->voice) break; #if 1 /*fixme:test again*/ if (s1->abc_type == ABC_T_NOTE) #endif if (s1->pits[s1->nhd] > down) down = s1->pits[s1->nhd]; } if (down == -127) { if (staff_chg) s->staff++; continue; } if (s->pits[0] < down + 3) { staff_chg = 1; s->staff++; continue; } up -= s->pits[s->nhd]; down = s->pits[0] - down; if (!staff_chg) { if (up < down + 3) continue; staff_chg = 1; } else { if (up < down - 3) { staff_chg = 0; continue; } } s->staff++; } } } /* -- set the x offset of the grace notes -- */ static float set_graceoffs(struct SYMBOL *s) { struct SYMBOL *g, *next; int m; float xx, dx, gspleft, gspinside, gspright; struct note *notes[MAXHD]; gspleft = (cfmt.gracespace >> 16) * 0.1; gspinside = ((cfmt.gracespace >> 8) & 0xff) * 0.1; gspright = (cfmt.gracespace & 0xff) * 0.1; xx = 0; for (g = s->extra; ; g = g->next) { if (g->type == NOTEREST) break; } g->sflags |= S_BEAM_ST; for ( ; ; g = g->next) { if (g->type != NOTEREST) { if (!g->next) break; continue; } set_head_shift(g); for (m = 0; m <= g->nhd; m++) notes[m] = &g->u.note.notes[m]; acc_shift(notes, g->nhd + 1, 7); dx = 0; for (m = g->nhd; m >= 0; m--) { if (g->u.note.notes[m].shac > dx) dx = g->u.note.notes[m].shac; } xx += dx; g->x = xx; if (g->nflags <= 0) g->sflags |= S_BEAM_ST | S_BEAM_END; next = g->next; if (!next) { g->sflags |= S_BEAM_END; break; } if (next->nflags <= 0 || (next->flags & ABC_F_SPACE)) g->sflags |= S_BEAM_END; if (g->sflags & S_BEAM_END) { next->sflags |= S_BEAM_ST; xx += gspinside / 4; } if (g->nflags <= 0) xx += gspinside / 4; if (g->y > next->y + 8) xx -= 1.5; xx += gspinside; } xx += gspleft + gspright; next = s->next; if (next && next->abc_type == ABC_T_NOTE) { /* if before a note */ if (g->y >= 3 * (next->pits[next->nhd] - 18)) xx -= 1; /* above, a bit closer */ else if ((g->sflags & S_BEAM_ST) && g->y < 3 * (next->pits[0] - 18) - 7) xx += 2; /* below with flag, a bit further */ } /* return the whole width */ return xx; } /* -- compute the width needed by the guitar chords / annotations -- */ static float gchord_width(struct SYMBOL *s, float wlnote, float wlw) { struct SYMBOL *s2; struct gch *gch; int ix; float lspc, rspc, w, alspc, arspc; lspc = rspc = alspc = arspc = 0; for (ix = 0, gch = s->gch; ix < MAXGCH; ix++, gch++) { if (gch->type == '\0') break; switch (gch->type) { default: { /* default = above */ float wl; wl = -gch->x; if (wl > lspc) lspc = wl; w = gch->w + 2 - wl; if (w > rspc) rspc = w; break; } case '<': /* left */ w = gch->w + wlnote; if (w > alspc) alspc = w; break; case '>': /* right */ w = gch->w + s->wr; if (w > arspc) arspc = w; break; } } /* adjust width for no clash */ s2 = s->prev; if (s2) { if (s2->gch) { for (s2 = s->ts_prev; ; s2 = s2->ts_prev) { if (s2 == s->prev) { AT_LEAST(wlw, lspc); break; } if (s2->sflags & S_SEQST) lspc -= s2->shrink; } } if (alspc != 0) AT_LEAST(wlw, alspc); } s2 = s->next; if (s2) { if (s2->gch) { for (s2 = s->ts_next; ; s2 = s2->ts_next) { if (s2 == s->next) { AT_LEAST(s->wr, rspc); break; } if (s2->sflags & S_SEQST) rspc -= 8; } } if (arspc != 0) AT_LEAST(s->wr, arspc); } return wlw; } /* -- set the width needed by the lyrics -- */ static float ly_width(struct SYMBOL *s, float wlw) { struct SYMBOL *k; struct lyrics *ly = s->ly; struct lyl *lyl; struct tblt_s *tblt; float align, xx, w; int i; /* check if the lyrics contain tablature definition */ for (i = 0; i < 2; i++) { tblt = voice_tb[s->voice].tblts[i]; if (!tblt) continue; if (tblt->pitch == 0) { /* yes, no width */ for (i = 0; i < MAXLY; i++) { if ((lyl = ly->lyl[i]) == NULL) continue; lyl->s = 0; } return wlw; } } align = 0; for (i = 0; i < MAXLY; i++) { float swfac, shift; char *p; lyl = ly->lyl[i]; if (!lyl) continue; p = lyl->t; w = lyl->w; swfac = lyl->f->swfac; xx = w + 2 * cwid(' ') * swfac; if (s->type == GRACE) { // %%graceword shift = s->wl; } else if ((isdigit((unsigned char) *p) && strlen(p) > 2) || p[1] == ':' || *p == '(' || *p == ')') { float sz; if (*p == '(') { sz = cwid((unsigned char) *p); } else { sz = 0; while (*p != '\0') { /*fixme: KO when '\ooo'*/ if (*p == '\\') { p++; continue; } sz += cwid((unsigned char) *p); if (*p == ' ') break; p++; } } sz *= swfac; shift = (w - sz + 2 * cwid(' ') * swfac) * VOCPRE; if (shift > 20) shift = 20; shift += sz; if (isdigit((unsigned char) lyl->t[0])) { if (shift > align) align = shift; } } else if (*p == LY_HYPH || *p == LY_UNDER) { shift = 0; } else { shift = xx * VOCPRE; if (shift > 20) shift = 20; } lyl->s = shift; AT_LEAST(wlw, shift); xx -= shift; shift = 2 * cwid(' ') * swfac; for (k = s->next; k; k = k->next) { switch (k->type) { case NOTEREST: if (!k->ly || !k->ly->lyl[i]) xx -= 9; else if (k->ly->lyl[i]->t[0] == LY_HYPH || k->ly->lyl[i]->t[0] == LY_UNDER) xx -= shift; else break; if (xx <= 0) break; continue; case CLEF: case TIMESIG: case KEYSIG: xx -= 10; continue; default: xx -= 5; break; } break; } if (xx > s->wr) s->wr = xx; } if (align > 0) { for (i = 0; i < MAXLY; i++) { if ((lyl = ly->lyl[i]) == 0) continue; if (isdigit((unsigned char) lyl->t[0])) lyl->s = align; } } return wlw; } /* -- set the width of a symbol -- */ /* This routine sets the minimal left and right widths wl,wr * so that successive symbols are still separated when * no extra glue is put between them */ static void set_width(struct SYMBOL *s) { struct SYMBOL *s2; int i, m; float xx, w, wlnote, wlw; switch (s->type) { case NOTEREST: /* set the note widths */ s->wr = wlnote = hw_tb[s->head]; /* room for shifted heads and accidental signs */ if (s->xmx > 0) s->wr += s->xmx + 4; s2 = s->prev; if (s2) { switch (s2->type) { case BAR: case CLEF: case KEYSIG: case TIMESIG: wlnote += 3; break; } } for (m = 0; m <= s->nhd; m++) { xx = s->u.note.notes[m].shhd; if (xx < 0) AT_LEAST(wlnote, -xx + 5); if (s->u.note.notes[m].acc) { AT_LEAST(wlnote, s->u.note.notes[m].shac + ((s->u.note.notes[m].acc & 0xf8) ? 6.5 : 4.5)); } } if (s2) { switch (s2->type) { case BAR: case CLEF: case KEYSIG: case TIMESIG: wlnote -= 3; break; } } /* room for the decorations */ if (s->u.note.dc.n != 0) wlnote += deco_width(s); /* space for flag if stem goes up on standalone note */ if ((s->sflags & (S_BEAM_ST | S_BEAM_END)) == (S_BEAM_ST | S_BEAM_END) && s->stem > 0 && s->nflags > 0) AT_LEAST(s->wr, s->xmx + 9); // was 12, then removed, then back again /* leave room for dots and set their offset */ if (s->dots > 0) { switch (s->head) { case H_SQUARE: case H_OVAL: s->xmx += 2; break; case H_EMPTY: s->xmx += 1; break; } AT_LEAST(s->wr, s->xmx + 12); if (s->dots >= 2) s->wr += 3.5 * (s->dots - 1); } /* if a tremolo on 2 notes, have space for the small beam(s) */ if ((s->sflags & (S_TREM2 | S_BEAM_END)) == (S_TREM2 | S_BEAM_END)) AT_LEAST(wlnote, 20); wlw = wlnote; if (s2) { switch (s2->type) { case NOTEREST: /* extra space when up stem - down stem */ if (s2->abc_type == ABC_T_REST) break; if (s2->stem > 0 && s->stem < 0) AT_LEAST(wlw, 7); /* make sure helper lines don't overlap */ if ((s->y > 27 && s2->y > 27) || (s->y < -3 && s2->y < -3)) AT_LEAST(wlw, 6); /* have ties wide enough */ if (s2->sflags & S_TI1) AT_LEAST(wlw, 14); break; case CLEF: /* extra space at start of line */ if ((s2->sflags & S_SECOND) || s2->aux) break; wlw += 8; break; case KEYSIG: /* case TIMESIG: */ wlw += 4; break; } } /* leave room for guitar chord and annotations */ if (s->gch) wlw = gchord_width(s, wlnote, wlw); /* leave room for vocals under note */ /* related to draw_lyrics() */ if (s->ly) wlw = ly_width(s, wlw); /* if preceeded by a grace note sequence, adjust */ if (s2 && s2->type == GRACE) s->wl = wlnote - 4.5; else s->wl = wlw; break; case SPACE: xx = s->u.note.notes[0].shhd * 0.5; s->wr = xx; if (s->gch) xx = gchord_width(s, xx, xx); if (s->u.note.dc.n != 0) xx += deco_width(s); s->wl = xx; break; case BAR: // if (s->sflags & S_NOREPBRA) // break; if (!(s->flags & ABC_F_INVIS)) { int bar_type; bar_type = s->u.bar.type; switch (bar_type) { case B_BAR: w = 5 + 3; break; case (B_BAR << 4) + B_COL: case (B_COL << 4) + B_BAR: w = 5 + 3 + 3 + 5; break; case (B_COL << 4) + B_COL: w = 5 + 5 + 3 + 3 + 3 + 5; break; default: w = 5; for (;;) { switch (bar_type & 0x0f) { case B_OBRA: case B_CBRA: w += 3; break; case B_COL: w += 2; break; } bar_type >>= 4; if (bar_type == 0) break; w += 3; } break; } s->wl = w; if (s->next && s->next->type != TIMESIG) s->wr = 8; else s->wr = 5; // s->shhd[0] = (w - 5) * -0.5; } if (s->u.bar.dc.n != 0) s->wl += deco_width(s); /* have room for the repeat numbers / guitar chord */ if (s->gch && strlen(s->text) < 4) s->wl = gchord_width(s, s->wl, s->wl); break; case CLEF: /* shift the clef to the left - see draw_symbols() */ // if (!(s->flags & ABC_F_INVIS)) { s->wl = 12 + 10; s->wr = (s->aux ? 10 : 12) - 10; // } break; case KEYSIG: { int n1, n2, esp; s->wl = 3; esp = 4; if (s->u.key.nacc == 0) { n1 = s->u.key.sf; /* new key sig */ if (cfmt.cancelkey || n1 == 0) n2 = s->aux; /* old key */ else n2 = 0; if (n1 * n2 >= 0) { /* if no natural */ if (n1 < 0) n1 = -n1; if (n2 < 0) n2 = -n2; if (n2 > n1) n1 = n2; } else { n1 -= n2; if (n1 < 0) n1 = -n1; esp += 3; /* see extra space in draw_keysig() */ } } else { int last_acc; n1 = n2 = s->u.key.nacc; last_acc = s->u.key.accs[0]; for (i = 1; i < n2; i++) { if (s->u.key.pits[i] > s->u.key.pits[i - 1] + 6 || s->u.key.pits[i] < s->u.key.pits[i - 1] - 6) n1--; /* octave */ else if (s->u.key.accs[i] != last_acc) esp += 3; last_acc = s->u.key.accs[i]; } } s->wr = 5.5 * n1 + esp; break; } case TIMESIG: /* !!tied to draw_timesig()!! */ w = 0; for (i = 0; i < s->u.meter.nmeter; i++) { int l; l = sizeof s->u.meter.meter[i].top; if (s->u.meter.meter[i].top[l - 1] == '\0') { l = strlen(s->u.meter.meter[i].top); if (s->u.meter.meter[i].top[1] == '|' || s->u.meter.meter[i].top[1] == '.') l--; /* 'C|' */ } if (s->u.meter.meter[i].bot[0] != '\0') { int l2; l2 = sizeof s->u.meter.meter[i].bot; if (s->u.meter.meter[i].bot[l2 - 1] == '\0') l2 = strlen(s->u.meter.meter[i].bot); if (l2 > l) l = l2; } w += 6.5 * l; } s->wl = w; s->wr = w + 7; break; case MREST: s->wl = 40 / 2 + 16; s->wr = 40 / 2 + 16; break; case GRACE: s->wl = set_graceoffs(s); if (s->ly) ly_width(s, 0); break; case STBRK: if (s->next && s->next->type == CLEF) { s->wr = 2; s->next->aux = 0; /* big clef */ } else { s->wr = 8; } s->wl = s->xmx; break; #if 0 case TEMPO: case PART: case TUPLET: case CUSTOS: #endif case FMTCHG: /* no space */ break; default: bug("Cannot set width for symbol", 1); } } /* -- set the natural space -- */ static float set_space(struct SYMBOL *s) { struct SYMBOL *s2; int i, len, l, stemdir, prev_time; float space; //fixme: s->ts_prev never NULL ? // prev_time = !s->ts_prev ? s->time : s->ts_prev->time; prev_time = s->ts_prev->time; len = s->time - prev_time; /* time skip */ if (len == 0) { switch (s->type) { case MREST: return s->wl + 16; /*fixme:do same thing at start of line*/ case NOTEREST: if (s->ts_prev->type == BAR) { i = 2; if (s->nflags < -2) i = 0; return space_tb[i]; } break; } return 0; } if (s->ts_prev->type == MREST) return s->ts_prev->wr + 16 + 3; // (bar wl=5 wr=8) if (smallest_duration >= MINIM) { if (smallest_duration >= SEMIBREVE) len /= 4; else len /= 2; } if (len >= CROTCHET) { if (len < MINIM) i = 5; else if (len < SEMIBREVE) i = 6; else if (len < BREVE) i = 7; else if (len < BREVE * 2) i = 8; else i = 9; } else { if (len >= QUAVER) i = 4; else if (len >= SEMIQUAVER) i = 3; else if (len >= SEMIQUAVER / 2) i = 2; else if (len >= SEMIQUAVER / 4) i = 1; else i = 0; } l = len - ((SEMIQUAVER / 8) << i); space = space_tb[i]; if (l != 0) { if (l < 0) { space = space_tb[0] * len / (SEMIQUAVER / 8); } else { if (i >= 9) i = 8; space += (space_tb[i + 1] - space_tb[i]) * l / len; } } if (s->dur == 0) { if (s->type == BAR) { if (s->u.bar.type & 0xf0) space *= 0.8; /* complex bar */ else space *= 0.7; } return space; } /* reduce spacing within a beam */ if (!(s->sflags & S_BEAM_ST)) space *= fnnp; /* decrease spacing when stem down followed by stem up */ /*fixme:to be done later, after x computed in sym_glue*/ if (s->abc_type == ABC_T_NOTE && s->nflags >= -1 && s->stem > 0) { stemdir = 1; for (s2 = s->ts_prev; s2 && s2->time == prev_time; s2 = s2->ts_prev) { if (s2->nflags < -1 || s2->stem > 0) { stemdir = 0; break; } } if (stemdir) { for (s2 = s->ts_next; s2 && s2->time == s->time; s2 = s2->ts_next) { if (s2->nflags < -1 || s2->stem < 0) { stemdir = 0; break; } } if (stemdir) space *= 0.9; } } return space; } /* -- set the width and space of all symbols -- */ /* this function is called once for the whole tune * then, once per music line up to the first sequence */ static void set_allsymwidth(struct SYMBOL *last_s) { #if 1 // float space; float new_val, maxx; struct SYMBOL *s = tsfirst, *s2; float xa = 0; float xl[MAXSTAFF]; memset(xl, 0, sizeof xl); /* loop on all symbols */ while (1) { maxx = xa; s2 = s; // space = 0; do { set_width(s); new_val = xl[s->staff] + s->wl; if (new_val > maxx) maxx = new_val; // if (s->ts_prev) { // new_val = set_space(s); // if (space < new_val) // space = new_val; // } s = s->ts_next; } while (s != last_s && (s->sflags & S_SEQST) == 0); /* set the spaces at start of sequence */ s2->shrink = maxx - xa; if (s2->ts_prev) s2->space = set_space(s2); if (s2->shrink == 0 && s2->space == 0 && s2->type == CLEF) { s2->sflags &= ~S_SEQST; /* no space */ s2->time = s2->ts_prev->time; } if (s == last_s) return; // update the min left space per staff xa = maxx; s = s2; do { if (xl[s->staff] < xa + s->wr) xl[s->staff] = xa + s->wr; s = s->ts_next; } while ((s->sflags & S_SEQST) == 0); } // not reached #else struct VOICE_S *p_voice; struct SYMBOL *s, *s2, *s3; struct tblt_s *tblt; int i; float new_val, shrink, space; /* set the space of the starting symbols */ new_val = 0; s = tsfirst; for (;;) { set_width(s); if (new_val < s->wl) new_val = s->wl; s = s->ts_next; if (s == last_s || (s->sflags & S_SEQST)) break; } tsfirst->shrink = new_val; /* loop on all remaining symbols */ while (s != last_s) { s2 = s; shrink = space = 0; do { int ymx1, ymn1, ymx2, ymn2; float wl; /* set the minimum space before and after the symbol */ set_width(s2); /* calculate the minimum space before the symbol, * looping in the previous time sequence */ if (s2->type == BAR) { ymx1 = 50; ymn1 = -50; } else { ymx1 = s2->ymx; ymn1 = s2->ymn; } wl = s2->wl; new_val = 0; for (s3 = s->ts_prev; s3; s3 = s3->ts_prev) { if (new_val < s3->wr && s3->type == NOTEREST && s2->type == NOTEREST) new_val = s3->wr; if (s3->staff == s2->staff && (!(s3->flags & ABC_F_INVIS) || s3->voice == s2->voice) && new_val < s3->wr + wl) { switch (s3->type) { case NOTEREST: if (s2->type == NOTEREST) { new_val = s3->wr + wl; break; } /* fall thru */ default: ymx2 = s3->ymx; ymn2 = s3->ymn; if (ymn1 > ymx2 || ymx1 < ymn2) break; /* fall thru */ case SPACE: case BAR: case CLEF: case TIMESIG: case KEYSIG: new_val = s3->wr + wl; break; } } if (s3->sflags & S_SEQST) { if (new_val != 0) break; wl -= s3->shrink; if (wl < 0) break; } } if (shrink < new_val) shrink = new_val; new_val = set_space(s2); if (space < new_val) space = new_val; if ((s2 = s2->ts_next) == last_s) break; } while (!(s2->sflags & S_SEQST)); /* set the spaces at start of sequence */ if (shrink == 0 && space == 0 && s->type == CLEF) { s->sflags &= ~S_SEQST; /* no space */ s->time = s->ts_prev->time; } else { s->shrink = shrink; s->space = space; } s = s2; } /* have room for the tablature header */ space = 0; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { for (i = 0; i < 2; i++) { if ((tblt = p_voice->tblts[i]) == NULL) continue; if (tblt->wh > space) space = tblt->wh; } } if (space == 0) return; shrink = 0; for (s = tsfirst; s != last_s; s = s->ts_next) { if (s->shrink != 0) shrink += s->shrink; if (s->abc_type == ABC_T_NOTE) break; } if (s != last_s && shrink < space) { while (!(s->sflags & S_SEQST)) s = s->ts_prev; s->shrink += space - shrink; } #endif } /* change a symbol into a rest */ static void to_rest(struct SYMBOL *s) { s->type = NOTEREST; s->abc_type = ABC_T_REST; s->sflags &= S_NL | S_SEQST; s->doty = -1; s->u.note.dc.n = 0; s->gch = NULL; s->extra = NULL; s->u.note.slur_st = s->u.note.slur_end = 0; /*fixme: should set many parameters for set_width*/ // set_width(s); } /* -- set the repeat sequences / measures -- */ static void set_repeat(struct SYMBOL *g, /* repeat format */ struct SYMBOL *s) /* first note */ { struct SYMBOL *s2, *s3; int i, j, n, dur, staff, voice; staff = s->staff; voice = s->voice; /* treat the sequence repeat */ if ((n = g->doty) < 0) { /* number of notes / measures */ n = -n; i = n; /* number of notes to repeat */ for (s3 = s->prev; s3; s3 = s3->prev) { if (s3->dur == 0) { if (s3->type == BAR) { error(0, s3, "Bar in sequence to repeat"); goto delrep; } continue; } if (--i <= 0) break; } if (!s3) { error(0, s, "Not enough symbols to repeat"); goto delrep; } dur = s->time - s3->time; i = g->nohdi1 * n; /* number of notes/rests to repeat */ for (s2 = s; s2; s2 = s2->next) { if (s2->dur == 0) { if (s2->type == BAR) { error(0, s2, "Bar in repeat sequence"); goto delrep; } continue; } if (--i <= 0) break; } if (!s2 || !s2->next) { /* should have some symbol */ error(0, s, "Not enough symbols after repeat sequence"); goto delrep; } for (s2 = s->prev; s2 != s3; s2 = s2->prev) { if (s2->abc_type == ABC_T_NOTE) { s2->sflags |= S_BEAM_END; break; } } for (j = g->nohdi1; --j >= 0; ) { i = n; /* number of notes/rests */ if (s->dur != 0) i--; s2 = s->ts_next; while (i > 0) { if (s2->staff == staff) { s2->extra = NULL; unlksym(s2); if (s2->voice == voice && s2->dur) i--; } s2 = s2->ts_next; } to_rest(s); s->dur = s->u.note.notes[0].len = dur; // s->sflags |= S_REPEAT | S_BEAM_ST; s->sflags |= S_REPEAT; set_width(s); if (s->sflags & S_SEQST) s->space = set_space(s); s->head = H_SQUARE; for (s = s2; s; s = s->ts_next) { if (s->staff == staff && s->voice == voice && s->dur) break; } } goto delrep; /* done */ } /* check the measure repeat */ i = n; /* number of measures to repeat */ for (s2 = s->prev->prev ; s2; s2 = s2->prev) { if (s2->type == BAR || s2->time == tsfirst->time) { if (--i <= 0) break; } } if (!s2) { error(0, s, "Not enough measures to repeat"); goto delrep; } dur = s->time - s2->time; /* repeat duration */ if (n == 1) i = g->nohdi1; /* repeat number */ else i = n; /* check only 2 measures */ for (s2 = s; s2; s2 = s2->next) { if (s2->type == BAR) { if (--i <= 0) break; } } if (!s2) { error(0, s, "Not enough bars after repeat measure"); goto delrep; } /* if many 'repeat 2 measures' * insert a new %%repeat after the next bar */ i = g->nohdi1; /* repeat number */ if (n == 2 && i > 1) { s2 = s2->next; if (!s2) { error(0, s, "Not enough bars after repeat measure"); goto delrep; } g->nohdi1 = 1; s = (struct SYMBOL *) getarena(sizeof *s); memcpy(s, g, sizeof *s); s->next = s2->extra; if (s->next) s->next->prev = s; s->prev = NULL; s2->extra = s; s->nohdi1 = --i; } /* replace */ dur /= n; if (n == 2) { /* repeat 2 measures (once) */ s3 = s; for (s2 = s->ts_next; ;s2 = s2->ts_next) { if (s2->staff != staff) continue; if (s2->voice == voice && s2->type == BAR) break; s2->extra = NULL; unlksym(s2); } to_rest(s3); s3->dur = s3->u.note.notes[0].len = dur; s3->flags = ABC_F_INVIS; if (s3->sflags & S_SEQST) s3->space = set_space(s3); s2->u.bar.len = 2; if (s2->sflags & S_SEQST) s2->space = set_space(s2); s3 = s2->next; for (s2 = s3->ts_next; ;s2 = s2->ts_next) { if (s2->staff != staff) continue; if (s2->voice == voice && s2->type == BAR) break; s2->extra = NULL; unlksym(s2); } to_rest(s3); s3->dur = s3->u.note.notes[0].len = dur; s3->flags = ABC_F_INVIS; set_width(s3); if (s3->sflags & S_SEQST) s3->space = set_space(s3); if (s2->sflags & S_SEQST) s2->space = set_space(s2); return; } /* repeat 1 measure */ s3 = s; for (j = g->nohdi1; --j >= 0; ) { for (s2 = s3->ts_next; ; s2 = s2->ts_next) { if (s2->staff != staff) continue; if (s2->voice == voice && s2->type == BAR) break; s2->extra = NULL; unlksym(s2); } to_rest(s3); s3->dur = s3->u.note.notes[0].len = dur; // s3->sflags |= S_REPEAT | S_BEAM_ST; s3->sflags |= S_REPEAT; if (s3->sflags & S_SEQST) s3->space = set_space(s3); if (s2->sflags & S_SEQST) s2->space = set_space(s2); if (g->nohdi1 == 1) { s3->doty = 1; break; } s3->doty = g->nohdi1 - j + 1; /* number to print above the repeat rest */ s3 = s2->next; } return; delrep: /* remove the %%repeat */ g->aux = -1; } /* add a custos before the symbol of the next line */ static void custos_add(struct SYMBOL *s) { struct VOICE_S *p_voice; struct SYMBOL *new_s, *s2; int i; s2 = s; for (;;) { if (!s2) return; if (s2->abc_type == ABC_T_NOTE) break; s2 = s2->next; } p_voice = &voice_tb[s->voice]; p_voice->last_sym = s->prev; if (!p_voice->last_sym) p_voice->sym = NULL; p_voice->time = s->time; new_s = sym_add(p_voice, CUSTOS); new_s->next = s; s->prev = new_s; new_s->ts_prev = s->ts_prev; new_s->ts_prev->ts_next = new_s; new_s->ts_next = s; s->ts_prev = new_s; new_s->sflags |= S_SEQST; new_s->wl = 8; new_s->wr = 4; new_s->shrink = s->shrink; if (new_s->shrink < 8 + 4) new_s->shrink = 8 + 4; new_s->space = s2->space; new_s->nhd = s2->nhd; for (i = 0; i <= new_s->nhd; i++) { // new_s->u.note.notes[i].pit = s->u.note.notes[i].pit; new_s->pits[i] = s2->pits[i]; new_s->u.note.notes[i].len = CROTCHET; } new_s->flags = ABC_F_STEMLESS; } /* -- define the beginning of a new music line -- */ static struct SYMBOL *set_nl(struct SYMBOL *s) { struct SYMBOL *s2, *extra, *new_sy; struct VOICE_S *p_voice; int done; /* if explicit EOLN, cut on the next symbol */ if ((s->sflags & S_EOLN) && !cfmt.keywarn && !cfmt.timewarn) { s = s->next; if (!s) return s; while (!(s->sflags & S_SEQST)) s = s->ts_prev; goto setnl; } /* if normal symbol, cut here */ switch (s->type) { case CLEF: case BAR: break; case KEYSIG: if (cfmt.keywarn && !s->u.key.empty) break; goto normal; case TIMESIG: if (cfmt.timewarn) break; goto normal; case GRACE: /* don't cut on a grace note */ s = s->next; if (!s) return s; /* fall thru */ default: normal: /* cut on the next symbol */ s = s->next; if (!s) return s; while (!(s->sflags & S_SEQST)) s = s->ts_prev; goto setnl; } /* go back to handle the staff breaks at end of line */ for (; s; s = s->ts_prev) { if (!(s->sflags & S_SEQST)) continue; switch (s->type) { case CLEF: case KEYSIG: case TIMESIG: continue; } break; } done = 0; new_sy = extra = NULL; for ( ; ; s = s->ts_next) { if (!s) return s; if (s->sflags & S_NEW_SY) new_sy = s; if (!(s->sflags & S_SEQST)) continue; if (done < 0) break; switch (s->type) { case BAR: if (done) goto cut_here; done = 1; break; case STBRK: if (s->doty == 0) { /* if not forced */ unlksym(s); /* remove */ break; } done = -1; /* keep the next symbols on the next line */ break; case TIMESIG: if (!cfmt.timewarn) goto cut_here; break; case CLEF: if (done) goto cut_here; break; case KEYSIG: if (!cfmt.keywarn || s->u.key.empty) goto cut_here; break; default: if (!done || (s->prev && s->prev->type == GRACE)) break; goto cut_here; } if (s->extra) { if (!extra) extra = s; else error(0, s, "abcm2ps problem: " "Extra symbol may be misplaced"); } } cut_here: if (extra /* extra symbol(s) to be moved */ && extra != s) { s2 = extra->extra; while (s2->next) s2 = s2->next; s2->next = s->extra; s->extra = extra->extra; extra->extra = NULL; } if (new_sy && s != new_sy) { new_sy->sflags &= ~S_NEW_SY; s->sflags |= S_NEW_SY; } setnl: if (cfmt.custos && !first_voice->next) { custos_add(s); } else { s2 = s->ts_prev; switch (s2->type) { case BAR: case FMTCHG: case CLEF: case KEYSIG: case TIMESIG: break; default: /* add an extra symbol at eol */ p_voice = &voice_tb[s2->voice]; p_voice->last_sym = s2; p_voice->time = s->time; s2 = s2->next; extra = sym_add(p_voice, FMTCHG); extra->next = s2; if (s2) s2->prev = extra; extra->ts_prev = extra->prev; extra->ts_prev->ts_next = extra; extra->ts_next = s; s->ts_prev = extra; extra->aux = -1; extra->sflags |= S_SEQST; extra->wl = 6; //fixme: wr/shrink/space are not needed extra->wr = 6; // extra->shrink = extra->prev->wr + 6; // extra->space = extra->prev->space; extra->shrink = s->shrink; extra->space = s->space; if (s->x != 0) extra->x = s->x - 1; // { /* auto break */ // for (s2 = s->ts_next; ; s2 = s2->ts_next) { // if (s2->x != 0) { // extra->x = s2->x - 1; // break; // } // } // } break; } } s->sflags |= S_NL; return s; } /* -- search where to cut the lines according to the staff width -- */ static struct SYMBOL *set_lines(struct SYMBOL *first, /* first symbol */ struct SYMBOL *last, /* last symbol / 0 */ float lwidth, /* w - (clef & key sig) */ float indent) /* for start of tune */ { struct SYMBOL *s, *s2, *s3; float x, xmin, xmax, wwidth, shrink, space; int nlines, beam, bar_time; /* calculate the whole size of the piece of tune */ wwidth = indent; for (s = first; s != last; s = s->ts_next) { if (!(s->sflags & S_SEQST)) continue; s->x = wwidth; shrink = s->shrink; if ((space = s->space) < shrink) wwidth += shrink; else wwidth += shrink * cfmt.maxshrink + space * (1 - cfmt.maxshrink); } /* loop on cutting the tune into music lines */ s = first; for (;;) { nlines = wwidth / lwidth + 0.999; if (nlines <= 1) { if (last) last = set_nl(last); return last; } /* try to cut on a measure bar */ s2 = first = s; xmin = s->x + wwidth / nlines * cfmt.breaklimit; xmax = s->x + lwidth; for ( ; s != last; s = s->ts_next) { x = s->x; if (x == 0) continue; if (x > xmax) break; if (s->type != BAR) continue; if (x > xmin) goto cut_here; s2 = s; // keep the last bar } /* if a bar, cut here */ if (s == last) return last; if (s->type == BAR) goto cut_here; bar_time = s2->time; /* try to avoid to cut a beam */ beam = s2->type == NOTEREST && (s2->sflags & (S_BEAM_ST | S_BEAM_END)) == 0 ? 1 : 0; s = s2; /* restart from start or last bar */ s2 = s3 = NULL; xmax -= 6; // a FORMAT will be added for ( ; s != last; s = s->ts_next) { if ((s->sflags & (S_BEAM_ST | S_BEAM_END)) == S_BEAM_ST) { beam++; continue; } if ((s->sflags & (S_BEAM_ST | S_BEAM_END)) == S_BEAM_END) beam--; x = s->x; if (x < xmin) continue; //--fixme // if (x + 2 * s->shrink >= xmax) if (x + s->shrink >= xmax) break; if (beam != 0) continue; s2 = s; if ((s->time - bar_time) % (CROTCHET / 2) == 0) s3 = s; } if (s3) s2 = s3; if (s2) s = s2; while (s->x == 0 || s->x + s->shrink * 2 >= xmax) s = s->ts_prev; cut_here: if (s->sflags & S_NL) { /* already set here - advance */ error(0, s, "Line split problem - " "adjust maxshrink and/or breaklimit"); nlines = 2; for (s = s->ts_next; s != last; s = s->ts_next) { if (s->x == 0) continue; if (--nlines <= 0) break; } } s = set_nl(s); if (!s || (last && s->time >= last->time)) break; wwidth -= s->x - first->x; } return s; } /* -- cut the tune into music lines -- */ static void cut_tune(float lwidth, float indent) { struct VOICE_S *p_voice; struct SYMBOL *s, *s2; int i; float xmin; /* adjust the line width according to the starting clef * and key signature */ /*fixme: may change in the tune*/ #if 1 s = tsfirst; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { i = p_voice - voice_tb; if (cursys->voice[i].range >= 0) break; } lwidth -= 12 + 10 // clef.wl + 12 - 10 // clef.wr + 3 // key.wl + 3 + p_voice->key.sf * 5.5; // key.wr #else for (s = tsfirst; s; s = s->ts_next) { if (s->shrink == 0) continue; if (s->type != CLEF && s->type != KEYSIG) break; lwidth -= s->shrink; } #endif if (cfmt.custos && !first_voice->next) lwidth -= 12; if (cfmt.continueall) { set_lines(s, NULL, lwidth, indent); return; } /* if asked, count the measures and set the EOLNs */ if ((i = cfmt.barsperstaff) != 0) { s2 = s; for ( ; s; s = s->ts_next) { if (s->type != BAR || s->aux == 0) continue; if (--i > 0) continue; s->sflags |= S_EOLN; i = cfmt.barsperstaff; } s = s2; } /* cut at explicit end of line, checking the line width */ xmin = indent; s2 = s; for ( ; s; s = s->ts_next) { if (!(s->sflags & (S_SEQST | S_EOLN))) continue; xmin += s->shrink; if (xmin > lwidth) { if (cfmt.linewarn) error(0, s, "Line overfull (%.0fpt of %.0fpt)", xmin, lwidth); // for (s = s->ts_next; s; s = s->ts_next) { for ( ; s; s = s->ts_next) { if (s->sflags & S_EOLN) break; } s = s2 = set_lines(s2, s, lwidth, indent); if (!s) break; xmin = s->shrink; indent = 0; continue; } if (!(s->sflags & S_EOLN)) continue; s2 = set_nl(s); s->sflags &= ~S_EOLN; s = s2; if (!s) break; xmin = s->shrink; indent = 0; } } /* -- set the y values of some symbols -- */ static void set_yval(struct SYMBOL *s) { //fixme: staff_tb is not yet loaded // int top, bot; // top = staff_tb[s->staff].topbar; // bot = staff_tb[s->staff].botbar; switch (s->type) { case CLEF: if ((s->sflags & S_SECOND) || (s->flags & ABC_F_INVIS)) { // s->ymx = s->ymn = (top + bot) / 2; s->ymx = s->ymn = 12; break; } s->y = (s->u.clef.line - 1) * 6; switch (s->u.clef.type) { default: /* treble / perc */ // s->y = -2 * 6; // s->ymx = 24 + 12; // s->ymn = -9; s->ymx = s->y + 28; s->ymn = s->y - 14; break; case ALTO: // s->y = -3 * 6; // s->ymx = 24 + 2; // s->ymn = -1; s->ymx = s->y + 13; s->ymn = s->y - 11; break; case BASS: // s->y = -4 * 6; // s->ymx = 24 + 2; // s->ymn = 2; s->ymx = s->y + 7; s->ymn = s->y - 12; break; } if (s->aux) { // small clef s->ymx -= 2; s->ymn += 2; } if (s->ymx < 26) s->ymx = 26; if (s->ymn > -1) s->ymn = -1; // s->y += s->u.clef.line * 6; // if (s->y > 0) // s->ymx += s->y; // else if (s->y < 0) // s->ymn += s->y; if (s->u.clef.octave > 0) s->ymx += 9; else if (s->u.clef.octave < 0) s->ymn -= 9; break; case KEYSIG: if (s->u.key.sf > 2) s->ymx = 24 + 10; else if (s->u.key.sf > 0) s->ymx = 24 + 6; else s->ymx = 24 + 2; s->ymn = -2; break; default: // s->ymx = top + 2; s->ymx = 24 + 2; s->ymn = -2; break; } } // set the clefs (treble or bass) in a 'auto clef' sequence // return the starting clef type static int set_auto_clef(int staff, struct SYMBOL *s_start, int clef_type_start) { struct SYMBOL *s; struct SYMBOL *s_last, *s_last_chg; int clef_type, min, max, time; /* get the max and min pitches in the sequence */ max = 12; /* "F," */ min = 20; /* "G" */ for (s = s_start; s; s = s->ts_next) { if ((s->sflags & S_NEW_SY) && s != s_start) break; if (s->staff != staff) continue; if (s->abc_type != ABC_T_NOTE) { if (s->type == CLEF) { if (s->u.clef.type != AUTOCLEF) break; unlksym(s); } continue; } if (s->pits[0] < min) min = s->pits[0]; else if (s->pits[s->nhd] > max) max = s->pits[s->nhd]; } if (min >= 19 /* upper than 'F' */ || (min >= 13 && clef_type_start != BASS)) /* or 'G,' */ return TREBLE; if (max <= 13 /* lower than 'G,' */ || (max <= 19 && clef_type_start != TREBLE)) /* or 'F' */ return BASS; /* set clef changes */ if (clef_type_start == AUTOCLEF) { if ((max + min) / 2 >= 16) clef_type_start = TREBLE; else clef_type_start = BASS; } clef_type = clef_type_start; s_last = s; s_last_chg = NULL; for (s = s_start; s != s_last; s = s->ts_next) { struct SYMBOL *s2, *s3; //, *s4; if ((s->sflags & S_NEW_SY) && s != s_start) break; if (s->staff != staff || s->abc_type != ABC_T_NOTE) continue; /* check if a clef change may occur */ time = s->time; if (clef_type == TREBLE) { if (s->pits[0] > 12 /* F, */ || s->pits[s->nhd] > 20) { /* G */ if (s->pits[0] > 20) s_last_chg = s; continue; } s2 = s->ts_prev; if (s2 && s2->time == time && s2->staff == staff && s2->abc_type == ABC_T_NOTE && s2->pits[0] >= 19) /* F */ continue; s2 = s->ts_next; if (s2 && s2->staff == staff && s2->time == time && s2->abc_type == ABC_T_NOTE && s2->pits[0] >= 19) /* F */ continue; } else { if (s->pits[0] < 12 /* F, */ || s->pits[s->nhd] < 20) { /* G */ if (s->pits[s->nhd] < 12) s_last_chg = s; continue; } s2 = s->ts_prev; if (s2 && s2->time == time && s2->staff == staff && s2->abc_type == ABC_T_NOTE && s2->pits[0] <= 13) /* G, */ continue; s2 = s->ts_next; if (s2 && s2->staff == staff && s2->time == time && s2->abc_type == ABC_T_NOTE && s2->pits[0] <= 13) /* G, */ continue; } /* if first change, change the starting clef */ if (!s_last_chg) { clef_type = clef_type_start = clef_type == TREBLE ? BASS : TREBLE; s_last_chg = s; continue; } /* go backwards and search where to insert a clef change */ s3 = s; for (s2 = s->ts_prev; s2 != s_last_chg; s2 = s2->ts_prev) { if (s2->staff != staff) continue; if (s2->type == BAR && s2->voice == s->voice) { s3 = s2; break; } if (s2->abc_type != ABC_T_NOTE) continue; #if 0 /* exit loop if a clef change cannot occur */ if (clef_type == TREBLE) { if (s2->pits[0] >= 19) /* F */ break; } else { if (s2->pits[s2->nhd] <= 13) /* G, */ break; } #endif /* have a 2nd choice on beam start */ if ((s2->sflags & S_BEAM_ST) && !voice_tb[s2->voice].second) s3 = s2; } /* no change possible if no insert point */ if (s3->time == s_last_chg->time) { s_last_chg = s; continue; } s_last_chg = s; /* insert a clef change */ clef_type = clef_type == TREBLE ? BASS : TREBLE; s2 = insert_clef(s3, clef_type, clef_type == TREBLE ? 2 : 4); s2->sflags |= S_CLEF_AUTO; // s3->prev->staff = staff; } return clef_type_start; } /* set the auto clefs */ /* this function is called once at start of tune generation */ /* * global variables: * - staff_tb[staff].clef = clefs at start of line (here, start of tune) * (created here, updated on clef draw) * - voice_tb[voice].clef = clefs at end of generation * (created on voice creation, updated here) */ static void set_clefs(void) { struct SYSTEM *sy; struct VOICE_S *p_voice; struct SYMBOL *s, *s2, *g; int staff, voice, pitch, new_type, new_line, old_lvl; struct { struct SYMBOL *clef; short autoclef; short mid; } staff_clef[MAXSTAFF]; old_lvl = lvlarena(1); // keep the staff clefs // create the staff table memset(staff_tb, 0, sizeof staff_tb); for (staff = 0; staff <= nstaff; staff++) { staff_clef[staff].clef = NULL; staff_clef[staff].autoclef = 1; } // set the starting clefs of the staves sy = cursys; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { voice = p_voice - voice_tb; if (sy->voice[voice].range < 0) continue; staff = sy->voice[voice].staff; if (!sy->voice[voice].second) { // main voices if (p_voice->stafflines) sy->staff[staff].stafflines = p_voice->stafflines; if (p_voice->staffscale != 0) sy->staff[staff].staffscale = p_voice->staffscale; if (sy->voice[voice].sep) sy->staff[staff].sep = sy->voice[voice].sep; if (sy->voice[voice].maxsep) sy->staff[staff].maxsep = sy->voice[voice].maxsep; } s = p_voice->s_clef; if (!sy->voice[voice].second && !(s->sflags & S_CLEF_AUTO)) staff_clef[staff].autoclef = 0; } for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { voice = p_voice - voice_tb; if (sy->voice[voice].range < 0 || sy->voice[voice].second) // main voices continue; staff = sy->voice[voice].staff; s = p_voice->s_clef; if (staff_clef[staff].autoclef) { s->u.clef.type = set_auto_clef(staff, tsfirst, s->u.clef.type); s->u.clef.line = s->u.clef.type == TREBLE ? 2 : 4; } staff_clef[staff].clef = staff_tb[staff].s_clef = s; } for (staff = 0; staff <= sy->nstaff; staff++) staff_clef[staff].mid = (strlen(sy->staff[staff].stafflines) - 1) * 3; for (s = tsfirst; s; s = s->ts_next) { for (g = s->extra ; g; g = g->next) { if (g->type == FMTCHG && g->aux == REPEAT) { set_repeat(g, s); break; } } // handle %%staves if (s->sflags & S_NEW_SY) { sy = sy->next; for (staff = 0; staff <= nstaff; staff++) staff_clef[staff].autoclef = 1; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { voice = p_voice - voice_tb; if (sy->voice[voice].range < 0) continue; staff = sy->voice[voice].staff; if (!sy->voice[voice].second) { if (p_voice->stafflines) sy->staff[staff].stafflines = p_voice->stafflines; if (p_voice->staffscale != 0) sy->staff[staff].staffscale = p_voice->staffscale; if (sy->voice[voice].sep) sy->staff[staff].sep = sy->voice[voice].sep; if (sy->voice[voice].maxsep) sy->staff[staff].maxsep = sy->voice[voice].maxsep; } s2 = p_voice->s_clef; if (!(s2->sflags & S_CLEF_AUTO)) staff_clef[staff].autoclef = 0; } for (staff = 0; staff <= sy->nstaff; staff++) staff_clef[staff].mid = (strlen(sy->staff[staff].stafflines) - 1) * 3; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { voice = p_voice - voice_tb; if (sy->voice[voice].range < 0 || sy->voice[voice].second) continue; staff = sy->voice[voice].staff; s2 = p_voice->s_clef; if (s2->sflags & S_CLEF_AUTO) { //fixme: the staff may have other voices with explicit clefs... // if (!staff_clef[staff].autoclef) // ??? new_type = set_auto_clef(staff, s, staff_clef[staff].clef ? staff_clef[staff].clef->u.clef.type : AUTOCLEF); new_line = new_type == TREBLE ? 2 : 4; } else { new_type = s2->u.clef.type; new_line = s2->u.clef.line; } if (!staff_clef[staff].clef) { // new staff if (s2->sflags & S_CLEF_AUTO) { if (s2->u.clef.type != AUTOCLEF) { p_voice->s_clef = (struct SYMBOL *) getarena(sizeof *s); memcpy(p_voice->s_clef, s2, sizeof *p_voice->s_clef); } p_voice->s_clef->u.clef.type = new_type; p_voice->s_clef->u.clef.line = new_line; } staff_tb[staff].s_clef = staff_clef[staff].clef = p_voice->s_clef; continue; } // old staff if (new_type == staff_clef[staff].clef->u.clef.type && new_line == staff_clef[staff].clef->u.clef.line) continue; g = s; while (g && g->voice != voice) g = g->ts_next; if (!g) continue; if (g->type != CLEF) { g = insert_clef(g, new_type, new_line); if (s2->sflags & S_CLEF_AUTO) g->sflags |= S_CLEF_AUTO; } staff_clef[staff].clef = p_voice->s_clef = g; } } if (s->type != CLEF) { s->mid = staff_clef[s->staff].mid; continue; } if (s->u.clef.type == AUTOCLEF) { s->u.clef.type = set_auto_clef(s->staff, s->ts_next, staff_clef[s->staff].clef->u.clef.type); s->u.clef.line = s->u.clef.type == TREBLE ? 2 : 4; } p_voice = &voice_tb[s->voice]; p_voice->s_clef = s; if (s->sflags & S_SECOND) { /*fixme:%%staves:can this happen?*/ // if (!s->prev) // break; unlksym(s); continue; } staff = s->staff; // may have been inserted on %%staves // if (s->sflags & S_CLEF_AUTO) { // unlksym(s); // continue; // } if (staff_tb[staff].s_clef) { if (s->u.clef.type == staff_clef[staff].clef->u.clef.type && s->u.clef.line == staff_clef[staff].clef->u.clef.line && !(s->sflags & S_NEW_SY)) { // unlksym(s); continue; } } else { // the voice moved to a new staff with a forced clef staff_tb[staff].s_clef = s; } staff_clef[staff].clef = s; } /* set a pitch to the symbols of voices with no note */ sy = cursys; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { voice = p_voice - voice_tb; if (sy->voice[voice].range < 0) continue; s2 = p_voice->sym; if (!s2 || s2->pits[0] != 127) continue; staff = sy->voice[voice].staff; switch (staff_tb[staff].s_clef->u.clef.type) { default: pitch = 22; /* 'B' */ break; case ALTO: pitch = 16; /* 'C' */ break; case BASS: pitch = 10; /* 'D,' */ break; } for (s = s2; s; s = s->next) s->pits[0] = pitch; } lvlarena(old_lvl); } /* -- set the pitch of the notes according to the clefs -- */ /* also treat the auto clefs and set the vertical offset of the symbols */ /* this function is called only once per tune * then, once per music line up to the old sequence */ static void set_pitch(struct SYMBOL *last_s) { struct SYMBOL *s, *g; int staff, delta, dur; signed char staff_delta[MAXSTAFF]; static const signed char delta_tb[4] = { 0 - 2 * 2, 6 - 3 * 2, 12 - 4 * 2, 0 - 3 * 2 }; for (staff = 0; staff <= nstaff; staff++) { s = staff_tb[staff].s_clef; staff_delta[staff] = delta_tb[s->u.clef.type] + s->u.clef.line * 2 + s->u.clef.transpose; } dur = BASE_LEN; for (s = tsfirst; s != last_s; s = s->ts_next) { int np, m; staff = s->staff; switch (s->type) { case CLEF: staff_delta[staff] = delta_tb[s->u.clef.type] + s->u.clef.line * 2 + s->u.clef.transpose; set_yval(s); break; case GRACE: for (g = s->extra; g; g = g->next) { if (g->type != NOTEREST) continue; delta = staff_delta[g->staff]; if (delta != 0 && voice_tb[s->voice].key.instr != K_DRUM) { for (m = g->nhd; m >= 0; m--) g->pits[m] += delta; } g->ymn = 3 * (g->pits[0] - 18) - 2; g->ymx = 3 * (g->pits[g->nhd] - 18) + 2; } set_yval(s); break; case KEYSIG: s->u.key.clef_delta = staff_delta[staff]; /* keep the current clef */ // s->ymx = 24 + 10; // s->ymn = -2; // break; /* fall thru */ default: set_yval(s); break; case MREST: if (s->flags & ABC_F_INVIS) break; s->ymx = 24 + 15; s->ymn = -2; break; case NOTEREST: if (s->abc_type != ABC_T_NOTE && !first_voice->next) { s->y = 12; /* rest single voice */ s->ymx = 24; s->ymn = 0; break; } np = s->nhd; delta = staff_delta[staff]; if (delta != 0 && voice_tb[s->voice].key.instr != K_DRUM) { for (m = np; m >= 0; m--) s->pits[m] += delta; } if (s->abc_type == ABC_T_NOTE) { s->ymx = 3 * (s->pits[np] - 18) + 4; s->ymn = 3 * (s->pits[0] - 18) - 4; } else { s->y = (s->pits[0] - 18) / 2 * 6; s->ymx = s->y + rest_sp[5 - s->nflags][0]; s->ymn = s->y - rest_sp[5 - s->nflags][1]; } if (s->dur < dur) dur = s->dur; break; } } smallest_duration = dur; } /* -- set the stem direction when multi-voices -- */ /* this function is called only once per tune */ static void set_stem_dir(void) { struct SYSTEM *sy; struct SYMBOL *s, *t, *u; int i, staff, nst, rvoice, voice; struct { int nvoice; struct { int voice; short ymn; short ymx; } st[4]; /* (no more than 4 voices per staff) */ } stb[MAXSTAFF]; struct { signed char st1, st2; /* (a voice cannot be on more than 2 staves) */ } vtb[MAXVOICE]; s = tsfirst; sy = cursys; nst = sy->nstaff; while (s) { for (staff = nst; staff >= 0; staff--) { stb[staff].nvoice = -1; for (i = 4; --i >= 0; ) { stb[staff].st[i].voice = -1; stb[staff].st[i].ymx = 0; stb[staff].st[i].ymn = 24; } } for (i = 0; i < MAXVOICE; i++) vtb[i].st1 = vtb[i].st2 = -1; /* get the max/min offsets in the delta time */ /*fixme: the stem height is not calculated yet*/ for (u = s; u; u = u->ts_next) { if (u->type == BAR) break; if (u->sflags & S_NEW_SY) { if (u != s) break; sy = sy->next; for (staff = nst; staff <= sy->nstaff; staff++) { stb[staff].nvoice = -1; for (i = 4; --i >= 0; ) { stb[staff].st[i].voice = -1; stb[staff].st[i].ymx = 0; stb[staff].st[i].ymn = 24; } } nst = sy->nstaff; } if (u->type != NOTEREST || (u->flags & ABC_F_INVIS)) continue; staff = u->staff; #if 1 /*fixme:test*/ if (staff > nst) { bug("set_stem_dir(): bad staff number\n", 1); } #endif voice = u->voice; if (vtb[voice].st1 < 0) { vtb[voice].st1 = staff; } else if (vtb[voice].st1 != staff) { if (staff > vtb[voice].st1) { if (staff > vtb[voice].st2) vtb[voice].st2 = staff; } else { if (vtb[voice].st1 > vtb[voice].st2) vtb[voice].st2 = vtb[voice].st1; vtb[voice].st1 = staff; } } rvoice = sy->voice[voice].range; for (i = stb[staff].nvoice; i >= 0; i--) { if (stb[staff].st[i].voice == rvoice) break; } if (i < 0) { if (++stb[staff].nvoice >= 4) bug("Too many voices per staff", 1); for (i = 0; i < stb[staff].nvoice; i++) { if (rvoice < stb[staff].st[i].voice) { memmove(&stb[staff].st[i + 1], &stb[staff].st[i], sizeof stb[staff].st[i] * (stb[staff].nvoice - i)); stb[staff].st[i].ymx = 0; stb[staff].st[i].ymn = 24; break; } } stb[staff].st[i].voice = rvoice; } if (u->abc_type != ABC_T_NOTE) continue; if (u->ymx > stb[staff].st[i].ymx) stb[staff].st[i].ymx = u->ymx; if (u->ymn < stb[staff].st[i].ymn) stb[staff].st[i].ymn = u->ymn; if (u->sflags & S_XSTEM) { if (u->ts_prev->staff != staff - 1 || u->ts_prev->abc_type != ABC_T_NOTE) { error(1, s, "Bad !xstem!"); u->sflags &= ~S_XSTEM; /*fixme:nflags KO*/ } else { u->ts_prev->multi = 1; u->multi = 1; u->flags |= ABC_F_STEMLESS; } } } for ( ; s != u; s = s->ts_next) { if (s->multi) continue; if (s->type != NOTEREST /* if not note nor rest */ && s->type != GRACE) continue; staff = s->staff; voice = s->voice; // if (!s->multi && vtb[voice].st2 >= 0) { if (vtb[voice].st2 >= 0) { if (staff == vtb[voice].st1) s->multi = -1; else if (staff == vtb[voice].st2) s->multi = 1; continue; } if (stb[staff].nvoice <= 0) { /* voice alone on the staff */ // if (s->multi) // continue; /*fixme:could be done in set_float()*/ if (s->sflags & S_FLOATING) { if (staff == voice_tb[voice].staff) s->multi = -1; else s->multi = 1; } continue; } rvoice = sy->voice[voice].range; for (i = stb[staff].nvoice; i >= 0; i--) { if (stb[staff].st[i].voice == rvoice) break; } if (i < 0) continue; /* voice ignored */ if (i == stb[staff].nvoice) { s->multi = -1; /* last voice */ } else { s->multi = 1; /* first voice(s) */ /* if 3 voices, and vertical space enough, * have stems down for the middle voice */ if (i != 0 && i + 1 == stb[staff].nvoice) { if (stb[staff].st[i].ymn - cfmt.stemheight > stb[staff].st[i + 1].ymx) s->multi = -1; /* special case for unison */ if (s->ts_prev && s->ts_prev->time == s->time && s->ts_prev->staff == s->staff && s->pits[s->nhd] == s->ts_prev->pits[0] && (s->sflags & (S_BEAM_ST | S_BEAM_END)) == (S_BEAM_ST | S_BEAM_END) && ((t = s->ts_next) == NULL || t->staff != s->staff || t->time != s->time)) s->multi = -1; } } } while (s && s->type == BAR) { if (s->sflags & S_NEW_SY) { sy = sy->next; nst = sy->nstaff; } s = s->ts_next; } } } /* -- adjust the offset of the rests when many voices -- */ /* this function is called only once per tune */ static void set_rest_offset(void) { struct SYSTEM *sy; struct SYMBOL *s, *s2; int nvoice, voice, end_time, not_alone, ymax, ymin, shift, dots; float dx; struct { struct SYMBOL *s; int staff; int end_time; } vtb[MAXVOICE], *v; memset(vtb, 0, sizeof vtb); sy = cursys; nvoice = 0; for (s = tsfirst; s; s = s->ts_next) { if (s->flags & ABC_F_INVIS) continue; if (s->sflags & S_NEW_SY) sy = sy->next; if (s->type != NOTEREST) continue; if (s->voice > nvoice) nvoice = s->voice; v = &vtb[s->voice]; v->s = s; v->staff = s->staff; v->end_time = s->time + s->dur; if (s->abc_type != ABC_T_REST) continue; /* check if clash with previous symbols */ ymin = -127; ymax = 127; not_alone = dots = 0; for (voice = 0, v = vtb; voice <= nvoice; voice++, v++) { s2 = v->s; if (!s2 || v->staff != s->staff || voice == s->voice) continue; if (v->end_time <= s->time) continue; not_alone++; if (sy->voice[voice].range < sy->voice[s->voice].range) { if (s2->time == s->time) { if (s2->ymn < ymax) { ymax = s2->ymn; if (s2->dots) dots = 1; } } else { if (s2->y < ymax) ymax = s2->y; } } else { if (s2->time == s->time) { if (s2->ymx > ymin) { ymin = s2->ymx; if (s2->dots) dots = 1; } } else { if (s2->y > ymin) ymin = s2->y; } } } /* check if clash with next symbols */ end_time = s->time + s->dur; for (s2 = s->ts_next; s2; s2 = s2->ts_next) { if (s2->time >= end_time) break; if (s2->staff != s->staff || s2->type != NOTEREST || (s2->flags & ABC_F_INVIS)) continue; not_alone++; if (sy->voice[s2->voice].range < sy->voice[s->voice].range) { if (s2->time == s->time) { if (s2->ymn < ymax) { ymax = s2->ymn; if (s2->dots) dots = 1; } } else { if (s2->y < ymax) ymax = s2->y; } } else { if (s2->time == s->time) { if (s2->ymx > ymin) { ymin = s2->ymx; if (s2->dots) dots = 1; } } else { if (s2->y > ymin) ymin = s2->y; } } } shift = ymax - s->ymx; if (shift < 0) { shift = (-shift + 5) / 6 * 6; if (s->ymn - shift >= ymin) { s->y -= shift; s->ymx -= shift; s->ymn -= shift; continue; } dx = dots ? 15 : 10; s->u.note.notes[0].shhd = dx; s->xmx = dx; continue; } shift = ymin - s->ymn; if (shift > 0) { shift = (shift + 5) / 6 * 6; if (s->ymx + shift <= ymax) { s->y += shift; s->ymx += shift; s->ymn += shift; continue; } dx = dots ? 15 : 10; s->u.note.notes[0].shhd = dx; s->xmx = dx; continue; } if (!not_alone) { s->y = 12; s->ymx = 24; s->ymn = 0; } } } /* -- create a starting symbol -- */ static struct SYMBOL *sym_new(int type, struct VOICE_S *p_voice, struct SYMBOL *last_s) /* same time */ { struct SYMBOL *s; s = (struct SYMBOL *) getarena(sizeof *s); memset(s, 0, sizeof *s); s->type = type; s->voice = p_voice - voice_tb; s->staff = p_voice->staff; s->time = last_s->time; s->next = p_voice->last_sym->next; if (s->next) s->next->prev = s; p_voice->last_sym->next = s; s->prev = p_voice->last_sym; p_voice->last_sym = s; s->ts_next = last_s; s->ts_prev = last_s->ts_prev; if (s->ts_prev) s->ts_prev->ts_next = s; if (!s->ts_prev || s->ts_prev->type != type) s->sflags |= S_SEQST; last_s->ts_prev = s; if (last_s->type == type && s->voice != last_s->voice) { last_s->sflags &= ~S_SEQST; last_s->shrink = 0; } s->fn = last_s->fn; s->linenum = last_s->linenum; s->colnum = last_s->colnum; return s; } /* -- init the symbols at start of a music line -- */ static void init_music_line(void) { struct VOICE_S *p_voice; struct SYMBOL *s, *last_s; int voice, staff; /* initialize the voices */ for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { voice = p_voice - voice_tb; p_voice->last_sym = p_voice->sym; if (cursys->voice[voice].range < 0) continue; p_voice->second = cursys->voice[voice].second; /* move the voice to a non empty staff */ staff = cursys->voice[voice].staff; while (staff < nstaff && cursys->staff[staff].empty) staff++; p_voice->staff = staff; } /* add a clef at start of the main voices */ last_s = tsfirst; while (last_s && last_s->type == CLEF) { /* move the starting clefs */ voice = last_s->voice; p_voice = &voice_tb[voice]; if (cursys->voice[voice].range >= 0 && !cursys->voice[voice].second) { last_s->aux = 0; /* normal clef */ p_voice->last_sym = p_voice->sym = last_s; } last_s = last_s->ts_next; } for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { if (p_voice->sym && p_voice->sym->type == CLEF) continue; voice = p_voice - voice_tb; if (cursys->voice[voice].range < 0 || cursys->voice[voice].second) continue; staff = cursys->voice[voice].staff; #if 0 if (last_s->voice == voice && last_s->type == CLEF) { last_s->aux = 0; /* normal clef */ #if 0 if (cursys->staff[staff].clef.invis) s->flags |= ABC_F_INVIS; #endif p_voice->last_sym = p_voice->sym = last_s; last_s = last_s->ts_next; continue; } #endif if (!staff_tb[staff].s_clef) continue; // no clef s = (struct SYMBOL *) getarena(sizeof *s); memset(s, 0, sizeof *s); memcpy(&s->u.clef, &staff_tb[staff].s_clef->u.clef, sizeof s->u.clef); s->type = CLEF; s->voice = voice; s->staff = staff; s->time = last_s ? last_s->time : 0; s->next = p_voice->sym; if (s->next) { s->next->prev = s; s->fn = s->next->fn; s->linenum = s->next->linenum; s->colnum = s->next->colnum; } p_voice->last_sym = p_voice->sym = s; s->ts_next = last_s; s->ts_prev = last_s ? last_s->ts_prev : NULL; if (!s->ts_prev) { tsfirst = s; s->sflags |= S_SEQST; } else { s->ts_prev->ts_next = s; } if (last_s) { last_s->ts_prev = s; if (last_s->type == CLEF) last_s->sflags &= ~S_SEQST; } // if (cursys->voice[voice].second) // s->sflags |= S_SECOND; if (staff_tb[staff].s_clef->u.clef.invis || cursys->staff[staff].empty) s->flags |= ABC_F_INVIS; // set_yval(s); } /* add keysig */ for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { voice = p_voice - voice_tb; if (cursys->voice[voice].range < 0 || cursys->voice[voice].second || cursys->staff[cursys->voice[voice].staff].empty) continue; if (last_s && last_s->voice == voice && last_s->type == KEYSIG) { p_voice->last_sym = last_s; last_s->aux = last_s->u.key.sf; // no key cancel last_s = last_s->ts_next; continue; } if (p_voice->key.sf != 0 || p_voice->key.nacc != 0) { s = sym_new(KEYSIG, p_voice, last_s); memcpy(&s->u.key, &p_voice->key, sizeof s->u.key); if (s->u.key.instr == K_Hp) s->aux = 3; /* "A" -> "D" => G natural */ // set_yval(s); } } /* add time signature if needed */ if (insert_meter & 1) { for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { voice = p_voice - voice_tb; if (cursys->voice[voice].range < 0 || cursys->voice[voice].second || cursys->staff[cursys->voice[voice].staff].empty || p_voice->meter.nmeter == 0) /* M:none */ continue; if (last_s && last_s->voice == voice && last_s->type == TIMESIG) { p_voice->last_sym = last_s; last_s = last_s->ts_next; continue; } s = sym_new(TIMESIG, p_voice, last_s); memcpy(&s->u.meter, &p_voice->meter, sizeof s->u.meter); // set_yval(s); } insert_meter &= ~1; // no meter any more } /* add bar if needed (for repeat bracket) */ for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { int bar_start; // if bar already, keep it in sequence voice = p_voice - voice_tb; bar_start = p_voice->bar_start; p_voice->bar_start = 0; if (last_s && last_s->voice == voice && last_s->type == BAR) { p_voice->last_sym = last_s; last_s = last_s->ts_next; continue; } if (!bar_start) continue; if (cursys->voice[voice].range < 0 || cursys->voice[voice].second || cursys->staff[cursys->voice[voice].staff].empty) continue; s = sym_new(BAR, p_voice, last_s); s->u.bar.type = bar_start & 0x0fff; if (bar_start & 0x8000) s->flags |= ABC_F_INVIS; if (bar_start & 0x4000) s->sflags |= S_NOREPBRA; if (bar_start & 0x2000) s->flags |= ABC_F_RBSTART; if (bar_start & 0x1000) s->sflags |= S_RBSTART; s->text = p_voice->bar_text; s->gch = p_voice->bar_gch; if (p_voice->bar_repeat) s->u.bar.repeat_bar = p_voice->bar_repeat; p_voice->bar_repeat = 0; p_voice->bar_text = NULL; p_voice->bar_gch = NULL; } /* if initialization of a new music line, compute the spacing, * including the first (old) sequence */ set_pitch(last_s); s = last_s; if (s) { for ( ; s; s = s->ts_next) if (s->sflags & S_SEQST) break; if (s) for (s = s->ts_next; s; s = s->ts_next) if (s->sflags & S_SEQST) break; } set_allsymwidth(s); /* set the width of the added symbols */ } /* -- set a pitch in all symbols and the start/stop of the beams -- */ static void set_words(struct VOICE_S *p_voice) { int pitch, beam_start; struct SYMBOL *s, *s2, *lastnote; for (s = p_voice->sym; s; s = s->next) { if (s->abc_type == ABC_T_NOTE) { pitch = s->pits[0]; break; } } if (!s) pitch = 127; /* no note */ beam_start = 1; lastnote = NULL; for (s = p_voice->sym; s; s = s->next) { switch (s->type) { default: if (s->flags & ABC_F_SPACE) beam_start = 1; break; case MREST: beam_start = 1; break; case BAR: if (!(s->sflags & S_BEAM_ON)) beam_start = 1; /* change the last long note to the square note */ if (!s->next && s->prev && s->prev->abc_type == ABC_T_NOTE && s->prev->dur >= BREVE) s->prev->head = H_SQUARE; break; case NOTEREST: if (s->sflags & S_TREM2) break; if (s->flags & ABC_F_SPACE) beam_start = 1; if (beam_start || s->nflags - s->aux <= 0) { if (lastnote) { lastnote->sflags |= S_BEAM_END; lastnote = NULL; } if (s->nflags - s->aux <= 0) { s->sflags |= (S_BEAM_ST | S_BEAM_END); } else if (s->abc_type == ABC_T_NOTE) { s->sflags |= S_BEAM_ST; beam_start = 0; } } if (s->sflags & S_BEAM_END) beam_start = 1; if (s->abc_type == ABC_T_NOTE) lastnote = s; break; } if (s->abc_type == ABC_T_NOTE) { pitch = s->pits[0]; // if (s->prev // && s->prev->abc_type != ABC_T_NOTE) { // s->prev->pits[0] = // (s->prev->pits[0] + pitch) // / 2; for (s2 = s->prev; s2; s2 = s2->prev) { if (s2->abc_type != ABC_T_REST) break; s2->pits[0] = pitch; } } else { s->pits[0] = pitch; } } if (lastnote) lastnote->sflags |= S_BEAM_END; } /* -- set the end of the repeat sequences -- */ static void set_rb(struct VOICE_S *p_voice) { struct SYMBOL *s, *s2; int n; s = p_voice->sym; while (s) { if (s->type != BAR || !(s->sflags & S_RBSTART) || (s->sflags & S_NOREPBRA)) { s = s->next; continue; } n = 0; s2 = NULL; for (s = s->next; s; s = s->next) { if (s->type != BAR) continue; n++; if (s->sflags & S_RBSTOP) break; if (!s->next) { s->flags |= ABC_F_RBSTOP; s->sflags |= S_RBSTOP; break; } if (n == cfmt.rbmin) s2 = s; if (n == cfmt.rbmax) { if (s2) s = s2; s->sflags |= S_RBSTOP; break; } } } } /* -- initialize the generator -- */ /* this function is called only once per tune */ static void set_global(void) { struct SYSTEM *sy; struct SYMBOL *s; struct VOICE_S *p_voice; int staff; static const signed char delpit[4] = {0, -7, -14, 0}; /* get the max number of staves */ sy = cursys; staff = cursys->nstaff; while ((sy = sy->next) != NULL) { if (sy->nstaff > staff) staff = sy->nstaff; } nstaff = staff; /* adjust the pitches if old abc2ps behaviour of clef definition */ if (cfmt.abc2pscompat) { int i; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { int delta; struct SYMBOL *g; if (p_voice->octave != 0) continue; #if 0 /* (the clefs in the voice table are not yet initialized) */ // i = p_voice->staff; // i = cursys->staff[i].clef.type; i = cursys->voice[p_voice - voice_tb].clef.type; #else i = p_voice->s_clef->u.clef.type; #endif if (i == PERC) continue; delta = delpit[i]; for (s = p_voice->sym; s; s = s->next) { switch (s->type) { case CLEF: i = s->u.clef.type; if (!s->u.clef.check_pitch) i = 0; delta = delpit[i]; break; case NOTEREST: if (delta == 0) break; if (s->abc_type == ABC_T_REST) break; for (i = s->nhd; i >= 0; i--) s->pits[i] += delta; break; case GRACE: if (delta == 0) break; for (g = s->extra; g; g = g->next) { if (g->type != NOTEREST) continue; for (i = g->nhd; i >= 0; i--) g->pits[i] += delta; } break; } } } } /* set the pitches, the words (beams) and the repeat brackets */ for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { set_words(p_voice); // if (!p_voice->second && !p_voice->norepbra) set_rb(p_voice); } /* set the staff of the floating voices */ set_float(); // set the clefs and adjust the pitches of all symbols set_clefs(); set_pitch(NULL); } /* -- return the left indentation of the staves -- */ static float set_indent(void) { int staff, voice; float w, maxw; struct VOICE_S *p_voice; char *p, *q; maxw = 0; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { voice = p_voice - voice_tb; if (cursys->voice[voice].range < 0) continue; staff = cursys->voice[voice].staff; if (cursys->staff[staff].empty) continue; if ((p = p_voice->new_name ? p_voice->nm : p_voice->snm) == NULL) continue; str_font(VOICEFONT); for (;;) { if ((q = strstr(p, "\\n")) != NULL) *q = '\0'; w = tex_str(p); if (w > maxw) maxw = w; if (!q) break; *q = '\\'; p = q + 2; } } if (maxw != 0) { w = 0; // for (staff = 0; staff <= nstaff; staff++) { for (staff = 0; staff <= cursys->nstaff; staff++) { if (cursys->staff[staff].flags & (OPEN_BRACE2 | OPEN_BRACKET2)) { w = 20; break; } if ((cursys->staff[staff].flags & (OPEN_BRACE | OPEN_BRACKET)) && w == 0) w = 10; } maxw += 4 * cwid(' ') * cfmt.font_tb[VOICEFONT].swfac + w; } if (insert_meter & 2) /* if indent */ maxw += cfmt.indent; return maxw; } /* -- decide on beams and on stem directions -- */ /* this routine is called only once per tune */ static void set_beams(struct SYMBOL *sym) { struct SYSTEM *sy; struct SYMBOL *s, *t, *g, *s_opp; int n, m, beam, laststem, mid_p; beam = 0; laststem = -1; s_opp = NULL; sy = cursys; for (s = sym; s; s = s->next) { if (s->abc_type != ABC_T_NOTE) { switch (s->type) { default: continue; case STAVES: sy = sy->next; continue; case GRACE: break; } g = s->extra; while (g->abc_type != ABC_T_NOTE) g = g->next; if (g->stem == 2) { /* opposite gstem direction */ s_opp = s; continue; } if (s->stem == 0 && (s->stem = s->multi) == 0) s->stem = 1; for (; g; g = g->next) { g->stem = s->stem; g->multi = s->multi; } continue; } mid_p = s->mid / 3 + 18; if (s->stem == 0 /* if not explicitly set */ && (s->stem = s->multi) == 0) { /* and alone on the staff */ /* notes in a beam have the same stem direction */ if (beam) { s->stem = laststem; } else if ((s->sflags & (S_BEAM_ST | S_BEAM_END)) == S_BEAM_ST) { /* start of beam */ int pu = s->pits[s->nhd], pd = s->pits[0]; beam = 1; for (t = s->next; t; t = t->next) { if (t->abc_type != ABC_T_NOTE) continue; if (t->stem || t->multi) { s->stem = t->multi ? t->multi : t->stem; break; } if (t->pits[t->nhd] > pu) pu = t->pits[t->nhd]; if (t->pits[0] < pd) pd = t->pits[0]; if (t->sflags & S_BEAM_END) break; } if (t->sflags & S_BEAM_END) { mid_p *= 2; if (pu + pd < mid_p) { s->stem = 1; } else if (pu + pd > mid_p) { s->stem = -1; } else { if (cfmt.bstemdown) s->stem = -1; } } if (!s->stem) s->stem = laststem; } else { // no beam n = s->pits[s->nhd] + s->pits[0]; if (n == mid_p * 2) { n = 0; for (m = 0; m <= s->nhd; m++) n += s->pits[m]; mid_p *= s->nhd + 1; } else { mid_p *= 2; } if (n < mid_p) s->stem = 1; else if (n > mid_p) s->stem = -1; else if (cfmt.bstemdown) s->stem = -1; else s->stem = laststem; } } else { /* stem set by set_stem_dir */ if ((s->sflags & (S_BEAM_ST | S_BEAM_END)) == S_BEAM_ST) /* start of beam */ beam = 1; } if (s->sflags & S_BEAM_END) beam = 0; laststem = s->stem; if (s_opp) { /* opposite gstem direction */ for (g = s_opp->extra; g; g = g->next) g->stem = -laststem; s_opp->stem = -laststem; s_opp = NULL; } } } /* handle unison in voice overlap */ static int same_head(struct SYMBOL *s1, struct SYMBOL *s2) { int i1, i2, l1, l2, i11, i12, i21, i22; float sh1, sh2; if ((s1->sflags & (S_SHIFTUNISON_1 | S_SHIFTUNISON_2)) == (S_SHIFTUNISON_1 | S_SHIFTUNISON_2)) return 0; if ((l1 = s1->dur) >= SEMIBREVE) return 0; if ((l2 = s2->dur) >= SEMIBREVE) return 0; if (s1->flags & s2->flags & ABC_F_STEMLESS) return 0; if (s1->dots != s2->dots) { // if ((s1->sflags & (S_SHIFTUNISON_1 | S_SHIFTUNISON_2)) if ((s1->sflags & S_SHIFTUNISON_1) || s1->dots * s2->dots != 0) return 0; } if (s1->stem * s2->stem > 0) return 0; /* check if a common unison */ i1 = i2 = 0; if (s1->pits[0] > s2->pits[0]) { if (s1->stem < 0) return 0; while (s2->pits[i2] != s1->pits[0]) { if (++i2 > s2->nhd) return 0; } } else if (s1->pits[0] < s2->pits[0]) { if (s2->stem < 0) return 0; while (s2->pits[0] != s1->pits[i1]) { if (++i1 > s1->nhd) return 0; } } if (s2->u.note.notes[i2].acc != s1->u.note.notes[i1].acc) return 0; i11 = i1; i21 = i2; sh1 = s1->u.note.notes[i1].shhd; sh2 = s2->u.note.notes[i2].shhd; do { i1++; i2++; if (i1 > s1->nhd) { // if (s1->pits[0] < s2->pits[0]) // return 0; break; } if (i2 > s2->nhd) { // if (s1->pits[0] > s2->pits[0]) // return 0; break; } if (s2->u.note.notes[i2].acc != s1->u.note.notes[i1].acc) return 0; if (sh1 < s1->u.note.notes[i1].shhd) sh1 = s1->u.note.notes[i1].shhd; if (sh2 < s2->u.note.notes[i2].shhd) sh2 = s2->u.note.notes[i2].shhd; } while (s2->pits[i2] == s1->pits[i1]); if (i1 <= s1->nhd) { if (i2 <= s2->nhd) return 0; if (s2->stem > 0) return 0; } else if (i2 <= s2->nhd) { if (s1->stem > 0) return 0; } i12 = i1; i22 = i2; if (l1 == l2) goto same_head; if (l1 < l2) { l1 = l2; l2 = s1->dur; } if (l1 < MINIM) { if (s2->dots > 0) goto head_2; if (s1->dots > 0) goto head_1; goto same_head; } if (l2 < CROTCHET) { /* (l1 >= MINIM) */ // if ((s1->sflags & S_SHIFTUNISON_2) // || s1->dots != s2->dots) if (s1->sflags & S_SHIFTUNISON_2) return 0; if (s2->dur >= MINIM) goto head_2; goto head_1; } return 0; same_head: if (voice_tb[s1->voice].scale < voice_tb[s2->voice].scale) goto head_2; head_1: // s2->nohdi1 = i21; /* keep heads of 1st voice */ // s2->nohdi2 = i22; for (i2 = i21; i2 < i22; i2++) { s2->u.note.notes[i2].invisible = 1; s2->u.note.notes[i2].acc = 0; } for (i2 = 0; i2 <= s2->nhd; i2++) s2->u.note.notes[i2].shhd += sh1; return 1; head_2: // s1->nohdi1 = i11; /* keep heads of 2nd voice */ // s1->nohdi2 = i12; for (i1 = i11; i1 < i12; i1++) { s1->u.note.notes[i1].invisible = 1; s1->u.note.notes[i1].acc = 0; } for (i1 = 0; i1 <= s1->nhd; i1++) s1->u.note.notes[i1].shhd += sh2; return 1; } /* width of notes for voice overlap - index = head */ static float w_note[] = { 3.5, 3.7, 5, 7 }; /* handle unison with different accidentals */ static void unison_acc(struct SYMBOL *s1, struct SYMBOL *s2, int i1, int i2) { int m; float d; if (s2->u.note.notes[i2].acc == 0) { d = w_note[s2->head] * 2 + s2->xmx + s1->u.note.notes[i1].shac + 2; if (s1->u.note.notes[i1].acc & 0xf8) d += 2; if (s2->dots) d += 6; for (m = 0; m <= s1->nhd; m++) { s1->u.note.notes[m].shhd += d; s1->u.note.notes[m].shac -= d; } s1->xmx += d; } else { d = w_note[s1->head] * 2 + s1->xmx + s2->u.note.notes[i2].shac + 2; if (s2->u.note.notes[i2].acc & 0xf8) d += 2; if (s1->dots) d += 6; for (m = 0; m <= s2->nhd; m++) { s2->u.note.notes[m].shhd += d; s2->u.note.notes[m].shac -= d; } s2->xmx += d; } } #define MAXPIT (48 * 2) /* set the left space of a note/chord */ static void set_left(struct SYMBOL *s, float *left) { int m, i, j; float w_base, w, shift; for (i = 0; i < MAXPIT; i++) left[i] = -100; /* stem */ w = w_base = w_note[s->head]; if (s->nflags > -2) { if (s->stem > 0) { w = -w; i = s->pits[0] * 2; j = ((int) ((s->ymx - 2) / 3) + 18) * 2; } else { i = ((int) ((s->ymn + 2) / 3) + 18) * 2; j = s->pits[s->nhd] * 2; } if (i < 0) i = 0; for (; i < MAXPIT && i <= j; i++) left[i] = w; } /* notes */ if (s->stem > 0) shift = s->u.note.notes[0].shhd; /* previous shift */ else shift = s->u.note.notes[s->nhd].shhd; for (m = 0; m <= s->nhd; m++) { w = -s->u.note.notes[m].shhd + w_base + shift; i = s->pits[m] * 2; if (i < 0) i = 0; else if (i >= MAXPIT - 1) i = MAXPIT - 2; if (w > left[i]) left[i] = w; if (s->head != H_SQUARE) w -= 1; if (w > left[i - 1]) left[i - 1] = w; if (w > left[i + 1]) left[i + 1] = w; } } /* set the right space of a note/chord */ static void set_right(struct SYMBOL *s, float *right) { int m, i, j, k, flags; float w_base, w, shift; for (i = 0; i < MAXPIT; i++) right[i] = -100; /* stem and flags */ flags = s->nflags > 0 && (s->sflags & (S_BEAM_ST | S_BEAM_END)) == (S_BEAM_ST | S_BEAM_END); w = w_base = w_note[s->head]; if (s->nflags > -2) { if (s->stem < 0) { w = -w; i = ((int) ((s->ymn + 2) / 3) + 18) * 2; j = s->pits[s->nhd] * 2; k = i + 4; } else { i = s->pits[0] * 2; j = ((int) ((s->ymx - 2) / 3) + 18) * 2; k = i; // (have gcc happy) } if (i < 0) i = 0; for ( ; i < MAXPIT && i < j; i++) right[i] = w; } if (flags) { if (s->stem > 0) { if (s->xmx == 0) i = s->pits[s->nhd] * 2; else i = s->pits[0] * 2; i += 4; if (i < 0) i = 0; for (; i < MAXPIT && i <= j - 4; i++) right[i] = 11; } else { i = k; if (i < 0) i = 0; for (; i < MAXPIT && i <= s->pits[0] * 2 - 4; i++) right[i] = 3.5; } } /* notes */ if (s->stem > 0) shift = s->u.note.notes[0].shhd; /* previous shift */ else shift = s->u.note.notes[s->nhd].shhd; for (m = 0; m <= s->nhd; m++) { w = s->u.note.notes[m].shhd + w_base - shift; i = s->pits[m] * 2; if (i < 0) i = 0; else if (i >= MAXPIT - 1) i = MAXPIT - 2; if (w > right[i]) right[i] = w; if (s->head != H_SQUARE) w -= 1; if (w > right[i - 1]) right[i - 1] = w; if (w > right[i + 1]) right[i + 1] = w; } } /* -- shift the notes horizontally when voices overlap -- */ /* this routine is called only once per tune */ static void set_overlap(void) { struct SYMBOL *s, *s1, *s2, *s3; int i, i1, i2, m, sd, t, dp; float d, d2, dr, dr2, dx; float left1[MAXPIT], right1[MAXPIT], left2[MAXPIT], right2[MAXPIT]; float right3[MAXPIT], *pl, *pr; for (s = tsfirst; s; s = s->ts_next) { if (s->abc_type != ABC_T_NOTE || (s->flags & ABC_F_INVIS)) continue; /* treat the stem on two staves with different directions */ if ((s->sflags & S_XSTEM) && s->ts_prev->stem < 0) { s2 = s->ts_prev; for (m = 0; m <= s2->nhd; m++) { s2->u.note.notes[m].shhd += STEM_XOFF * 2; s2->u.note.notes[m].shac -= STEM_XOFF * 2; } s2->xmx += STEM_XOFF * 2; } /* search the next note at the same time on the same staff */ s2 = s; for (;;) { s2 = s2->ts_next; if (!s2) break; if (s2->time != s->time) { s2 = NULL; break; } if (s2->abc_type == ABC_T_NOTE && !(s2->flags & ABC_F_INVIS) && s2->staff == s->staff) break; } if (!s2) continue; s1 = s; /* set the dot vertical offset */ if (cursys->voice[s1->voice].range < cursys->voice[s2->voice].range) s2->doty = -3; else s1->doty = -3; /* no shift if no overlap */ if (s1->ymn > s2->ymx || s1->ymx < s2->ymn) continue; if (same_head(s1, s2)) continue; /* compute the minimum space for 's1 s2' and 's2 s1' */ set_right(s1, right1); set_left(s2, left2); s3 = s1->ts_prev; if (s3 && s3->time == s1->time && s3->staff == s1->staff && s3->abc_type == ABC_T_NOTE && !(s3->flags & ABC_F_INVIS)) { set_right(s3, right3); for (i = 0; i < MAXPIT; i++) { if (right3[i] > right1[i]) right1[i] = right3[i]; } } else { s3 = NULL; } d = -100; for (i = 0; i < MAXPIT; i++) { if (left2[i] + right1[i] > d) d = left2[i] + right1[i]; } if (d < -3) { // no clash if no dots clash if (!s1->dots || !s2->dots || s2->doty >= 0 || s1->stem > 0 || s2->stem < 0 || s1->pits[s1->nhd] + 2 != s2->pits[0] || (s2->pits[0] & 1)) continue; } set_right(s2, right2); set_left(s1, left1); if (s3) { set_left(s3, right3); for (i = 0; i < MAXPIT; i++) { if (right3[i] > left1[i]) left1[i] = right3[i]; } } d2 = dr = dr2 = -100; for (i = 0; i < MAXPIT; i++) { if (left1[i] + right2[i] > d2) d2 = left1[i] + right2[i]; if (right1[i] > dr) dr = right1[i]; if (right2[i] > dr2) dr2 = right2[i]; } /* check for unison with different accidentals * and clash of dots */ t = 0; i1 = s1->nhd; i2 = s2->nhd; for (;;) { dp = s1->pits[i1] - s2->pits[i2]; switch (dp) { case 0: if (s1->u.note.notes[i1].acc != s2->u.note.notes[i2].acc) { t = -1; break; } if (s2->u.note.notes[i2].acc) s2->u.note.notes[i2].acc = 0; if (s1->dots && s2->dots && (s1->pits[i1] & 1)) t = 1; break; case -1: // if (s1->dots && s2->dots) // t = 1; if (s1->dots && s2->dots) { if (s1->pits[i1] & 1) { s1->doty = 0; s2->doty = 0; } else { s1->doty = -3; s2->doty = -3; } } break; case -2: if (s1->dots && s2->dots && !(s1->pits[i1] & 1)) { // t = 1; s1->doty = 0; s2->doty = 0; break; } break; } if (t < 0) break; if (dp >= 0) { if (--i1 < 0) break; } if (dp <= 0) { if (--i2 < 0) break; } } if (t < 0) { /* unison and different accidentals */ unison_acc(s1, s2, i1, i2); continue; } sd = 0; pl = left2; pr = right2; if (s1->dots) { if (s2->dots) { if (!t) /* if no dot clash */ sd = 1; /* align the dots */ } } else if (s2->dots) { if (d2 + dr < d + dr2) sd = 1; /* align the dots */ } if (!s3 && d2 + dr < d + dr2) { s1 = s2; /* invert the voices */ s2 = s; d = d2; pl = left1; pr = right1; dr2 = dr; } d += 3; if (d < 0) d = 0; // (not return!) /* handle the previous shift */ m = s1->stem >= 0 ? 0 : s1->nhd; d += s1->u.note.notes[m].shhd; m = s2->stem >= 0 ? 0 : s2->nhd; d -= s2->u.note.notes[m].shhd; /* * room for the dots * - if the dots of v1 don't shift, adjust the shift of v2 * - otherwise, align the dots and shift them if clash */ if (s1->dots) { dx = 7.7 + s1->xmx + // x 1st dot 3.5 * s1->dots - 3.5 + // x last dot 3; // some space if (!sd) { d2 = -100; for (i1 = 0; i1 <= s1->nhd; i1++) { i = s1->pits[i1]; if (!(i & 1)) { if (s1->doty >= 0) i++; else i--; } i *= 2; if (i < 1) i = 1; else if (i >= MAXPIT - 1) i = MAXPIT - 2; if (pl[i] > d2) d2 = pl[i]; if (pl[i - 1] + 1 > d2) d2 = pl[i - 1] + 1; if (pl[i + 1] + 1 > d2) d2 = pl[i + 1] + 1; } if (dx + d2 + 2 > d) d = dx + d2 + 2; } else { if (dx < d + dr2 + s2->xmx) { d2 = 0; for (i1 = 0; i1 <= s1->nhd; i1++) { i = s1->pits[i1]; if (!(i & 1)) { if (s1->doty >= 0) i++; else i--; } i *= 2; if (i < 1) i = 1; else if (i >= MAXPIT - 1) i = MAXPIT - 2; if (pr[i] > d2) d2 = pr[i]; if (pr[i - 1] + 1> d2) d2 = pr[i - 1] = 1; if (pr[i + 1] + 1 > d2) d2 = pr[i + 1] + 1; } if (d2 > 4.5 && 7.7 + s1->xmx + 2 < d + d2 + s2->xmx) s2->xmx = d2 + 3 - 7.7; } } } for (m = s2->nhd; m >= 0; m--) { s2->u.note.notes[m].shhd += d; // if (s2->u.note.accs[m] != 0 // && s2->pits[m] < s1->pits[0] - 4) // s2->shac[m] -= d; } s2->xmx += d; if (sd) s1->xmx = s2->xmx; // align the dots } } /* -- set the stem lengths -- */ /* this routine is called only once per tune */ static void set_stems(void) { struct SYSTEM *sy; struct SYMBOL *s, *s2, *g; float slen, scale; int ymn, ymx, nflags, mid; sy = cursys; for (s = tsfirst; s; s = s->ts_next) { if (s->abc_type != ABC_T_NOTE) { int ymin, ymax; switch (s->type) { default: continue; case STAVES: sy = sy->next; continue; case GRACE: break; } ymin = ymax = s->mid; for (g = s->extra; g; g = g->next) { if (g->type != NOTEREST) continue; slen = GSTEM; if (g->nflags > 1) slen += 1.2 * (g->nflags - 1); ymn = 3 * (g->pits[0] - 18); ymx = 3 * (g->pits[g->nhd] - 18); if (s->stem >= 0) { g->y = ymn; g->ys = ymx + slen; ymx = (int) (g->ys + 0.5); } else { g->y = ymx; g->ys = ymn - slen; ymn = (int) (g->ys - 0.5); } ymx += 2; ymn -= 2; if (ymn < ymin) ymin = ymn; else if (ymx > ymax) ymax = ymx; g->ymx = ymx; g->ymn = ymn; } s->ymx = ymax; s->ymn = ymin; continue; } /* shift notes in chords (need stem direction to do this) */ set_head_shift(s); /* if start or end of beam, adjust the number of flags * with the other end */ nflags = s->nflags; if ((s->sflags & (S_BEAM_ST | S_BEAM_END)) == S_BEAM_ST) { if (s->sflags & S_FEATHERED_BEAM) nflags = ++s->nflags; for (s2 = s->next; /*s2*/; s2 = s2->next) { if (s2->abc_type == ABC_T_NOTE) { if (s->sflags & S_FEATHERED_BEAM) s2->nflags++; if (s2->sflags & S_BEAM_END) break; } } /* if (s2) */ if (s2->nflags > nflags) nflags = s2->nflags; } else if ((s->sflags & (S_BEAM_ST | S_BEAM_END)) == S_BEAM_END) { for (s2 = s->prev; /*s2*/; s2 = s2->prev) { if (s2->sflags & S_BEAM_ST) break; } /* if (s2) */ if (s2->nflags > nflags) nflags = s2->nflags; } /* set height of stem end */ mid = s->mid; slen = cfmt.stemheight; switch (nflags) { case 2: slen += 2; break; case 3: slen += 5; break; case 4: slen += 10; break; case 5: slen += 16; break; } if ((scale = voice_tb[s->voice].scale) != 1) slen *= (scale + 1) * 0.5; ymn = 3 * (s->pits[0] - 18); if (s->nhd > 0) { slen -= 2; ymx = 3 * (s->pits[s->nhd] - 18); } else { ymx = ymn; } if (s->aux != 0) slen += 2 * s->aux; /* tremolo */ if (s->flags & ABC_F_STEMLESS) { if (s->stem >= 0) { s->y = ymn; s->ys = ymx; } else { s->ys = ymn; s->y = ymx; } if (nflags == -4) /* if longa */ ymn -= 6; s->ymx = ymx + 4; s->ymn = ymn - 4; } else if (s->stem >= 0) { if (nflags >= 2) slen -= 1; if (s->pits[s->nhd] > 26 && (nflags <= 0 || (s->sflags & (S_BEAM_ST | S_BEAM_END)) != (S_BEAM_ST | S_BEAM_END))) { slen -= 2; if (s->pits[s->nhd] > 28) slen -= 2; } s->y = ymn; if (s->u.note.notes[0].ti1 != 0) /*fixme * || s->u.note.ti2[0] != 0) */ ymn -= 3; s->ymn = ymn - 4; s->ys = ymx + slen; if (s->ys < mid) s->ys = mid; s->ymx = (int) (s->ys + 2.5); } else { /* stem down */ if (s->pits[0] < 18 && (nflags <= 0 || (s->sflags & (S_BEAM_ST | S_BEAM_END)) != (S_BEAM_ST | S_BEAM_END))) { slen -= 2; if (s->pits[0] < 16) slen -= 2; } s->ys = ymn - slen; if (s->ys > mid) s->ys = mid; s->ymn = (int) (s->ys - 2.5); s->y = ymx; /*fixme:the tie may be lower*/ if (s->u.note.notes[s->nhd].ti1 != 0) ymx += 3; s->ymx = ymx + 4; } } } /* -- split up unsuitable bars at end of staff -- */ static void check_bar(struct SYMBOL *s) { struct VOICE_S *p_voice; int bar_type, i; p_voice = &voice_tb[s->voice]; /* search the last bar */ while (s->type == CLEF || s->type == KEYSIG || s->type == TIMESIG) { if (s->type == TIMESIG && s->time > p_voice->sym->time) /* if not empty voice */ insert_meter |= 1; /* meter in the next line */ if ((s = s->prev) == NULL) return; } if (s->type != BAR) return; if (s->u.bar.repeat_bar) { p_voice->bar_start = B_OBRA; p_voice->bar_text = s->text; p_voice->bar_gch = s->gch; p_voice->bar_repeat = 1; s->text = NULL; s->gch = NULL; s->u.bar.repeat_bar = 0; if (s->flags & ABC_F_INVIS) p_voice->bar_start |= 0x8000; if (s->sflags & S_NOREPBRA) p_voice->bar_start |= 0x4000; if (s->flags & ABC_F_RBSTART) p_voice->bar_start |= 0x2000; if (s->sflags & S_RBSTART) p_voice->bar_start |= 0x1000; } bar_type = s->u.bar.type; if (bar_type == B_COL) /* ':' */ return; if ((bar_type & 0x0f) != B_COL) /* if not left repeat bar */ return; if (!(s->sflags & S_RRBAR)) { /* 'xx:' (not ':xx:') */ if (bar_type == ((B_SINGLE << 8) | B_LREP)) { p_voice->bar_start = B_LREP; s->u.bar.type = B_DOUBLE; return; } p_voice->bar_start = bar_type & 0x0fff; if (s->flags & ABC_F_INVIS) p_voice->bar_start |= 0x8000; if (s->sflags & S_NOREPBRA) p_voice->bar_start |= 0x4000; if (s->prev && s->prev->type == BAR) unlksym(s); else s->u.bar.type = B_BAR; return; } if (bar_type == B_DREP) { /* '::' */ s->u.bar.type = B_RREP; p_voice->bar_start = B_LREP; if (s->flags & ABC_F_INVIS) p_voice->bar_start |= 0x8000; if (s->sflags & S_NOREPBRA) p_voice->bar_start |= 0x4000; if (s->flags & ABC_F_RBSTART) p_voice->bar_start |= 0x2000; if (s->sflags & S_RBSTART) p_voice->bar_start |= 0x1000; return; } for (i = 0; bar_type != 0; i++) bar_type >>= 4; bar_type = s->u.bar.type; s->u.bar.type = bar_type >> ((i / 2) * 4); i = ((i + 1) / 2 * 4); bar_type &= 0x0fff; p_voice->bar_start = bar_type & ((1 << i) - 1); if (s->flags & ABC_F_INVIS) p_voice->bar_start |= 0x8000; if (s->sflags & S_NOREPBRA) p_voice->bar_start |= 0x4000; if (s->flags & ABC_F_RBSTART) p_voice->bar_start |= 0x2000; if (s->sflags & S_RBSTART) p_voice->bar_start |= 0x1000; } /* -- move the symbols of an empty staff to the next one -- */ static void sym_staff_move(int staff) // struct SYMBOL *s, // struct SYSTEM *sy) { struct SYMBOL *s; // for (;;) { for (s = tsfirst; s; s = s->ts_next) { if (s->sflags & S_NL) break; if (s->staff == staff && s->type != CLEF) { s->staff++; s->flags |= ABC_F_INVIS; } // s = s->ts_next; // if (s == tsnext || s->sflags & S_NEW_SY) // break; } } /* -- adjust the empty flag of a brace system -- */ static void set_brace(struct SYSTEM *sy, char *empty, char *empty_gl) { int staff, i, empty_fl; /* if a system brace has empty and non empty staves, keep all staves */ for (staff = 0; staff <= nstaff; staff++) { if (!(sy->staff[staff].flags & (OPEN_BRACE | OPEN_BRACE2))) continue; empty_fl = 0; i = staff; while (staff <= nstaff) { empty_fl |= empty[staff] ? 1 : 2; if (cursys->staff[staff].flags & (CLOSE_BRACE | CLOSE_BRACE2)) break; staff++; } if (empty_fl == 3) { /* if empty and not empty staves */ while (i <= staff) { empty[i] = 0; empty_gl[i++] = 0; } } } } /* -- define the start and end of a piece of tune -- */ /* tsnext becomes the beginning of the next line */ static void set_piece(void) { struct SYSTEM *sy; struct SYMBOL *s; struct VOICE_S *p_voice; struct STAFF_S *p_staff; int staff; char empty[MAXSTAFF], empty_gl[MAXSTAFF]; /* reset the staves */ sy = cursys; for (staff = 0; staff <= nstaff; staff++) { p_staff = &staff_tb[staff]; p_staff->y = 0; /* staff system not computed */ p_staff->stafflines = sy->staff[staff].stafflines; p_staff->staffscale = sy->staff[staff].staffscale; } /* search the next end of line, * set the repeat measures, (remove some dble bars?) * and flag the empty staves */ memset(empty, 1, sizeof empty); memset(empty_gl, 1, sizeof empty_gl); for (s = tsfirst; s; s = s->ts_next) { if (s->sflags & S_NL) break; if (s->sflags & S_NEW_SY) { set_brace(sy, empty, empty_gl); for (staff = 0; staff <= nstaff; staff++) { sy->staff[staff].empty = empty[staff]; empty[staff] = 1; } sy = sy->next; for (staff = 0; staff <= sy->nstaff; staff++) { p_staff = &staff_tb[staff]; p_staff->stafflines = sy->staff[staff].stafflines; if (!p_staff->stafflines) p_staff->stafflines = "|||||"; p_staff->staffscale = sy->staff[staff].staffscale; if (p_staff->staffscale == 0) p_staff->staffscale = 1; } } if (!empty[s->staff]) continue; switch (s->type) { case GRACE: empty_gl[s->staff] = empty[s->staff] = 0; break; case NOTEREST: case SPACE: case MREST: if (cfmt.staffnonote > 1) { empty_gl[s->staff] = empty[s->staff] = 0; } else if (!(s->flags & ABC_F_INVIS)) { if (s->abc_type == ABC_T_NOTE || cfmt.staffnonote != 0) empty_gl[s->staff] = empty[s->staff] = 0; } break; } } tsnext = s; /* set the last empty staves */ set_brace(sy, empty, empty_gl); for (staff = 0; staff <= nstaff; staff++) sy->staff[staff].empty = empty[staff]; /* define the offsets of the measure bars */ for (staff = 0; staff <= nstaff; staff++) { int i, l; char *stafflines; if (empty_gl[staff]) continue; p_staff = &staff_tb[staff]; stafflines = p_staff->stafflines; l = strlen(stafflines); p_staff->topbar = 6 * (l - 1); for (i = 0; i < l - 1; i++) if (stafflines[i] != '.') break; p_staff->botbar = i * 6; if (i >= l - 2) { // 0, 1 or 2 lines p_staff->botbar -= 6; p_staff->topbar += 6; } } /* move the symbols of the empty staves to the next staff */ // sy = cursys; for (staff = 0; staff < nstaff; staff++) { #if 1 if (empty_gl[staff]) sym_staff_move(staff); #else if (sy->staff[staff].empty) sym_staff_move(staff, tsfirst, sy); } if (sy->next) { for (s = tsfirst; s; s = s->ts_next) { if (s->sflags & S_NL) break; if (s->sflags & S_NEW_SY) { sy = sy->next; for (staff = 0; staff < nstaff; staff++) { if (sy->staff[staff].empty) sym_staff_move(staff, s, sy); } if (!sy->next) break; } } #endif } /* initialize the music line */ init_music_line(); /* if last music line, nothing more to do */ if (!tsnext) return; s = tsnext; s->sflags &= ~S_NL; s = s->ts_prev; s->ts_next = NULL; /* set the end of the voices */ for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { int voice; voice = p_voice - voice_tb; for (s = tsnext->ts_prev; s; s = s->ts_prev) { if (s->voice == voice) { s->next = NULL; check_bar(s); break; } } if (!s) p_voice->sym = NULL; } } /* -- position the symbols along the staff -- */ static void set_sym_glue(float width) { struct SYMBOL *s; float beta0, alfa, beta; int some_grace; float xmin, x, xmax, spafac; /* calculate the whole space of the symbols */ some_grace = 0; xmin = x = xmax = 0; s = tsfirst; for (;;) { if (s->type == GRACE) some_grace = 1; if (s->sflags & S_SEQST) { float space; xmin += s->shrink; if ((space = s->space) < s->shrink) space = s->shrink; x += space; if (cfmt.stretchstaff) space *= 1.8; xmax += space; } if (!s->ts_next) break; s = s->ts_next; } /* set max shrink and stretch */ if (!cfmt.continueall) beta0 = BETA_X; else beta0 = BETA_C; /* memorize the glue for the last music line */ if (tsnext) { if (x - width >= 0) { beta_last = 0; } else { beta_last = (width - x) / (xmax - x); /* stretch */ if (beta_last > beta0) { if (cfmt.stretchstaff) { if (!cfmt.continueall && cfmt.linewarn) { error(0, s, "Line underfull (%.0fpt of %.0fpt)", beta0 * xmax + (1 - beta0) * x, width); } } else { width = x; beta_last = 0; } } } } else { /* if last music line */ if (x < width) { beta = (width - x) / (xmax - x); /* stretch */ if (beta >= beta_last) { beta = beta_last * xmax + (1 - beta_last) * x; /* shrink underfull last line same as previous */ if (beta < width * (1. - cfmt.stretchlast)) width = beta; } } } spafac = width / x; /* space expansion factor */ /* define the x offsets of all starting symbols */ x = xmax = 0; s = tsfirst; for (;;) { if (s->sflags & S_SEQST) { float new_space; new_space = s->shrink; if (s->space != 0) { if (new_space < s->space * spafac) new_space = s->space * spafac; xmax += s->space * spafac * 1.8; } x += new_space; xmax += new_space; s->x = x; s->xmax = xmax; } if (!s->ts_next) break; s = s->ts_next; } /* if the last symbol is not a bar, add some extra space */ switch (s->type) { case BAR: case FMTCHG: break; case CUSTOS: x += s->wr; xmin += s->wr; xmax += s->wr; break; default: { float min; min = s->wr; while (!(s->sflags & S_SEQST)) { s = s->ts_prev; if (s->wr > min) min = s->wr; } xmin += min + 3; if (tsnext && tsnext->space * 0.8 > s->wr + 4) { x += tsnext->space * 0.8 * spafac; xmax += tsnext->space * 0.8 * spafac * 1.8; } else { #if 1 x += min + 4; xmax += min + 4; #else /*fixme:should calculate the space according to the last symbol duration */ x += (min + 4) * spafac; xmax += (min + 4) * spafac * 1.8; #endif } break; } } /* calculate the exact glue */ if (x >= width) { beta = 0; if (x == xmin) { alfa = 1; // no extra space } else { alfa = (x - width) / (x - xmin); /* shrink */ if (alfa > 1) { error(1, s, "Line too much shrunk (%.0f/%0.fpt of %.0fpt)", xmin, x, width); // uncomment for staff greater than music line // alfa = 1; } } realwidth = xmin * alfa + x * (1 - alfa); } else { alfa = 0; if (xmax > x) beta = (width - x) / (xmax - x); /* stretch */ else beta = 1; /* (no note) */ if (beta > beta0) { if (!cfmt.stretchstaff) beta = 0; } realwidth = xmax * beta + x * (1 - beta); } /* set the final x offsets */ s = tsfirst; if (alfa != 0) { if (alfa < 1) { x = xmin = 0; for (; s; s = s->ts_next) { if (s->sflags & S_SEQST) { xmin += s->shrink * alfa; x = xmin + s->x * (1 - alfa); } s->x = x; } } else { alfa = realwidth / x; x = 0; for (; s; s = s->ts_next) { if (s->sflags & S_SEQST) x = s->x * alfa; s->x = x; } } } else { x = 0; for (; s; s = s->ts_next) { if (s->sflags & S_SEQST) x = s->xmax * beta + s->x * (1 - beta); s->x = x; } } /* set the x offsets of the grace notes */ if (some_grace) { for (s = tsfirst; s; s = s->ts_next) { struct SYMBOL *g; if (s->type != GRACE) continue; x = s->x - s->wl + (cfmt.gracespace >> 16) * 0.1; for (g = s->extra; g; g = g->next) if (g->type == NOTEREST) g->x += x; } } } /* -- initialize a new music line -- */ static void new_music_line(void) { struct VOICE_S *p_voice; struct SYMBOL *s; int voice; /* set the first symbol of each voice */ tsfirst->ts_prev = NULL; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { p_voice->sym = NULL; /* may have no symbol */ voice = p_voice - voice_tb; for (s = tsfirst; s; s = s->ts_next) { if (s->voice == voice) { p_voice->sym = s; s->prev = NULL; break; } } } } /* -- initialize the start of generation / new music line -- */ static void gen_init(void) { struct SYMBOL *s; for (s = tsfirst ; s; s = s->ts_next) { if (s->extra) { output_ps(s, 0); if (!s->extra && s->type == FMTCHG) { unlksym(s); if (!tsfirst) return; } } if (s->sflags & S_NEW_SY) { s->sflags &= ~S_NEW_SY; cursys = cursys->next; } switch (s->type) { case CLEF: case KEYSIG: case TIMESIG: continue; // default: // break; /* may be Q: */ } return; } tsfirst = NULL; /* no more notes */ } /* -- show the errors -- */ static void error_show(void) { struct SYMBOL *s; for (s = tsfirst; s; s = s->ts_next) { if (s->flags & ABC_F_ERROR) { putxy(s->x, staff_tb[s->staff].y + s->y); a2b("showerror\n"); } } } /* -- delay output until the staves are defined (by draw_systems) -- */ static float delayed_output(float indent) { float line_height; char *outbuf_sav, *mbf_sav, *tmpbuf; outbuf_sav = outbuf; mbf_sav = mbf; tmpbuf = malloc(outbufsz); if (!tmpbuf) { error(1, NULL, "Out of memory for delayed outbuf - abort"); exit(EXIT_FAILURE); } mbf = outbuf = tmpbuf; *outbuf = '\0'; outft = -1; draw_sym_near(); outbuf = outbuf_sav; mbf = mbf_sav; outft = -1; line_height = draw_systems(indent); a2b("%s", tmpbuf); free(tmpbuf); return line_height; } /* -- generate the music -- */ void output_music(void) { struct VOICE_S *p_voice; float lwidth, indent; /* set the staff system if any STAVES at start of the next line */ gen_init(); if (!tsfirst) return; check_buffer(); set_global(); /* initialize the generator */ if (first_voice->next) { /* if many voices */ // if (cfmt.combinevoices >= 0) combine_voices(); set_stem_dir(); /* set the stems direction in 'multi' */ } for (p_voice = first_voice; p_voice; p_voice = p_voice->next) set_beams(p_voice->sym); /* decide on beams */ set_stems(); /* set the stem lengths */ if (first_voice->next) { /* when multi-voices */ set_rest_offset(); /* set the vertical offset of rests */ set_overlap(); /* shift the notes on voice overlap */ } set_acc_shft(); // set the horizontal offset of accidentals set_allsymwidth(NULL); /* set the width of all symbols */ lwidth = ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth) - cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale; if (lwidth < 50) { error(1, 0, "Bad page width %.1f", lwidth); lwidth = 10 CM; } indent = set_indent(); cut_tune(lwidth, indent); beta_last = 0; for (;;) { /* loop per music line */ float line_height; set_piece(); indent = set_indent(); set_sym_glue(lwidth - indent); if (indent != 0) a2b("%.2f 0 T\n", indent); /* do indentation */ line_height = delayed_output(indent); draw_all_symb(); draw_all_deco(); if (showerror) error_show(); bskip(line_height); if (indent != 0) { a2b("%.2f 0 T\n", -indent); insert_meter &= ~2; // no more indentation } tsfirst = tsnext; gen_init(); if (!tsfirst) break; buffer_eob(0); new_music_line(); } outft = -1; } /* -- reset the generator -- */ void reset_gen(void) { if (cfmt.fields[0] & (1 << ('M' - 'A'))) insert_meter = 3; /* insert meter and indent */ else insert_meter = 2; /* indent only */ } �����������������������������������������������abcm2ps-8.14.11/musicfont.fmt�����������������������������������������������������������������������0000664�0000000�0000000�00000002150�13762665467�0015760�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������% example of musical symbols by font % --- SVG definitions --- beginsvg <style type="text/css"> @font-face { font-family: 'MusicFont'; src: local(FreeSerif); font-weight: normal; font-style: normal; } </style> endsvg beginps svg /tclef{/MusicFont 35 selectfont M -12 0 RM(\ud834\udd1e)show}! /bclef{/MusicFont 35 selectfont M -12 -3 RM(\ud834\udd22)show}! /csig{/MusicFont 32 selectfont M -7 0 RM(\ud834\udd34)show}! % the brace does not work with Opera /brace{/MusicFont 35 selectfont gsave T -8 0 M -.04 mul 1 exch scale (\ud834\udd14)show grestore}! endps % --- PostScript definitions --- beginps nosvg % with FreeSerif /tclef{/FreeSerif 35 selectfont M -12 0 RM/g_clef glyphshow}! /bclef{/FreeSerif 35 selectfont M -12 -3 RM/f_clef glyphshow}! /csig{/FreeSerif 32 selectfont M -8 0 RM/common_time glyphshow}! /brace{/FreeSerif 35 selectfont gsave T -8 0 M -.04 mul 1 exch scale /brace glyphshow grestore}! % with PMW-Music (use "GS_FONTPATH=/usr/local/src/pmw-4.21/psfonts/ gs Out.ps") %/tclef{/PMW-Music 15 selectfont M -10 0 RM(!)show}! % I let your search the other symbols.. endps ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������abcm2ps-8.14.11/newfeatures.abc���������������������������������������������������������������������0000664�0000000�0000000�00000006153�13762665467�0016247�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������%%textfont Helvetica-Bold 21 %%center Examples for new features in abc2ps-1.2.5 %%textfont * 16 %%center Oct. 5 1997 %%textfont Times-Roman 16 X:1 T:Scale with Treble Clef M:C %%freegchord 1 % abcm2ps: don't print 'b' as a flat sign K:C treble % abcm2ps: must be forced because too low at the beginning L: 1/4 "C,"C,"D,"D,"E,"E,"F,"F, "G,"G,"A,"A,"B,"B,\ | "C"C"D"D"E"E"F"F "G"G"A"A"B"B| "c"c "d"d"e"e"f"f "g"g"a"a"b"b"c'"c' | X:2 T:Scale with Bass and Alto Clef M:C K:C bass % abcm2ps: no transposition L: 1/4 P:Bass "C,,,"C,,,"D,,,"D,,,"E,,,"E,,,"F,,,"F,,, "G,,,"G,,,"A,,,"A,,,"B,,,"B,,,|\ "C,,"C,,"D,,"D,,"E,,"E,,"F,,"F,, "G,,"G,,"A,,"A,,"B,,"B,,|\ "C,"C, "D,"D,"E,"E,"F,"F, "G,"G,"A,"A,"B,"B,"C"C | P:Alto %abcm2ps: don't have a new key signature %K:C alto K: clef=alto "C,,"C,,"D,,"D,,"E,,"E,,"F,,"F,, "G,,"G,,"A,,"A,,"B,,"B,,\ | "C,"C,"D,"D,"E,"E,"F,"F, "G,"G,"A,"A,"B,"B,| "C"C "D"D"E"E"F"F "G"G"A"A"B"B"c"c | X:3 T:Clef Changes Within Tune T:In-Line Info Fields by [..] % text moved out of tune header - change in abcm2ps-7.x.x M:C L: 1/8 K:C %%begintext align Here is an example for a block of text which is associated with a specific tune. It will only be printed if this tune (number 3) is selected. The text should be placed after the "T:" field and before the block is terminated by a blank line. Text which is outside a block is always printed; for example, the title at the top of the page. %%endtext cdef gabc' [K:bass] | C,D,E,F, G,A,B,C [K:D treble] | cdef gabc' | %%sep %%text Note: this line and the separator above are also associated with this tune. X:4 T:Vocals T:Note also the trill C:Music: Trad. C:Text: Anonymous M:C K:C L: 1/4 e>e ez || edTc2 | ed(c2 | e2 c2- | Hc4) |] w: *** 1.~~Three blind mice, three blind mice.___ w: *** 2.~~See how~they run, see how~they ru-uuu-un._ X:6 T:Invisible Rests Using X M:C K:C L: 1/4 "F"z4|"F"x4|"F"z4|"F"x4|"Bb"z4|"Bb"x4|"F"z4|"F"x4|"C"z4|"Bb"x4|"F"z4|"F"x4|| %%leftmargin 3cm X:5 T:Scotland The Brave T:Demonstrating the Bagpipe Mode and Output Formatting %%titleleft %%titlefont Helvetica-Bold 24 %%subtitlefont Helvetica-Bold 16 %%composerspace 0.4cm %%composerfont Helvetica 13 %%staffwidth 5.5in %%scale 0.75 %%staffsep 55 C:Trad. C:from PS file by Alan S. Watt P:March L:1/8 M:4/4 K:HP e|{g}A2 {GdGe}A>B {gcd}c{e}A {gcd}ce| {ag}a2{g}a2 {GdG}ae {gcd}c{e}A| {Gdc}d2 {g}f>d {gcd}ce {gcd}c{e}A|{GdG}B2{gef}e2{A}e>f {g}e/>d/{g}c/>B/| {g}A2 {GdGe}A>B {gcd}c{e}A {gcd}ce| {ag}a2{g}a2 {GdG}ae {gcd}c{e}A| {Gdc}d2 {g}f>d {gcd}ce {gcd}c{e}A|{GdG}B2{g}A>B {G}A2 {gcd}ce|| %%vskip 1cm %%textfont Times-Roman 16 %%begintext Summary of changes: - Bass and alto clefs. - Vocals. - In-line info fields can be coded using [...]. - Subtitles now printed without "or:". - Can be more than one composer field. - Predefined formats: standard, pretty, pretty2 (flag -p, -P). - Format page layout by .fmt file selected with flag -F. - Format page layout by %%-pseudocomments in abc file. - Other pseudocomments: %%sep, %%vskip, %%newpage. - Text output using %%text, %%center, and %%begintext .. %%endtext. - "x" functions like a rest but is invisible on the page. - Bagpipe mode for K:HP. %%endtext ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������abcm2ps-8.14.11/parse.c�����������������������������������������������������������������������������0000664�0000000�0000000�00000406736�13762665467�0014541�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Parsing functions. * * This file is part of abcm2ps. * * Copyright (C) 1998-2020 Jean-François Moine (http://moinejf.free.fr) * Adapted from abc2ps, Copyright (C) 1996-1998 Michael Methfessel * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <ctype.h> #include <regex.h> #include "abcm2ps.h" /* options = external formatting */ struct symsel_s { /* symbol selection */ short bar; short time; char seq; }; struct brk_s { /* music line break */ struct brk_s *next; struct symsel_s symsel; }; struct voice_opt_s { /* voice options */ struct voice_opt_s *next; struct SYMBOL *s; /* list of options (%%xxx) */ }; struct tune_opt_s { /* tune options */ struct tune_opt_s *next; struct voice_opt_s *voice_opts; struct SYMBOL *s; /* list of options (%%xxx) */ }; int nstaff; /* (0..MAXSTAFF-1) */ struct SYMBOL *tsfirst; /* first symbol in the time sorted list */ struct VOICE_S voice_tb[MAXVOICE]; /* voice table */ struct VOICE_S *first_voice; /* first voice */ struct SYSTEM *cursys; /* current system */ static struct SYSTEM *parsys; /* current system while parsing */ struct FORMAT dfmt; /* current global format */ int nbar; /* current measure number */ struct map *maps; /* note mappings */ static struct voice_opt_s *voice_opts, *tune_voice_opts; static struct tune_opt_s *tune_opts, *cur_tune_opts; static struct brk_s *brks; static struct symsel_s clip_start, clip_end; static INFO info_glob; /* global info definitions */ static char *deco_glob[256]; /* global decoration table */ static struct map *maps_glob; /* save note maps */ static int over_time; /* voice overlay start time */ static int over_mxtime; /* voice overlay max time */ static short over_bar; /* voice overlay in a measure */ static short over_voice; /* main voice in voice overlay */ static int staves_found; /* time of the last %%staves */ static int abc2win; static int capo; // capo indication float multicol_start; /* (for multicol) */ static float multicol_max; static float lmarg, rmarg; static void get_clef(struct SYMBOL *s); static struct SYMBOL *get_info(struct SYMBOL *s); static void get_key(struct SYMBOL *s); static void get_meter(struct SYMBOL *s); static void get_voice(struct SYMBOL *s); static void get_note(struct SYMBOL *s); static struct SYMBOL *process_pscomment(struct SYMBOL *s); static void ps_def(struct SYMBOL *s, char *p, char use); static void set_tblt(struct VOICE_S *p_voice); static void set_tuplet(struct SYMBOL *s); /* -- weight of the symbols -- */ static char w_tb[NSYMTYPES] = { /* !! index = symbol type !! */ 0, 9, /* 1- note / rest */ 3, /* 2- space */ 2, /* 3- bar */ 1, /* 4- clef */ 6, /* 5- timesig */ 5, /* 6- keysig */ 0, /* 7- tempo */ 0, /* 8- staves */ 9, /* 9- mrest */ 0, /* 10- part */ 3, /* 11- grace */ 0, /* 12- fmtchg */ 8, /* 13- tuplet */ 7, /* 14- stbrk */ 7 /* 15- custos */ }; /* key signature transposition tables */ static signed char cde2fcg[7] = {0, 2, 4, -1, 1, 3, 5}; static char cgd2cde[7] = {0, 4, 1, 5, 2, 6, 3}; /* -- link a ABC symbol into the current voice -- */ static void sym_link(struct SYMBOL *s, int type) { struct VOICE_S *p_voice = curvoice; if (!p_voice->ignore) { s->prev = p_voice->last_sym; if (s->prev) p_voice->last_sym->next = s; else p_voice->sym = s; p_voice->last_sym = s; //fixme:test bug // } else { // if (p_voice->sym) // p_voice->last_sym = p_voice->sym = s; } s->type = type; s->voice = p_voice - voice_tb; s->staff = p_voice->cstaff; s->time = p_voice->time; s->posit = p_voice->posit; } /* -- add a new symbol in a voice -- */ struct SYMBOL *sym_add(struct VOICE_S *p_voice, int type) { struct SYMBOL *s; struct VOICE_S *p_voice2; s = (struct SYMBOL *) getarena(sizeof *s); memset(s, 0, sizeof *s); p_voice2 = curvoice; curvoice = p_voice; sym_link(s, type); curvoice = p_voice2; if (p_voice->second) s->sflags |= S_SECOND; if (p_voice->floating) s->sflags |= S_FLOATING; if (s->prev) { s->fn = s->prev->fn; s->linenum = s->prev->linenum; s->colnum = s->prev->colnum; } return s; } /* -- expand a multi-rest into single rests and measure bars -- */ static void mrest_expand(struct SYMBOL *s) { struct VOICE_S *p_voice; struct SYMBOL *s2, *next; struct decos dc; int nb, dt; nb = s->u.bar.len; dt = s->dur / nb; /* change the multi-rest (type bar) to a single rest */ memcpy(&dc, &s->u.bar.dc, sizeof dc); memset(&s->u.note, 0, sizeof s->u.note); s->type = NOTEREST; s->abc_type = ABC_T_REST; // s->nhd = 0; s->dur = s->u.note.notes[0].len = dt; s->head = H_FULL; s->nflags = -2; /* add the bar(s) and rest(s) */ next = s->next; p_voice = &voice_tb[s->voice]; p_voice->last_sym = s; p_voice->time = s->time + dt; p_voice->cstaff = s->staff; s2 = s; while (--nb > 0) { s2 = sym_add(p_voice, BAR); s2->abc_type = ABC_T_BAR; s2->u.bar.type = B_SINGLE; s2 = sym_add(p_voice, NOTEREST); s2->abc_type = ABC_T_REST; s2->flags = s->flags; s2->dur = s2->u.note.notes[0].len = dt; s2->head = H_FULL; s2->nflags = -2; p_voice->time += dt; } s2->next = next; if (next) next->prev = s2; /* copy the mrest decorations to the last rest */ memcpy(&s2->u.note.dc, &dc, sizeof s2->u.note.dc); } /* -- sort all symbols by time and vertical sequence -- */ static void sort_all(void) { struct SYSTEM *sy; struct SYMBOL *s, *prev, *s2; struct VOICE_S *p_voice; int fl, voice, time, w, wmin, multi, mrest_time; int nb, r, set_sy, new_sy; // nv struct SYMBOL *vtb[MAXVOICE]; signed char vn[MAXVOICE]; /* voice indexed by range */ /* memset(vtb, 0, sizeof vtb); */ mrest_time = -1; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) vtb[p_voice - voice_tb] = p_voice->sym; /* initialize the voice order */ sy = cursys; set_sy = 1; new_sy = 0; prev = NULL; fl = 1; /* (have gcc happy) */ multi = -1; /* (have gcc happy) */ for (;;) { if (set_sy) { fl = 1; // start a new sequence // if (!new_sy) { if (1) { set_sy = 0; multi = -1; memset(vn, -1, sizeof vn); for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { voice = p_voice - voice_tb; r = sy->voice[voice].range; if (r < 0) continue; vn[r] = voice; multi++; } } } /* search the min time and symbol weight */ wmin = time = (unsigned) ~0 >> 1; /* max int */ // nv = nb = 0; for (r = 0; r < MAXVOICE; r++) { voice = vn[r]; if (voice < 0) break; s = vtb[voice]; if (!s || s->time > time) continue; w = w_tb[s->type]; if (s->time < time) { time = s->time; wmin = w; // nb = 0; } else if (w < wmin) { wmin = w; // nb = 0; } #if 0 if (!(s->sflags & S_SECOND)) { nv++; if (s->type == BAR) nb++; } #endif if (s->type == MREST) { if (s->u.bar.len == 1) mrest_expand(s); else if (multi > 0) mrest_time = time; } } if (wmin > 127) break; /* done */ #if 0 /* align the measure bars */ if (nb != 0 && nb != nv) { /* if other symbol than bars */ wmin = (unsigned) ~0 >> 1; for (r = 0; r < MAXVOICE; r++) { voice = vn[r]; if (voice < 0) break; s = vtb[voice]; if (!s || s->time > time || s->type == BAR) continue; w = w_tb[s->type]; if (w < wmin) wmin = w; } if (wmin > 127) wmin = w_tb[BAR]; } #endif /* if some multi-rest and many voices, expand */ if (time == mrest_time) { nb = 0; for (r = 0; r < MAXVOICE; r++) { voice = vn[r]; if (voice < 0) break; s = vtb[voice]; if (!s || s->time != time) continue; w = w_tb[s->type]; if (w != wmin) continue; if (s->type != MREST) { mrest_time = -1; /* some note or rest */ break; } if (nb == 0) { nb = s->u.bar.len; } else if (nb != s->u.bar.len) { mrest_time = -1; /* different duration */ break; } } if (mrest_time < 0) { for (r = 0; r < MAXVOICE; r++) { voice = vn[r]; if (voice < 0) break; s = vtb[voice]; if (s && s->type == MREST) mrest_expand(s); } } } /* link the vertical sequence */ for (r = 0; r < MAXVOICE; r++) { voice = vn[r]; if (voice < 0) break; s = vtb[voice]; if (!s || s->time != time || w_tb[s->type] != wmin) continue; if (s->type == STAVES) { // change STAVES to a flag sy = sy->next; set_sy = new_sy = 1; if (s->prev) s->prev->next = s->next; else voice_tb[voice].sym = s->next; if (s->next) s->next->prev = s->prev; } else { if (fl) { fl = 0; s->sflags |= S_SEQST; } if (new_sy) { new_sy = 0; s->sflags |= S_NEW_SY; } s->ts_prev = prev; if (prev) { prev->ts_next = s; //fixme: bad error when the 1st voice is second // if (s->type == BAR // && (s->sflags & S_SECOND) // && prev->type != BAR // && !(s->flags & ABC_F_INVIS)) // error(1, s, "Bad measure bar"); } else { tsfirst = s; } prev = s; } vtb[voice] = s->next; } fl = wmin; /* start a new sequence if some space */ } if (!prev) return; /* if no bar or format_change at end of tune, add a dummy symbol */ if ((prev->type != BAR && prev->type != FMTCHG) || new_sy) { p_voice = &voice_tb[prev->voice]; p_voice->last_sym = prev; s = sym_add(p_voice, FMTCHG); s->aux = -1; s->time = prev->time + prev->dur; s->sflags = S_SEQST; if (new_sy) s->sflags |= S_NEW_SY; prev->ts_next = s; s->ts_prev = prev; for (;;) { prev->sflags &= ~S_EOLN; if (prev->sflags & S_SEQST) break; prev = prev->ts_prev; } } /* if Q: from tune header, put it at start of the music */ s2 = info['Q' - 'A']; if (!s2) return; info['Q' - 'A'] = NULL; s = tsfirst->extra; while (s) { if (s->type == TEMPO) return; /* already a tempo */ s = s->next; } s = tsfirst; s2->type = TEMPO; s2->voice = s->voice; s2->staff = s->staff; s2->time = s->time; if (s->extra) { s2->next = s->extra; s2->next->prev = s2; } s->extra = s2; } /* -- move the symbols with no width to the next symbol -- */ static void voice_compress(void) { struct VOICE_S *p_voice; struct SYMBOL *s, *s2, *s3, *ns; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { //8.7.0 - for fmt at end of music line // if (p_voice->ignore) // continue; p_voice->ignore = 0; for (s = p_voice->sym; s; s = s->next) { if (s->time >= staves_found) break; } ns = NULL; for ( ; s; s = s->next) { switch (s->type) { #if 0 // test case KEYSIG: /* remove the empty key signatures */ if (s->u.key.empty) { if (s->prev) s->prev->next = s->next; else p_voice->sym = s->next; if (s->next) s->next->prev = s->prev; continue; } break; #endif case FMTCHG: s2 = s->extra; if (s2) { /* dummy format */ if (!ns) ns = s2; if (s->prev) { s->prev->next = s2; s2->prev = s->prev; } if (!s->next) { ns = NULL; break; } while (s2->next) s2 = s2->next; s->next->prev = s2; s2->next = s->next; } /* fall thru */ case TEMPO: case PART: case TUPLET: if (!ns) ns = s; continue; case MREST: /* don't shift P: and Q: */ if (!ns) continue; s2 = (struct SYMBOL *) getarena(sizeof *s); memset(s2, 0, sizeof *s2); s2->type = SPACE; s2->u.note.notes[1].len = -1; s2->flags = ABC_F_INVIS; s2->voice = s->voice; s2->staff = s->staff; s2->time = s->time; s2->sflags = s->sflags; s2->next = s; s2->prev = s->prev; s2->prev->next = s2; s->prev = s2; s = s2; break; } if (s->flags & ABC_F_GRACE) { if (!ns) ns = s; while (!(s->flags & ABC_F_GR_END)) s = s->next; s2 = (struct SYMBOL *) getarena(sizeof *s); memcpy(s2, s, sizeof *s2); s2->abc_type = 0; s2->type = GRACE; s2->dur = 0; s2->next = s->next; if (s2->next) { s2->next->prev = s2; if (cfmt.graceword) { for (s3 = s2->next; s3; s3 = s3->next) { switch (s3->type) { case SPACE: continue; case NOTEREST: s2->ly = s3->ly; s3->ly = NULL; default: break; } break; } } } else { p_voice->last_sym = s2; } s2->prev = s; s->next = s2; s = s2; // with w_tb[BAR] = 2, // the grace notes go after the bar // if before a bar, change the grace time if (s->next && s->next->type == BAR) s->time--; } if (!ns) continue; s->extra = ns; s->prev->next = NULL; s->prev = ns->prev; if (s->prev) s->prev->next = s; else p_voice->sym = s; ns->prev = NULL; ns = NULL; } /* when symbols with no space at end of tune, * add a dummy format */ if (ns) { s = sym_add(p_voice, FMTCHG); s->aux = -1; /* nothing */ s->extra = ns; s->prev->next = NULL; /* unlink */ s->prev = ns->prev; if (s->prev) s->prev->next = s; else p_voice->sym = s; ns->prev = NULL; } } } /* -- duplicate the voices as required -- */ static void voice_dup(void) { struct VOICE_S *p_voice, *p_voice2; struct SYMBOL *s, *s2, *g, *g2; int voice; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { if ((voice = p_voice->clone) < 0) continue; p_voice->clone = -1; p_voice2 = &voice_tb[voice]; for (s = p_voice->sym; s; s = s->next) { //fixme: there may be other symbols before the %%staves at this same time if (s->time >= staves_found) break; } for ( ; s; s = s->next) { if (s->type == STAVES) continue; s2 = (struct SYMBOL *) getarena(sizeof *s2); memcpy(s2, s, sizeof *s2); s2->prev = p_voice2->last_sym; s2->next = NULL; if (p_voice2->sym) p_voice2->last_sym->next = s2; else p_voice2->sym = s2; p_voice2->last_sym = s2; s2->voice = voice; s2->staff = p_voice2->staff; if (p_voice2->second) s2->sflags |= S_SECOND; else s2->sflags &= ~S_SECOND; if (p_voice2->floating) s2->sflags |= S_FLOATING; else s2->sflags &= ~S_FLOATING; s2->ly = NULL; g = s2->extra; if (!g) continue; g2 = (struct SYMBOL *) getarena(sizeof *g2); memcpy(g2, g, sizeof *g2); s2->extra = g2; s2 = g2; s2->voice = voice; s2->staff = p_voice2->staff; for (g = g->next; g; g = g->next) { g2 = (struct SYMBOL *) getarena(sizeof *g2); memcpy(g2, g, sizeof *g2); s2->next = g2; g2->prev = s2; s2 = g2; s2->voice = voice; s2->staff = p_voice2->staff; } } } } /* -- create a new staff system -- */ static void system_new(void) { struct SYSTEM *new_sy; int staff, voice; new_sy = (struct SYSTEM *) getarena(sizeof *new_sy); if (!parsys) { memset(new_sy, 0, sizeof *new_sy); for (voice = 0; voice < MAXVOICE; voice++) { new_sy->voice[voice].range = -1; } for (staff = 0; staff < MAXSTAFF; staff++) { new_sy->staff[staff].stafflines = "|||||"; new_sy->staff[staff].staffscale = 1; } cursys = new_sy; } else { for (voice = 0; voice < MAXVOICE; voice++) { // update the previous system // if (parsys->voice[voice].range < 0 // || parsys->voice[voice].second) // continue; staff = parsys->voice[voice].staff; if (voice_tb[voice].stafflines) parsys->staff[staff].stafflines = voice_tb[voice].stafflines; if (voice_tb[voice].staffscale != 0) parsys->staff[staff].staffscale = voice_tb[voice].staffscale; } memcpy(new_sy, parsys, sizeof *new_sy); for (voice = 0; voice < MAXVOICE; voice++) { new_sy->voice[voice].range = -1; new_sy->voice[voice].second = 0; } for (staff = 0; staff < MAXSTAFF; staff++) new_sy->staff[staff].flags = 0; parsys->next = new_sy; } parsys = new_sy; } /* -- initialize the voices and staves -- */ /* this routine is called when starting the generation */ static void system_init(void) { voice_compress(); voice_dup(); sort_all(); /* define the time / vertical sequences */ // if (!tsfirst) // return; // parsys->nstaff = nstaff; /* save the number of staves */ } /* go to a global (measure + time) */ static struct SYMBOL *go_global_time(struct SYMBOL *s, struct symsel_s *symsel) { struct SYMBOL *s2; int bar_time; if (symsel->bar <= 1) { /* special case: there is no measure 0/1 */ // && nbar == -1) { /* see set_bar_num */ if (symsel->bar == 0) goto chk_time; for (s2 = s; s2; s2 = s2->ts_next) { if (s2->type == BAR && s2->time != 0) break; } if (s2->time < voice_tb[cursys->top_voice].meter.wmeasure) s = s2; goto chk_time; } for ( ; s; s = s->ts_next) { if (s->type == BAR && s->aux >= symsel->bar) break; } if (!s) return NULL; if (symsel->seq != 0) { int seq; seq = symsel->seq; for (s = s->ts_next; s; s = s->ts_next) { if (s->type == BAR && s->aux == symsel->bar) { if (--seq == 0) break; } } if (!s) return NULL; } chk_time: if (symsel->time == 0) return s; bar_time = s->time + symsel->time; while (s->time < bar_time) { s = s->ts_next; if (!s) return s; } do { s = s->ts_prev; /* go back to the previous sequence */ } while (!(s->sflags & S_SEQST)); return s; } /* treat %%clip */ static void do_clip(void) { struct SYMBOL *s, *s2; struct SYSTEM *sy; struct VOICE_S *p_voice; int voice; /* remove the beginning of the tune */ s = tsfirst; if (clip_start.bar > 0 || clip_start.time > 0) { s = go_global_time(s, &clip_start); if (!s) { tsfirst = NULL; return; } /* update the start of voices */ sy = cursys; for (s2 = tsfirst; s2 != s; s2 = s2->ts_next) { if (s->sflags & S_NEW_SY) sy = sy->next; switch (s2->type) { case CLEF: voice_tb[s2->voice].s_clef = s2; break; case KEYSIG: memcpy(&voice_tb[s2->voice].key, &s2->u.key, sizeof voice_tb[0].key); break; case TIMESIG: memcpy(&voice_tb[s2->voice].meter, &s2->u.meter, sizeof voice_tb[0].meter); break; } } cursys = sy; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { voice = p_voice - voice_tb; for (s2 = s; s2; s2 = s2->ts_next) { if (s2->voice == voice) { s2->prev = NULL; break; } } p_voice->sym = s2; } tsfirst = s; s->ts_prev = NULL; } /* remove the end of the tune */ s = go_global_time(s, &clip_end); if (!s) return; /* keep the current sequence */ do { s = s->ts_next; if (!s) return; } while (!(s->sflags & S_SEQST)); /* cut the voices */ for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { voice = p_voice - voice_tb; for (s2 = s->ts_prev; s2; s2 = s2->ts_prev) { if (s2->voice == voice) { s2->next = NULL; break; } } if (!s2) p_voice->sym = NULL; } s->ts_prev->ts_next = NULL; } /* -- set the bar numbers and treat %%clip / %%break -- */ static void set_bar_num(void) { struct SYMBOL *s, *s2, *s3; int bar_time, wmeasure, tim; int bar_num, bar_rep; wmeasure = voice_tb[cursys->top_voice].meter.wmeasure; bar_rep = nbar; /* don't count a bar at start of line */ for (s = tsfirst; ; s = s->ts_next) { if (!s) return; switch (s->type) { case TIMESIG: case CLEF: case KEYSIG: case FMTCHG: case STBRK: continue; case BAR: if (s->aux) { nbar = s->aux; /* (%%setbarnb) */ break; } if (s->u.bar.repeat_bar && s->text && !cfmt.contbarnb) { if (s->text[0] == '1') { bar_rep = nbar; } else { nbar = bar_rep; /* restart bar numbering */ s->aux = nbar; } } break; } break; } /* set the measure number on the top bars * and move the clefs before the measure bars */ bar_time = s->time + wmeasure; /* for incomplete measure at start of tune */ bar_num = nbar; for ( ; s; s = s->ts_next) { switch (s->type) { case CLEF: if (s->sflags & S_NEW_SY) break; for (s2 = s->ts_prev; s2; s2 = s2->ts_prev) { if (s2->sflags & S_NEW_SY) { s2 = NULL; break; } switch (s2->type) { case BAR: if (s2->sflags & S_SEQST) break; continue; case MREST: case NOTEREST: case SPACE: case STBRK: case TUPLET: s2 = NULL; break; default: continue; } break; } if (!s2) break; /* move the clef */ s->next->prev = s->prev; s->prev->next = s->next; s->ts_next->ts_prev = s->ts_prev; s->ts_prev->ts_next = s->ts_next; s->next = s2; s->prev = s2->prev; if (s->prev) s->prev->next = s; s2->prev = s; s->ts_next = s2; s->ts_prev = s2->ts_prev; if (s->ts_prev) s->ts_prev->ts_next = s; s2->ts_prev = s; // if (s->sflags & S_NEW_SY) { // s->sflags &= ~S_NEW_SY; // s->ts_next->sflags |= S_NEW_SY; // } s3 = s->extra; if (s3) { if (s->ts_next->extra) { while (s3->next) s3 = s3->next; s3->next = s->ts_next->extra; s->ts_next->extra = s->extra; } else { s->ts_next->extra = s3; } s->extra = NULL; } s = s2; break; case TIMESIG: wmeasure = s->u.meter.wmeasure; if (s->time < bar_time) bar_time = s->time + wmeasure; break; case MREST: bar_num += s->u.bar.len - 1; while (s->ts_next && s->ts_next->type != BAR) s = s->ts_next; break; case BAR: // if (s->flags & ABC_F_INVIS) // break; if (s->aux) { bar_num = s->aux; /* (%%setbarnb) */ // if (s->time < bar_time) { // s->aux = 0; break; // } } else { if (s->time < bar_time) /* incomplete measure */ break; bar_num++; } /* check if any repeat bar at this time */ tim = s->time; s2 = s; do { if (s2->type == BAR && s2->u.bar.repeat_bar && s2->text && !cfmt.contbarnb) { if (s2->text[0] == '1') bar_rep = bar_num; else /* restart bar numbering */ bar_num = bar_rep; break; } s2 = s2->next; } while (s2 && s2->time == tim); s->aux = bar_num; bar_time = s->time + wmeasure; break; } } /* do the %%clip stuff */ if (clip_start.bar >= 0) { if (bar_num <= clip_start.bar || nbar > clip_end.bar) { tsfirst = NULL; return; } do_clip(); } /* do the %%break stuff */ { struct brk_s *brk; int nbar_min; // if (nbar == 1) // nbar = -1; /* see go_global_time */ nbar_min = nbar; if (nbar_min == 1) nbar_min = -1; for (brk = brks; brk; brk = brk->next) { if (brk->symsel.bar <= nbar_min || brk->symsel.bar > bar_num) continue; s = go_global_time(tsfirst, &brk->symsel); if (s) s->sflags |= S_EOLN; } } if (cfmt.measurenb < 0) /* if no display of measure bar */ nbar = bar_num; /* update in case of more music to come */ } /* -- generate a piece of tune -- */ static void generate(void) { int old_lvl, voice; struct VOICE_S *p_voice; system_init(); if (!tsfirst) return; /* no symbol */ set_bar_num(); if (!tsfirst) return; /* no more symbol */ old_lvl = lvlarena(2); output_music(); clrarena(2); /* clear generation */ lvlarena(old_lvl); /* reset the parser */ for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { voice = p_voice - voice_tb; p_voice->sym = p_voice->last_sym = NULL; p_voice->time = 0; p_voice->have_ly = 0; p_voice->staff = cursys->voice[voice].staff; p_voice->second = cursys->voice[voice].second; p_voice->s_clef->time = 0; p_voice->lyric_start = NULL; } staves_found = 0; // (for voice compress/dup) } /* -- output the music and lyrics after tune -- */ static void gen_ly(int eob) { generate(); if (info['W' - 'A']) { put_words(info['W' - 'A']); info['W' - 'A'] = NULL; } if (eob) buffer_eob(0); } /* * for transpose purpose, check if a pitch is already in the measure or * if it is tied from a previous note, and return the associated accidental */ static int acc_same_pitch(int pitch) { struct SYMBOL *s = curvoice->last_sym->prev; int i, time; // the overlaid voices may have no measure bars // if (curvoice->id[0] == '&') // s = voice_tb[curvoice->mvoice].last_sym; if (!s) return -1; time = s->time; for (; s; s = s->prev) { switch (s->abc_type) { case ABC_T_BAR: if (s->time < time) return -1; /* no same pitch */ for (;;) { s = s->prev; if (!s) return -1; if (s->abc_type == ABC_T_NOTE) { if (s->time + s->dur == time) break; return -1; } if (s->time < time) return -1; } for (i = 0; i <= s->nhd; i++) { if (s->u.note.notes[i].pit == pitch && s->u.note.notes[i].ti1) return s->u.note.notes[i].acc; } return -1; case ABC_T_NOTE: for (i = 0; i <= s->nhd; i++) { if (s->u.note.notes[i].pit == pitch) return s->u.note.notes[i].acc; } break; } } return -1; } /* transpose a note / chord */ static void note_transpose(struct SYMBOL *s) { int i, j, m, n, d, a, dp, i1, i2, i3, i4, sf_old; static const signed char acc1[6] = {0, 1, 0, -1, 2, -2}; static const char acc2[5] = {A_DF, A_FT, A_NT, A_SH, A_DS}; m = s->nhd; sf_old = curvoice->okey.sf; i2 = curvoice->ckey.sf - sf_old; dp = cgd2cde[(i2 + 4 * 7) % 7]; if (curvoice->transpose < 0 && dp != 0) dp -= 7; dp += curvoice->transpose / 3 / 12 * 7; for (i = 0; i <= m; i++) { /* pitch */ n = s->u.note.notes[i].pit; s->u.note.notes[i].pit += dp; s->pits[i] += dp; /* accidental */ i1 = cde2fcg[(n + 5 + 16 * 7) % 7]; /* fcgdaeb */ a = s->u.note.notes[i].acc & 0x07; if (a == 0) { if (curvoice->okey.nacc == 0) { if (sf_old > 0) { if (i1 < sf_old - 1) a = A_SH; } else if (sf_old < 0) { if (i1 >= sf_old + 6) a = A_FT; } } else { for (j = 0; j < curvoice->okey.nacc; j++) { if ((n + 16 * 7 - curvoice->okey.pits[j]) % 7 == 0) { a = curvoice->okey.accs[j]; break; } } } } i3 = i1 + i2 + acc1[a] * 7; i1 = ((i3 + 1 + 21) / 7 + 2 - 3 + 32 * 5) % 5; a = acc2[(unsigned) i1]; if (s->u.note.notes[i].acc != 0) { ; } else if (curvoice->ckey.empty) { /* key none */ if (a == A_NT || acc_same_pitch(s->u.note.notes[i].pit) >= 0) continue; } else if (curvoice->ckey.nacc > 0) { /* acc list */ i4 = cgd2cde[(unsigned) ((i3 + 16 * 7) % 7)]; for (j = 0; j < curvoice->ckey.nacc; j++) { if ((i4 + 16 * 7 - curvoice->ckey.pits[j]) % 7 == 0) break; } if (j < curvoice->ckey.nacc) continue; } else { continue; } i1 = s->u.note.notes[i].acc & 0x07; i4 = s->u.note.notes[i].acc >> 3; if (i4 != 0 /* microtone */ && i1 != a) { /* different accidental type */ if (s->u.note.microscale) { n = i4; d = s->u.note.microscale; } else { n = parse.micro_tb[i4]; d = ((n & 0xff) + 1) * 2; n = (n >> 8) + 1; } //fixme: double sharps/flats ?*/ //fixme: does not work in all cases (tied notes, previous accidental) switch (a) { case A_NT: if (n >= d / 2) { n -= d / 2; a = i1; } else { a = i1 == A_SH ? A_FT : A_SH; } break; case A_DS: if (n >= d / 2) { s->u.note.notes[i].pit += 1; s->pits[i] += 1; n -= d / 2; } else { n += d / 2; } a = i1; break; case A_DF: if (n >= d / 2) { s->u.note.notes[i].pit -= 1; s->pits[i] -= 1; n -= d / 2; } else { n += d / 2; } a = i1; break; } if (s->u.note.microscale) { i4 = n; } else { d = d / 2 - 1 + ((n - 1) << 8); for (i4 = 1; i4 < MAXMICRO; i4++) { if (parse.micro_tb[i4] == d) break; if (parse.micro_tb[i4] == 0) { parse.micro_tb[i4] = d; break; } } if (i4 == MAXMICRO) { error(1, s, "Too many microtone accidentals"); i4 = 0; } } } s->u.note.notes[i].acc = (i4 << 3) | a; } } /* transpose a guitar chord */ static void gch_tr1(struct SYMBOL *s, int i, int i2) { char *p = &s->text[i], *q = p + 1, *new_txt; int l, latin; int n, a, i1, i3, i4; static const char note_names[] = "CDEFGAB"; static const char *latin_names[7] = { "Do", "Ré", "Mi", "Fa", "Sol", "La", "Si" }; static const char *acc_name[5] = {"bb", "b", "", "#", "##"}; /* main chord */ latin = 0; switch (*p) { case 'A': case 'B': n = *p - 'A' + 5; break; case 'C': case 'E': case 'G': n = *p - 'C'; break; case 'D': if (p[1] == 'o') { latin++; n = 0; /* Do */ break; } n = 1; break; case 'F': if (p[1] == 'a') latin++; /* Fa */ n = 3; break; case 'L': latin++; /* La */ n = 5; break; case 'M': latin++; /* Mi */ n = 2; break; case 'R': latin++; if (p[1] != 'e') latin++; /* Ré */ n = 1; /* Re */ break; case 'S': latin++; if (p[1] == 'o') { latin++; n = 4; /* Sol */ } else { n = 6; /* Si */ } break; case '/': // bass only latin--; break; default: return; } q += latin; /* allocate a new string */ new_txt = getarena(strlen(s->text) + 6); l = p - s->text; memcpy(new_txt, s->text, l); s->text = new_txt; new_txt += l; p = q; if (latin >= 0) { // if some chord a = 0; while (*p == '#') { a++; p++; } while (*p == 'b') { a--; p++; } // if (*p == '=') // p++; i3 = cde2fcg[n] + i2 + a * 7; i4 = cgd2cde[(unsigned) ((i3 + 16 * 7) % 7)]; i1 = ((i3 + 1 + 21) / 7 + 2 - 3 + 32 * 5) % 5; /* accidental */ if (latin == 0) *new_txt++ = note_names[i4]; else new_txt += sprintf(new_txt, "%s", latin_names[i4]); new_txt += sprintf(new_txt, "%s", acc_name[i1]); } /* bass */ while (*p != '\0' && *p != '\n' && *p != '/') // skip 'm'/'dim'.. *new_txt++ = *p++; if (*p == '/') { *new_txt++ = *p++; //fixme: latin names not treated q = strchr(note_names, *p); if (q) { p++; n = q - note_names; if (*p == '#') { a = 1; p++; } else if (*p == 'b') { a = -1; p++; } else { a = 0; } i3 = cde2fcg[n] + i2 + a * 7; i4 = cgd2cde[(unsigned) ((i3 + 16 * 7) % 7)]; i1 = ((i3 + 1 + 21) / 7 + 2 - 3 + 32 * 5) % 5; *new_txt++ = note_names[i4]; new_txt += sprintf(new_txt, "%s", acc_name[i1]); } } strcpy(new_txt, p); } static void gch_capo(struct SYMBOL *s) { char *p = s->text, *q, *r; int i, l, li = 0; static const char *capo_txt = " (capo: %d)"; static signed char cap_trans[] = {0, 5, -2, 3, -4, 1, -6, -1, 4, -3, 2, -5}; // search the chord symbols for (;;) { if (!strchr("^_<>@", *p)) break; p = strchr(p, '\n'); if (!p) return; p++; } // add a capo chord symbol i = p - s->text; q = strchr(p + 1, '\n'); if (q) l = q - p; else l = strlen(p); if (!capo) { capo = 1; li = strlen(capo_txt); } r = (char *) getarena(strlen(s->text) + l + li + 1); i += l; strncpy(r, s->text, i); // annotations + chord symbol r[i++] = '\n'; strncpy(r + i, p, l); // capo if (li) { sprintf(r + i + l, capo_txt, cfmt.capo); l += li; } if (q) strcpy(r + i + l, q); // ending annotations s->text = r; gch_tr1(s, i, cap_trans[cfmt.capo % 12]); } static void gch_transpose(struct SYMBOL *s) { int in_ch = 0; int i2 = curvoice->ckey.sf - curvoice->okey.sf; char *o = s->text, *p = o; // search the chord symbols for (;;) { if (in_ch || !strchr("^_<>@", *p)) { gch_tr1(s, p - s->text, i2); p = s->text + (p - o); o = s->text; for (p++; *p; p++) { if (strchr("\t;\n", *p)) break; } if (!*p) break; switch (*p) { case '\t': in_ch = 1; break; case ';': in_ch = !strchr("^_<>@", p[1]); break; default: in_ch = 0; break; } } else { p = strchr(p, '\n'); if (!p) break; } p++; } } /* -- build the guitar chords / annotations -- */ static void gch_build(struct SYMBOL *s) { struct gch *gch; char *p, *q, antype, sep; float w, h_ann, h_gch, y_above, y_below, y_left, y_right; float xspc; int l, ix, box, gch_place; if (s->posit.gch == SL_HIDDEN) return; s->gch = getarena(sizeof *s->gch * MAXGCH); memset(s->gch, 0, sizeof *s->gch * MAXGCH); if (curvoice->transpose != 0) gch_transpose(s); if (cfmt.capo) gch_capo(s); /* split the guitar chords / annotations * and initialize their vertical offsets */ gch_place = s->posit.gch == SL_BELOW ? -1 : 1; h_gch = cfmt.font_tb[cfmt.gcf].size; h_ann = cfmt.font_tb[cfmt.anf].size; y_above = y_below = y_left = y_right = 0; box = cfmt.gchordbox; p = s->text; gch = s->gch; sep = '\n'; antype = 'g'; /* (compiler warning) */ for (;;) { if (sep != 'n' && strchr("^_<>@", *p)) { gch->font = cfmt.anf; antype = *p++; if (antype == '@') { int n; float xo, yo; if (sscanf(p, "%f,%f%n", &xo, &yo, &n) != 2) { error(1, s, "Error in annotation \"@\""); } else { p += n; if (*p == ' ') p++; gch->x = xo; gch->y = yo; } } } else if (sep == '\n') { gch->font = cfmt.gcf; gch->box = box; antype = 'g'; } else { gch->font = (gch - 1)->font; gch->box = (gch - 1)->box; } gch->type = antype; switch (antype) { default: /* guitar chord */ if (gch_place < 0) break; /* below */ y_above += h_gch; if (box) y_above += 2; break; case '^': /* above */ y_above += h_ann; break; case '_': /* below */ break; case '<': /* left */ y_left += h_ann * 0.5; break; case '>': /* right */ y_right += h_ann * 0.5; break; case '@': /* absolute */ if (gch->x == 0 && gch->y == 0 && gch != s->gch && s->gch->type == '@') { /* if not 1st line */ gch->x = (gch - 1)->x; gch->y = (gch - 1)->y - h_ann; } break; } gch->idx = p - s->text; for (;;) { switch (*p) { default: p++; continue; case '\\': p++; if (*p == 'n') { p[-1] = '\0'; break; /* sep = 'n' */ } p++; continue; case '&': /* skip "&xxx;" */ for (;;) { switch (*p) { default: p++; continue; case ';': p++; case '\0': case '\n': case '\\': break; } break; } continue; case '\0': case ';': case '\n': break; } break; } sep = *p; if (sep == '\0') break; *p++ = '\0'; gch++; if (gch - s->gch >= MAXGCH) { error(1, s, "Too many guitar chords / annotations"); break; } } /* change the accidentals in the guitar chords */ for (ix = 0, gch = s->gch; ix < MAXGCH; ix++, gch++) { if (gch->type == '\0') break; if (gch->type != 'g') continue; p = s->text + gch->idx; q = p; for (; *p != '\0'; p++) { switch (*p) { case '#': case 'b': case '=': if (p == q /* 1st char or after a slash */ || (p != q + 1 /* or invert '\' behaviour */ && p[-1] == '\\')) break; /* set the accidentals as unused utf-8 values * (see subs.c) */ switch (*p) { case '#': *p = 0x01; break; case 'b': *p = 0x02; break; default: /* case '=': */ *p = 0x03; break; } if (p[-1] == '\\') { p--; l = strlen(p); memmove(p, p + 1, l); } break; case ' ': case '/': q = p + 1; break; } } } /* set the offsets and widths */ /*fixme:utf8*/ for (ix = 0, gch = s->gch; ix < MAXGCH; ix++, gch++) { if (gch->type == '\0') break; if (gch->type == '@') continue; /* no width */ p = s->text + gch->idx; str_font(gch->font); w = tex_str(p); gch->w = w; // + 4; switch (gch->type) { case '_': /* below */ xspc = w * GCHPRE; if (xspc > 8) xspc = 8; gch->x = -xspc; y_below -= h_ann; gch->y = y_below; break; case '^': /* above */ xspc = w * GCHPRE; if (xspc > 8) xspc = 8; gch->x = -xspc; y_above -= h_ann; gch->y = y_above; break; default: /* guitar chord */ xspc = w * GCHPRE; if (xspc > 8) xspc = 8; gch->x = -xspc; if (gch_place < 0) { /* below */ y_below -= h_gch; gch->y = y_below; if (box) { y_below -= 2; gch->y -= 1; } } else { y_above -= h_gch; gch->y = y_above; if (box) { y_above -= 2; gch->y -= 1; } } break; case '<': /* left */ gch->x = -(w + 6); y_left -= h_ann; gch->y = y_left; break; case '>': /* right */ gch->x = 6; y_right -= h_ann; gch->y = y_right; break; } } } /* get the note which will receive a lyric word */ static struct SYMBOL *next_lyric_note(struct SYMBOL *s) { while (s && (s->abc_type != ABC_T_NOTE || (s->flags & ABC_F_GRACE))) s = s->next; return s; } /* -- parse lyric (vocal) lines (w:) -- */ static struct SYMBOL *get_lyric(struct SYMBOL *s) { struct SYMBOL *s1, *s2; char word[128], *p, *q; int ln, cont; struct FONTSPEC *f; curvoice->have_ly = curvoice->posit.voc != SL_HIDDEN; if (curvoice->ignore) { for (;;) { if (!s->abc_next) return s; switch (s->abc_next->abc_type) { case ABC_T_PSCOM: s = process_pscomment(s->abc_next); continue; case ABC_T_INFO: if (s->abc_next->text[0] == 'w' || s->abc_next->text[0] == '+') { s = s->abc_next; continue; } break; } return s; } } f = &cfmt.font_tb[cfmt.vof]; str_font(cfmt.vof); /* (for tex_str) */ /* treat all w: lines */ cont = 0; ln = -1; s2 = s1 = NULL; // have gcc happy for (;;) { if (!cont) { if (ln >= MAXLY- 1) { error(1, s, "Too many lyric lines"); ln--; } ln++; s2 = s1; s1 = curvoice->lyric_start; if (!s1) s1 = curvoice->sym; else s1 = s1->next; if (!s1) { error(1, s, "w: without music"); return s; } } else { cont = 0; } /* scan the lyric line */ p = &s->text[2]; while (*p != '\0') { while (isspace((unsigned char) *p)) p++; if (*p == '\0') break; if (*p == '\\' && p[1] == '\0') { cont = 1; break; } switch (*p) { case '|': while (s1 && s1->type != BAR) { s2 = s1; s1 = s1->next; } if (!s1) { error(1, s2, "Not enough bar lines for lyric line"); goto ly_next; } s2 = s1; s1 = s1->next; p++; continue; case '-': word[0] = LY_HYPH; word[1] = '\0'; p++; break; case '_': word[0] = LY_UNDER; word[1] = '\0'; p++; break; case '*': word[0] = '\0'; p++; break; default: q = word; for (;;) { unsigned char c; c = *p; switch (c) { case '\0': case ' ': case '\t': case '_': case '*': case '|': break; case '~': c = ' '; goto addch; case '-': c = LY_HYPH; goto addch; case '\\': if (p[1] == '\0') break; switch (p[1]) { case '~': case '_': case '*': case '|': case '-': case ' ': case '\\': c = *++p; break; } /* fall thru */ default: addch: if (q < &word[sizeof word - 1]) *q++ = c; p++; if (c == LY_HYPH) break; continue; } break; } *q = '\0'; break; } /* store the word in the next note */ if (s1) { /* for error */ s2 = s1; s1 = next_lyric_note(s1); } if (!s1) { if (!s2) s2 = s; error(1, s2, "Too many words in lyric line"); goto ly_next; } if (word[0] != '\0' && s1->posit.voc != SL_HIDDEN) { struct lyl *lyl; float w; if (!s1->ly) { s1->ly = (struct lyrics *) getarena(sizeof (struct lyrics)); memset(s1->ly, 0, sizeof (struct lyrics)); } /* handle the font change at start of text */ q = word; if (*q == '$' && isdigit((unsigned char) q[1]) && (unsigned) (q[1] - '0') < FONT_UMAX) { int ft; ft = q[1] - '0'; if (ft == 0) ft = cfmt.vof; f = &cfmt.font_tb[ft]; str_font(ft); q += 2; } w = tex_str(q); q = tex_buf; lyl = (struct lyl *) getarena(sizeof *s1->ly->lyl[0] - sizeof s1->ly->lyl[0]->t + strlen(q) + 1); s1->ly->lyl[ln] = lyl; lyl->f = f; lyl->w = w; strcpy(lyl->t, q); /* handle the font changes inside the text */ while (*q != '\0') { if (*q == '$' && isdigit((unsigned char) q[1]) && (unsigned) (q[1] - '0') < FONT_UMAX) { int ft; q++; ft = *q - '0'; if (ft == 0) ft = cfmt.vof; f = &cfmt.font_tb[ft]; str_font(ft); } q++; } } s2 = s1; s1 = s1->next; } /* loop if more lyrics */ ly_next: for (;;) { if (!s->abc_next) goto ly_upd; switch (s->abc_next->abc_type) { case ABC_T_PSCOM: s = process_pscomment(s->abc_next); f = &cfmt.font_tb[cfmt.vof]; /* may have changed */ str_font(cfmt.vof); continue; case ABC_T_INFO: if (s->abc_next->text[0] != 'w' && s->abc_next->text[0] != '+') goto ly_upd; s = s->abc_next; if (s->text[0] == '+') cont = 1; if (!cont) { s1 = next_lyric_note(s1); if (s1) { error(1, s1, "Not enough words for lyric line"); } } break; /* more lyric */ default: goto ly_upd; } break; } } /* the next lyrics will go into the next notes */ ly_upd: //fixme: no error with abc-2.1 if (next_lyric_note(s1)) error(0, s1, "Not enough words for lyric line"); // fill the w: with 'blank syllabes' curvoice->lyric_start = curvoice->last_sym; return s; } /* -- add a voice in the linked list -- */ static void voice_link(struct VOICE_S *p_voice) { struct VOICE_S *p_voice2; p_voice2 = first_voice; for (;;) { if (p_voice2 == p_voice) return; if (!p_voice2->next) break; p_voice2 = p_voice2->next; } p_voice2->next = p_voice; } /* -- get a voice overlay -- */ static void get_over(struct SYMBOL *s) { struct VOICE_S *p_voice, *p_voice2, *p_voice3; int range, voice, voice2, voice3; static char tx_wrong_dur[] = "Wrong duration in voice overlay"; static char txt_no_note[] = "No note in voice overlay"; /* treat the end of overlay */ p_voice = curvoice; if (p_voice->ignore) return; if (s->abc_type == ABC_T_BAR || s->u.v_over.type == V_OVER_E) { if (!p_voice->last_sym) { error(1, s, txt_no_note); return; } p_voice->last_sym->sflags |= S_BEAM_END; over_bar = 0; if (over_time < 0) { error(1, s, "Erroneous end of voice overlap"); return; } if (p_voice->time != over_mxtime) error(1, s, tx_wrong_dur); curvoice = &voice_tb[over_voice]; over_mxtime = 0; over_voice = -1; over_time = -1; return; } /* treat the full overlay start */ if (s->u.v_over.type == V_OVER_S) { over_voice = p_voice - voice_tb; over_time = p_voice->time; return; } /* (here is treated a new overlay - '&') */ /* create the extra voice if not done yet */ if (!p_voice->last_sym) { error(1, s, txt_no_note); return; } p_voice->last_sym->sflags |= S_BEAM_END; voice2 = s->u.v_over.voice; p_voice2 = &voice_tb[voice2]; if (parsys->voice[voice2].range < 0) { int clone; if (cfmt.abc2pscompat) { error(1, s, "Cannot have %%%%abc2pscompat"); cfmt.abc2pscompat = 0; } clone = p_voice->clone >= 0; p_voice2->id[0] = '&'; p_voice2->id[1] = '\0'; p_voice2->second = 1; parsys->voice[voice2].second = 1; p_voice2->scale = p_voice->scale; p_voice2->octave = p_voice->octave; p_voice2->transpose = p_voice->transpose; memcpy(&p_voice2->key, &p_voice->key, sizeof p_voice2->key); memcpy(&p_voice2->ckey, &p_voice->ckey, sizeof p_voice2->ckey); memcpy(&p_voice2->okey, &p_voice->okey, sizeof p_voice2->okey); p_voice2->posit = p_voice->posit; p_voice2->staff = p_voice->staff; p_voice2->cstaff = p_voice->cstaff; p_voice2->color = p_voice->color; p_voice2->map_name = p_voice->map_name; range = parsys->voice[p_voice - voice_tb].range; for (voice = 0; voice < MAXVOICE; voice++) { if (parsys->voice[voice].range > range) parsys->voice[voice].range += clone + 1; } parsys->voice[voice2].range = range + 1; voice_link(p_voice2); if (clone) { for (voice3 = MAXVOICE; --voice3 >= 0; ) { if (parsys->voice[voice3].range < 0) break; } if (voice3 > 0) { p_voice3 = &voice_tb[voice3]; strcpy(p_voice3->id, p_voice2->id); p_voice3->second = 1; parsys->voice[voice3].second = 1; p_voice3->scale = voice_tb[p_voice->clone].scale; parsys->voice[voice3].range = range + 2; voice_link(p_voice3); p_voice2->clone = voice3; } else { error(1, s, "Too many voices for overlay cloning"); } } } voice = p_voice - voice_tb; // p_voice2->cstaff = p_voice2->staff = parsys->voice[voice2].staff // = parsys->voice[voice].staff; // if ((voice3 = p_voice2->clone) >= 0) { // p_voice3 = &voice_tb[voice3]; // p_voice3->cstaff = p_voice3->staff // = parsys->voice[voice3].staff // = parsys->voice[p_voice->clone].staff; // } if (over_time < 0) { /* first '&' in a measure */ int time; over_bar = 1; over_mxtime = p_voice->time; over_voice = voice; time = p_voice2->time; for (s = p_voice->last_sym; /*s*/; s = s->prev) { if (s->type == BAR || s->time <= time) /* (if start of tune) */ break; } over_time = s->time; } else { if (over_mxtime == 0) over_mxtime = p_voice->time; else if (p_voice->time != over_mxtime) error(1, s, tx_wrong_dur); } p_voice2->time = over_time; curvoice = p_voice2; } struct staff_s { short voice; short flags; }; /* -- parse %%staves / %%score -- */ static void parse_staves(struct SYMBOL *s, struct staff_s *staves) { char *p; int voice, flags_st, brace, bracket, parenth, err; short flags; struct staff_s *p_staff; /* define the voices */ err = 0; flags = 0; brace = bracket = parenth = 0; flags_st = 0; voice = 0; p = s->text + 7; while (*p != '\0' && !isspace((unsigned char) *p)) p++; while (*p != '\0') { switch (*p) { case ' ': case '\t': break; case '[': if (parenth || brace + bracket >= 2) { error(1, s, "Misplaced '[' in %%%%staves"); err = 1; break; } if (brace + bracket == 0) flags |= OPEN_BRACKET; else flags |= OPEN_BRACKET2; bracket++; flags_st <<= 8; flags_st |= OPEN_BRACKET; break; case '{': if (parenth || brace || bracket >= 2) { error(1, s, "Misplaced '{' in %%%%staves"); err = 1; break; } if (bracket == 0) flags |= OPEN_BRACE; else flags |= OPEN_BRACE2; brace++; flags_st <<= 8; flags_st |= OPEN_BRACE; break; case '(': if (parenth) { error(1, s, "Misplaced '(' in %%%%staves"); err = 1; break; } flags |= OPEN_PARENTH; parenth++; flags_st <<= 8; flags_st |= OPEN_PARENTH; break; case '*': if (brace && !parenth && !(flags & (OPEN_BRACE | OPEN_BRACE2))) flags |= FL_VOICE; break; case '+': flags |= MASTER_VOICE; break; default: if (!isalnum((unsigned char) *p) && *p != '_') { error(1, s, "Bad voice ID in %%%%staves"); err = 1; break; } if (voice >= MAXVOICE) { error(1, s, "Too many voices in %%%%staves"); err = 1; break; } { int i, v; char sep, *q; q = p; while (isalnum((unsigned char) *p) || *p == '_') p++; sep = *p; *p = '\0'; /* search the voice in the voice table */ v = -1; for (i = 0; i < MAXVOICE; i++) { if (strcmp(q, voice_tb[i].id) == 0) { v = i; break; } } if (v < 0) { error(1, s, "Voice '%s' of %%%%staves has no symbol", q); err = 1; // break; p_staff = staves; } else { p_staff = staves + voice++; p_staff->voice = v; } *p = sep; } for ( ; *p != '\0'; p++) { switch (*p) { case ' ': case '\t': continue; case ']': if (!(flags_st & OPEN_BRACKET)) { error(1, s, "Misplaced ']' in %%%%staves"); err = 1; break; } bracket--; if (brace + bracket == 0) flags |= CLOSE_BRACKET; else flags |= CLOSE_BRACKET2; flags_st >>= 8; continue; case '}': if (!(flags_st & OPEN_BRACE)) { error(1, s, "Misplaced '}' in %%%%staves"); err = 1; break; } brace--; if (bracket == 0) flags |= CLOSE_BRACE; else flags |= CLOSE_BRACE2; flags &= ~FL_VOICE; flags_st >>= 8; continue; case ')': if (!(flags_st & OPEN_PARENTH)) { error(1, s, "Misplaced ')' in %%%%staves"); err = 1; break; } parenth--; flags |= CLOSE_PARENTH; flags_st >>= 8; continue; case '|': flags |= STOP_BAR; continue; } break; } p_staff->flags = flags; flags = 0; if (*p == '\0') break; continue; } if (*p == '\0') break; p++; } if (flags_st != 0) { error(1, s, "'}', ')' or ']' missing in %%%%staves"); err = 1; } if (err) { int i; for (i = 0; i < voice; i++) staves[i].flags = 0; } if (voice < MAXVOICE) staves[voice].voice = -1; } /* -- get staves definition (%%staves / %%score) -- */ static void get_staves(struct SYMBOL *s) { // struct SYMBOL *s2; struct VOICE_S *p_voice, *p_voice2; struct staff_s *p_staff, staves[MAXVOICE]; int i, flags, voice, staff, range, dup_voice, maxtime; memset(staves, 0, sizeof staves); parse_staves(s, staves); if (staves[0].voice < 0) // if error return; voice_compress(); voice_dup(); /* create a new staff system */ curvoice = p_voice = first_voice; maxtime = p_voice->time; flags = p_voice->sym != NULL; for (p_voice = p_voice->next; p_voice; p_voice = p_voice->next) { if (p_voice->time > maxtime) maxtime = p_voice->time; if (p_voice->sym) flags = 1; } if (flags == 0 /* if first %%staves */ || (maxtime == 0 && staves_found < 0)) { for (voice = 0; voice < MAXVOICE; voice++) parsys->voice[voice].range = -1; } else { /* * create a new staff system and * link the staves in a voice which is seen from * the previous system - see sort_all */ // p_voice = curvoice; if (parsys->voice[curvoice - voice_tb].range < 0) { for (voice = 0; voice < MAXVOICE; voice++) { if (parsys->voice[voice].range >= 0) { curvoice = &voice_tb[voice]; break; } } /*fixme: should check if voice < MAXVOICE*/ } curvoice->time = maxtime; // put the staves before a measure bar (see draw_bar()) // s2 = curvoice->last_sym; // if (s2 && s2->type == BAR && s2->time == maxtime) { // curvoice->last_sym = s2->prev; // if (!curvoice->last_sym) // curvoice->sym = NULL; // sym_link(s, STAVES); // s->next = s2; // s2->prev = s; // curvoice->last_sym = s2; // } else { sym_link(s, STAVES); // link the staves in the current voice // } s->state = ABC_S_HEAD; /* (output PS sequences immediately) */ parsys->nstaff = nstaff; system_new(); } staves_found = maxtime; /* initialize the voices */ for (voice = 0, p_voice = voice_tb; voice < MAXVOICE; voice++, p_voice++) { p_voice->second = 0; p_voice->floating = 0; p_voice->ignore = 0; p_voice->time = maxtime; } /* create the 'clone' voices */ dup_voice = MAXVOICE; range = 0; p_staff = staves; parsys->top_voice = p_staff->voice; for (i = 0; i < MAXVOICE && p_staff->voice >= 0; i++, p_staff++) { voice = p_staff->voice; p_voice = &voice_tb[voice]; if (parsys->voice[voice].range >= 0) { if (parsys->voice[dup_voice - 1].range >= 0) { error(1, s, "Too many voices for cloning"); continue; } voice = --dup_voice; /* duplicate the voice */ p_voice2 = &voice_tb[voice]; memcpy(p_voice2, p_voice, sizeof *p_voice2); p_voice2->next = NULL; p_voice2->sym = p_voice2->last_sym = NULL; p_voice2->tblts[0] = p_voice2->tblts[1] = NULL; p_voice2->clone = -1; while (p_voice->clone > 0) p_voice = &voice_tb[p_voice->clone]; p_voice->clone = voice; p_voice = p_voice2; p_staff->voice = voice; } parsys->voice[voice].range = range++; voice_link(p_voice); } /* change the behavior from %%staves to %%score */ if (s->text[3] == 't') { /* if %%staves */ for (i = 0, p_staff = staves; i < MAXVOICE - 2 && p_staff->voice >= 0; i++, p_staff++) { flags = p_staff->flags; if (!(flags & (OPEN_BRACE | OPEN_BRACE2))) continue; if ((flags & (OPEN_BRACE | CLOSE_BRACE)) == (OPEN_BRACE | CLOSE_BRACE) || (flags & (OPEN_BRACE2 | CLOSE_BRACE2)) == (OPEN_BRACE2 | CLOSE_BRACE2)) continue; if (p_staff[1].flags != 0) continue; if ((flags & OPEN_PARENTH) || (p_staff[2].flags & OPEN_PARENTH)) continue; /* {a b c} --> {a *b c} */ if (p_staff[2].flags & (CLOSE_BRACE | CLOSE_BRACE2)) { p_staff[1].flags |= FL_VOICE; /* {a b c d} --> {(a b) (c d)} */ } else if (p_staff[2].flags == 0 && (p_staff[3].flags & (CLOSE_BRACE | CLOSE_BRACE2))) { p_staff->flags |= OPEN_PARENTH; p_staff[1].flags |= CLOSE_PARENTH; p_staff[2].flags |= OPEN_PARENTH; p_staff[3].flags |= CLOSE_PARENTH; } } } /* set the staff system */ staff = -1; for (i = 0, p_staff = staves; i < MAXVOICE && p_staff->voice >= 0; i++, p_staff++) { flags = p_staff->flags; if ((flags & (OPEN_PARENTH | CLOSE_PARENTH)) == (OPEN_PARENTH | CLOSE_PARENTH)) { flags &= ~(OPEN_PARENTH | CLOSE_PARENTH); p_staff->flags = flags; } voice = p_staff->voice; p_voice = &voice_tb[voice]; if (flags & FL_VOICE) { p_voice->floating = 1; p_voice->second = 1; } else { #if MAXSTAFF < MAXVOICE if (staff >= MAXSTAFF - 1) { error(1, s, "Too many staves"); } else #endif staff++; parsys->staff[staff].flags = 0; } p_voice->staff = p_voice->cstaff = parsys->voice[voice].staff = staff; parsys->staff[staff].flags |= flags; if (flags & OPEN_PARENTH) { p_voice2 = p_voice; while (i < MAXVOICE) { i++; p_staff++; voice = p_staff->voice; p_voice = &voice_tb[voice]; if (p_staff->flags & MASTER_VOICE) { p_voice2->second = 1; p_voice2 = p_voice; } else { p_voice->second = 1; } p_voice->staff = p_voice->cstaff = parsys->voice[voice].staff = staff; if (p_staff->flags & CLOSE_PARENTH) break; } parsys->staff[staff].flags |= p_staff->flags; } } if (staff < 0) staff = 0; parsys->nstaff = nstaff = staff; /* change the behaviour of '|' in %%score */ if (s->text[3] == 'c') { /* if %%score */ for (staff = 0; staff < nstaff; staff++) parsys->staff[staff].flags ^= STOP_BAR; } for (voice = 0; voice < MAXVOICE; voice++) { p_voice = &voice_tb[voice]; parsys->voice[voice].second = p_voice->second; staff = p_voice->staff; if (staff > 0) p_voice->norepbra = !(parsys->staff[staff - 1].flags & STOP_BAR); if (p_voice->floating && staff == nstaff) p_voice->floating = 0; } curvoice = &voice_tb[parsys->top_voice]; } /* -- re-initialize all potential voices -- */ static void voice_init(void) { struct VOICE_S *p_voice; int i; for (i = 0, p_voice = voice_tb; i < MAXVOICE; i++, p_voice++) { p_voice->sym = p_voice->last_sym = NULL; p_voice->lyric_start = NULL; p_voice->bar_start = 0; p_voice->time = 0; p_voice->slur_st = 0; p_voice->hy_st = 0; p_voice->tie = 0; p_voice->rtie = 0; } } /* output a pdf mark */ static void put_pdfmark(char *p) { unsigned char c, *q; int u; p = trim_title(p, NULL); /* check if pure ASCII without '\', '(' nor ')'*/ for (q = (unsigned char *) p; *q != '\0'; q++) { switch (*q) { case '\\': case '(': case ')': break; default: if (*q >= 0x80) break; continue; } break; } if (*q == '\0') { a2b("[/Title(%s)/OUT pdfmark\n", p); return; } /* build utf-8 mark */ a2b("[/Title<FEFF"); q = (unsigned char *) p; u = -1; while (*q != '\0') { c = *q++; if (c < 0x80) { if (u >= 0) { a2b("%04X", u); u = -1; } a2b("%04X", (int) c); continue; } if (c < 0xc0) { u = (u << 6) | (c & 0x3f); continue; } if (u >= 0) { a2b("%04X", u); u = -1; } if (c < 0xe0) u = c & 0x1f; else if (c < 0xf0) u = c & 0x0f; else u = c & 0x07; } if (u >= 0) { a2b("%04X", u); u = -1; } a2b(">/OUT pdfmark\n"); } /* rebuild a tune header for %%tune filter */ static char *tune_header_rebuild(struct SYMBOL *s) { struct SYMBOL *s2; char *header, *p; int len; len = 0; s2 = s; for (;;) { if (s2->abc_type == ABC_T_INFO) { len += strlen(s2->text) + 1; if (s2->text[0] == 'K') break; } s2 = s2->abc_next; } header = malloc(len + 1); p = header; for (;;) { if (s->abc_type == ABC_T_INFO) { strcpy(p, s->text); p += strlen(p); *p++ = '\n'; if (s->text[0] == 'K') break; } s = s->abc_next; } *p++ = '\0'; return header; } /* apply the options to the current tune */ static void tune_filter(struct SYMBOL *s) { struct tune_opt_s *opt; struct SYMBOL *s1, *s2; regex_t r; char *header, *p; int ret; header = tune_header_rebuild(s); for (opt = tune_opts; opt; opt = opt->next) { struct SYMBOL *last_staves; p = &opt->s->text[2 + 5]; /* "%%tune RE" */ while (isspace((unsigned char) *p)) p++; ret = regcomp(&r, p, REG_EXTENDED | REG_NEWLINE | REG_NOSUB); if (ret) continue; ret = regexec(&r, header, 0, NULL, 0); regfree(&r); if (ret) continue; /* apply the options */ cur_tune_opts = opt; last_staves = s->abc_next; for (s1 = opt->s->next; s1; s1 = s1->next) { /* replace the next %%staves/%%score */ if (s1->abc_type == ABC_T_PSCOM && (strncmp(&s1->text[2], "staves", 6) == 0 || strncmp(&s1->text[2], "score", 5) == 0)) { while (last_staves) { if (last_staves->abc_type == ABC_T_PSCOM && (strncmp(&last_staves->text[2], "staves", 6) == 0 || strncmp(&last_staves->text[2], "score", 5) == 0)) { last_staves->text = s1->text; last_staves = last_staves->abc_next; break; } last_staves = last_staves->abc_next; } continue; } s2 = (struct SYMBOL *) getarena(sizeof *s2); memcpy(s2, s1, sizeof *s2); process_pscomment(s2); } cur_tune_opts = NULL; tune_voice_opts = opt->voice_opts; // for %%voice //fixme: what if many %%tune's with %%voice inside? } free(header); } /* apply the options of the current voice */ static void voice_filter(void) { struct voice_opt_s *opt; struct SYMBOL *s; regex_t r; int pass, ret; char *p; /* scan the global, then the tune options */ pass = 0; opt = voice_opts; for (;;) { if (!opt) { if (pass != 0) break; opt = tune_voice_opts; if (!opt) break; pass++; } p = &opt->s->text[2 + 6]; /* "%%voice RE" */ while (isspace((unsigned char) *p)) p++; ret = regcomp(&r, p, REG_EXTENDED | REG_NOSUB); if (ret) goto next_voice; ret = regexec(&r, curvoice->id, 0, NULL, 0); if (ret && curvoice->nm) ret = regexec(&r, curvoice->nm, 0, NULL, 0); regfree(&r); if (ret) goto next_voice; /* apply the options */ for (s = opt->s->next; s; s = s->next) { struct SYMBOL *s2; s2 = (struct SYMBOL *) getarena(sizeof *s2); memcpy(s2, s, sizeof *s2); process_pscomment(s2); } next_voice: opt = opt->next; } } /* -- check if a pseudo-comment may be in the tune header -- */ static int check_header(struct SYMBOL *s) { switch (s->text[2]) { case 'E': if (strncmp(s->text + 2, "EPS", 3) == 0) return 0; break; case 'm': if (strncmp(s->text + 2, "multicol", 8) == 0) return 0; break; } return 1; } /* -- set the global definitions after the first K: or middle-tune T:'s -- */ static void set_global_def(void) { struct VOICE_S *p_voice; int i; for (i = MAXVOICE, p_voice = voice_tb; --i >= 0; p_voice++) { switch (p_voice->key.instr) { case 0: if (!pipeformat) { // p_voice->transpose = cfmt.transpose; break; } //fall thru case K_HP: case K_Hp: if (p_voice->posit.std == 0) p_voice->posit.std = SL_BELOW; break; } // if (p_voice->key.empty) // p_voice->key.sf = 0; if (!cfmt.autoclef && p_voice->s_clef && (p_voice->s_clef->sflags & S_CLEF_AUTO)) { p_voice->s_clef->u.clef.type = TREBLE; p_voice->s_clef->sflags &= ~S_CLEF_AUTO; } } /* switch to the 1st voice */ curvoice = &voice_tb[parsys->top_voice]; } /* -- get the global definitions after the first K: or middle-tune T:'s -- */ static struct SYMBOL *get_global_def(struct SYMBOL *s) { struct SYMBOL *s2; for (;;) { s2 = s->abc_next; if (!s2) break; switch (s2->abc_type) { case ABC_T_INFO: switch (s2->text[0]) { case 'K': s = s2; s->state = ABC_S_HEAD; get_key(s); continue; case 'I': case 'M': case 'Q': s = s2; s->state = ABC_S_HEAD; s = get_info(s); continue; } break; case ABC_T_PSCOM: if (!check_header(s2)) break; s = s2; s->state = ABC_S_HEAD; s = process_pscomment(s); continue; } break; } set_global_def(); return s; } /* save the global note maps */ static void save_maps(void) { struct map *omap, *map; struct note_map *onotes, *notes; omap = maps; if (!omap) { maps_glob = NULL; return; } maps_glob = map = getarena(sizeof *maps_glob); for (;;) { memcpy(map, omap, sizeof *map); onotes = omap->notes; if (onotes) { map->notes = notes = getarena(sizeof *notes); for (;;) { memcpy(notes, onotes, sizeof *notes); onotes = onotes->next; if (!onotes) break; notes->next = getarena(sizeof *notes); notes = notes->next; } } omap = omap->next; if (!omap) break; map->next = getarena(sizeof *map); map = map->next; } } /* -- identify info line, store in proper place -- */ static struct SYMBOL *get_info(struct SYMBOL *s) { struct SYMBOL *s2; struct VOICE_S *p_voice; char *p; char info_type; int old_lvl; static char *state_txt[] = {"global", "header", "tune"}; /* change arena to global or tune */ old_lvl = lvlarena(s->state != ABC_S_GLOBAL); info_type = s->text[0]; switch (info_type) { case 'd': break; case 'I': s = process_pscomment(s); /* same as pseudo-comment */ break; case 'K': get_key(s); if (s->state != ABC_S_HEAD) break; info['K' - 'A'] = s; /* first K:, end of tune header */ tunenum++; if (!epsf) { // if (!cfmt.oneperpage) // use_buffer = cfmt.splittune != 1; bskip(cfmt.topspace); } a2b("%% --- xref %s\n", &info['X' - 'A']->text[2]); // (for index) write_heading(); block_put(); /* information for index * (pdfmark must be after title show for Adobe Distiller) */ s2 = info['T' - 'A']; p = &s2->text[2]; if (*p != '\0') { a2b("%% --- font "); outft = -1; set_font(TITLEFONT); /* font in comment */ a2b("\n"); outft = -1; } if (cfmt.pdfmark) { if (*p != '\0') put_pdfmark(p); if (cfmt.pdfmark > 1) { for (s2 = s2->next; s2; s2 = s2->next) { p = &s2->text[2]; if (*p != '\0') put_pdfmark(p); } } } nbar = cfmt.measurefirst; /* measure numbering */ over_voice = -1; over_time = -1; over_bar = 0; capo = 0; reset_gen(); s = get_global_def(s); if (!(cfmt.fields[0] & (1 << ('Q' - 'A')))) info['Q' - 'A'] = NULL; /* apply the filter for the voice '1' */ voice_filter(); /* activate the default tablature if not yet done */ if (!first_voice->tblts[0]) set_tblt(first_voice); break; case 'L': switch (s->state) { case ABC_S_HEAD: { int i, auto_len; auto_len = s->u.length.base_length < 0; for (i = MAXVOICE, p_voice = voice_tb; --i >= 0; p_voice++) p_voice->auto_len = auto_len; break; } case ABC_S_TUNE: curvoice->auto_len = s->u.length.base_length < 0; break; } break; case 'M': get_meter(s); break; case 'P': { struct VOICE_S *curvoice_sav; if (s->state != ABC_S_TUNE) { info['P' - 'A'] = s; break; } if (!(cfmt.fields[0] & (1 << ('P' - 'A')))) break; /* * If not in the main voice, then, * if the voices are synchronized and no P: yet in the main voice, * the misplaced P: goes into the main voice. */ p_voice = &voice_tb[parsys->top_voice]; if (curvoice != p_voice) { if (curvoice->time != p_voice->time) break; if (p_voice->last_sym && p_voice->last_sym->type == PART) break; // already a P: curvoice_sav = curvoice; curvoice = p_voice; sym_link(s, PART); curvoice = curvoice_sav; break; } sym_link(s, PART); break; } case 'Q': if (!(cfmt.fields[0] & (1 << ('Q' - 'A')))) break; if (s->state != ABC_S_TUNE) { info['Q' - 'A'] = s; break; } if (curvoice != &voice_tb[parsys->top_voice]) break; /* tempo only for first voice */ s2 = curvoice->last_sym; if (s2) { /* keep last Q: */ int tim; tim = s2->time; do { if (s2->type == TEMPO) { if (!s2->next) curvoice->last_sym = s2->prev; else s2->next->prev = s2->prev; if (!s2->prev) curvoice->sym = s2->next; else s2->prev->next = s2->next; break; } s2 = s2->prev; } while (s2 && s2->time == tim); } sym_link(s, TEMPO); break; case 'r': case 's': break; case 'T': if (s->state == ABC_S_GLOBAL) break; if (s->state == ABC_S_HEAD) /* in tune header */ goto addinfo; gen_ly(1); /* in tune */ p = &s->text[2]; if (*p != '\0') { write_title(s); a2b("%% --- + (%s) ---\n", p); if (cfmt.pdfmark) put_pdfmark(p); } voice_init(); reset_gen(); /* (display the time signature) */ s = get_global_def(s); break; case 'U': deco[s->u.user.symbol] = parse.deco_tb[s->u.user.value - 128]; break; case 'u': break; case 'V': get_voice(s); /* handle here the possible clef which could be replaced * in case of filter */ if (s->abc_next && s->abc_next->abc_type == ABC_T_CLEF) { s = s->abc_next; get_clef(s); } if (s->state == ABC_S_TUNE && !curvoice->last_sym && curvoice->time == 0) voice_filter(); break; case 'w': if (s->state != ABC_S_TUNE) break; if (!(cfmt.fields[1] & (1 << ('w' - 'a')))) { while (s->abc_next) { if (s->abc_next->abc_type != ABC_T_INFO || s->abc_next->text[0] != '+') break; s = s->abc_next; } break; } s = get_lyric(s); break; case 'W': if (s->state == ABC_S_GLOBAL || !(cfmt.fields[0] & (1 << ('W' - 'A')))) break; goto addinfo; case 'X': if (!epsf) { buffer_eob(0); /* flush stuff left from %% lines */ write_buffer(); //fixme: 8.6.2 if (cfmt.oneperpage) close_page(); // else if (in_page) else use_buffer = cfmt.splittune != 1; } memcpy(&dfmt, &cfmt, sizeof dfmt); /* save global values */ memcpy(&info_glob, &info, sizeof info_glob); memcpy(deco_glob, deco, sizeof deco_glob); save_maps(); info['X' - 'A'] = s; if (tune_opts) tune_filter(s); break; default: if (info_type >= 'A' && info_type <= 'Z') { struct SYMBOL *prev; if (s->state == ABC_S_TUNE) break; addinfo: prev = info[info_type - 'A']; if (!prev || (prev->state == ABC_S_GLOBAL && s->state != ABC_S_GLOBAL)) { info[info_type - 'A'] = s; } else { while (prev->next) prev = prev->next; prev->next = s; } while (s->abc_next && s->abc_next->abc_type == ABC_T_INFO && s->abc_next->text[0] == '+') { prev = s; s = s->abc_next; prev->next = s; } s->prev = prev; break; } if (s->state != ABC_S_GLOBAL) error(1, s, "%s info '%c:' not treated", state_txt[(int) s->state], info_type); break; } lvlarena(old_lvl); return s; } /* -- set head type, dots, flags for note -- */ void identify_note(struct SYMBOL *s, int dur, int *p_head, int *p_dots, int *p_flags) { int head, dots, flags; if (dur % 12 != 0) error(1, s, "Invalid note duration"); dur /= 12; /* see BASE_LEN for values */ if (dur == 0) error(1, s, "Note too short"); for (flags = 5; dur != 0; dur >>= 1, flags--) { if (dur & 1) break; } dur >>= 1; switch (dur) { case 0: dots = 0; break; case 1: dots = 1; break; case 3: dots = 2; break; case 7: dots = 3; break; default: error(1, s, "Note too much dotted"); dots = 3; break; } flags -= dots; if (flags >= 0) { head = H_FULL; } else switch (flags) { default: error(1, s, "Note too long"); flags = -4; /* fall thru */ case -4: head = H_SQUARE; break; case -3: head = cfmt.squarebreve ? H_SQUARE : H_OVAL; break; case -2: head = H_OVAL; break; case -1: head = H_EMPTY; break; } *p_head = head; *p_flags = flags; *p_dots = dots; } /* -- adjust the duration and time of symbols in a measure when L:auto -- */ static void adjust_dur(struct SYMBOL *s) { struct SYMBOL *s2; int time, auto_time; /* search the start of the measure */ s2 = curvoice->last_sym; if (!s2) return; /* the bar time is correct if there is multi-rests */ if (s2->type == MREST || s2->type == BAR) /* in second voice */ return; while (s2->type != BAR && s2->prev) s2 = s2->prev; time = s2->time; auto_time = curvoice->time - time; /* remove the invisible rest at start of tune */ if (time == 0) { while (s2 && s2->dur == 0) s2 = s2->next; if (s2 && s2->abc_type == ABC_T_REST && (s2->flags & ABC_F_INVIS)) { time += s2->dur * curvoice->wmeasure / auto_time; if (s2->prev) s2->prev->next = s2->next; else curvoice->sym = s2->next; if (s2->next) s2->next->prev = s2->prev; s2 = s2->next; } } if (curvoice->wmeasure == auto_time) return; /* already good duration */ for (; s2; s2 = s2->next) { int i, head, dots, nflags; s2->time = time; if (s2->dur == 0 || (s2->flags & ABC_F_GRACE)) continue; s2->dur = s2->dur * curvoice->wmeasure / auto_time; time += s2->dur; if (s2->type != NOTEREST) continue; for (i = 0; i <= s2->nhd; i++) s2->u.note.notes[i].len = s2->u.note.notes[i].len * curvoice->wmeasure / auto_time; identify_note(s2, s2->u.note.notes[0].len, &head, &dots, &nflags); s2->head = head; s2->dots = dots; s2->nflags = nflags; if (s2->nflags <= -2) s2->flags |= ABC_F_STEMLESS; else s2->flags &= ~ABC_F_STEMLESS; } curvoice->time = s->time = time; } /* -- measure bar -- */ static void get_bar(struct SYMBOL *s) { int bar_type; struct SYMBOL *s2; if (s->u.bar.repeat_bar && curvoice->norepbra && !curvoice->second) s->sflags |= S_NOREPBRA; if (curvoice->auto_len) adjust_dur(s); bar_type = s->u.bar.type; s2 = curvoice->last_sym; if (s2 && s2->type == SPACE) { s2->time--; // keep the space at the right place } else if (s2 && s2->type == BAR) { /* remove the invisible repeat bars when no shift is needed */ if (bar_type == B_OBRA && !s2->text && (curvoice == &voice_tb[parsys->top_voice] || (parsys->staff[curvoice->staff - 1].flags & STOP_BAR) || (s->sflags & S_NOREPBRA))) { s2->text = s->text; s2->u.bar.repeat_bar = s->u.bar.repeat_bar; s2->flags |= s->flags & (ABC_F_RBSTART | ABC_F_RBSTOP); s2->sflags |= s->sflags & (S_NOREPBRA | S_RBSTART | S_RBSTOP); s = s2; goto gch_build; } /* merge back-to-back repeat bars */ if (bar_type == B_LREP && !s->text) { if (s2->u.bar.type == B_RREP) { s2->u.bar.type = B_DREP; s2->flags |= ABC_F_RBSTOP; s2->sflags |= S_RBSTOP; return; } if (s2->u.bar.type == B_DOUBLE) { s2->u.bar.type = (B_SINGLE << 8) | B_LREP; s2->flags |= ABC_F_RBSTOP; s2->sflags |= S_RBSTOP; return; } } } /* link the bar in the voice */ /* the bar must appear before a key signature */ if (s2 && s2->type == KEYSIG && (!s2->prev || s2->prev->type != BAR)) { curvoice->last_sym = s2->prev; if (!curvoice->last_sym) curvoice->sym = NULL; sym_link(s, BAR); s->next = s2; s2->prev = s; curvoice->last_sym = s2; } else { sym_link(s, BAR); } s->staff = curvoice->staff; /* original staff */ /* set some flags */ switch (bar_type) { case B_OBRA: case (B_OBRA << 4) + B_CBRA: s->flags |= ABC_F_INVIS; break; case (B_COL << 8) + (B_BAR << 4) + B_COL: case (B_COL << 12) + (B_BAR << 8) + (B_BAR << 4) + B_COL: bar_type = (B_COL << 4) + B_COL; /* :|: and :||: -> :: */ s->u.bar.type = bar_type; break; case (B_BAR << 4) + B_BAR: if (!cfmt.rbdbstop) break; case (B_OBRA << 4) + B_BAR: case (B_BAR << 4) + B_CBRA: s->flags |= ABC_F_RBSTOP; s->sflags |= S_RBSTOP; break; } if (s->u.bar.dc.n > 0) deco_cnv(&s->u.bar.dc, s, NULL); /* convert the decorations */ /* build the gch */ gch_build: if (s->text) { if (!s->u.bar.repeat_bar) { gch_build(s); /* build the guitar chords */ } else { s->gch = getarena(sizeof *s->gch * 2); memset(s->gch, 0, sizeof *s->gch * 2); s->gch->type = 'r'; s->gch->font = REPEATFONT; str_font(REPEATFONT); s->gch->w = tex_str(s->text); s->gch->x = 4 + 4; } } } /* -- activate the tablature from the command line '-T' -- */ static void set_tblt(struct VOICE_S *p_voice) { struct tblt_s *tblt; int i; for (i = 0; i < ncmdtblt; i++) { if (!cmdtblts[i].active) continue; if (cmdtblts[i].vn[0] != '\0') { if (strcmp(cmdtblts[i].vn, p_voice->id) != 0 && (p_voice->nm == 0 || strcmp(cmdtblts[i].vn, p_voice->nm) != 0) && (p_voice->snm == 0 || strcmp(cmdtblts[i].vn, p_voice->snm) != 0)) continue; } tblt = tblts[cmdtblts[i].index]; if (p_voice->tblts[0] == tblt || p_voice->tblts[1] == tblt) continue; if (p_voice->tblts[0] == 0) p_voice->tblts[0] = tblt; else p_voice->tblts[1] = tblt; } } /* -- do a tune -- */ void do_tune(void) { struct VOICE_S *p_voice; struct SYMBOL *s, *s1, *s2; int i; /* initialize */ lvlarena(0); nstaff = 0; staves_found = -1; for (i = 0; i < MAXVOICE; i++) { p_voice = &voice_tb[i]; s1 = (struct SYMBOL *) getarena(sizeof *s1); memset(s1, 0, sizeof *s1); s1->type = CLEF; s1->voice = i; if (cfmt.autoclef) { s1->u.clef.type = AUTOCLEF; s1->sflags = S_CLEF_AUTO; } else { s1->u.clef.type = TREBLE; } s1->u.clef.line = 2; /* treble clef on 2nd line */ p_voice->s_clef = s1; p_voice->meter.wmeasure = 1; // M:none p_voice->wmeasure = 1; p_voice->scale = 1; p_voice->clone = -1; p_voice->over = -1; p_voice->posit = cfmt.posit; p_voice->stafflines = NULL; // p_voice->staffscale = 0; } curvoice = first_voice = voice_tb; reset_deco(); abc2win = 0; clip_start.bar = -1; clip_end.bar = (short unsigned) ~0 >> 1; parsys = NULL; system_new(); /* create the 1st staff system */ parsys->top_voice = parsys->voice[0].range = 0; /* implicit voice */ if (!epsf) { //fixme: 8.6.2 #if 1 // fixme: should already be 0 use_buffer = 0; #else if (cfmt.oneperpage) { use_buffer = 0; close_page(); } else { if (in_page) // ?? use_buffer = cfmt.splittune != 1; } #endif } else { use_buffer = 1; marg_init(); } /* set the duration of all notes/rests * (this is needed for tuplets and the feathered beams) */ for (s = parse.first_sym; s; s = s->abc_next) { switch (s->abc_type) { case ABC_T_EOLN: if (s->u.eoln.type == 2) abc2win = 1; break; case ABC_T_NOTE: case ABC_T_REST: s->dur = s->u.note.notes[0].len; break; } } if (voice_tb[0].id[0] == '\0') { /* single voice */ voice_tb[0].id[0] = '1'; /* implicit V:1 */ voice_tb[0].id[1] = '\0'; } /* scan the tune */ for (s = parse.first_sym; s; s = s->abc_next) { if (s->flags & ABC_F_LYRIC_START) curvoice->lyric_start = curvoice->last_sym; switch (s->abc_type) { case ABC_T_INFO: s = get_info(s); break; case ABC_T_PSCOM: s = process_pscomment(s); break; case ABC_T_NOTE: case ABC_T_REST: if (curvoice->space && !(s->flags & ABC_F_GRACE)) { curvoice->space = 0; s->flags |= ABC_F_SPACE; } get_note(s); break; case ABC_T_BAR: if (over_bar) get_over(s); get_bar(s); break; case ABC_T_CLEF: get_clef(s); break; case ABC_T_EOLN: if (cfmt.breakoneoln || (s->flags & ABC_F_SPACE)) curvoice->space = 1; if (cfmt.continueall || cfmt.barsperstaff || s->u.eoln.type == 1) /* if '\' */ continue; if (s->u.eoln.type == 0 /* if normal eoln */ && abc2win && parse.abc_vers != (2 << 16)) continue; if (parsys->voice[curvoice - voice_tb].range == 0 && curvoice->last_sym) curvoice->last_sym->sflags |= S_EOLN; if (!cfmt.alignbars) continue; /* normal */ /* align bars */ while (s->abc_next) { /* treat the lyrics */ if (s->abc_next->abc_type != ABC_T_INFO) break; switch (s->abc_next->text[0]) { case 'w': s = get_info(s->abc_next); continue; case 'd': case 's': s = s->abc_next; continue; } break; } i = (curvoice - voice_tb) + 1; if (i < cfmt.alignbars) { curvoice = &voice_tb[i]; continue; } generate(); buffer_eob(0); curvoice = &voice_tb[0]; continue; case ABC_T_MREST: { int dur; dur = curvoice->wmeasure * s->u.bar.len; if (curvoice->second) { curvoice->time += dur; break; } sym_link(s, MREST); s->dur = dur; curvoice->time += dur; if (s->text) gch_build(s); /* build the guitar chords */ if (s->u.bar.dc.n > 0) deco_cnv(&s->u.bar.dc, s, NULL); break; } case ABC_T_MREP: { int n; s2 = curvoice->last_sym; if (!s2 || s2->type != BAR) { error(1, s, "No bar before measure repeat"); break; } if (curvoice->ignore) break; n = s->u.bar.len; if (curvoice->second) { curvoice->time += curvoice->wmeasure * n; break; } s2 = sym_add(curvoice, NOTEREST); s2->abc_type = ABC_T_REST; s2->flags |= ABC_F_INVIS; s2->dur = curvoice->wmeasure; curvoice->time += s2->dur; if (n == 1) { s->abc_next->u.bar.len = n; /* <n> in the next bar */ break; } while (--n > 0) { s2 = sym_add(curvoice, BAR); s2->u.bar.type = B_SINGLE; if (n == s->u.bar.len - 1) s2->u.bar.len = s->u.bar.len; s2 = sym_add(curvoice, NOTEREST); s2->abc_type = ABC_T_REST; s2->flags |= ABC_F_INVIS; s2->dur = curvoice->wmeasure; curvoice->time += s2->dur; } break; } case ABC_T_V_OVER: get_over(s); continue; case ABC_T_TUPLET: set_tuplet(s); break; default: continue; } if (s->type == 0) continue; if (curvoice->second) s->sflags |= S_SECOND; if (curvoice->floating) s->sflags |= S_FLOATING; } gen_ly(0); put_history(); buffer_eob(1); if (epsf) { write_eps(); } else { write_buffer(); // if (!cfmt.oneperpage && in_page) // use_buffer = cfmt.splittune != 1; } if (info['X' - 'A']) { memcpy(&cfmt, &dfmt, sizeof cfmt); /* restore global values */ memcpy(&info, &info_glob, sizeof info); memcpy(deco, deco_glob, sizeof deco); maps = maps_glob; info['X' - 'A'] = NULL; } /* free the parsing resources */ { struct brk_s *brk, *brk2; brk = brks; while (brk) { brk2 = brk->next; free(brk); brk = brk2; } brks = brk; /* (NULL) */ } } /* check if a K: or M: may go to the tune key and time signatures */ static int is_tune_sig(void) { struct SYMBOL *s; if (!curvoice->sym) return 1; if (curvoice->time != 0) return 0; /* not at start of tune */ for (s = curvoice->sym; s; s = s->next) { switch (s->type) { case TEMPO: case PART: case FMTCHG: break; default: return 0; } } return 1; } /* -- get a clef definition (in K: or V:) -- */ static void get_clef(struct SYMBOL *s) { struct SYMBOL *s2; struct VOICE_S *p_voice; int voice; p_voice = curvoice; s->type = CLEF; if (s->abc_prev->abc_type == ABC_T_INFO) { switch (s->abc_prev->text[0]) { case 'K': if (s->abc_prev->state != ABC_S_HEAD) break; for (voice = 0; voice < MAXVOICE; voice++) { voice_tb[voice].s_clef = s; if (s->u.clef.type == PERC) voice_tb[voice].perc = 1; } return; case 'V': /* clef relative to a voice definition in the header */ p_voice = &voice_tb[(int) s->abc_prev->u.voice.voice]; curvoice = p_voice; break; } } if (is_tune_sig()) { p_voice->s_clef = s; } else { /* clef change */ #if 0 sym_link(s, CLEF); #else /* the clef must appear before a key signature or a bar */ s2 = p_voice->last_sym; if (s2 && s2->prev && s2->time == curvoice->time // if no time skip && (s2->type == KEYSIG || s2->type == BAR)) { struct SYMBOL *s3; for (s3 = s2; s3->prev; s3 = s3->prev) { switch (s3->prev->type) { case KEYSIG: case BAR: continue; } break; } p_voice->last_sym = s3->prev; sym_link(s, CLEF); s->next = s3; s3->prev = s; p_voice->last_sym = s2; } else { sym_link(s, CLEF); } #endif s->aux = 1; /* small clef */ } p_voice->perc = s->u.clef.type == PERC; if (s->u.clef.type == AUTOCLEF) s->sflags |= S_CLEF_AUTO; } /* -- treat %%clef -- */ static void clef_def(struct SYMBOL *s) { char *p; int clef, clef_line; char str[80]; clef = -1; clef_line = 2; p = &s->text[2 + 5]; /* skip %%clef */ while (isspace((unsigned char) *p)) p++; /* clef name */ switch (*p) { case '\"': /* user clef name */ p = get_str(str, p, sizeof str); s->u.clef.name = (char *) getarena(strlen(str) + 1); strcpy(s->u.clef.name, str); clef = TREBLE; break; case 'G': clef = TREBLE; p++; break; case 'F': clef = BASS; clef_line = 4; p++; break; case 'C': clef = ALTO; clef_line = 3; p++; break; case 'P': clef = PERC; p++; break; case 't': if (strncmp(p, "treble", 6) == 0) { clef = TREBLE; p += 6; } if (strncmp(p, "tenor", 5) == 0) { clef = ALTO; clef_line = 4; p += 5; } break; case 'a': if (strncmp(p, "alto", 4) == 0) { clef = ALTO; clef_line = 3; p += 4; } else if (strncmp(p, "auto", 4) == 0) { clef = AUTOCLEF; s->sflags |= S_CLEF_AUTO; p += 4; } break; case 'b': if (strncmp(p, "bass", 4) == 0) { clef = BASS; clef_line = 4; p += 4; } break; case 'p': if (strncmp(p, "perc", 4) == 0) { clef = PERC; p += 4; } break; case 'n': if (strncmp(p, "none", 4) == 0) { clef = TREBLE; s->u.clef.invis = 1; s->flags |= ABC_F_INVIS; p += 4; } break; } if (clef < 0) { error(1, s, "Unknown clef '%s'", p); return; } /* clef line */ switch (*p) { case '1': case '2': case '3': case '4': case '5': clef_line = *p++ - '0'; break; } /* +/-/^/_8 */ if (p[1] == '8') { switch (*p) { case '^': s->u.clef.transpose = -7; case '+': s->u.clef.octave = 1; break; case '_': s->u.clef.transpose = 7; case '-': s->u.clef.octave = -1; break; } } /* handle the clef */ s->abc_type = ABC_T_CLEF; s->u.clef.type = clef; s->u.clef.line = clef_line; get_clef(s); } /* transpose a key */ static void key_transpose(struct key_s *key) { int t, sf; t = curvoice->transpose / 3; sf = (t & ~1) + (t & 1) * 7 + key->sf; switch ((curvoice->transpose + 210) % 3) { case 1: sf = (sf + 4 + 12 * 4) % 12 - 4; /* more sharps */ break; case 2: sf = (sf + 7 + 12 * 4) % 12 - 7; /* more flats */ break; default: sf = (sf + 5 + 12 * 4) % 12 - 5; /* Db, F# or B */ break; } key->sf = sf; } /* -- set the accidentals when K: with modified accidentals -- */ static void set_k_acc(struct SYMBOL *s) { int i, j, nacc; char accs[8], pits[8]; static char sharp_tb[8] = {26, 23, 27, 24, 21, 25, 22}; static char flat_tb[8] = {22, 25, 21, 24, 20, 23, 26}; if (s->u.key.sf > 0) { for (nacc = 0; nacc < s->u.key.sf; nacc++) { accs[nacc] = A_SH; pits[nacc] = sharp_tb[nacc]; } } else { for (nacc = 0; nacc < -s->u.key.sf; nacc++) { accs[nacc] = A_FT; pits[nacc] = flat_tb[nacc]; } } for (i = 0; i < s->u.key.nacc; i++) { for (j = 0; j < nacc; j++) { // if ((pits[j] - s->u.key.pits[i]) % 7 == 0) { if (pits[j] == s->u.key.pits[i]) { accs[j] = s->u.key.accs[i]; break; } } if (j == nacc) { if (nacc >= sizeof accs) { error(1, s, "Too many accidentals"); } else { accs[j] = s->u.key.accs[i]; pits[j] = s->u.key.pits[i]; nacc++; } } } for (i = 0; i < nacc; i++) { s->u.key.accs[i] = accs[i]; s->u.key.pits[i] = pits[i]; } s->u.key.nacc = nacc; } /* -- get a key signature definition (K:) -- */ static void get_key(struct SYMBOL *s) { struct VOICE_S *p_voice; struct SYMBOL *s2; struct key_s okey; /* original key */ int i; // int delta; if (s->u.key.octave != NO_OCTAVE) curvoice->octave = s->u.key.octave; if (s->u.key.cue > 0) curvoice->scale = 0.7; else if (s->u.key.cue < 0) curvoice->scale = 1; if (s->u.key.stafflines) curvoice->stafflines = s->u.key.stafflines; if (s->u.key.staffscale != 0) curvoice->staffscale = s->u.key.staffscale; if (s->u.key.empty == 1) /* clef only */ return; if (s->u.key.sf != 0 && !s->u.key.exp && s->u.key.nacc != 0) set_k_acc(s); memcpy(&okey, &s->u.key, sizeof okey); if (s->state == ABC_S_HEAD) { /* if first K: (start of tune) */ for (i = MAXVOICE, p_voice = voice_tb; --i >= 0; p_voice++) p_voice->transpose = cfmt.transpose; // curvoice->transpose = cfmt.transpose; } if (curvoice->transpose != 0) { key_transpose(&s->u.key); #if 0 /* transpose explicit accidentals */ //fixme: not correct - transpose adds or removes accidentals... if (s->u.key.nacc > 0) { struct VOICE_S voice, *voice_sav; struct SYMBOL note; memset(&voice, 0, sizeof voice); voice.transpose = curvoice->transpose; memcpy(&voice.ckey, &s->u.key, sizeof voice.ckey); voice.ckey.empty = 2; voice.ckey.nacc = 0; memset(¬e, 0, sizeof note); --fixme memcpy(note.u.note.pits, voice.ckey.pits, sizeof note.u.note.pits); memcpy(note.u.note.accs, voice.ckey.accs, sizeof note.u.note.accs); note.nhd = s->u.key.nacc; voice_sav = curvoice; curvoice = &voice; note_transpose(¬e); memcpy(s->u.key.pits, note.u.note.pits, sizeof s->u.key.pits); memcpy(s->u.key.accs, note.u.note.accs, sizeof s->u.key.accs); curvoice = voice_sav; } #endif } // calculate the tonic delta // s->u.key.key_delta = (cgd2cde[(s->u.key.sf + 7) % 7] + 14 + s->u.key.mode) % 7; s->u.key.key_delta = (cgd2cde[(s->u.key.sf + 7) % 7] + 14) % 7; if (s->state == ABC_S_HEAD) { /* start of tune */ for (i = MAXVOICE, p_voice = voice_tb; --i >= 0; p_voice++) { memcpy(&p_voice->key, &s->u.key, sizeof p_voice->key); memcpy(&p_voice->ckey, &s->u.key, sizeof p_voice->ckey); memcpy(&p_voice->okey, &okey, sizeof p_voice->okey); if (p_voice->key.empty) p_voice->key.sf = 0; if (s->u.key.octave != NO_OCTAVE) p_voice->octave = s->u.key.octave; if (s->u.key.stafflines) p_voice->stafflines = s->u.key.stafflines; if (s->u.key.staffscale != 0) p_voice->staffscale = s->u.key.staffscale; //fixme: update parsys->voice[voice].stafflines = stafflines; ? } return; } /* ABC_S_TUNE (K: cannot be ABC_S_GLOBAL) */ if (is_tune_sig()) { /* define the starting key signature */ memcpy(&curvoice->key, &s->u.key, sizeof curvoice->key); memcpy(&curvoice->ckey, &s->u.key, sizeof curvoice->ckey); memcpy(&curvoice->okey, &okey, sizeof curvoice->okey); switch (curvoice->key.instr) { case 0: if (!pipeformat) { // curvoice->transpose = cfmt.transpose; break; } //fall thru case K_HP: case K_Hp: if (curvoice->posit.std == 0) curvoice->posit.std = SL_BELOW; break; } if (curvoice->key.empty) curvoice->key.sf = 0; return; } /* key signature change */ if ((!s->abc_next || s->abc_next->abc_type != ABC_T_CLEF) /* if not explicit clef */ && curvoice->ckey.sf == s->u.key.sf /* and same key */ && curvoice->ckey.nacc == 0 && s->u.key.nacc == 0 && curvoice->ckey.empty == s->u.key.empty && cfmt.keywarn) /* (if not key warning, * keep all key signatures) */ return; /* ignore */ if (!curvoice->ckey.empty) s->aux = curvoice->ckey.sf; /* previous key signature */ memcpy(&curvoice->ckey, &s->u.key, sizeof curvoice->ckey); memcpy(&curvoice->okey, &okey, sizeof curvoice->okey); if (s->u.key.empty) s->u.key.sf = 0; /* the key signature must appear before a time signature */ s2 = curvoice->last_sym; if (s2 && s2->type == TIMESIG) { curvoice->last_sym = s2->prev; if (!curvoice->last_sym) curvoice->sym = NULL; sym_link(s, KEYSIG); s->next = s2; s2->prev = s; curvoice->last_sym = s2; } else { sym_link(s, KEYSIG); } } /* -- set meter from M: -- */ static void get_meter(struct SYMBOL *s) { struct VOICE_S *p_voice; int i; switch (s->state) { case ABC_S_GLOBAL: /*fixme: keep the values and apply to all tunes?? */ break; case ABC_S_HEAD: for (i = MAXVOICE, p_voice = voice_tb; --i >= 0; p_voice++) { memcpy(&p_voice->meter, &s->u.meter, sizeof p_voice->meter); p_voice->wmeasure = s->u.meter.wmeasure; } break; case ABC_S_TUNE: curvoice->wmeasure = s->u.meter.wmeasure; if (is_tune_sig()) { memcpy(&curvoice->meter, &s->u.meter, sizeof curvoice->meter); reset_gen(); /* (display the time signature) */ break; } if (s->u.meter.nmeter == 0) break; /* M:none */ sym_link(s, TIMESIG); break; } } /* -- treat a 'V:' -- */ static void get_voice(struct SYMBOL *s) { struct VOICE_S *p_voice; int voice; voice = s->u.voice.voice; p_voice = &voice_tb[voice]; if (parsys->voice[voice].range < 0) { if (cfmt.alignbars) { error(1, s, "V: does not work with %%%%alignbars"); } if (staves_found < 0) { if (!s->u.voice.merge) { #if MAXSTAFF < MAXVOICE if (nstaff >= MAXSTAFF - 1) { error(1, s, "Too many staves"); return; } #endif nstaff++; } else { p_voice->second = 1; parsys->voice[voice].second = 1; } p_voice->staff = p_voice->cstaff = nstaff; parsys->voice[voice].staff = nstaff; parsys->nstaff = nstaff; { int range, i; range = 0; for (i = 0; i < MAXVOICE; i++) { if (parsys->voice[i].range > range) range = parsys->voice[i].range; } parsys->voice[voice].range = range + 1; voice_link(p_voice); } } else { p_voice->ignore = 1; p_voice->staff = p_voice->cstaff = nstaff + 1; } } /* if something has changed, update */ if (s->u.voice.fname != 0) { p_voice->nm = s->u.voice.fname; p_voice->new_name = 1; } if (s->u.voice.nname != 0) p_voice->snm = s->u.voice.nname; if (s->u.voice.octave != NO_OCTAVE) p_voice->octave = s->u.voice.octave; switch (s->u.voice.dyn) { case 1: p_voice->posit.dyn = SL_ABOVE; p_voice->posit.vol = SL_ABOVE; break; case -1: p_voice->posit.dyn = SL_BELOW; p_voice->posit.vol = SL_BELOW; break; } switch (s->u.voice.lyrics) { case 1: p_voice->posit.voc = SL_ABOVE; break; case -1: p_voice->posit.voc = SL_BELOW; break; } switch (s->u.voice.gchord) { case 1: p_voice->posit.gch = SL_ABOVE; break; case -1: p_voice->posit.gch = SL_BELOW; break; } switch (s->u.voice.stem) { case 1: p_voice->posit.std = SL_ABOVE; break; case -1: p_voice->posit.std = SL_BELOW; break; case 2: p_voice->posit.std = 0; /* auto */ break; } switch (s->u.voice.gstem) { case 1: p_voice->posit.gsd = SL_ABOVE; break; case -1: p_voice->posit.gsd = SL_BELOW; break; case 2: p_voice->posit.gsd = 0; /* auto */ break; } if (s->u.voice.scale != 0) p_voice->scale = s->u.voice.scale; else if (s->u.voice.cue > 0) p_voice->scale = 0.7; else if (s->u.voice.cue < 0) p_voice->scale = 1; if (s->u.voice.stafflines) p_voice->stafflines = s->u.voice.stafflines; if (s->u.voice.staffscale != 0) p_voice->staffscale = s->u.voice.staffscale; if (!p_voice->combine) p_voice->combine = cfmt.combinevoices; set_tblt(p_voice); /* if in tune, switch to this voice */ if (s->state == ABC_S_TUNE) curvoice = p_voice; } /* sort the notes of the chord by pitch (lowest first) */ void sort_pitch(struct SYMBOL *s) { int i, nx, k; struct note v_note; unsigned char new_order[MAXHD], inv_order[MAXHD]; for (i = 0; i <= s->nhd; i++) new_order[i] = i; for (;;) { nx = 0; for (i = 1; i <= s->nhd; i++) { if (s->u.note.notes[i].pit >= s->u.note.notes[i - 1].pit) continue; memcpy(&v_note, &s->u.note.notes[i], sizeof v_note); memcpy(&s->u.note.notes[i], &s->u.note.notes[i - 1], sizeof v_note); memcpy(&s->u.note.notes[i - 1], &v_note, sizeof v_note); k = s->pits[i]; s->pits[i] = s->pits[i - 1]; s->pits[i - 1] = k; k = new_order[i]; new_order[i] = new_order[i - 1]; new_order[i - 1] = k; nx++; } if (nx == 0) break; } /* change the indexes of the note head decorations */ if (s->nhd > 0) { for (i = 0; i <= s->nhd; i++) inv_order[new_order[i]] = i; for (i = 0; i <= s->u.note.dc.n; i++) { k = s->u.note.dc.tm[i].m; if (k >= 0) s->u.note.dc.tm[i].m = inv_order[k]; } } } // set the map of the notes static void set_map(struct SYMBOL *s) { struct map *map; struct note_map *note_map; struct note *note; int m, delta; for (map = maps; map; map = map->next) { if (strcmp(map->name, curvoice->map_name) == 0) break; } if (!map) return; // !? // loop on the note maps, then on the notes of the chord delta = curvoice->ckey.key_delta; for (m = 0; m <= s->nhd; m++) { note = &s->u.note.notes[m]; for (note_map = map->notes; note_map; note_map = note_map->next) { switch (note_map->type) { case MAP_ONE: if (note->pit == note_map->pit && note->acc == note_map->acc) break; continue; case MAP_OCT: if ((note->pit - note_map->pit + 28 ) % 7 == 0 && note->acc == note_map->acc) break; continue; case MAP_KEY: if ((note->pit + 28 - delta - note_map->pit) % 7 == 0) break; continue; default: // MAP_ALL break; } note->head = note_map->heads; note->color = note_map->color; if (note_map->print_pit != -128) { note->pit = note_map->print_pit; s->pits[m] = note->pit; note->acc = note_map->print_acc; } break; } } } /* -- note or rest -- */ static void get_note(struct SYMBOL *s) { struct SYMBOL *prev; int i, m, delta; prev = curvoice->last_sym; m = s->nhd; /* insert the note/rest in the voice */ sym_link(s, s->u.note.notes[0].len != 0 ? NOTEREST : SPACE); if (!(s->flags & ABC_F_GRACE)) curvoice->time += s->dur; if (curvoice->octave) { delta = curvoice->octave * 7; for (i = 0; i <= m; i++) { s->u.note.notes[i].pit += delta; s->pits[i] += delta; } } /* convert the decorations * (!beam-accel! and !beam-rall! may change the note duration) * (!8va(! may change ottava) */ if (s->u.note.dc.n > 0) deco_cnv(&s->u.note.dc, s, prev); if (curvoice->ottava) { delta = curvoice->ottava; for (i = 0; i <= m; i++) s->pits[i] += delta; } s->combine = curvoice->combine; s->color = curvoice->color; if (curvoice->perc) s->sflags |= S_PERC; else if (s->abc_type == ABC_T_NOTE && curvoice->transpose != 0) note_transpose(s); if (!(s->flags & ABC_F_GRACE)) { switch (curvoice->posit.std) { case SL_ABOVE: s->stem = 1; break; case SL_BELOW: s->stem = -1; break; case SL_HIDDEN: s->flags |= ABC_F_STEMLESS;; break; } } else { /* grace note - adjust its duration */ int div; if (curvoice->key.instr != K_HP && curvoice->key.instr != K_Hp && !pipeformat) { div = 2; if (!prev || !(prev->flags & ABC_F_GRACE)) { if (s->flags & ABC_F_GR_END) div = 1; /* one grace note */ } } else { div = 4; /* bagpipe */ } for (i = 0; i <= m; i++) s->u.note.notes[i].len /= div; s->dur /= div; switch (curvoice->posit.gsd) { case SL_ABOVE: s->stem = 1; break; case SL_BELOW: s->stem = -1; break; case SL_HIDDEN: s->stem = 2; break; /* opposite */ } } s->nohdi1 = s->nohdi2 = -1; /* change the figure of whole measure rests */ if (s->abc_type == ABC_T_REST) { if (s->dur == curvoice->wmeasure) { if (s->dur < BASE_LEN * 2) s->u.note.notes[0].len = BASE_LEN; else if (s->dur < BASE_LEN * 4) s->u.note.notes[0].len = BASE_LEN * 2; else s->u.note.notes[0].len = BASE_LEN * 4; } } else { /* sort the notes of the chord by pitch (lowest first) */ if (!(s->flags & ABC_F_GRACE) && curvoice->map_name) set_map(s); sort_pitch(s); } /* get the max head type, number of dots and number of flags */ if (!curvoice->auto_len || (s->flags & ABC_F_GRACE)) { int head, dots, nflags, l; if ((l = s->u.note.notes[0].len) != 0) { identify_note(s, l, &head, &dots, &nflags); s->head = head; s->dots = dots; s->nflags = nflags; for (i = 1; i <= m; i++) { if (s->u.note.notes[i].len == l) continue; identify_note(s, s->u.note.notes[i].len, &head, &dots, &nflags); if (head > s->head) s->head = head; if (dots > s->dots) s->dots = dots; if (nflags > s->nflags) s->nflags = nflags; } if (s->sflags & S_XSTEM) s->nflags = 0; /* word start+end */ } } if (s->nflags <= -2) s->flags |= ABC_F_STEMLESS; if (s->sflags & (S_TREM1 | S_TREM2)) { if (s->nflags > 0) s->nflags += s->aux; else s->nflags = s->aux; if ((s->sflags & S_TREM2) && (s->sflags & S_BEAM_END)) { /* if 2nd note - see deco.c */ prev->head = s->head; prev->aux = s->aux; prev->nflags = s->nflags; prev->flags |= (s->flags & ABC_F_STEMLESS); } } for (i = 0; i <= m; i++) { if (s->u.note.notes[i].sl1 != 0) s->sflags |= S_SL1; if (s->u.note.notes[i].sl2 != 0) s->sflags |= S_SL2; if (s->u.note.notes[i].ti1 != 0) s->sflags |= S_TI1; } switch (cfmt.shiftunison) { case 0: break; case 1: s->sflags |= S_SHIFTUNISON_1; break; case 2: s->sflags |= S_SHIFTUNISON_2; break; default: s->sflags |= S_SHIFTUNISON_1 | S_SHIFTUNISON_2; break; } /* build the guitar chords */ if (s->text) gch_build(s); } static char *get_val(char *p, float *v) { char tmp[32], *r = tmp; while (isspace((unsigned char) *p)) p++; while ((isdigit((unsigned char) *p) && r < &tmp[32 - 1]) || *p == '-' || *p == '.') *r++ = *p++; *r = '\0'; sscanf(tmp, "%f", v); return p; } // parse <path .../> from %%beginsvg and convert to Postscript static void parse_path(char *p, char *q, char *id, int idsz) { struct SYMBOL *s; char *buf, *r, *t, *op = NULL, *width, *scale, *trans; int i, fill, npar = 0; float x1, y1, x, y; char *rmax; r = strstr(p, "class=\""); if (!r || r > q) return; r += 7; fill = strncmp(r, "fill", 4) == 0; width = strstr(p, "stroke-width:"); scale = strstr(p, "scale("); if (scale && scale > q) scale = NULL; trans = strstr(p, "translate("); if (trans && trans > q) trans = NULL; for (;;) { p = strstr(p, "d=\""); if (!p) return; if (isspace((unsigned char) p[-1])) // (check not 'id=..") break; p += 3; } i = (int) (q - p) * 4 + 200; // estimated PS buffer size if (i > TEX_BUF_SZ) buf = malloc(i); else buf = tex_buf; rmax=buf + i; r = buf; *r++ = '/'; idsz -= 5; strncpy(r, id + 4, idsz); r += idsz; strcpy(r, "{gsave T "); r += strlen(r); if (scale || trans) { if (scale) { scale += 6; // "scale(" t = get_val(scale, &x1); if (*t == ',') t = get_val(t + 1, &y1); else y1 = x1; } if (trans) { trans += 10; // "translate(" t = get_val(trans, &x) + 1; //"," t = get_val(t, &y); } if (!scale) r += sprintf(r, "%.2f %.2f T ", x, -y); else if (!trans) r += sprintf(r, "%.2f %.2f scale ", x1, y1); else if (scale > trans) r += sprintf(r, "%.2f %.2f T %.2f %.2f scale ", x, -y, x1, y1); else r += sprintf(r, "%.2f %.2f scale %.2f %.2f T ", x1, y1, x, -y); } strcpy(r, "0 0 M\n"); r += strlen(r); if (width && width < q) { *r++ = ' '; width += 13; while (isdigit(*width) || *width == '.') *r++ = *width++; *r++ = ' '; *r++ = 'S'; *r++ = 'L'; *r++ = 'W'; } p += 3; for (;;) { if (*p == '\0' || *p == '"') break; i = 0; switch (*p++) { default: if ((isdigit((unsigned char) p[-1])) || p[-1] == '-' || p[-1] == '.') { if (!npar) continue; p--; // same op break; } continue; case 'M': op = "M"; npar = 2; break; case 'm': op = "RM"; npar = 2; break; case 'L': op = "L"; npar = 2; break; case 'l': op = "RL"; npar = 2; break; case 'H': op = "H"; npar = 1; break; case 'h': op = "h"; npar = 1; break; case 'V': op = "V"; npar = 1; break; case 'v': *r++ = ' '; *r++ = '0'; op = "RL"; i = 1; npar = 2; break; case 'z': op = "closepath"; npar = 0; break; case 'C': op = "C"; npar = 6; break; case 'c': op = "RC"; npar = 6; break; // case 'A': // op = "arc"; // break; // case 'a': // op = "arc"; // break; case 'q': op = "RC"; npar = 2; p = get_val(p, &x1); p = get_val(p, &y1); t = get_val(p, &x); t = get_val(t, &y); r += sprintf(r, " %.2f %.2f %.2f %.2f", x1*2/3, -y1*2/3, x1+(x-x1)*2/3, -y1-(y-y1)*2/3); break; case 't': op = "RC"; npar = 2; x1 = x - x1; y1 = y - y1; t = get_val(p, &x); t = get_val(t, &y); r += sprintf(r, " %.2f %.2f %.2f %.2f", x1*2/3, -y1*2/3, x1+(x-x1)*2/3, -y1-(y-y1)*2/3); break; } *r++ = ' '; for ( ; i < npar; i++) { while (isspace((unsigned char) *p)) p++; if (i & 1) { // y is inverted if (*p == '-') p++; else if (*p != '0' || p[1] != ' ') *r++ = '-'; } while ((isdigit((unsigned char) *p)) || *p == '-' || *p == '.') *r++ = *p++; *r++ = ' '; } if (*op == 'h') { *r++ = '0'; *r++ = ' '; op = "RL"; } strcpy(r, op); r += strlen(r); if (r + 30 > rmax) bug("Buffer overflow in SVG to PS", 1); } strcpy(r, fill ? " fill" : " stroke"); r += strlen(r); strcpy(r, "\ngrestore}!"); r += strlen(r); s = getarena(sizeof(struct SYMBOL)); memset(s, 0, sizeof(struct SYMBOL)); s->text = getarena(strlen(buf) + 1); strcpy(s->text, buf); ps_def(s, s->text, 'p'); if (buf != tex_buf) free(buf); } // parse <defs> .. </defs> from %%beginsvg static void parse_defs(char *p, char *q) { char *id, *r; int idsz; for (;;) { id = strstr(p, "id=\""); if (!id || id > q) return; r = strchr(id + 4, '"'); if (!r) return; idsz = r + 1 - id; // if SVG output, mark the id as defined if (svg || epsf > 1) { svg_def_id(id, idsz); p = r; continue; } // convert SVG to PS p = id; while (*p != '<') p--; if (strncmp(p, "<path ", 6) == 0) { r = strstr(p, "/>"); parse_path(p + 6, r, id, idsz); if (!r) break; p = r + 2; continue; } break; } } // extract the SVG defs from %%beginsvg and // convert to PostScript when PS output // move to the SVG glyphs when SVG output static void svg_ps(char *p) { char *q; for (;;) { q = strstr(p, "<defs>"); if (!q) break; p = strstr(q, "</defs>"); if (!p) { error(1, NULL, "No </defs> in %%beginsvg"); break; } parse_defs(q + 6, p); } } /* -- treat a postscript or SVG definition -- */ static void ps_def(struct SYMBOL *s, char *p, char use) /* cf user_ps_add() */ { if (!svg && epsf <= 1) { /* if PS output */ if (secure // || use == 'g' // SVG || use == 's') // PS for SVG return; } else { /* if SVG output */ if (use == 'p' // PS for PS || (use == 'g' // SVG && file_initialized > 0)) return; } if (s->abc_prev) s->state = s->abc_prev->state; if (s->state == ABC_S_TUNE) { if (use == 'g') // SVG return; sym_link(s, FMTCHG); s->aux = PSSEQ; s->text = p; // s->flags |= ABC_F_INVIS; return; } if (use == 'g') { // SVG svg_ps(p); if (!svg && epsf <= 1) return; } if (file_initialized > 0 || mbf != outbuf) a2b("%s\n", p); else user_ps_add(p, use); } /* get a symbol selection */ /* measure_number [ ":" time_numerator "/" time_denominator ] */ static char *get_symsel(struct symsel_s *symsel, char *p) { char *q; int tn, td, n; symsel->bar = strtod(p, &q); if (*q >= 'a' && *q <= 'z') symsel->seq = *q++ - 'a'; else symsel->seq = 0; if (*q == ':') { if (sscanf(q + 1, "%d/%d%n", &tn, &td, &n) != 2 || td <= 0) return 0; symsel->time = BASE_LEN * tn / td; q += 1 + n; } else { symsel->time = 0; } return q; } /* free the voice options */ static void free_voice_opt(struct voice_opt_s *opt) { struct voice_opt_s *opt2; while (opt) { opt2 = opt->next; free(opt); opt = opt2; } } // get a color static int get_color(char *p) { int i, color; static const struct { char *name; int color; } col_tb[] = { { "aqua", 0x00ffff }, { "black", 0x000000 }, { "blue", 0x0000ff }, { "fuchsia", 0xff00ff }, { "gray", 0x808080 }, { "green", 0x008000 }, { "lime", 0x00ff00 }, { "maroon", 0x800000 }, { "navy", 0x000080 }, { "olive", 0x808000 }, { "purple", 0x800080 }, { "red", 0xff0000 }, { "silver", 0xc0c0c0 }, { "teal", 0x008080 }, { "white", 0xffffff }, { "yellow", 0xffff00 }, }; if (*p == '#') { if (sscanf(p, "#%06x", &color) != 1 || (unsigned) color > 0x00ffffff) return -1; return color; } for (i = sizeof col_tb / sizeof col_tb[0]; --i >= 0; ) { if (strncasecmp(p, col_tb[i].name, strlen(col_tb[i].name)) == 0) break; } if (i < 0) return -1; return col_tb[i].color; } /* get a transposition */ static int get_transpose(char *p) { int val, pit1, pit2, acc; static int pit_st[7] = {0, 2, 4, 5, 7, 9, 11}; if (isdigit(*p) || *p == '-' || *p == '+') { sscanf(p, "%d", &val); val *= 3; switch (p[strlen(p) - 1]) { default: return val; case '#': val++; break; case 'b': val += 2; break; } if (val > 0) return val; return val - 3; } // by music interval p = parse_acc_pit(p, &pit1, &acc); if (acc < 0) { error(1, NULL, " in %%%%transpose"); return 0; } pit1 += 126 - 2; // for value > 0 and 'C' % 7 == 0 pit1 = (pit1 / 7) * 12 + pit_st[pit1 % 7]; switch (acc) { case A_DS: pit1 += 2; break; case A_SH: pit1++; break; case A_FT: pit1--; break; case A_DF: pit1 -= 2; break; } p = parse_acc_pit(p, &pit2, &acc); if (acc < 0) { error(1, NULL, " in %%%%transpose"); return 0; } pit2 += 126 - 2; pit2 = (pit2 / 7) * 12 + pit_st[pit2 % 7]; switch (acc) { case A_DS: pit2 += 2; break; case A_SH: pit2++; break; case A_FT: pit2--; break; case A_DF: pit2 -= 2; break; } val = (pit2 - pit1) * 3; switch (acc) { default: return val; case A_DS: case A_SH: val++; break; case A_FT: case A_DF: val += 2; break; } if (val > 0) return val; return val - 3; } // create a note mapping // %%map map_name note [print [heads]] [param]* static void get_map(char *p) { struct map *map; struct note_map *note_map; char *name, *q; int l, type, pit, acc; if (*p == '\0') return; /* map name */ name = p; while (!isspace((unsigned char) *p) && *p != '\0') p++; l = p - name; /* base note */ while (isspace((unsigned char) *p)) p++; if (*p == '*') { type = MAP_ALL; p++; } else if (strncmp(p, "octave,", 7) == 0) { type = MAP_OCT; p += 7; } else if (strncmp(p, "key,", 4) == 0) { type = MAP_KEY; p += 4; } else if (strncmp(p, "all", 3) == 0) { type = MAP_ALL; while (!isspace((unsigned char) *p) && *p != '\0') p++; } else { type = MAP_ONE; } if (type != MAP_ALL) { p = parse_acc_pit(p, &pit, &acc); if (acc < 0) // if error pit = acc = 0; if (type == MAP_OCT || type == MAP_KEY) { pit %= 7; if (type == MAP_KEY) acc = A_NULL; } } else { pit = acc = 0; } // get/create the map for (map = maps; map; map = map->next) { if (strncmp(name, map->name, l) == 0) break; } if (!map) { map = getarena(sizeof *map); map->next = maps; maps = map; map->name = getarena(l + 1); strncpy(map->name, name, l); map->name[l] = '\0'; map->notes = NULL; } for (note_map = map->notes; note_map; note_map = note_map->next) { if (note_map->type == type && note_map->pit == pit && note_map->acc == acc) break; } if (!note_map) { note_map = getarena(sizeof *note_map); memset(note_map, 0, sizeof *note_map); note_map->next = map->notes; map->notes = note_map; note_map->type = type; note_map->pit = pit; note_map->acc = acc; note_map->print_pit = -128; note_map->color = -1; } /* try the optional 'print' and 'heads' parameters */ while (isspace((unsigned char) *p)) p++; if (*p == '\0') return; q = p; while (!isspace((unsigned char) *q) && *q != '\0') { if (*q == '=') break; q++; } if (isspace((unsigned char) *q) || *q == '\0') { if (*p != '*') { p = parse_acc_pit(p, &pit, &acc); if (acc >= 0) { note_map->print_pit = pit; note_map->print_acc = acc; } if (*p == '\0') return; } p = q; while (isspace((unsigned char) *p)) p++; if (*p == '\0') return; q = p; while (!isspace((unsigned char) *q) && *q != '\0') { if (*q == '=') break; q++; } if (isspace((unsigned char) *q) || *q == '\0') { name = p; p = q; l = p - name; note_map->heads = getarena(l + 1); strncpy(note_map->heads, name, l); note_map->heads[l] = '\0'; } } /* loop on the parameters */ for (;;) { while (isspace((unsigned char) *p)) p++; if (*p == '\0') break; if (strncmp(p, "heads=", 6) == 0) { p += 6; name = p; while (!isspace((unsigned char) *p) && *p != '\0') p++; l = p - name; note_map->heads = getarena(l + 1); strncpy(note_map->heads, name, l); note_map->heads[l] = '\0'; } else if (strncmp(p, "print=", 6) == 0) { p += 6; p = parse_acc_pit(p, &pit, &acc); if (acc >= 0) { note_map->print_pit = pit; note_map->print_acc = acc; } } else if (strncmp(p, "color=", 6) == 0) { int color; color = get_color(p + 6); if (color < 0) { error(1, NULL, "Bad color in %%%%map"); return; } note_map->color = color; } while (!isspace((unsigned char) *p) && *p != '\0') p++; } } /* -- process a pseudo-comment (%% or I:) -- */ static struct SYMBOL *process_pscomment(struct SYMBOL *s) { char w[32], *p, *q; int voice; float h1; int lock = 0; p = s->text + 2; /* skip '%%' */ q = p + strlen(p) - 5; if (q > p && strncmp(q, " lock", 5) == 0) { lock = 1; *q = '\0'; } p = get_str(w, p, sizeof w); if (s->state == ABC_S_HEAD && !check_header(s)) { error(1, s, "Cannot have %%%%%s in tune header", w); return s; } switch (w[0]) { case 'b': if (strcmp(w, "beginps") == 0 || strcmp(w, "beginsvg") == 0) { char use; if (w[5] == 'p') { if (strncmp(p, "svg", 3) == 0) use = 's'; else if (strncmp(p, "nosvg", 5) == 0) use = 'p'; else use = 'b'; } else { use = 'g'; } p = s->text + 2 + 7; while (*p != '\0' && *p != '\n') p++; if (*p == '\0') return s; /* empty */ ps_def(s, p + 1, use); return s; } if (strcmp(w, "begintext") == 0) { int job; if (s->state == ABC_S_TUNE) { if (!multicol_start) gen_ly(1); } else if (s->state == ABC_S_GLOBAL) { if (epsf || !in_fname) return s; } p = s->text + 2 + 9; while (*p == ' ' || *p == '\t') p++; if (*p != '\n') { job = get_textopt(p); while (*p != '\0' && *p != '\n') p++; if (*p == '\0') return s; /* empty */ } else { job = cfmt.textoption; } if (job != T_SKIP) { p++; write_text(w, p, job); } return s; } if (strcmp(w, "break") == 0) { struct brk_s *brk; if (s->state != ABC_S_HEAD) { error(1, s, "%%%%%s ignored", w); return s; } if (*p == '\0') return s; for (;;) { brk = malloc(sizeof *brk); p = get_symsel(&brk->symsel, p); if (!p) { error(1, s, "Bad selection in %%%%%s", w); return s; } brk->next = brks; brks = brk; if (*p != ',' && *p != ' ') break; p++; } return s; } break; case 'c': if (strcmp(w, "center") == 0) goto center; if (strcmp(w, "clef") == 0) { if (s->state != ABC_S_GLOBAL) clef_def(s); return s; } if (strcmp(w, "clip") == 0) { if (!cur_tune_opts) { error(1, s, "%%%%%s not in %%%%tune sequence", w); return s; } /* %%clip <symbol selection> "-" <symbol selection> */ if (*p != '-') { p = get_symsel(&clip_start, p); if (!p) { error(1, s, "Bad start in %%%%%s", w); return s; } if (*p != '-') { error(1, s, "Lack of '-' in %%%%%s", w); return s; } } p++; p = get_symsel(&clip_end, p); if (!p) { error(1, s, "Bad end in %%%%%s", w); return s; } if (clip_start.bar < 0) clip_start.bar = 0; if (clip_end.bar < clip_start.bar || (clip_end.bar == clip_start.bar && clip_end.time <= clip_start.time)) { clip_end.bar = (short unsigned) ~0 >> 1; } return s; } break; case 'd': if (strcmp(w, "deco") == 0) { deco_add(p); return s; } if (strcmp(w, "dynamic") == 0) { set_voice_param(curvoice, s->state, w, p); return s; } break; case 'E': if (strcmp(w, "EPS") == 0) { float x1, y1, x2, y2; FILE *fp; char fn[STRL1], line[STRL1]; gen_ly(1); if (secure || cfmt.textoption == T_SKIP) return s; get_str(line, p, sizeof line); if ((fp = open_file(line, "eps", fn)) == NULL) { error(1, s, "No such file: %s", line); return s; } /* get the bounding box */ x1 = x2 = 0; while (fgets(line, sizeof line, fp)) { if (strncmp(line, "%%BoundingBox:", 14) == 0) { if (sscanf(&line[14], "%f %f %f %f", &x1, &y1, &x2, &y2) == 4) break; } } fclose(fp); if (x1 == x2) { error(1, s, "No bounding box in '%s'", fn); return s; } if (cfmt.textoption == T_CENTER || cfmt.textoption == T_RIGHT) { float lw; lw = ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth) - cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale; if (cfmt.textoption == T_CENTER) x1 += (lw - (x2 - x1)) * 0.5; else x1 += lw - (x2 - x1); } a2b("\001"); /* include file (must be the first after eob) */ bskip(y2 - y1); a2b("%.2f %.2f%%%s\n", x1, -y1, fn); buffer_eob(0); return s; } break; case 'g': if (strcmp(w, "gchord") == 0 || strcmp(w, "gstemdir") == 0) { set_voice_param(curvoice, s->state, w, p); return s; } if (strcmp(w, "glyph") == 0) { if (!svg && epsf <= 1) glyph_add(p); return s; } break; case 'm': if (strcmp(w, "map") == 0) { get_map(p); return s; } if (strcmp(w, "maxsysstaffsep") == 0) { if (s->state != ABC_S_TUNE) break; parsys->voice[curvoice - voice_tb].maxsep = scan_u(p, 0); return s; } if (strcmp(w, "multicol") == 0) { float bposy; generate(); if (strncmp(p, "start", 5) == 0) { if (!in_page) a2b("%%\n"); /* initialize the output */ buffer_eob(0); bposy = get_bposy(); multicol_max = multicol_start = bposy; lmarg = cfmt.leftmargin; rmarg = cfmt.rightmargin; } else if (strncmp(p, "new", 3) == 0) { if (multicol_start == 0) { error(1, s, "%%%%%s new without start", w); } else { buffer_eob(0); bposy = get_bposy(); if (bposy < multicol_start) bskip((bposy - multicol_start) / cfmt.scale); if (bposy < multicol_max) multicol_max = bposy; cfmt.leftmargin = lmarg; cfmt.rightmargin = rmarg; } } else if (strncmp(p, "end", 3) == 0) { if (multicol_start == 0) { error(1, s, "%%%%%s end without start", w); } else { buffer_eob(0); bposy = get_bposy(); if (bposy > multicol_max) bskip((bposy - multicol_max) / cfmt.scale); else a2b("%%\n"); /* force write_buffer */ cfmt.leftmargin = lmarg; cfmt.rightmargin = rmarg; multicol_start = 0; buffer_eob(0); if (!info['X' - 'A'] && !epsf) write_buffer(); } } else { error(1, s, "Unknown keyword '%s' in %%%%%s", p, w); } return s; } break; case 'M': if (strcmp(w, "MIDI") == 0 && strncmp(p, "temperamentequal", 16) == 0) { int n; if (cfmt.nedo) { error(1, s, "%%%%MIDI temperamentequal redefined"); return s; } p += 16; while (isspace((unsigned char) *p)) p++; n = atoi(p); if (n < 7 || n > 53) { error(1, s, "Bad value in %%%%MIDI temperamentequal"); return s; } cfmt.nedo = n; } break; case 'n': if (strcmp(w, "newpage") == 0) { if (epsf || !in_fname) return s; if (s->state == ABC_S_TUNE) generate(); buffer_eob(0); write_buffer(); // use_buffer = 0; if (isdigit((unsigned char) *p)) pagenum = atoi(p); close_page(); if (s->state == ABC_S_TUNE) bskip(cfmt.topspace); return s; } break; case 'p': if (strcmp(w, "pos") == 0) { // %%pos <type> <position> p = get_str(w, p, sizeof w); set_voice_param(curvoice, s->state, w, p); return s; } if (strcmp(w, "ps") == 0 || strcmp(w, "postscript") == 0) { ps_def(s, p, 'b'); return s; } break; case 'o': if (strcmp(w, "ornament") == 0) { set_voice_param(curvoice, s->state, w, p); return s; } break; case 'r': if (strcmp(w, "repbra") == 0) { if (s->state != ABC_S_TUNE) return s; curvoice->norepbra = strchr("0FfNn", *p) || *p == '\0'; return s; } if (strcmp(w, "repeat") == 0) { int n, k; if (s->state != ABC_S_TUNE) return s; if (!curvoice->last_sym) { error(1, s, "%%%s cannot start a tune", w); return s; } if (*p == '\0') { n = 1; k = 1; } else { n = atoi(p); if (n < 1 || (curvoice->last_sym->type == BAR && n > 2)) { error(1, s, "Incorrect 1st value in %%%%%s", w); return s; } while (*p != '\0' && !isspace((unsigned char) *p)) p++; while (isspace((unsigned char) *p)) p++; if (*p == '\0') { k = 1; } else { k = atoi(p); if (k < 1) { // || (curvoice->last_sym->type == BAR // && n == 2 // && k > 1)) { error(1, s, "Incorrect 2nd value in %%%%%s", w); return s; } } } s->aux = REPEAT; if (curvoice->last_sym->type == BAR) s->doty = n; else s->doty = -n; sym_link(s, FMTCHG); s->nohdi1 = k; s->text = NULL; return s; } break; case 's': if (strcmp(w, "setbarnb") == 0) { if (s->state == ABC_S_TUNE) { struct SYMBOL *s2; int n; n = atoi(p); for (s2 = s->abc_next; s2; s2 = s2->abc_next) { if (s2->abc_type == ABC_T_BAR) { s2->aux = n; break; } } return s; } strcpy(w, "measurefirst"); break; } if (strcmp(w, "sep") == 0) { float h2, len, lwidth; if (s->state == ABC_S_TUNE) { gen_ly(0); } else if (s->state == ABC_S_GLOBAL) { if (epsf || !in_fname) return s; } lwidth = (cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth) - cfmt.leftmargin - cfmt.rightmargin; h1 = h2 = len = 0; if (*p != '\0') { h1 = scan_u(p, 0); while (*p != '\0' && !isspace((unsigned char) *p)) p++; while (isspace((unsigned char) *p)) p++; } if (*p != '\0') { h2 = scan_u(p, 0); while (*p != '\0' && !isspace((unsigned char) *p)) p++; while (isspace((unsigned char) *p)) p++; } if (*p != '\0') len = scan_u(p, 0); if (h1 < 1) h1 = 0.5 CM; if (h2 < 1) h2 = h1; if (len < 1) len = 3.0 CM; bskip(h1); a2b("%.1f %.1f sep0\n", len / cfmt.scale, (lwidth - len) * 0.5 / cfmt.scale); bskip(h2); buffer_eob(0); return s; } if (strcmp(w, "staff") == 0) { int staff; if (s->state != ABC_S_TUNE) return s; if (*p == '+') staff = curvoice->cstaff + atoi(p + 1); else if (*p == '-') staff = curvoice->cstaff - atoi(p + 1); else staff = atoi(p) - 1; if ((unsigned) staff > (unsigned) nstaff) { error(1, s, "Bad staff in %%%%%s", w); return s; } curvoice->floating = 0; curvoice->cstaff = staff; return s; } if (strcmp(w, "staffbreak") == 0) { if (s->state != ABC_S_TUNE) return s; if (isdigit(*p)) { s->xmx = scan_u(p, 0); if (s->xmx < 0) { error(1, s, "Bad value in %%%%%s", w); return s; } if (p[strlen(p) - 1] == 'f') s->doty = 1; } else { s->xmx = 0.5 CM; if (*p == 'f') s->doty = 1; } sym_link(s, STBRK); return s; } if (strcmp(w, "stafflines") == 0) { if (isdigit((unsigned char) *p)) { switch (atoi(p)) { case 0: p = "..."; break; case 1: p = "..|"; break; case 2: p = ".||"; break; case 3: p = ".|||"; break; case 4: p = "||||"; break; case 5: p = "|||||"; break; case 6: p = "||||||"; break; case 7: p = "|||||||"; break; case 8: p = "||||||||"; break; default: error(1, s, "Bad number of lines"); break; } } else { int l; l = strlen(p); q = p; p = getarena(l + 1); strcpy(p, q); } if (s->state != ABC_S_TUNE) { for (voice = 0; voice < MAXVOICE; voice++) voice_tb[voice].stafflines = p; } else { curvoice->stafflines = p; } return s; } if (strcmp(w, "staffscale") == 0) { char *q; float scale; scale = strtod(p, &q); if (scale < 0.3 || scale > 2 || (*q != '\0' && *q != ' ')) { error(1, s, "Bad value in %%%%%s", w); return s; } if (s->state != ABC_S_TUNE) { for (voice = 0; voice < MAXVOICE; voice++) voice_tb[voice].staffscale = scale; } else { curvoice->staffscale = scale; } return s; } if (strcmp(w, "staves") == 0 || strcmp(w, "score") == 0) { if (s->state == ABC_S_GLOBAL) return s; get_staves(s); return s; } if (strcmp(w, "stemdir") == 0) { set_voice_param(curvoice, s->state, w, p); return s; } if (strcmp(w, "sysstaffsep") == 0) { if (s->state != ABC_S_TUNE) break; parsys->voice[curvoice - voice_tb].sep = scan_u(p, 0); return s; } break; case 't': if (strcmp(w, "text") == 0) { int job; center: if (s->state == ABC_S_TUNE) { gen_ly(1); } else if (s->state == ABC_S_GLOBAL) { if (epsf || !in_fname) return s; } if (w[0] == 'c') { job = T_CENTER; } else { job = cfmt.textoption; switch(job) { case T_SKIP: return s; case T_LEFT: case T_RIGHT: case T_CENTER: break; default: job = T_LEFT; break; } } write_text(w, p, job); return s; } if (strcmp(w, "tablature") == 0) { struct tblt_s *tblt; int i, j; tblt = tblt_parse(p); if (tblt == 0) return s; switch (s->state) { case ABC_S_TUNE: case ABC_S_HEAD: for (i = 0; i < ncmdtblt; i++) { if (cmdtblts[i].active) continue; j = cmdtblts[i].index; if (j < 0 || tblts[j] == tblt) return s; } /* !! 2 tblts per voice !! */ if (curvoice->tblts[0] == tblt || curvoice->tblts[1] == tblt) break; if (curvoice->tblts[1]) { error(1, s, "Too many tablatures for voice %s", curvoice->id); break; } if (!curvoice->tblts[0]) curvoice->tblts[0] = tblt; else curvoice->tblts[1] = tblt; break; } return s; } if (strcmp(w, "transpose") == 0) { struct VOICE_S *p_voice; struct SYMBOL *s2; int i, val; val = get_transpose(p); switch (s->state) { case ABC_S_GLOBAL: cfmt.transpose = val; return s; case ABC_S_HEAD: { cfmt.transpose += val; for (i = MAXVOICE, p_voice = voice_tb; --i >= 0; p_voice++) { p_voice->transpose = cfmt.transpose; memcpy(&p_voice->key, &p_voice->okey, sizeof p_voice->key); key_transpose(&p_voice->key); memcpy(&p_voice->ckey, &p_voice->key, sizeof p_voice->ckey); if (p_voice->key.empty) p_voice->key.sf = 0; } return s; } } curvoice->transpose = cfmt.transpose + val; s2 = curvoice->sym; if (!s2) { memcpy(&curvoice->key, &curvoice->okey, sizeof curvoice->key); key_transpose(&curvoice->key); memcpy(&curvoice->ckey, &curvoice->key, sizeof curvoice->ckey); if (curvoice->key.empty) curvoice->key.sf = 0; return s; } for (;;) { if (s2->type == KEYSIG) break; if (s2->time == curvoice->time) { s2 = s2->prev; if (s2) continue; } s2 = s; s2->abc_type = ABC_T_INFO; s2->text = (char *) getarena(2); s2->text[0] = 'K'; s2->text[1] = '\0'; sym_link(s2, KEYSIG); // if (!curvoice->ckey.empty) // s2->aux = curvoice->ckey.sf; s2->aux = curvoice->key.sf; break; } memcpy(&s2->u.key, &curvoice->okey, sizeof s2->u.key); key_transpose(&s2->u.key); memcpy(&curvoice->ckey, &s2->u.key, sizeof curvoice->ckey); if (curvoice->key.empty) s2->u.key.sf = 0; return s; } if (strcmp(w, "tune") == 0) { struct SYMBOL *s2, *s3; struct tune_opt_s *opt, *opt2; if (s->state != ABC_S_GLOBAL) { error(1, s, "%%%%%s ignored", w); return s; } /* if void %%tune, remove all tune options */ if (*p == '\0') { opt = tune_opts; while (opt) { free_voice_opt(opt->voice_opts); opt2 = opt->next; free(opt); opt = opt2; } tune_opts = NULL; return s; } if (strcmp(p, "end") == 0) return s; /* end of previous %%tune */ /* search the end of the tune options */ s2 = s; for (;;) { s3 = s2->abc_next; if (!s3) break; if (s3->abc_type != ABC_T_NULL && (s3->abc_type != ABC_T_PSCOM || strncmp(&s3->text[2], "tune ", 5) == 0)) break; s2 = s3; } /* search if already a same %%tune */ opt2 = NULL; for (opt = tune_opts; opt; opt = opt->next) { if (strcmp(opt->s->text, s->text) == 0) break; opt2 = opt; } if (opt) { free_voice_opt(opt->voice_opts); if (s2 == s) { /* no option */ if (!opt2) tune_opts = opt->next; else opt2->next = opt->next; free(opt); return s; } opt->voice_opts = NULL; } else { if (s2 == s) /* no option */ return s; opt = malloc(sizeof *opt); memset(opt, 0, sizeof *opt); opt->next = tune_opts; tune_opts = opt; } /* link the options */ opt->s = s3 = s; cur_tune_opts = opt; s = s->abc_next; for (;;) { if (s->abc_type != ABC_T_PSCOM) continue; if (strncmp(&s->text[2], "voice ", 6) == 0) { s = process_pscomment(s); } else { s->state = ABC_S_HEAD; /* !! no reverse link !! */ s3->next = s; s3 = s; } if (s == s2) break; s = s->abc_next; } cur_tune_opts = NULL; return s; } break; case 'u': if (strcmp(w, "user") == 0) { deco[s->u.user.symbol] = parse.deco_tb[s->u.user.value - 128]; return s; } break; case 'v': if (strcmp(w, "vocal") == 0) { set_voice_param(curvoice, s->state, w, p); return s; } if (strcmp(w, "voice") == 0) { struct SYMBOL *s2, *s3; struct voice_opt_s *opt, *opt2; if (s->state != ABC_S_GLOBAL) { error(1, s, "%%%%voice ignored"); return s; } /* if void %%voice, free all voice options */ if (*p == '\0') { if (cur_tune_opts) { free_voice_opt(cur_tune_opts->voice_opts); cur_tune_opts->voice_opts = NULL; } else { free_voice_opt(voice_opts); voice_opts = NULL; } return s; } if (strcmp(p, "end") == 0) return s; /* end of previous %%voice */ if (cur_tune_opts) opt = cur_tune_opts->voice_opts; else opt = voice_opts; /* search the end of the voice options */ s2 = s; for (;;) { s3 = s2->abc_next; if (!s3) break; if (s3->abc_type != ABC_T_NULL && (s3->abc_type != ABC_T_PSCOM || strncmp(&s3->text[2], "score ", 6) == 0 || strncmp(&s3->text[2], "staves ", 7) == 0 || strncmp(&s3->text[2], "tune ", 5) == 0 || strncmp(&s3->text[2], "voice ", 6) == 0)) break; s2 = s3; } /* if already the same %%voice * remove the options */ opt2 = NULL; for ( ; opt; opt = opt->next) { if (strcmp(opt->s->text, s->text) == 0) { if (!opt2) { if (cur_tune_opts) cur_tune_opts->voice_opts = NULL; else voice_opts = NULL; } else { opt2->next = opt->next; } free(opt); break; } opt2 = opt; } if (s2 == s) /* no option */ return s; opt = malloc(sizeof *opt + strlen(p)); memset(opt, 0, sizeof *opt); if (cur_tune_opts) { opt->next = cur_tune_opts->voice_opts; cur_tune_opts->voice_opts = opt; } else { opt->next = voice_opts; voice_opts = opt; } /* link the options */ opt->s = s3 = s; for ( ; s != s2; s = s->abc_next) { if (s->abc_next->abc_type != ABC_T_PSCOM) continue; s->abc_next->state = ABC_S_TUNE; s3->next = s->abc_next; s3 = s3->next; } return s; } if (strcmp(w, "voicecolor") == 0) { int color; if (!curvoice) return s; color = get_color(p); if (color < 0) error(1, s, "Bad color in %%%%voicecolor"); else curvoice->color = color; return s; } if (strcmp(w, "voicecombine") == 0) { int combine; if (sscanf(p, "%d", &combine) != 1) { error(1, s, "Bad value in %%%%voicecombine"); return s; } switch (s->state) { case ABC_S_GLOBAL: cfmt.combinevoices = combine; break; case ABC_S_HEAD: for (voice = 0; voice < MAXVOICE; voice++) voice_tb[voice].combine = combine; break; default: curvoice->combine = combine; break; } return s; } if (strcmp(w, "voicemap") == 0) { if (s->state != ABC_S_TUNE) { for (voice = 0; voice < MAXVOICE; voice++) voice_tb[voice].map_name = p; } else { curvoice->map_name = p; } return s; } if (strcmp(w, "voicescale") == 0) { char *q; float scale; scale = strtod(p, &q); if (scale < 0.6 || scale > 1.5 || (*q != '\0' && *q != ' ')) { error(1, s, "Bad %%%%voicescale value"); return s; } if (s->state != ABC_S_TUNE) { for (voice = 0; voice < MAXVOICE; voice++) voice_tb[voice].scale = scale; } else { curvoice->scale = scale; } return s; } if (strcmp(w, "volume") == 0) { set_voice_param(curvoice, s->state, w, p); return s; } if (strcmp(w, "vskip") == 0) { if (s->state == ABC_S_TUNE) { gen_ly(0); } else if (s->state == ABC_S_GLOBAL) { if (epsf || !in_fname) return s; } bskip(scan_u(p, 0)); buffer_eob(0); return s; } break; } if (s->state == ABC_S_TUNE) { if (strcmp(w, "leftmargin") == 0 || strcmp(w, "rightmargin") == 0 || strcmp(w, "scale") == 0) { generate(); block_put(); } } interpret_fmt_line(w, p, lock); if (cfmt.alignbars && strcmp(w, "alignbars") == 0) { int i; generate(); if ((unsigned) cfmt.alignbars > MAXSTAFF) { error(1, s, "Too big value in %%%%alignbars"); cfmt.alignbars = MAXSTAFF; } if (staves_found >= 0) /* (compatibility) */ cfmt.alignbars = nstaff + 1; first_voice = curvoice = voice_tb; for (i = 0; i < cfmt.alignbars; i++) { voice_tb[i].staff = voice_tb[i].cstaff = i; voice_tb[i].next = &voice_tb[i + 1]; parsys->staff[i].flags |= STOP_BAR; parsys->voice[i].staff = i; parsys->voice[i].range = i; } i--; voice_tb[i].next = NULL; parsys->nstaff = nstaff = i; } return s; } /* -- set the duration of notes/rests in a tuplet -- */ /*fixme: KO if voice change*/ /*fixme: KO if in a grace sequence*/ static void set_tuplet(struct SYMBOL *t) { struct SYMBOL *s, *s1; int l, r, lplet, grace; r = t->u.tuplet.r_plet; grace = t->flags & ABC_F_GRACE; l = 0; for (s = t->abc_next; s; s = s->abc_next) { if (s->abc_type == ABC_T_TUPLET) { struct SYMBOL *s2; int l2, r2; r2 = s->u.tuplet.r_plet; l2 = 0; for (s2 = s->abc_next; s2; s2 = s2->abc_next) { switch (s2->abc_type) { case ABC_T_NOTE: case ABC_T_REST: break; case ABC_T_EOLN: if (s2->u.eoln.type != 1) { error(1, t, "End of line found inside a nested tuplet"); return; } continue; default: continue; } if (s2->u.note.notes[0].len == 0) continue; if (grace ^ (s2->flags & ABC_F_GRACE)) continue; s1 = s2; l2 += s1->dur; if (--r2 <= 0) break; } l2 = l2 * s->u.tuplet.q_plet / s->u.tuplet.p_plet; s->aux = l2; l += l2; r -= s->u.tuplet.r_plet; if (r == 0) break; if (r < 0) { error(1, t, "Bad nested tuplet"); break; } s = s2; continue; } switch (s->abc_type) { case ABC_T_NOTE: case ABC_T_REST: break; case ABC_T_EOLN: if (s->u.eoln.type != 1) { error(1, t, "End of line found inside a tuplet"); return; } continue; default: continue; } if (s->u.note.notes[0].len == 0) /* space ('y') */ continue; if (grace ^ (s->flags & ABC_F_GRACE)) continue; s1 = s; l += s->dur; if (--r <= 0) break; } if (!s) { error(1, t, "End of tune found inside a tuplet"); return; } if (t->aux != 0) /* if nested tuplet */ lplet = t->aux; else lplet = (l * t->u.tuplet.q_plet) / t->u.tuplet.p_plet; r = t->u.tuplet.r_plet; for (s = t->abc_next; s; s = s->abc_next) { int olddur; if (s->abc_type == ABC_T_TUPLET) { int r2; r2 = s->u.tuplet.r_plet; s1 = s; olddur = s->aux; s1->aux = (olddur * lplet) / l; l -= olddur; lplet -= s1->aux; r -= r2; for (;;) { s = s->abc_next; if (s->abc_type != ABC_T_NOTE && s->abc_type != ABC_T_REST) continue; if (s->u.note.notes[0].len == 0) continue; if (grace ^ (s->flags & ABC_F_GRACE)) continue; if (--r2 <= 0) break; } if (r <= 0) goto done; continue; } if (s->abc_type != ABC_T_NOTE && s->abc_type != ABC_T_REST) continue; if (s->u.note.notes[0].len == 0) continue; s->sflags |= S_IN_TUPLET; if (grace ^ (s->flags & ABC_F_GRACE)) continue; s1 = s; olddur = s->dur; s1->dur = (olddur * lplet) / l; if (--r <= 0) break; l -= olddur; lplet -= s1->dur; } done: if (grace) { error(1, t, "Tuplets in grace note sequence not yet treated"); } else { sym_link(t, TUPLET); t->aux = cfmt.tuplets; } } ����������������������������������abcm2ps-8.14.11/sample.abc��������������������������������������������������������������������������0000664�0000000�0000000�00000006663�13762665467�0015206�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������% Sample file to test various features of abc2ps U: M = !tenuto! % abcm2ps: default M is lowermordent % abcm2ps >= 7.6.0 - definition of '...' (ellipsis) for PostScript %%glyph 2026 ellipsis X:1 T:Scale T:Second Title T:Third Title M:C %K:C % abcm2ps: automatic clef processing gives a bass clef K:C treble L: 1/4 "C,"C,"D,"D,"E,"E,"F,"F, "G,"G,"A,"A,"B,"B,\ | "C"C"D"D"E"E"F"F "G"G"A"A"B"B| "c"c "d"d"e"e"f"f "g"g"a"a"b"b"c'"c' | X:3 T:Short and Long Notes, Beams and Tuplets C:Trad M:C K:C L: 1/8 c8| d4 e2 fg | C c C/ c/ d/e/ d// C// C/D/E/F/ | d>e d>>e e<fe (3CDE (4fgab | X:4 T:Key signature, Accidentals and Decorations M:C K:A L: 1/4 ^C=C_C ^G=G_G | .F/.G/.A/ Ma/Mg/Mf/ Jc J^c J[c^f] J[c^g] |\ {f}e {C}D {cd}c {E^c}a2 {dedc}d| uc vc'-c'/Mb/Mc'/Ma/ | (b4 | b/).a/.b/.c'/ | ~A ~g \ % RA Rg MA Mg | .A .g vf/-g/ (u.a/.b/) | uR~M.c2 Hg4 || % abcm2ps: '-' is not a slur RA Rg MA Mg | .A .g (vf/g/) (u.a/.b/) | uR~M.c2 Hg4 || X:5 T:Bars and Rests M:6/8 L: 1/4 K:D [| z4 |] z2 |: z z/z// :| z2> :: z2 z>z | f>z g>z || X:6 T:Chords, Unions, First and Second Endings M:9/8 K:D L: 1/4 %|: [D2FA] ~+d2fa+ | [c2e][df][eg]|\ % abcm2ps: '+' is no more handled |: [D2F2A2] ~[d2f2a2] | [c2e2][df][eg]|\ [cc] [dd] [F/A][G/B][D/F]>[C/E] [G/e][A/e] [G/e]>.[A/e][B/e]>.[c/e]\ |1 (3[A//df][A//ef][A//cg] [G3Ce] :|2 (3[A//=df][^A//dg][_A//_c^g] [e3gc] |] X:7 T:Slurs and Ties T: Title with funny chars like \005 Çéñô Àçäßö © … M:C| K:Ebm [| (CDEF) ((3efg) ((3gag)| (C2 EF) (ef(ga)) | ((c2 (3(d)ef) e2)\ A2-|A4 c4-|(c4(e4)|a8) |] X:8 T:Changing Time or Key Signatures, Guitar Chords M: 6/8 K: G "Em"ABc def |\ M: 9/8 % abcm2ps: '\' is no more handled %"Am7"A,CC DFF GBB |\M:4/4\"G"a,2b,2"D"c2d2 ||\L:1/4\\K:Bb\"Eb"e4| "Am7"A,CC DFF GBB |[M:4/4]"G"a,2b,2"D"c2d2 ||[L:1/4][K:Bb]"Eb"e4-| K: Gb M: 6/8 L:1/8 % abcm2ps: basic length is not reset on M: %| -"Gb"ede edc | def fed |1 "A"efg "D"gfe | e6 \ % abcm2ps: cannot have '-' before a note | "Gb"ede edc | def fed |1 "A"efg "D"gfe | e6 \ :|2 "G"gag "F"f"Em"e"D7"d | "C"c6 |] X:9 T:Strange tuplet cases M:C K:C L: 1/8 (3cde c(3d/e/f/ |(3zcd (3z/c/d/ (3czc c(4d/e/f/z/ d2-(3def | \ (3::2 c4d2 (3::4 cde/f/ (3gfe | (3z2G,2A,2 (3C2E2G2 |e-(5e//f//g//f//g// de- (5e//f//g//f//g// |\ (6z/c/e/g/e/c/ (6z/c/e/g/e/c/ | (3d/e/f/g-(3g/f/e/d || X:10 T:Chords with many accidentals M: 6/8 K: G [^c^d] [^c^e] [^c^f] [^c^g] [^c^a] [^c^b] |\ [^C^D] [^C^E] [^C^F] [^C^G] [^C^A] [^C^B] |\ [^c^d^e] [^c^d^f] [^c^f^g] [^c^f^a] [^c=d=f^g_a_b] |] [^c^f^a] [^c^f^b] [^c^f^c'] [^c^f^d'] [^c^f^e'] |\ [^c^e^f] [^c^f^g] [^c^g^a] [^c^g^b] |\ [^c^d^c'] [^c^e^c'] [^A^e^c'] [^G^e^c'] \ [^c_e^g][^c^f^g][^c^g^a][^c^d^g][^G^e^c'] || X:11 T:Horizontal beams M:9/8 K:C L: 1/8 c,d,c, d,e,d, e,f,e, b,cb, | c,/d,/c,/ d,/e,/d,/ e,/f,/e,/ |\ c,//d,//c,// d,//e,//d,// e,//f,//e,// | cdc ded efe b,a,b,| c/d/c/ d/e/d/ e/f/e/ | c//d//c// d//e//d// e//f//e// | c'd'c' d'e'd' e'f'e' f'g'f' | c'/d'/c'/ d'/e'/d'/ e'/f'/e'/ |\ c'//d'//c'// d'//e'//d'// e'//f'//e'// | X:12 T:Gracenotes L:1/8 M:C K:D FA{c}AF DF{^dc}A f{A}df f{AGA}df |{B}D2 {A}D2 {G}D2 {F}D2 {E}D2 |\ {E}c2 {F}c2 {G}c2 {A}c2 {B}c2 | {A}^c2 {gcd}c2 {gAGAG}A2{g}c<{GdG}e {Gdc}d>c {gBd}B<{e}G |\ {G}[G4e4] {FGAB}[^c4A4] {ef}[e4c4] {d'c'bagfedcB_AcBFGC}D4| X: 13 T: Vocals M: C| K: F L: 1/4 BA |: "Gm"G2AB|"C7"cd2e|"F"f2fe|"Dm"dA2d| w: Close your eyes and I'll kiss you, to-mor-row I'll miss you; re- "Bb"d2dc|"Gm"B2GF|"Eb"G4-|G2 z2| w:mem-ber I'll al-ways be true._ �����������������������������������������������������������������������������abcm2ps-8.14.11/sample2.abc�������������������������������������������������������������������������0000664�0000000�0000000�00000005716�13762665467�0015266�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������% Sample file to test various features of abcm2ps %%footer abcm2ps - sample2 U: N = !tenuto! X:1 T:All clefs M:C L:1/4 K:C bass "^bass"G,CEG|[K:bass3]"^bass3"G,CEG|[K:alto4]"^alto4"G,CEG|[K:alto]"^alto"G,CEG| [K:alto2]"^alto2"G,CEG|[K:alto1]"^alto1"G,CEG|[K:treble]"^treble"G,CEG| [K:treble1]"^treble1"G,CEG|[K:treble-8]"^treble-8"G,CEG|[K:treble+8]"^treble+8"G,CEG| X:2 T:Key signature change T:and multi-measure rest M:2 L:1/4 K:C Z4|"C"CEGc|[K:A]"A"Acea|[K:B]"B"Bdfb|[K:A]"A"Acea| [K:Eb]"Eb"EGBe|[K:Cb]"Cb"CEGc|[K:C]"C"CEGc| X:3 T:All clefs with max signatures M:C L:1/4 K:C# bass C,E,G,C|[K:Cb]C,E,G,C|[K:C# bass3]C,E,G,C| [K:Cb]C,E,G,C|[K:C# alto4]G,CEG|[K:Cb]G,CEG| [K:C# alto]G,CEG|[K:Cb]G,CEG|[K:C# alto2]CEGc| [K:Cb]CEGc|[K:C# alto1]CEGc|[K:Cb]CEGc| [K:C# treble]CEGc|[K:Cb]CEGc|[K:C]CEGc| X:4 T:Guitar chords - annotations M:none L:1/4 K:C "^no time""^signature"CD"gchord""^on bar"|EF\ "^appogiattura"{B}c "^acciaccatura"{/B}c \ "^three;annot;lines"G "^and""^four""^annot""^lines!"c| \ "^Fa#"^F "^Sib"_B "^Fa="=F \ "F#"^F "Bb"_B|| X:5 T:Standard decorations M:none L:1/8 K:C ~C.D JENF HCRD TEuF vcLB MAPG ScOB| w: \~ . J N H R T u v L M P S O w: grace dot slide tenuto fermata roll trill upbow downbow \ w: emphasis lmordent umordent segno coda X:6 T:All decorations M:none L:1/8 K:C !0!C!1!D !2!E!3!F !4!G!5!A !+!B!accent!c|\ w:~0 ~1 ~2 ~3 ~4 ~5 ~+ accent !breath!C!crescendo(!D !crescendo)!E!D.C.!F !diminuendo(!G!diminuendo)!A !f!B!ffff!c| w:breath crescendo( crescendo) D.C. diminuendo( diminuendo) ~f ffff !fine!C!invertedfermata!D !longphrase!E !mediumphrase!F !mf!G!open!A !p!B!pppp!c| w:fine invertedfermata longphrase mediumphrase mf open ~p pppp !pralltriller!C!sfz!D !shortphrase!E !snap!F !thumb!G!turn!A!wedge!B!D.S.!c| w:pralltriller sfz shortphrase snap thumb turn wedge D.S. X:7 T:Non standard decorations C:Composer O:Origin R:Rhythm M:none L:1/8 K:C !turnx!G!invertedturn!A !invertedturnx!B !arpeggio![EGc]|\ w:turnx invertedturn invertedturnx arpeggio !trill(!c4-|!trill)!c3| w:trill( trill) X:8 T:Decorations on two voices T:(also in 'd:' lines) %%infoline 1 C:Composer O:Origin R:Rhythm M:C %%staves (1 2) K:C V:1 ~c.dJeNf cdef|aabc' gabc'|!coda!cdef gfec|| d: * * * * HRTu|!mf! |!sfz! *** ***!D.S.! V:2 CDEF CDEF|ffga efga|C D EF [EG]FEC|| d: ~.JN HRTu|~.JN HRTu|!5!!4!M* !5! M d: | |* P !3! !4! X:9 T:Beams L:1/16 M:4/4 K:C (3CDE(3FGA B/c/d/e/d/c/B/A/ (3zDE(3FGz z/c/d/e/d/c/B/z/|(3CDz(3zGA B/c/d/z/z/c/B/A/ G8| X:10 T:Voice overlap T:invisible and dashed bars M:2/4 L:1/8 %%staves (1 2) K:C V:1 FEDC:GGGG|G2 G2|c4[|]GABc| V:2 GABc:FEDC|GD G>D|cBAG[|]G4| X:11 T:Clef transpositions M:C L:1/4 K:C %%titleleft 1 T:No transposition "^clef=treble""A,"A,"B,"B,"C"C"D"D|\ [K:alto]"^alto""A,"A,"B,"B,"C"C"D"D|\ [K:bass]"^bass""A,"A,"B,"B,"C"C"D"D| T:abc2ps compatible clef transposition %%abc2pscompat 1 [K:treble]"^treble""A,"A,"B,"B,"C"C"D"D|\ [K:alto]"^alto""A"A"B"B"c"c"d"d|\ [K:bass]"^bass""a"a"b"b"c'"c'"d'"d'| %%titleleft 0 ��������������������������������������������������abcm2ps-8.14.11/sample3.abc�������������������������������������������������������������������������0000664�0000000�0000000�00000004227�13762665467�0015263�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������%%deco alcoda 3 dacs 20 0 0 Al Coda %%header " $T\n Page $P" %%footer " $F" X:1 T:abcm2ps version 3 features M:2/4 3/4 K:C C2D2 E2F2G2{FG}|G4-G6|1.2[M:5/4 (2/4 3/4)]G4{AB}z6:|["last time"A2B2 c6|| %%multicol start %%rightmargin 11.5cm [M:2/4]C2G2|C4| %%multicol new %%leftmargin 11.5cm %%begintext %% Multi column text on the right after a blank line. %%endtext %%multicol end %%multicol start %%rightmargin 11.5cm %%begintext And now at the left side. Here, there are spaces to align the text on the right. %%endtext %%multicol new %%leftmargin 11.5cm CD EF|GA Bc| %%multicol end % %%center Scale change inside the tune %%scale 0.6 %%leftmargin 7cm %%rightmargin 11cm CD EF| %%scale 1.2 %%rightmargin 4cm % must set the right margin before the left margin %%leftmargin 11cm CD EF| %%leftmargin 5cm %%scale 1 %%EPS sample3.eps %%scale 0.75 % set the scale back to default % restore the left and right margins %%leftmargin 1.78cm %%rightmargin 1.78cm %%center Is this centered? X:2 T:Staff break M:none L:1/8 K:G exp ^F ^F, clef=C %%staffbreak 0.2cm [K:G clef=treble][M:C]SDE FG|AG !alcoda!FEO|DE FES||\ %%staffbreak 2cm [K:G clef=treble]OC2D2|| X:3 T:Staff break multi-staves M:none L:1/8 %%staves {1 2} K:G V:1 [K:G exp ^F ^F, clef=C] %%staffbreak 0.2cm V:2 bass %%staffbreak 0.2cm V:1 [K:G clef=treble][M:C]SDE FG|AG !alcoda!FEO|DE FES||\ V:2 [K:G clef=bass][M:C]D,4|A,4|D,4||\ V:1 %%staffbreak 2cm [K:G clef=treble]OC2D2|| V:2 %%staffbreak 2cm [K:G clef=bass]A,,2D,,2|| X:4 T:Voice overlay and chords M:C L:1/4 K:C [C2G]A[B3/G2]c/|cdef&ABcd|GA{[FA]}[GB][Ac]|defg&Bcde| GABc|efga&FGAB&CDEF|GA(&Bc|de&FG|AB&)cd| X:5 T:Annotations and guitar chords M:C L:1/8 %%gchordbox 1 K:C "^annotation""gchord in box"CD"_below"EG "<left"cd">right"ec|\ "@20,-50anywhere"gf"<("">)"ed (c4y)|| X:6 T:Grace notes M:C L:1/4 %%graceslurs 0 K:C V:1 name="voice name\non\nthree lines" {B2}A{B}A{B/}A{B//}A|{c4B4}A{c2B2}A{cB}A{c/B/}A| {a2g2f2 e2d2c2B4}A4|\ {(AB)}c{(AB}c){(GA)B}c{G(AB}c)|\ (c2{BA)G}{(G/A/B/) (G/A/B/)}c2|| X:7 T:Drum M:C L:1/8 %%staves (1 2) K:C clef=perc V:1 ^a^f[c^f]^f ^f^f[c^f]^f|^a^f[c^f]^f ^f/c/^f c/^a3/|| V:2 FF/F/ z3/F/ zF/F/ z/F3/|FF/F/ z3/F/ z3/F/ z/F3/|| �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������abcm2ps-8.14.11/sample3.eps�������������������������������������������������������������������������0000664�0000000�0000000�00000000301�13762665467�0015312�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������%!PS-Adobe-3.0 EPSF-3.0 %%BoundingBox: 0 0 50 40 %%Pages: 0 %%EndProlog 0 0 moveto 25 40 rlineto 25 -40 rlineto -50 0 rlineto fill 1.0 setgray /Times-Roman 14 selectfont 13 8 moveto (EPS) show �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������abcm2ps-8.14.11/sample4.abc�������������������������������������������������������������������������0000664�0000000�0000000�00000005751�13762665467�0015267�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������% sample4.abc - this file is part of abcm2ps % definitions for tablature %%beginps /hn{ 1 1 1 setrgbcolor 1 SLW 1 index 2 sub 1 index M 8 0 RL stroke 0 0 0 setrgbcolor 3 sub M /Helvetica-Bold 8 selectfont show}! %%endps %%deco head-3 3 hn 0 0 0 3 %%deco head-4 3 hn 0 0 0 4 %%deco head-5 3 hn 0 0 0 5 U:i=!invisible! U:j=!head-3! U:k=!head-4! U:l=!head-5! X:1 %%abc-version 2.0 T:ABC-2.0 new fea\ % strange continuation line tures M:C L:1/4 %%measurenb 1 K:C CDEF | GABc | FEDC [I:setbarnb 10] | [CEG]4 |] s:"^annotation;in 's:' line"**+1+|||H| X:2 T:abcm2ps-4.x.x new features %%graceslurs 0 %%breakoneoln 0 % needed with abcm2ps-5.x.x M:C L:1/4 Q:1/4=ca. 80 K:none "^slur above.."('FG) "^..below"(,FG)|\ "^dashed tie.."A.-A "^..slur".(ce/)d/"^beam;crossing;a bar"!beamon!|\ c/B/ "^chord slur"{(,G}[CA)]3|| "^standard tuplet"(3A/A/A/ "^no bracket"[I:tuplets 1 0 0](3A/A/A/ \ "^forced bracket"[I:tuplets 2 0 0](3A/A/A/ "^ratio"[I:tuplets 0 0 2](3A/A/A/| \ "^nested tuplets"[I:tuplets 2 0 2](7:8:8(3A/A/A/ A/A/A/A/A/| "^1/4 tone;sharp"^/G ^G "^3/4 tone;sharp"^3/2G ^^G |\ "^1/4 tone;flat"_/G _G "^3/4 tone;flat"_3/2G __G|"^deco on;grace note"{.A4.B4}c4|| % tremolo: note the position of the decorations and the note lengths "^tremolo"C/c/C/c/ C!trem1!c | C2!trem1!c2 | \ C//[Gc]//C//[Gc]// C//[Gc]//C//[Gc]// C!trem2![Gc] | C2!trem2![Gc]2 || % C///D///E///F///G///F///E///D///\ C///D///E///F///G///F///E///D/// \ "^beam break"C///D///E///F///!beambr2!G///F///E///D///\ !beambr1!C///D///E///F///!beambr2!G///F///E///D///|| %%staves 1 2 %%rightmargin 12cm V:1 "^cross staff stems"cc//c//c//c//c2 | [CEGc]2[CEGc]2|| V:2 bass !xstem!C,C,//!xstem!C,//C,//!xstem!C,//!xstem!C,2 | !xstem![C,,C,]2[C,,C,]2|| X:3 T:Yesterday T:(extract) M:4/4 L:1/8 %%staves (melody1 chords melody2) K:F V:melody1 %%stemdir down e3/d/ d6 | z2 d/d3/ cB/A/- AG | w: far a-way, | Now it looks as though_ they're | V:chords "Dm"x4 "Dm/C"x4 | "Bb"x4 "C7"x4 | V:melody2 %%stemdir up %%voicescale 0.8 e3/d/ d6 | x2 d/dc/.- c/B3/ AG | w: used to be, | There's a shad-_ow hang-ing | X:4 T:Fantaisie C:Camille Saint-Saëns M:C L:1/8 %%staves [recit GO pos ped] K:Eb V:recit name="Récit" z8|z8|z8| V:GO treble name="Grand\nOrgue" z2[G,B,E][I:staff 3][G,B,E] \ [I:staff 2][A,CF][I:staff 3][A,CF] [I:staff 2][CEA][I:staff 3][CEA]| \ [I:staff 2][A,CF][I:staff 3][A,CF] [I:staff 2][B,DG][I:staff 3][B,DG] \ [I:staff 2][CEA][I:staff 3][CEA] [I:staff 2][DAc][I:staff 3][DAc]| \ [I:staff 2][EGB][I:staff 3][EGB] [I:staff 2][GBe][I:staff 3][GBe] \ [I:staff 2][EGB][I:staff 3][EGB] [I:staff 2][DFA][I:staff 3][DFA]| V:pos treble name=Positif z2x6|x8|x8| V:ped name="Pédale" E,,8-|E,,8-|E,,2G,,2B,,2=B,,2| X:5 T:Special staves M:9/8 L:1/8 %%staves [1 2] | (3 4) K:Cm V:1 stem=up [CGceg]6[C-,G-,ceg]3-|[CGceg]3[CGceg]6| % tablature V:2 clef=none %%stafflines 6 %%staffscale 1.6 %%staffnonote 2 [K:none][M:none]i[jGlBldkfja]6i[jGlBldkfja]3|iA3i[jGlBldkfja]6| % percussion V:3 clef=perc %%stafflines 1 [K:none]^c3^c3^c^c^c|^c3^c3^c^c^c| V:4 clef=perc stafflines=1 [K:none]A3A3A3|A6A3| �����������������������abcm2ps-8.14.11/sample5.abc�������������������������������������������������������������������������0000664�0000000�0000000�00000003645�13762665467�0015270�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������% sample5.abc - this file is part of abcm2ps %%writefields NF 1 %%infoname F "This file is part of the " F:abcm2ps sources at http://moinejf.free.fr/ X:1 %%titleformat T,TTQ-1C1A1 T:New features in abcm2ps-5.x.x T:Subtitle one T:Subtitle two C:This is the composer field A:This is the Area/Author field N:Here are some notes (N:) N:on two lines M:c. C=4/4 L:1/4 Q:"Tempo in the tune header" K:C Q:"Tempo in the tune body" "C"C"Dm G"D2"C"C"^<- guitar chords between symbols"|\ C//E//G//E// \ [I:repeat 4 3]C//E//G//E// "^sequence repeat"C//E//G//E// C//E//G//E//| "^measure repeat"GABc|[I:repeat 1 2]GABc|GABc|\ G2d2|B2d2|[I:repeat 2]G2d2|B2d2|c4|| "^tremolo 2 notes"\ z/ C//!trem1!c// C/!trem1!c/ C!trem1!c | C2!trem1!c2 | \ z/ C//!trem2!c// C/!trem2!c/ C!trem2!c | C2!trem2!c2 | "^tremolo 1 note"\ z C//!/!c// c//!/!C// C/!/!c/ c/!/!C/ | \ z C//!//!c// c//!//!C// C/!//!c/ c/!//!C/ | z !/!C/ !/!c/ !/!C !/!c | !/!C2 !/!c2 | \ z !//!C/ !//!c/ !//!C !//!c | !//!C2 !//!c2 || !/!C4 | !/!c4 | !//!C4 | !//!c4 | %%writefields F 0 X:2 T:Romance sans paroles C:F.B. Mendelssohn M:12/8 L:1/8 K:Eb %%staves {M A1 A2} V:M % Melody x6z2z(B3|\ V:A1 %%voicescale 0.8 z(G,[B,E]) z(G,[B,E]) z(G,[B,E]) z(G,[B,E])|\ V:A2 %%staffscale 0.8 E,,3G,,3B,,3G,,3|\ %%staves M V:M A2GF2EE3)E3|(E3c6)(B2A|G3F3B3F3)| X:3 T:Fugue II T:(extract from the well-tempered clavichord II) C:J.S. Bach M:none L:1/8 K:Cm %%rightmargin 0.5cm %%leftmargin 0.5cm %%staves 1s 3s | {(1 2) 3} V:1s name="Ossia" %%staffscale 0.8 V:3s %%staffscale 0.8 %%clef bass V:1 gc fe/d/ e>e d=e\ V:2 B_A/G/ c/[I:staff +1]C/[I:staff -1]G- G/G/=A B/cD/\ V:3 B,/G,/ C=A,=B, CF, _B,_A,/G,/\ %%staves (1s 2) 3s | {(1 2) 3} V:1s |fB _e_d/c/ dG\ V:3s |_A,_D- D/C/F/E/ D/C/D-\ V:1 |fB _ed/c/ dG\ V:2 |z/c/B/_A/ G=A BG\ V:3 |=A,=D- D/C/F/E/ D/C/_D-\ %%staves 1s 3s | {(1 2) (3 4)} V:1 e2-|e/e/d- d/c/=B cg de/f/| V:2 _AG/F/|G>F ED CeA2| V:3 D/C/B,/_A,/|B,=B,CD G,/C/B,/C/- C/B,/C-| V:4 x2|z2G,2E,2F,2| �������������������������������������������������������������������������������������������abcm2ps-8.14.11/sample8.html������������������������������������������������������������������������0000664�0000000�0000000�00000002030�13762665467�0015475�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>ABC HTML formatting example

ABC in HTML formatting example

This file contains 3 ABC sequences:

  • a normal ABC tune
  • a ABC tune in a textline
  • 2 tunes selected from an external ABC file (voice.abc)

To generate the music, do:

	abcm2ps sample8.html -z -O Out.html
and this will create the file Out.html.

X:1 T:An durzunell %%infoline 1 C:Trad. O:Brittany M:6/8 K:Bb B,2DF2F|FB GF2z|DGFC2D|B,3-B,2z|B,2DF2F|FB GF2z|DGFC2D|B,3-B,2z|| B,2CD2D|DE DC2z|DEFG2F|C3-C2z|B,2DF2F|FB GF2z|DGFC2D|B,6||

Beginning of 'Au clair de la lune' X:2 %%bgcolor lightblue %%musicspace 0 M:C L:auto %%pagewidth 14cm K:C cccd|ed|cedd|c||

%abc2.1 %%select 2,4 %%abc-include voices.abc
abcm2ps-8.14.11/subs.c000066400000000000000000001165351376266546700143760ustar00rootroot00000000000000/* * Low-level utilities. * * This file is part of abcm2ps. * * Copyright (C) 1998-2019 Jean-François Moine (http://moinejf.free.fr) * Adapted from abc2ps, Copyright (C) 1996-1998 Michael Methfessel * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. */ #include #include #include #include #include #include #ifdef HAVE_PANGO #include #include #endif #include "abcm2ps.h" char tex_buf[TEX_BUF_SZ]; /* result of tex_str() */ int outft = -1; /* last font in the output file */ static int stropx; /* index current string output operation */ static float strlw; /* line width */ static int curft; /* current (wanted) font */ static int defft; /* default font */ static char strtx; /* PostScript text outputing (bits) */ #define TX_STR 1 /* string started */ #define TX_ARR 2 /* glyph/string array started */ /* width of characters according to the encoding */ /* these are the widths for Times-Roman, extracted from the 'a2ps' package */ /*fixme-hack: set 500 to control characters for utf-8*/ static short cw_tb[] = { 500,500,500,500,500,500,500,500, // 00 500,500,500,500,500,500,500,500, 500,500,500,500,500,500,500,500, // 10 500,500,500,500,500,500,500,500, 250,333,408,500,500,833,778,333, // 20 333,333,500,564,250,564,250,278, 500,500,500,500,500,500,500,500, // 30 500,500,278,278,564,564,564,444, 921,722,667,667,722,611,556,722, // 40 722,333,389,722,611,889,722,722, 556,722,667,556,611,722,722,944, // 50 722,722,611,333,278,333,469,500, 333,444,500,444,500,444,333,500, // 60 500,278,278,500,278,778,500,500, 500,500,333,389,278,500,500,722, // 70 500,500,444,480,200,480,541,500, }; static struct u_ps { struct u_ps *next; char text[2]; } *user_ps; /* -- print message for internal error and maybe stop -- */ void bug(char *msg, int fatal) { error(1, NULL, "Internal error: %s.", msg); if (fatal) { fprintf(stderr, "Emergency stop.\n\n"); exit(EXIT_FAILURE); } fprintf(stderr, "Trying to continue...\n"); } /* -- print an error message -- */ void error(int sev, /* 0: warning, 1: error */ struct SYMBOL *s, char *fmt, ...) { va_list args; if (s) { if (s->fn) fprintf(stderr, "%s:%d:%d: ", s->fn, s->linenum, s->colnum); s->flags |= ABC_F_ERROR; } fprintf(stderr, sev == 0 ? "warning: " : "error: "); va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, "\n"); if (sev > severity) severity = sev; } /* -- capitalize a string -- */ static void cap_str(char *p) { while (*p != '\0') { #if 1 /* pb with toupper - works with ASCII and some latin characters only */ unsigned char c; c = (unsigned char) *p; if (c >= 'a' && c <= 'z') { *p = c & ~0x20; } else if (c == 0xc3) { p++; c = *p; if (c >= 0xa0 && c <= 0xbe && c != 0xb7) *p = c & ~0x20; } else if (c == 0xc4) { p++; c = *p; if (c >= 0x81 && c <= 0xb7 && (c & 0x01)) (*p)--; } #else *p = toupper((unsigned char) *p); #endif p++; } } /* -- return the character width -- */ float cwid(unsigned char c) { if (c >= 0x80) { if (c < 0xc0) return 0; // not start of utf8 character c = 'a'; } return (float) cw_tb[c] / 1000.; } /* -- change string taking care of some tex-style codes -- */ /* Return an estimated width of the string. */ float tex_str(char *s) { char *d, *p; unsigned char c1; unsigned maxlen, i; float w, swfac; w = 0; d = tex_buf; maxlen = sizeof tex_buf - 1; /* have room for EOS */ if ((i = curft) <= 0) i = defft; swfac = cfmt.font_tb[i].swfac; while (1) { c1 = (unsigned char) *s++; if (c1 == '\0') break; switch (c1) { case '\\': c1 = *s++; if (c1 == '\0') { *d = '\0'; return w; } switch (c1) { case 'n': c1 = '\n'; break; case 't': c1 = '\t'; break; } break; case '$': if (isdigit((unsigned char) *s) && (unsigned) (*s - '0') < FONT_UMAX) { i = *s - '0'; if (i == 0) i = defft; swfac = cfmt.font_tb[i].swfac; if (--maxlen <= 0) break; *d++ = c1; c1 = *s++; goto addchar_nowidth; } if (*s == '$') { if (--maxlen <= 0) break; *d++ = c1; s++; } break; case '&': /* treat XML characters */ if (svg || epsf > 1) { p = strchr(s, ';'); if (!p || p - s >= 10) break; *d++ = c1; while (s <= p) *d++ = *s++; w += cwid('a') * swfac; continue; } if (*s == '#') { int j; long v; if (s[1] == 'x') i = sscanf(s, "#x%lx;%n", &v, &j); else i = sscanf(s, "#%ld;%n", &v, &j); if (i != 1) { error(0, NULL, "Bad XML char reference"); break; } if (v < 0x80) { /* convert to UTF-8 */ *d++ = v; } else if (v < 0x800) { *d++ = 0xc0 | (v >> 6); *d++ = 0x80 | (v & 0x3f); } else if (v < 0x10000) { *d++ = 0xe0 | (v >> 12); *d++ = 0x80 | ((v >> 6) & 0x3f); *d++ = 0x80 | (v & 0x3f); } else { *d++ = 0xf0 | (v >> 18); *d++ = 0x80 | ((v >> 12) & 0x3f); *d++ = 0x80 | ((v >> 6) & 0x3f); *d++ = 0x80 | (v & 0x3f); } w += cwid('a') * swfac; s += j; continue; } if (strncmp(s, "lt;", 3) == 0) { c1 = '<'; s += 3; } else if (strncmp(s, "gt;", 3) == 0) { c1 = '>'; s += 3; } else if (strncmp(s, "amp;", 4) == 0) { c1 = '&'; s += 4; } else if (strncmp(s, "apos;", 5) == 0) { c1 = '\''; s += 5; } else if (strncmp(s, "quot;", 5) == 0) { c1 = '"'; s += 5; } break; } if (c1 >= 0x80) { if (c1 >= 0xc0) w += cwid('a') * swfac; // start of unicode char } else if (c1 <= 5) { /* accidentals from gchord */ if (--maxlen < 4) break; switch (c1) { case 1: *d++ = 0xe2; *d++ = 0x99; *d++ = 0xaf; break; case 2: *d++ = 0xe2; *d++ = 0x99; *d++ = 0xad; break; case 3: *d++ = 0xe2; *d++ = 0x99; *d++ = 0xae; break; case 4: *d++ = 0xf0; *d++ = 0x9d; *d++ = 0x84; *d++ = 0xaa; break; case 5: *d++ = 0xf0; *d++ = 0x9d; *d++ = 0x84; *d++ = 0xab; break; } w += cwid('a') * swfac; continue; } else { w += cwid(c1) * swfac; } addchar_nowidth: if (--maxlen <= 0) break; *d++ = c1; } *d = '\0'; if (maxlen <= 0) error(0, NULL, "Text too large - ignored part: '%s'", s); return w; } #ifdef HAVE_PANGO #define PG_SCALE (PANGO_SCALE * 72 / 96) /* 96 DPI */ static PangoFontDescription *desc_tb[MAXFONTS]; static PangoLayout *layout = (PangoLayout *) -1; static PangoAttrList *attrs; static int out_pg_ft = -1; /* current pango font */ static GString *pg_str; /* -- initialize the pango mechanism -- */ void pg_init(void) { static PangoContext *context; context = pango_font_map_create_context( pango_cairo_font_map_get_default()); if (context) layout = pango_layout_new(context); if (!layout) { error(0, NULL, "pango disabled\n"); cfmt.pango = 0; } else { pango_layout_set_wrap(layout, PANGO_WRAP_WORD); pg_str = g_string_sized_new(256); } } void pg_reset_font(void) { out_pg_ft = -1; } static void desc_font(int fnum) { int i; char font_name[128], *p, *q; static char *bad_tb[] = { "Times-Roman", "Times", "Helvetica", "Courier" }; static char *good_tb[] = { "serif", "serif", "sans-serif", "monospace" }; // build a font description replacing X11 font names by generic names if (!desc_tb[fnum]) { p = font_name; q = fontnames[fnum]; for (i = 0; i < sizeof bad_tb / sizeof bad_tb[0]; i++) { if (strncmp(q, bad_tb[i], strlen(bad_tb[i])) == 0) { p += sprintf(p, "%s", good_tb[i]); q += strlen(bad_tb[i]); break; } } snprintf(p, sizeof font_name - 5, "%s 10", q); // (dummy size) // change '-' to a space while (*p != '\0') { if (*p == '-') *p = ' '; if (isupper(*p)) *p = tolower(*p); p++; } // add spaces between styles/weight p = strstr(&font_name[1], "italic"); if (p && p[-1] != ' ') { memmove(p + 1, p, strlen(p) + 1); *p = ' '; } p = strstr(&font_name[1], "oblique"); if (p && p[-1] != ' ') { memmove(p + 1, p, strlen(p) + 1); *p = ' '; } p = strstr(&font_name[1], "bold"); if (p && p[-1] != ' ') { memmove(p + 1, p, strlen(p) + 1); *p = ' '; } // create the font description desc_tb[fnum] = pango_font_description_from_string(font_name); } } /* output a line */ static void pg_line_output(PangoLayoutLine *line) { GSList *runs_list; PangoGlyphInfo *glyph_info; char tmp[256]; const char *fontname = NULL; int ret, glypharray; outft = -1; glypharray = 0; for (runs_list = line->runs; runs_list; runs_list = runs_list->next) { PangoLayoutRun *run = runs_list->data; PangoItem *item = run->item; PangoGlyphString *glyphs = run->glyphs; PangoAnalysis *analysis = &item->analysis; PangoFont *font = analysis->font; PangoFcFont *fc_font = PANGO_FC_FONT(font); FT_Face face = pango_fc_font_lock_face(fc_font); PangoFontDescription *ftdesc = pango_font_describe(font); int wi = pango_font_description_get_size(ftdesc); int i, c; for (i = 0; i < glyphs->num_glyphs; i++) { glyph_info = &glyphs->glyphs[i]; c = glyph_info->glyph; if (c == PANGO_GLYPH_EMPTY) continue; if (c & PANGO_GLYPH_UNKNOWN_FLAG) { c &= ~PANGO_GLYPH_UNKNOWN_FLAG; error(0, NULL, "char %04x not treated\n", c); continue; } ret = FT_Load_Glyph(face, c, // PangoGlyph = index FT_LOAD_NO_SCALE); if (ret != 0) { error(0, NULL, "freetype error %d\n", ret); } else if (FT_HAS_GLYPH_NAMES(face)) { if (FT_Get_Postscript_Name(face) != fontname) { fontname = FT_Get_Postscript_Name(face); if (glypharray) a2b("]glypharray"); a2b("\n/%s %.1f selectfont[", fontname, (float) wi / PG_SCALE); glypharray = 1; } FT_Get_Glyph_Name(face, c, tmp, sizeof tmp); a2b("/%s", tmp); } else { error(0, NULL, "!! no glyph %d in %s-%s\n", c, face->family_name, face->style_name); } } pango_fc_font_unlock_face(fc_font); } if (glypharray) a2b("]glypharray"); } static void str_font_change(int start, int end) { struct FONTSPEC *f; int fnum; PangoAttribute *attr1, *attr2; f = &cfmt.font_tb[curft]; fnum = f->fnum; if (f->size == 0) { error(0, NULL, "Font \"%s\" with a null size - set to 8", fontnames[fnum]); f->size = 8; } desc_font(fnum); attr1 = pango_attr_font_desc_new(desc_tb[fnum]); attr1->start_index = start; attr1->end_index = end; pango_attr_list_insert(attrs, attr1); attr2 = pango_attr_size_new((int) (f->size * PG_SCALE)); attr2->start_index = start; attr2->end_index = end; pango_attr_list_insert(attrs, attr2); } static void str_set_font(char *p) { GString *str; char *q; int start; str = pg_str; start = str->len; q = p; while (*p != '\0') { switch (*p) { case '$': if (isdigit((unsigned char) p[1]) && (unsigned) (p[1] - '0') < FONT_UMAX) { if (p > q) str = g_string_append_len(str, q, p - q); if (curft != p[1] - '0') { str_font_change(start, str->len); start = str->len; curft = p[1] - '0'; if (curft == 0) curft = defft; } p += 2; q = p; continue; } if (p[1] == '$') { str = g_string_append_len(str, q, p - q); q = ++p; } break; } p++; } if (p > q) { str = g_string_append_len(str, q, p - q); str_font_change(start, str->len); } pg_str = str; } /* -- output a string using the pango and freetype libraries -- */ static void str_pg_out(char *p, int action) { PangoLayoutLine *line; int wi; float w; if (out_pg_ft != curft) out_pg_ft = -1; /* guitar chord with TABs */ if (action == A_GCHEXP) { char *q; /* get the inter TAB width (see draw_gchord) */ q = mbf - 1; while (q[-1] != ' ') q--; mbf = q; w = atof(q); for (;;) { q = strchr(p, '\t'); if (!q) break; *q = '\0'; str_pg_out(p, A_LEFT); a2b(" %.1f 0 RM ", w); p = q + 1; } } attrs = pango_attr_list_new(); str_set_font(p); pango_layout_set_text(layout, pg_str->str, pg_str->len); pango_layout_set_attributes(layout, attrs); /* only one line */ line = pango_layout_get_line_readonly(layout, 0); switch (action) { case A_CENTER: case A_RIGHT: pango_layout_get_size(layout, &wi, NULL); if (action == A_CENTER) wi /= 2; // w = (float) wi / PG_SCALE; w = (float) wi / PANGO_SCALE; a2b(" -%.1f 0 RM", w); break; } pg_line_output(line); pango_layout_set_attributes(layout, NULL); pg_str = g_string_truncate(pg_str, 0); pango_attr_list_unref(attrs); } /* output a justified or filled paragraph */ static void pg_para_output(int job) { GSList *lines, *runs_list; PangoLayoutLine *line; PangoGlyphInfo *glyph_info; char tmp[256]; const char *fontname = NULL; int ret, glypharray; int wi; float y; pango_layout_set_text(layout, pg_str->str, pg_str->len - 1); /* remove the last space */ pango_layout_set_attributes(layout, attrs); outft = -1; glypharray = 0; wi = 0; y = 0; lines = pango_layout_get_lines_readonly(layout); for (; lines; lines = lines->next) { PangoRectangle pos; line = lines->data; pango_layout_line_get_extents(line, NULL, &pos); y += (float) pos.height * .87 /* magic! */ / PANGO_SCALE; for (runs_list = line->runs; runs_list; runs_list = runs_list->next) { PangoLayoutRun *run = runs_list->data; PangoItem *item = run->item; PangoGlyphString *glyphs = run->glyphs; PangoAnalysis *analysis = &item->analysis; PangoFont *font = analysis->font; PangoFcFont *fc_font = PANGO_FC_FONT(font); FT_Face face = pango_fc_font_lock_face(fc_font); PangoFontDescription *ftdesc = pango_font_describe(font); int i, g, set_move, x; if (pango_font_description_get_size(ftdesc) != wi) { wi = pango_font_description_get_size(ftdesc); fontname = NULL; } pango_layout_index_to_pos(layout, item->offset, &pos); x = pos.x; set_move = 1; for (i = 0; i < glyphs->num_glyphs; i++) { glyph_info = &glyphs->glyphs[i]; g = glyph_info->glyph; if (g == PANGO_GLYPH_EMPTY) continue; if (set_move) { set_move = 0; if (glypharray) { a2b("]glypharray"); glypharray = 0; } a2b("\n"); a2b("%.2f %.2f M ", (float) x / PANGO_SCALE, -y); } x += glyph_info->geometry.width; if (g & PANGO_GLYPH_UNKNOWN_FLAG) { g &= ~PANGO_GLYPH_UNKNOWN_FLAG; error(0, NULL, "char %04x not treated\n", g); continue; } ret = FT_Load_Glyph(face, g, // PangoGlyph = index FT_LOAD_NO_SCALE); if (ret != 0) { fprintf(stdout, "%%%% freetype error %d\n", ret); } else if (FT_HAS_GLYPH_NAMES(face)) { if (FT_Get_Postscript_Name(face) != fontname) { fontname = FT_Get_Postscript_Name(face); if (glypharray) a2b("]glypharray"); a2b("\n/%s %.1f selectfont[", fontname, (float) wi / PG_SCALE); glypharray = 1; } FT_Get_Glyph_Name((FT_FaceRec *) face, g, tmp, sizeof tmp); if (job == T_JUSTIFY && strcmp(tmp, "space") == 0) { set_move = 1; continue; } if (!glypharray) { a2b("["); glypharray = 1; } a2b("/%s", tmp); } else { a2b("%% glyph: %s %d\n", FT_Get_Postscript_Name(face), g); } } pango_fc_font_unlock_face(fc_font); if (glypharray) { a2b("]glypharray\n"); glypharray = 0; } } if (glypharray) { a2b("]glypharray\n"); glypharray = 0; } } bskip(y); pango_layout_set_attributes(layout, NULL); pg_str = g_string_truncate(pg_str, 0); } /* output of filled / justified text */ static void pg_write_text(char *s, int job, float parskip) { char *p; curft = defft; pango_layout_set_width(layout, strlw * PANGO_SCALE); pango_layout_set_justify(layout, job == T_JUSTIFY); attrs = pango_attr_list_new(); p = s; while (*p != '\0') { if (*p++ != '\n') continue; if (*p == '\n') { /* if empty line */ p[-1] = '\0'; tex_str(s); str_set_font(tex_buf); if (pg_str->len > 0) pg_para_output(job); bskip(parskip); buffer_eob(0); s = ++p; continue; } //fixme: maybe not useful p[-1] = ' '; } tex_str(s); str_set_font(tex_buf); if (pg_str->len) pg_para_output(job); pango_attr_list_unref(attrs); } /* check if pango is needed */ static int is_latin(unsigned char *p) { while (*p != '\0') { if (*p >= 0xc6) { if (*p == 0xe2) { if (p[1] != 0x99 || p[2] < 0xad || p[2] > 0xaf) return 0; p += 2; } else if (*p == 0xf0) { if (p[1] != 0x9d || p[2] != 0x84 || p[3] < 0xaa || p[3] > 0xab) return 0; } else { return 0; } } p++; } return 1; } #endif /* HAVE_PANGO */ /* -- set the default font of a string -- */ void str_font(int ft) { curft = defft = ft; } /* -- get the current and default fonts -- */ void get_str_font(int *cft, int *dft) { *cft = curft; *dft = defft; } /* -- set the current and default fonts -- */ void set_str_font(int cft, int dft) { curft = cft; defft = dft; } static char *strop_tb[] = { /* index = action (A_xxxx) * 2 */ "show", "arrayshow", // left "showc", "arrayshow", // center "showr", "arrayshow", // right "lyshow", "alyshow", // lyric "gcshow", "agcshow", // gchord "anshow", "aanshow", // annot "gxshow", "arrayshow", // gchexp "strop", "arrayshow", // (7 = justify) }; /* close a string */ static void str_end(int end) { if (strtx & TX_STR) { a2b(")"); strtx &= ~TX_STR; if (!(strtx & TX_ARR)) { a2b("%s", strop_tb[stropx]); return; } } if (!end || !(strtx & TX_ARR)) return; strtx &= ~TX_ARR; a2b("]%s", strop_tb[stropx + 1]); } /* check if some non ASCII characters */ static int non_ascii_p(char *p) { while (*p != '\0') { if ((signed char) *p++ < 0) return 1; } return 0; } /* -- output one string -- */ static void str_ft_out1(char *p, int l) { if (curft != outft) { str_end(1); a2b(" "); set_font(curft); } if (!(strtx & TX_STR)) { a2b("("); strtx |= TX_STR; } a2b("%.*s", l, p); } /* -- output a string handling the font changes -- */ static void str_ft_out(char *p, int end) { int use_glyph; char *q; use_glyph = !svg && epsf <= 1 && /* not SVG */ get_font_encoding(curft) == 0; /* utf-8 font */ if (use_glyph && non_ascii_p(p)) { if (curft != outft) { str_end(1); a2b(" "); set_font(curft); } str_end(0); if (!(strtx & TX_ARR)) { a2b("["); strtx |= TX_ARR; } } q = p; while (*p != '\0') { if ((signed char) *p < 0 && use_glyph) { if (p > q) str_ft_out1(q, p - q); str_end(0); if (curft != outft) { str_end(1); a2b(" "); set_font(curft); } if (!(strtx & TX_ARR)) { a2b("["); strtx |= TX_ARR; } q = p = glyph_out(p); continue; } switch (*p) { case '$': if (isdigit((unsigned char) p[1]) && (unsigned) (p[1] - '0') < FONT_UMAX) { if (p > q) str_ft_out1(q, p - q); if (curft != p[1] - '0') { curft = p[1] - '0'; if (curft == 0) curft = defft; use_glyph = !svg && epsf <= 1 && get_font_encoding(curft) == 0; } p += 2; q = p; continue; } if (p[1] == '$') { str_ft_out1(q, p - q); q = ++p; } break; case '(': case ')': case '\\': if (p > q) str_ft_out1(q, p - q); str_ft_out1("\\", 1); q = p; break; } p++; } if (p > q) str_ft_out1(q, p - q); if (end && strtx) str_end(1); } /* -- output a string, handling the font changes -- */ void str_out(char *p, int action) { if (curft <= 0) /* first call */ curft = defft; /* special case when font change at start of text */ if (*p == '$' && isdigit((unsigned char) p[1]) && (unsigned) (p[1] - '0') < FONT_UMAX) { if (curft != p[1] - '0') { curft = p[1] - '0'; if (curft == 0) curft = defft; } p += 2; } #ifdef HAVE_PANGO //fixme: pango KO if user modification of ly/gc/an/gxshow /* use pango if some characters are out of the utf-array (in syms.c) */ if (cfmt.pango) { if (cfmt.pango == 2 || !is_latin((unsigned char *) p)) { str_pg_out(p, action); /* output the string */ return; } } #endif stropx = action * 2; /* direct output if no font change and only ASCII characters */ if (!strchr(p, '$') && !non_ascii_p(p)) { str_ft_out(p, 1); /* output the string */ return; } /* if not left aligned, build a PS function */ switch (action) { case A_CENTER: case A_RIGHT: if (!svg && epsf <= 1) { a2b("/str{"); outft = -1; stropx = 0; } /* fall thru */ // default: // if (!svg && epsf <= 1) /* if not SVG */ // stropx++; break; } str_ft_out(p, 1); /* output the string */ /* if not left aligned, call the PS function */ if (svg || epsf > 1) /* not for SVG */ return; if (action == A_CENTER || action == A_RIGHT) { a2b("}def\n" "strw w"); if (action == A_CENTER) a2b(" 0.5 mul"); a2b(" neg 0 RM str"); } } /* -- output a string with TeX translation -- */ void put_str(char *str, int action) { tex_str(str); str_out(tex_buf, action); a2b("\n"); } /* -- output a header information -- */ static void put_inf(struct SYMBOL *s) { char *p; p = s->text; if (p[1] == ':') p += 2; while (isspace((unsigned char) *p)) p++; put_str(p, A_LEFT); } /* -- output a header format '111 (222)' -- */ static void put_inf2r(struct SYMBOL *s1, struct SYMBOL *s2, int action) { char buf[256], *p, *q; if (!s1) { s1 = s2; s2 = NULL; } p = &s1->text[2]; if (s1->text[0] == 'T') p = trim_title(p, s1); if (s2) { buf[sizeof buf - 1] = '\0'; strncpy(buf, p, sizeof buf - 1); q = buf + strlen(buf); if (q < buf + sizeof buf - 4) { *q++ = ' '; *q++ = '('; p = &s2->text[2]; strncpy(q, p, buf + sizeof buf - 2 - q); q += strlen(q); *q++ = ')'; *q = '\0'; } p = buf; } put_str(p, action); } /* -- write a text block (%%begintext / %%text / %%center) -- */ void write_text(char *cmd, char *s, int job) { int nw; #ifdef HAVE_PANGO int do_pango; #endif float lineskip, parskip, strw; char *p; struct FONTSPEC *f; str_font(TEXTFONT); strlw = ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth) - cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale; f = &cfmt.font_tb[TEXTFONT]; lineskip = f->size * cfmt.lineskipfac; parskip = f->size * cfmt.parskipfac; /* follow lines */ switch (job) { case T_LEFT: case T_CENTER: case T_RIGHT: switch (job) { case T_LEFT: #if T_LEFT != A_LEFT job = A_LEFT; #endif strlw = 0; break; case T_CENTER: #if T_CENTER != A_CENTER job = A_CENTER; #endif strlw /= 2; break; default: #if T_RIGHT != A_RIGHT job = A_RIGHT; #endif break; } p = s; while (*s != '\0') { while (*p != '\0' && *p != '\n') p++; if (*p != '\0') *p++ = '\0'; if (*s == '\0') { // new paragraph bskip(parskip); buffer_eob(0); while (*p == '\n') { bskip(lineskip); p++; } if (*p == '\0') goto skip; } else { bskip(lineskip); a2b("%.1f 0 M", strlw); put_str(s, job); } s = p; } goto skip; } /* fill or justify lines */ #ifdef HAVE_PANGO do_pango = cfmt.pango; if (do_pango == 1) do_pango = !is_latin((unsigned char *) s); if (do_pango) { pg_write_text(s, job, parskip); goto skip; } #endif // curft = defft; nw = 0; /* number of words */ strw = 0; /* have gcc happy */ stropx = (job == T_FILL ? A_LEFT : 7) * 2; while (*s != '\0') { float lw; if (*s == '\n') { /* empty line = new paragraph */ if (strtx) { str_end(1); if (job == T_JUSTIFY) a2b("}def\n" "/strop/show load def str"); a2b("\n"); } // a2b("\n"); bskip(parskip); buffer_eob(0); // while (isspace((unsigned char) *s)) // s++; while (*s == '\n') { bskip(lineskip); s++; } if (*s == '\0') goto skip; nw = 0; // a2b("0 0 M"); // if (job != T_FILL) { // a2b("/str{"); // outft = -1; // } // continue; } if (nw == 0) { /* if new paragraph */ bskip(lineskip); a2b("0 0 M"); if (job != T_FILL) { a2b("/str{"); outft = -1; } strw = 0; /* current line width */ } /* get a word */ p = s; while (*p != '\0' && !isspace((unsigned char) *p)) p++; if (*p != '\0') { char *q; q = p; if (*p != '\n') { do { p++; } while (*p != '\n' && isspace((unsigned char) *p)); } if (*p == '\n') p++; *q = '\0'; } lw = tex_str(s); if (strw + lw > strlw) { str_end(1); if (job == T_JUSTIFY) { int n; n = nw - 1; if (n <= 0) n = 1; if (svg || epsf > 1) a2b("}def\n" "%.1f jshow" "/strop/show load def str", strlw); else a2b("}def\n" "strw" "/w %.1f w sub %d div def" "/strop/jshow load def str", strlw, n); } a2b("\n"); bskip(lineskip); a2b("0 0 M"); if (job == T_JUSTIFY) { a2b("/str{"); outft = -1; } nw = 0; strw = 0; } if (nw != 0) { str_ft_out1(" ", 1); strw += cwid(' ') * cfmt.font_tb[curft].swfac; } str_ft_out(tex_buf, 0); strw += lw; nw++; s = p; } if (strtx) { str_end(1); if (job == T_JUSTIFY) a2b("}def\n" "/strop/show load def str"); } // if (mbf[-1] != '\n') a2b("\n"); skip: bskip(parskip); buffer_eob(0); } /* -- output a line of words after tune -- */ static int put_wline(char *p, float x, int right) { char *q, *r, sep; while (isspace((unsigned char) *p)) p++; if (*p == '$' && isdigit((unsigned char) p[1]) && (unsigned) (p[1] - '0') < FONT_UMAX) { if (curft != p[1] - '0') { curft = p[1] - '0'; if (curft == 0) curft = defft; } p += 2; } r = NULL; q = p; if (isdigit((unsigned char) *p) || (*p != '\0' && p[1] == '.')) { while (*p != '\0') { p++; if (*p == ' ' || p[-1] == ':' || p[-1] == '.') break; } r = p; while (*p == ' ') p++; } if (r) { sep = *r; *r = '\0'; a2b("%.1f 0 M", x); put_str(q, A_RIGHT); *r = sep; } if (*p != '\0') { a2b("%.1f 0 M", x + 5); put_str(p, A_LEFT); } return *p == '\0' && r == 0; } /* -- output the words after tune -- */ void put_words(struct SYMBOL *words) { struct SYMBOL *s, *s_end, *s2; char *p; int i, n, have_text, max2col; float middle; buffer_eob(0); str_font(WORDSFONT); /* see if we may have 2 columns */ middle = 0.5 * ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth) - cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale; max2col = (int) ((middle - 45.) / (cwid('a') * cfmt.font_tb[WORDSFONT].swfac)); n = 0; have_text = 0; for (s = words; s != 0; s = s->next) { p = &s->text[2]; /*fixme:utf8*/ if ((int) strlen(p) > max2col) { n = 0; break; } if (*p == '\0') { if (have_text) { n++; have_text = 0; } } else { have_text = 1; } } if (n > 0) { n++; n /= 2; i = n; have_text = 0; s_end = words; for (;;) { p = &s_end->text[2]; while (isspace((unsigned char) *p)) p++; if (*p == '\0') { if (have_text && --i <= 0) break; have_text = 0; } else { have_text = 1; } s_end = s_end->next; } s2 = s_end->next; } else { s_end = NULL; s2 = NULL; } /* output the text */ bskip(cfmt.wordsspace); for (s = words; s || s2; ) { //fixme:should also permit page break on stanza start if (s && s->text[2] == '\0') buffer_eob(0); bskip(cfmt.lineskipfac * cfmt.font_tb[WORDSFONT].size); if (s) { put_wline(&s->text[2], 45., 0); s = s->next; if (s == s_end) s = NULL; } if (s2) { if (put_wline(&s2->text[2], 20. + middle, 1)) { if (--n == 0) { if (s) { n++; } else if (s2->next) { /* center the last words */ /*fixme: should compute the width average.. */ middle *= 0.6; } } } s2 = s2->next; } } // buffer_eob(0); } /* -- output history -- */ void put_history(void) { struct SYMBOL *s, *s2; int font; unsigned u; float w, h; char tmp[265]; font = 0; for (s = info['I' - 'A']; s; s = s->next) { u = s->text[0] - 'A'; if (!(cfmt.fields[0] & (1 << u)) || (s2 = info[u]) == NULL) continue; if (!font) { bskip(cfmt.textspace); str_font(HISTORYFONT); font = 1; } get_str(tmp, &s->text[1], sizeof tmp); w = tex_str(tmp); h = cfmt.font_tb[HISTORYFONT].size * cfmt.lineskipfac; set_font(HISTORYFONT); // a2b("0 0 M(%s)show ", tex_buf); a2b("0 0 M"); str_out(tex_buf, A_LEFT); for (;;) { put_inf(s2); if ((s2 = s2->next) == NULL) break; if (s2->text[0] == '+' && s2->text[1] == ':') { put_str(" ", A_LEFT); } else { bskip(h); a2b("%.2f 0 M ", w); } } bskip(h * 1.2); buffer_eob(0); } } // -- move trailing article to front, set to uppercase letters and add xref char *trim_title(char *p, struct SYMBOL *title) { char *b, *q, *r; static char buf[STRL1]; q = NULL; if (cfmt.titletrim) { q = strrchr(p, ','); if (q) { if (q[1] != ' ' || !isupper((unsigned char) q[2])) { q = NULL; } else if (cfmt.titletrim == 1) { // (true) if (strlen(q) > 7 /* word no more than 5 characters */ || strchr(q + 2, ' ')) q = NULL; } else { if (strlen(q) > cfmt.titletrim - 2) q = NULL; } } } if (title != info['T' - 'A'] || !(cfmt.fields[0] & (1 << ('X' - 'A')))) title = NULL; if (!q && !title && !cfmt.titlecaps) return p; /* keep the title as it is */ b = buf; r = &info['X' - 'A']->text[2]; if (title && *r != '\0') { if (strlen(p) + strlen(r) + 3 >= sizeof buf) { error(1, NULL, "Title or X: too long"); return p; } b += sprintf(b, "%s. ", r); } else { if (strlen(p) >= sizeof buf) { error(1, NULL, "Title too long"); return p; } } if (q) sprintf(b, "%s %.*s", q + 2, (int) (q - p), p); else strcpy(b, p); if (cfmt.titlecaps) cap_str(buf); return buf; } /* -- write a title -- */ void write_title(struct SYMBOL *s) { char *p; float sz; p = &s->text[2]; if (*p == '\0') return; if (s == info['T' - 'A']) { sz = cfmt.font_tb[TITLEFONT].size; bskip(cfmt.titlespace + sz); str_font(TITLEFONT); a2b("%% --- title"); } else { sz = cfmt.font_tb[SUBTITLEFONT].size; bskip(cfmt.subtitlespace + sz); str_font(SUBTITLEFONT); a2b("%% --- titlesub"); } a2b(" %s\n", p); if (cfmt.titleleft) a2b("0"); else a2b("%.1f", 0.5 * ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth) - cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale); a2b(" %.1f M ", sz * 0.2); p = trim_title(p, s); put_str(p, cfmt.titleleft ? A_LEFT : A_CENTER); } /* -- write heading with format -- */ static void write_headform(float lwidth) { char *p, *q; struct SYMBOL *s; struct FONTSPEC *f; int align, i; unsigned j; float x, y, xa[3], ya[3], sz, yb[3]; /* !! see action A_xxx */ char inf_nb[26]; INFO inf_s; char inf_ft[26]; float inf_sz[26]; char fmt[64]; memset(inf_nb, 0, sizeof inf_nb); memset(inf_ft, HISTORYFONT, sizeof inf_ft); inf_ft['A' - 'A'] = INFOFONT; inf_ft['C' - 'A'] = COMPOSERFONT; inf_ft['O' - 'A'] = COMPOSERFONT; inf_ft['P' - 'A'] = PARTSFONT; inf_ft['Q' - 'A'] = TEMPOFONT; inf_ft['R' - 'A'] = INFOFONT; inf_ft['T' - 'A'] = TITLEFONT; inf_ft['X' - 'A'] = TITLEFONT; memcpy(inf_s, info, sizeof inf_s); memset(inf_sz, 0, sizeof inf_sz); inf_sz['A' - 'A'] = cfmt.infospace; inf_sz['C' - 'A'] = cfmt.composerspace; inf_sz['O' - 'A'] = cfmt.composerspace; inf_sz['R' - 'A'] = cfmt.infospace; p = cfmt.titleformat; j = 0; for (;;) { while (isspace((unsigned char) *p)) p++; if (*p == '\0') break; i = *p - 'A'; if ((unsigned) i < 26) { inf_nb[i]++; switch (p[1]) { default: align = A_CENTER; break; case '1': align = A_RIGHT; p++; break; case '-': align = A_LEFT; p++; break; } if (j < sizeof fmt - 4) { fmt[j++] = i; fmt[j++] = align; } } else if (*p == ',') { if (j < sizeof fmt - 3) fmt[j++] = 126; /* next line */ } else if (*p == '+') { if (j > 0 && fmt[j - 1] < 125 && j < sizeof fmt - 4) { fmt[j++] = 125; /* concatenate */ fmt[j++] = 0; } /*new fixme: add free text "..." ?*/ } p++; } fmt[j++] = 126; /* newline */ fmt[j] = 127; /* end of format */ ya[0] = ya[1] = ya[2] = cfmt.titlespace; xa[0] = 0; xa[1] = lwidth * 0.5; xa[2] = lwidth; p = fmt; for (;;) { yb[0] = yb[1] = yb[2] = y = 0; q = p; for (;;) { i = *q++; if (i >= 126) /* if newline */ break; align = *q++; if (yb[align] != 0 || i == 125) continue; s = inf_s[i]; if (s == 0 || inf_nb[i] == 0) continue; j = inf_ft[i]; f = &cfmt.font_tb[j]; sz = f->size * 1.1 + inf_sz[i]; if (y < sz) y = sz; yb[align] = sz; /*fixme:should count the height of the concatenated field*/ } for (i = 0; i < 3; i++) ya[i] += y - yb[i]; for (;;) { i = *p++; if (i >= 126) /* if newline */ break; align = *p++; if (i == 125) continue; s = inf_s[i]; if (!s || inf_nb[i] == 0) continue; j = inf_ft[i]; str_font(j); x = xa[align]; f = &cfmt.font_tb[j]; sz = f->size * 1.1 + inf_sz[i]; y = ya[align] + sz; if (s->text[2] != '\0') { if (i == 'T' - 'A') { if (s == info['T' - 'A']) a2b("%% --- title"); else a2b("%% --- titlesub"); a2b(" %s\n", &s->text[2]); } a2b("%.1f %.1f M ", x, -y); } if (*p == 125) { /* concatenate */ p += 2; /*fixme: do it work with different fields*/ if (*p == i && p[1] == align && s->next) { char buf[256], *r; q = s->text; if (q[1] == ':') q += 2; while (isspace((unsigned char) *q)) q++; if (i == 'T' - 'A') q = trim_title(q, s); strncpy(buf, q, sizeof buf - 1); buf[sizeof buf - 1] = '\0'; j = strlen(buf); if (j < sizeof buf - 1) { buf[j] = ' '; buf[j + 1] = '\0'; } s = s->next; q = s->text; if (q[1] == ':') q += 2; while (isspace((unsigned char) *q)) q++; if (s->text[0] == 'T'/* && s->text[1] == ':'*/) q = trim_title(q, s); r = buf + strlen(buf); strncpy(r, q, buf + sizeof buf - r - 1); tex_str(buf); str_out(tex_buf, align); a2b("\n"); inf_nb[i]--; p += 2; } else { put_inf2r(s, NULL, align); } } else if (i == 'Q' - 'A') { /* special case for tempo */ if (align != A_LEFT) { float w; w = -tempo_width(s); if (align == A_CENTER) w *= 0.5; a2b("%.1f 0 RM ", w); } write_tempo(s, 0, 0.75); info['Q' - 'A'] = NULL; /* don't display in tune */ } else { put_inf2r(s, NULL, align); } if (inf_s[i] == info['T' - 'A']) { inf_ft[i] = SUBTITLEFONT; str_font(SUBTITLEFONT); f = &cfmt.font_tb[SUBTITLEFONT]; inf_sz[i] = cfmt.subtitlespace; sz = f->size * 1.1 + inf_sz[i]; } s = s->next; if (inf_nb[i] == 1) { while (s) { y += sz; a2b("%.1f %.1f M ", x, -y); put_inf2r(s, 0, align); s = s->next; } } inf_s[i] = s; inf_nb[i]--; ya[align] = y; } if (ya[1] > ya[0]) ya[0] = ya[1]; if (ya[2] > ya[0]) ya[0] = ya[2]; if (*p == 127) { bskip(ya[0]); break; } ya[1] = ya[2] = ya[0]; } } /* -- output the tune heading -- */ void write_heading(void) { struct SYMBOL *s, *rhythm, *area, *author, *composer, *origin; float lwidth, down1, down2; lwidth = ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth) - cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale; if (cfmt.titleformat && cfmt.titleformat[0] != '\0') { write_headform(lwidth); bskip(cfmt.musicspace); return; } /* titles */ if (cfmt.fields[0] & (1 << ('T' - 'A'))) { for (s = info['T' - 'A']; s; s = s->next) write_title(s); } /* rhythm, composer, origin */ down1 = cfmt.composerspace + cfmt.font_tb[COMPOSERFONT].size; rhythm = ((first_voice->key.instr == K_HP || first_voice->key.instr == K_Hp || pipeformat) && !cfmt.infoline && (cfmt.fields[0] & (1 << ('R' - 'A')))) ? info['R' - 'A'] : NULL; if (rhythm) { str_font(COMPOSERFONT); a2b("0 %.1f M ", -cfmt.composerspace); put_inf(rhythm); down1 = cfmt.composerspace; } area = author = NULL; if (parse.abc_vers != (2 << 16)) area = info['A' - 'A']; else author = info['A' - 'A']; composer = (cfmt.fields[0] & (1 << ('C' - 'A'))) ? info['C' - 'A'] : NULL; origin = (cfmt.fields[0] & (1 << ('O' - 'A'))) ? info['O' - 'A'] : NULL; if (composer || origin || author || cfmt.infoline) { float xcomp; int align; str_font(COMPOSERFONT); bskip(cfmt.composerspace); if (cfmt.aligncomposer < 0) { xcomp = 0; align = A_LEFT; } else if (cfmt.aligncomposer == 0) { xcomp = lwidth * 0.5; align = A_CENTER; } else { xcomp = lwidth; align = A_RIGHT; } down2 = down1; if (author) { for (;;) { bskip(cfmt.font_tb[COMPOSERFONT].size); down2 += cfmt.font_tb[COMPOSERFONT].size; a2b("0 0 M "); put_inf(author); if ((author = author->next) == NULL) break; } } if (composer || origin) { if (cfmt.aligncomposer >= 0 && down1 != down2) bskip(down1 - down2); s = composer; for (;;) { bskip(cfmt.font_tb[COMPOSERFONT].size); a2b("%.1f 0 M ", xcomp); put_inf2r(s, (!s || !s->next) ? origin : NULL, align); if (!s) break; if ((s = s->next) == NULL) break; down1 += cfmt.font_tb[COMPOSERFONT].size; } if (down2 > down1) bskip(down2 - down1); } rhythm = rhythm ? NULL : info['R' - 'A']; if ((rhythm || area) && cfmt.infoline) { /* if only one of rhythm or area then do not use ()'s * otherwise output 'rhythm (area)' */ str_font(INFOFONT); bskip(cfmt.font_tb[INFOFONT].size + cfmt.infospace); a2b("%.1f 0 M ", lwidth); put_inf2r(rhythm, area, A_RIGHT); down1 += cfmt.font_tb[INFOFONT].size + cfmt.infospace; } down2 = 0; } else { down2 = cfmt.composerspace; } /* parts */ if (info['P' - 'A'] && (cfmt.fields[0] & (1 << ('P' - 'A')))) { down1 = cfmt.partsspace + cfmt.font_tb[PARTSFONT].size - down1; if (down1 > 0) down2 += down1; if (down2 > 0.01) bskip(down2); str_font(PARTSFONT); a2b("0 0 M "); put_inf(info['P' - 'A']); down2 = 0; } bskip(down2 + cfmt.musicspace); } /* -- memorize a PS / SVG line -- */ /* 'use' may be: * 'g': SVG code * 'p': PS code for PS output only * 's': PS code for SVG output only * 'b': PS code for PS or SVG output */ void user_ps_add(char *s, char use) { struct u_ps *t, *r; int l; if (*s == '\0' || *s == '%') return; l = strlen(s); if (use == 'g') { t = malloc(sizeof *user_ps - sizeof user_ps->text + l + 6); sprintf(t->text, "%%svg %s", s); } else { t = malloc(sizeof *user_ps - sizeof user_ps->text + l + 2); sprintf(t->text, "%c%s", use, s); } t->next = NULL; if ((r = user_ps) == NULL) { user_ps = t; } else { while (r->next) r = r->next; r->next = t; } } /* -- output the user defined postscript sequences -- */ void user_ps_write(void) { struct u_ps *t; char *p; for (t = user_ps; t; t = t->next) { p = t->text; switch (*p) { case '\001': { /* PS file */ FILE *f; char line[BSIZE]; if ((f = fopen(p + 1, "r")) == NULL) { error(1, NULL, "Cannot open PS file '%s'", &t->text[1]); } else { while (fgets(line, sizeof line, f)) /* copy the file */ fputs(line, fout); fclose(f); } continue; } case '%': /* "%svg " = SVG code */ // if (svg || epsf > 1) // svg_write(t->text, strlen(t->text)); fputs(p + 5, fout); fputc('\n', fout); continue; case 'p': /* PS code for PS output only */ // if (secure || svg || epsf > 1) // continue; break; case 'b': /* PS code for both PS and SVG */ if (svg || epsf > 1) { svg_write(p + 1, strlen(p + 1)); continue; } // if (secure) // continue; break; case 's': /* PS code for SVG output only */ // if (!svg && epsf <= 1) // continue; svg_write(p + 1, strlen(&t->text[1])); continue; } fputs(p + 1, fout); fputc('\n', fout); } } abcm2ps-8.14.11/svg.c000066400000000000000000003051771376266546700142230ustar00rootroot00000000000000/* * SVG definitions. * * This file is part of abcm2ps. * * Copyright (C) 2011-2019 Jean-François Moine * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. */ #include #include #include #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #ifndef NaN #define NaN ((float) (2 << 22)) #endif #include "abcm2ps.h" enum elt_t { /* element types */ VAL, STR, SEQ, /* {..} */ BRK, /* [..] */ }; struct elt_s { struct elt_s *next; char type; union { float v; char *s; struct elt_s *e; } u; }; struct ps_sym_s { char *n; /* name */ struct elt_s *e; /* value */ int exec; /* current number of execution */ }; /* -- PostScript tiny interpreter -- */ #define NELTS 2048 /* number of elements per block */ #define NSYMS 512 /* max number of symbols */ static struct elt_s *elts; static struct elt_s *stack, *free_elt; static struct ps_sym_s ps_sym[NSYMS]; static int n_sym; static int ps_error; static int in_cnt; /* in [..] or {..} */ static char *path; static char path_buf[256]; /* graphical context */ static struct gc { float cx, cy; // current point (volatile) float xscale, yscale; // scale float xoffs, yoffs; // translate float rotate, sin, cos; // rotate char *font_n; // current font float font_s; char *font_n_old; float linewidth; int rgb; char dash[64]; } gcur, gold, gsave[8]; static int nsave; static float x_rot, y_rot; /* save x and y offset when rotate != 0 */ static int g; /* current container */ static int boxend; static char *defs; // SVG ID's from %%beginsvg static int defssz; /* abcm2ps definitions */ static struct { char *def; char use; char defined; } def_tb[] = { #define D_brace 0 { "\n"}, #define D_utclef 1 { "\n"}, #define D_tclef 2 { "\n", D_utclef}, #define D_stclef 3 { "\n", D_utclef}, #define D_ubclef 4 { "\n"}, #define D_bclef 5 { "\n", D_ubclef}, #define D_sbclef 6 { "\n", D_ubclef}, #define D_ucclef 7 { "\n"}, #define D_cclef 8 { "\n", D_ucclef}, #define D_scclef 9 { "\n", D_ucclef}, #define D_pclef 10 { "\n"}, #define D_hd 11 { "\n"}, #define D_Hd 12 { "\n"}, #define D_HD 13 { "\n"}, #define D_HDD 14 { "\n" " \n" " \n" "\n", D_HD}, #define D_breve 15 { "\n" " \n" " \n" "\n"}, #define D_longa 16 { "\n" " \n" " \n" "\n"}, #define D_ghd 17 { "\n"}, #define D_r00 18 { "\n"}, #define D_r0 19 { "\n"}, #define D_r1 20 { "\n"}, #define D_r2 21 { "\n"}, #define D_r4 22 { "\n"}, #define D_r8e 23 { "\n"}, #define D_r8 24 { "\n" " \n" " \n" "\n", D_r8e}, #define D_r16 25 { "\n" " \n" " \n" " \n" "\n", D_r8e}, #define D_r32 26 { "\n" " \n" " \n" " \n" " \n" "\n", D_r8e}, #define D_r64 27 { "\n" " \n" " \n" " \n" " \n" " \n" "\n", D_r8e}, #define D_r128 28 { "\n" " \n" " \n" " \n" " \n" " \n" " \n" "\n", D_r8e}, #define D_mrest 29 { "\n" " \n" " \n" "\n"}, #define D_usharp 30 { "\n"}, #define D_uflat 31 { "\n"}, #define D_unat 32 { "\n"}, #define D_udblesharp 33 { "\n"}, #define D_udbleflat 34 { "\n"}, #define D_sh0 35 { "\n", D_usharp}, #define D_ft0 36 { "\n", D_uflat}, #define D_nt0 37 { "\n", D_unat}, #define D_dsh0 38 { "\n", D_udblesharp}, #define D_dft0 39 { "\n", D_udbleflat}, #define D_sh1 40 { "\n" " \n" " \n" "\n"}, #define D_sh513 41 { "\n" " \n" " \n" "\n"}, #define D_ft1 42 { "\n" " \n" "\n", D_ft0}, #define D_ft513 43 { "\n" " \n" " \n" "\n"}, #define D_pshhd 44 { "\n" " \n" "\n", D_dsh0}, #define D_pfthd 45 { "\n" " \n" " \n" "\n", D_dsh0}, #define D_csig 46 { "\n"}, #define D_ctsig 47 { "\n" " \n" " \n" "\n", D_csig}, #define D_pmsig 48 { "\n"}, #define D_pMsig 49 { "\n" " \n" " \n" "\n", D_pmsig}, #define D_imsig 50 { "\n"}, #define D_iMsig 51 { "\n" " \n" " \n" "\n", D_imsig}, #define D_hl 52 { "\n"}, #define D_hl1 53 { "\n"}, #define D_hl2 54 { "\n"}, #define D_ghl 55 { "\n"}, #define D_rdots 56 { "\n" " \n" " \n" "\n"}, #define D_srep 57 { "\n"}, #define D_mrep 58 { "\n"}, #define D_mrep2 59 { "\n" " \n" " \n" "\n"}, #define D_accent 60 { "\n" " \n" "\n"}, #define D_umrd 61 { "\n"}, #define D_lmrd 62 { "\n" " \n" " \n" "\n", D_umrd}, #define D_grm 63 { "\n"}, #define D_stc 64 { "\n"}, #define D_sld 65 { "\n"}, #define D_emb 66 { "\n"}, #define D_hld 67 { "\n" " \n" " \n" "\n"}, #define D_cpu 68 { "\n"}, #define D_upb 69 { "\n"}, #define D_dnb 70 { "\n" " \n" " \n" "\n"}, #define D_sgno 71 { "\n" " \n" " \n" " \n" " \n" "\n"}, #define D_coda 72 { "\n" " \n" " \n" "\n"}, #define D_dplus 73 { "\n"}, #define D_lphr 74 { "\n"}, #define D_mphr 75 { "\n"}, #define D_sphr 76 { "\n"}, #define D_opend 77 { "\n"}, #define D_snap 78 { "\n"}, #define D_thumb 79 { "\n"}, #define D_turn 80 { "\n"}, #define D_turnx 81 { "\n" " \n" " \n" "\n", D_turn}, #define D_wedge 82 { "\n"}, #define D_ltr 83 { "\n"}, #define D_custos 84 { "\n" " \n" " \n" "\n"}, #define D_showerror 85 { "\n"}, #define D_sfz 86 { "sfz\n"}, #define D_trl 87 { "tr\n"}, #define D_marcato 88 { "\n"}, #define D_ped 89 { "Ped\n"}, #define D_pedoff 90 { "*\n"}, }; static struct { int index; char *def; } font_gl[] = { {D_brace, "\n"}, {D_sgno, "\n"}, {D_coda, "\n"}, {D_tclef, "\n"}, {D_cclef, "\n"}, {D_bclef, "\n"}, {D_pclef, "\n"}, {D_stclef, "\n"}, {D_scclef, "\n"}, {D_sbclef, "\n"}, {D_csig, "\n"}, {D_ctsig, "\n"}, {D_HDD, "\n"}, {D_breve, "\n"}, {D_HD, "\n"}, {D_Hd, "\n"}, {D_hd, "\n"}, {D_ft0, "\n"}, {D_nt0, "\n"}, {D_sh0, "\n"}, {D_dsh0, "\n"}, {D_pshhd, "\n"}, {D_dft0, "\n"}, {D_accent, "\n"}, {D_marcato, "\n"}, {D_hld, "\n"}, {D_r00, "\n"}, {D_r0, "\n"}, {D_r1, "\n"}, {D_r2, "\n"}, {D_r4, "\n"}, {D_r8, "\n"}, {D_r16, "\n"}, {D_r32, "\n"}, {D_r64, "\n"}, {D_r128, "\n"}, {D_mrest, "\n"}, {D_mrep, "\n"}, {D_mrep2, "\n"}, {D_turn, "\n"}, {D_umrd, "\n"}, {D_lmrd, "\n"}, {D_ped, "\n"}, {D_pedoff, "\n"}, {D_longa, "\n"}, }; // switch to a music font void svg_font_switch(void) { int i, j; for (i = 0; i < sizeof font_gl / sizeof font_gl[0]; i++) { j = font_gl[i].index; def_tb[j].def = font_gl[i].def; def_tb[j].use = 0; } } /* PS functions */ static void ps_exec(char *op); static void elts_link(struct elt_s *e) { int i; /* set the linkages - the first element is the link to the next block */ for (i = 1; i < NELTS - 1; i++) { e[i].next = &e[i + 1]; if (e[i].type == STR) free(e[i].u.s); e[i].type = VAL; } e[NELTS - 1].next = NULL; } /* (re)initialize all PS elements */ static void elts_reset(void) { struct elt_s *e; if (!elts) elts = calloc(sizeof *elts, NELTS); elts_link(elts); free_elt = elts + 1; /* link all blocks */ for (e = elts; e->u.e; e = e->u.e) { elts_link(e->u.e); e[NELTS - 1].next = e->u.e; } } static struct elt_s *elt_new(void) { struct elt_s *e; e = free_elt; if (!e) { e = calloc(sizeof *e, NELTS); if (!e) { fprintf(stderr, "svg: elt_new out of memory\n"); ps_error = 1; return e; } elts_link(e); e->u.e = elts; elts = e; e++; } free_elt = e->next; e->next = NULL; e->type = VAL; return e; } static void elt_free(struct elt_s *e) { struct elt_s *e2; e->next = free_elt; free_elt = e; switch (e->type) { case STR: free(e->u.s); e->type = VAL; e->u.v = 0; break; case SEQ: case BRK: e2 = e->u.e; e->type = VAL; e->u.v = 0; while (e2) { e = e2->next; elt_free(e2); e2 = e; } break; } } static struct elt_s *elt_dup(struct elt_s *e) { struct elt_s *e2, *e3, *e4; e2 = elt_new(); if (!e2) return e2; e2->type = e->type; switch (e->type) { case VAL: e2->u.v = e->u.v; break; case STR: e2->u.s = strdup(e->u.s); break; case SEQ: case BRK: e = e->u.e; if (!e) { e2->u.e = NULL; break; } e3 = e2->u.e = elt_dup(e); if (!e3) break; for (;;) { e = e->next; if (!e) break; e4 = elt_dup(e); if (!e4) break; e3->next = e4; e3 = e4; } e3->next = NULL; break; } return e2; } static void elt_dump(struct elt_s *e) { int type; type = e->type; switch (type) { case VAL: fprintf(stderr, " %.2f", e->u.v); break; case STR: fprintf(stderr, " %s", e->u.s); if (e->u.s[0] == '(') fprintf(stderr, ")"); break; case SEQ: case BRK: fprintf(stderr, type == SEQ ? " {" : " ["); e = e->u.e; while (e) { elt_dump(e); e = e->next; } fprintf(stderr, type == SEQ ? " }" : " ]"); } } static void elt_lst_dump(struct elt_s *e) { do { elt_dump(e); e = e->next; } while (e); } static struct ps_sym_s *ps_sym_lookup(char *name) { struct ps_sym_s *ps; if (n_sym == 0) return NULL; ps = &ps_sym[n_sym]; for (;;) { ps--; if (strcmp(ps->n, name) == 0) break; if (ps == ps_sym) return NULL; } return ps; } static struct ps_sym_s *ps_sym_def(char *name, struct elt_s *e) { struct ps_sym_s *ps; ps = ps_sym_lookup(name); if (ps) { elt_free(ps->e); } else { if (n_sym >= NSYMS) { fprintf(stderr, "svg: Too many PS symbols\n"); ps_error = 1; return NULL; } ps = &ps_sym[n_sym++]; ps->n = strdup(name); } ps->e = e; ps->exec = 0; return ps; } static void push(struct elt_s *e) { e->next = stack; stack = e; } static void stack_dump(void) { fprintf(stderr, "stack:"); if (stack) elt_lst_dump(stack); else fprintf(stderr, "(empty)"); fprintf(stderr, "\n"); } static struct elt_s *pop(int type) { struct elt_s *e; e = stack; if (!e) { fprintf(stderr, "svg pop: Stack empty\n"); ps_error = 1; return NULL; } if (e->type != type) { fprintf(stderr, "svg pop: Bad element type %d != %d\n", e->type, type); stack_dump(); ps_error = 1; return NULL; } stack = e->next; return e; } static float pop_free_val(void) { struct elt_s *e; e = pop(VAL); if (!e) return 0; e->next = free_elt; free_elt = e; return e->u.v; } static char *pop_free_str(void) { struct elt_s *e; char *s; e = pop(STR); if (!e) return NULL; s = e->u.s; e->type = VAL; e->next = free_elt; free_elt = e; return s; } /* PS condition code */ #define C_EQ 0 #define C_NE 1 #define C_GT 2 #define C_GE 3 #define C_LT 4 #define C_LE 5 static void cond(int type) { float v; char *s, *s2; if (!stack || !stack->next) { fprintf(stderr, "svg: Stack underflow in condition\n"); ps_error = 1; return; } /* string compare */ if (stack->type == STR && stack->next->type == STR) { s = pop_free_str(); s2 = stack->u.s; switch (type) { case C_EQ: stack->u.v = strcmp(s2, s) == 0; break; case C_NE: stack->u.v = strcmp(s2, s) != 0; break; default: fprintf(stderr, "svg: String condition not treated\n"); break; } free(s); free(s2); stack->type = VAL; return; } /* special case when 1 character strings */ if (stack->type == STR) { s = stack->u.s; stack->u.v = s[1]; free(s); stack->type = VAL; } if (stack->next->type == STR) { s = stack->next->u.s; stack->next->u.v = s[1]; free(s); stack->next->type = VAL; } v = pop_free_val(); if (stack->type != VAL) { fprintf(stderr, "svg: Bad type for condition\n"); ps_error = 1; return; } switch (type) { case C_EQ: stack->u.v = stack->u.v == v; break; case C_NE: stack->u.v = stack->u.v != v; break; case C_GT: stack->u.v = stack->u.v > v; break; case C_GE: stack->u.v = stack->u.v >= v; break; case C_LT: stack->u.v = stack->u.v < v; break; case C_LE: stack->u.v = stack->u.v <= v; break; } } /* output a xml string */ static void xml_str_out(char *p) { char *q, *r; for (q = p; *p != '\0';) { switch (*p++) { case '<': r = "<"; break; case '>': r = ">"; break; case '\'': r = "'"; break; case '"': r = """; break; case '&': if (*p == '#' || strncmp(p, "lt;", 3) == 0 || strncmp(p, "gt;", 3) == 0 || strncmp(p, "amp;", 4) == 0 || strncmp(p, "apos;", 5) == 0 || strncmp(p, "quot;", 5) == 0) continue; r = "&"; break; default: continue; } if (p - 1 != q) fwrite(q, 1, p - 1 - q, fout); q = p; fputs(r, fout); } if (p != q) fputs(q, fout); } /* -- output information about the generation in the XHTML/SVG headers -- */ static void gen_info(void) { unsigned i; time_t ltime; time(<ime); #ifndef WIN32 strftime(tex_buf, TEX_BUF_SZ, "%b %e, %Y %H:%M", localtime(<ime)); #else strftime(tex_buf, TEX_BUF_SZ, "%b %#d, %Y %H:%M", localtime(<ime)); #endif fprintf(fout, "\n" "\n", fout); } static void define_head(float w, float h) { static const char svg_head1[] = "\n" "\n" ""; fprintf(fout, svg_head1, w, h); if (cfmt.musicfont) { if (strchr(cfmt.musicfont, '(')) fprintf(fout, svg_font_style_url, cfmt.musicfont); else fprintf(fout, svg_font_style, cfmt.musicfont); } fputs(svg_head2, fout); } /* -- output the symbol definitions -- */ void define_svg_symbols(char *title, int num, float w, float h) { char *s; unsigned i; static const char svg_head3[] = " %s %d\n"; if (svg == 2) { /* if XHTML */ if (file_initialized <= 0) { if ((s = strrchr(in_fname, DIRSEP)) == NULL) s = in_fname; else s++; fputs("\n" "\n" "\n" "\n" "\n", fout); gen_info(); fprintf(fout, "\n" "%s\n" "\n" "\n", s); } else { fputs("
\n", fout); } define_head(w, h); xml_str_out(title); fprintf(fout, svg_head3, "page", num); // if (cfmt.bgcolor && cfmt.bgcolor[0] != '\0') // fprintf(fout, // "\n", // cfmt.bgcolor); } else { /* -g, -v or -z */ if (epsf != 3) { if (fout != stdout) fputs("\n" "\n", fout); // else if (svg) // fputs("

\n", fout); } define_head(w, h); xml_str_out(title); fprintf(fout, svg_head3, epsf ? "tune" : "page", num); fputs("\n", fout); gen_info(); if (cfmt.bgcolor && cfmt.bgcolor[0] != '\0') fprintf(fout, "\n", cfmt.bgcolor); } // reset the interpreter memset(&gcur, 0, sizeof gcur); gcur.xscale = gcur.yscale = 1; gcur.linewidth = 0.7; // default line width gcur.cos = 1; gcur.font_n = strdup(""); gcur.font_n_old = strdup(""); memcpy(&gold, &gcur, sizeof gold); x_rot = y_rot = 0; nsave = 0; for (i = 0; i < sizeof def_tb / sizeof def_tb[0]; i++) { if (def_tb[i].defined == 1) def_tb[i].defined = 0; } /* if new page, done */ if (file_initialized > 0) return; elts_reset(); n_sym = 0; in_cnt = 0; path = NULL; ps_error = 0; s = strdup("/defl 0 def\n" "/svg 1 def\n" "/dlw{0.7 SLW}def\n" "/gsc{gsave y T .8 dup scale 0 0}def\n"); svg_write(s, strlen(s)); free(s); } static void output_font(int span) { char *p, *fn; int i, imin; if (gcur.font_n[0] == '\0' && (span || !gcur.rgb)) return; fprintf(fout, " style=\""); if (!span && gcur.rgb) { fprintf(fout, "color:#%06x;", gcur.rgb); if (gcur.font_n[0] == '\0') { fprintf(fout, "\""); return; } } fprintf(fout, "font:"); fn = gcur.font_n; if (fn[0] == '/') fn++; imin = 255; p = strchr(fn, '-'); if (p) imin = p - fn; p = strstr(fn, "old"); if (p && (p[-1] == 'B' || p[-1] == 'b')) { fprintf(fout, "bold "); i = p - fn - 1; if (imin > i) imin = i; } p = strstr(fn, "talic"); if (p && (p[-1] == 'I' || p[-1] == 'i')) { fprintf(fout, "italic "); i = p - fn - 1; if (imin > i) imin = i; } p = strstr(fn, "blique"); if (p && (p[-1] == 'O' || p[-1] == 'o')) { fprintf(fout, "oblique "); i = p - fn - 1; if (imin > i) imin = i; } fprintf(fout, "%.2fpx %.*s\"", gcur.font_s, imin, fn); } static float strw(char *s) { unsigned char c; float w; w = 0; for (;;) { c = (unsigned char) *s++; if (c == '\0') break; w += cwid(c) * 1.1; } return w * gcur.font_s; } /* define the global container */ static void setg(int newg); static void defg1(void) { setg(0); fprintf(fout, "\n", gcur.dash); fprintf(fout, ">\n"); g = 1; memcpy(&gold, &gcur, sizeof gold); } /* * set the state of the containers * state: * 0: no container * 1: graphical container * 2: graphical container and text * newg: * 0: close both the text and the graphical container * 1: close only the text and reset the graphical container */ static void setg(int newg) { if (g == 2) { fputs("\n", fout); g = 1; } if (newg == 0) { if (g != 0) { fputs("\n", fout); if (gcur.rotate != 0) { gcur.xoffs = x_rot; gcur.yoffs = y_rot; x_rot = 0; y_rot = 0; } g = 0; } } else { gold.cx = gcur.cx; gold.cy = gcur.cy; if (memcmp(&gcur, &gold, sizeof gcur) != 0) defg1(); } } /* graphic path */ static void path_print(char *fmt, ...) { va_list args; char *p; va_start(args, fmt); vsnprintf(path_buf, sizeof path_buf, fmt, args); va_end(args); if (!path) { path = malloc(strlen(path_buf) + 1); p = path; } else { path = realloc(path, strlen(path) + strlen(path_buf) + 1); p = path + strlen(path); } if (!path) { fprintf(stderr, "Out of memory.\n"); exit(EXIT_FAILURE); } strcpy(p, path_buf); } static void path_def(void) { if (path) return; setg(1); path_print("\n", fout); // g = 1; // } else { setg(1); // } if (def_tb[def].defined) return; def_tb[def].defined = 1; fputs("\n", fout); i = def_tb[def].use; while (i != 0 && !def_tb[i].defined) { def_tb[i].defined = 1; fputs(def_tb[i].def, fout); i = def_tb[i].use; } fputs(def_tb[def].def, fout); fputs("\n", fout); } // SVG definition found in %%beginsvg // mark the id as defined if standard glyph // or create a PS symbol void svg_def_id(char *id, int idsz) { char *p; int i; for (i = 0; i < sizeof def_tb / sizeof def_tb[0]; i++) { p = strstr(def_tb[i].def, "id="); // (cannot be NULL) if (strncmp(p, id, idsz) == 0) { def_tb[i].defined = 2; // (don't erase) return; } } if (!defs) { defssz = 8192; defs = malloc(defssz); *defs = '\0'; } i = strlen(defs); if (idsz + i + 1 >= defssz) { defssz += 8192; defs = realloc(defs, defssz); } strncpy(defs + i, id, idsz); defs[i + idsz] = '\0'; } static void xysym(char *op, int use) { float x, y; if (use >= 0) def_use(use); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); fprintf(fout, "\n", x, y, op); } static void setxory(char *s, float v) { struct elt_s *e; struct ps_sym_s *sym; sym = ps_sym_lookup(s); if (!sym || sym->e->type != VAL) { e = elt_new(); if (!e) return; e->type = VAL; sym = ps_sym_def(s, e); if (!sym) return; } sym->e->u.v = v; } static void setxysym(char *op, int use) { float x, y; y = pop_free_val(); x = pop_free_val(); setxory("x", x); setxory("y", y); def_use(use); fprintf(fout, "\n", gcur.xoffs + x, gcur.yoffs - y, op); } /* gua gda (acciaccatura) */ static void acciac(char *op) { struct ps_sym_s *sym; float x, y, dx, dy; setg(1); dy = pop_free_val(); dx = pop_free_val(); sym = ps_sym_lookup("x"); x = gcur.xoffs + sym->e->u.v; sym = ps_sym_lookup("y"); y = gcur.yoffs - sym->e->u.v; if (op[1] == 'u') { x -= 1; y -= 4; } else { x -= 5; y += 4; } fprintf(fout, "\n", x, y, dx, -dy); } /* arp - ltr */ static void arp_ltr(char type) { float x, y, t; int n; def_use(D_ltr); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); n = (pop_free_val() + 5) / 6; if (type == 'a') { fprintf(fout, "\n"); t = x; x = -y; y = t; } y -= 4; while (--n >= 0) { fprintf(fout, "\n", x, y); x += 6; } if (type == 'a') fprintf(fout, "\n"); } // glissando static void gliss(int squiggle) { float x1, y1, x2, y2, ar, a, len; int n; if (squiggle) def_use(D_ltr); y1 = gcur.yoffs - pop_free_val(); x1 = gcur.xoffs + pop_free_val(); y2 = gcur.yoffs - pop_free_val(); x2 = gcur.xoffs + pop_free_val(); ar = atan((y2 - y1) / (x2 - x1)); a = ar / M_PI * 180; len = (x2 - x1 - 14) / cos(ar); fprintf(fout, "\n", x1, y1, a); if (squiggle) { n = (len + 2) / 6; x1 = 8; while (--n >= 0) { fprintf(fout, "\n", x1); x1 += 6; } } else { fprintf(fout, "\n", len); } fprintf(fout, "\n"); } /* sd su gd gu */ static void stem(char *op) { struct ps_sym_s *sym; float x, y, dx, h; ps_exec("dlw"); setg(1); h = pop_free_val(); if (op[0] == 's') dx = 3.5; else dx = GSTEM_XOFF; if (op[1] == 'd') dx = -dx; sym = ps_sym_lookup("x"); x = gcur.xoffs + sym->e->u.v + dx; sym = ps_sym_lookup("y"); y = gcur.yoffs - sym->e->u.v; fprintf(fout, "\n", x, y, -h); } /* * types: * s show / c showc / r showr / j jshow / b showb /x gxshow */ static void show(char type) { float x, y, w; char tmp[4], *s, *p, *q; int span; span = 0; gold.cx = gcur.cx; gold.cy = gcur.cy; if (memcmp(&gcur, &gold, sizeof gcur) != 0) { if (g == 2) span = 1; else defg1(); } x = gcur.cx; y = gcur.cy; switch (type) { case 'j': w = pop_free_val(); p = tmp; tmp[0] = '\0'; s = NULL; break; default: if (!stack) { fprintf(stderr, "svg top: Stack empty\n"); ps_error = 1; return; } if (stack->type == STR) { s = pop_free_str(); if (!s || s[0] != '(') { fprintf(stderr, "svg: No string\n"); ps_error = 1; return; } p = s + 1; /* remove '(' */ } else { p = tmp; tmp[0] = pop_free_val(); tmp[1] = '\0'; s = NULL; } w = strw(p); if (type == 'x') { /* gxshow */ w = pop_free_val(); /* inter TAB width */ q = strchr(p, '\t'); *q = '\0'; /* string after the 1st one */ } break; } if (span) { fprintf(fout, ""); } else if (g != 2) { fprintf(fout, "", fout); g = 2; } back: xml_str_out(p); if (span) fprintf(fout, ""); if (type == 'x') { p = p + strlen(p) + 1; /* next string of gxshow */ q = strchr(p, '\t'); if (q) { *q = '\0'; } else { /* restore the string width (!! tied to elt_free() !!) */ w = free_elt->u.v; type = 's'; } fprintf(fout, "", w); span = 1; goto back; } if (type == 'b') { setg(1); fprintf(fout, "\n", gcur.xoffs + gcur.cx - 2, gcur.yoffs - y - gcur.font_s + 2, w + 4, gcur.font_s + 1); } gcur.cx = x + w; if (s) free(s); } /* execute a sequence * returns 1 on 'exit' or error */ static int seq_exec(struct elt_s *e) { struct elt_s *e2; switch (e->type) { case STR: if (e->u.s[0] != '/' && e->u.s[0] != '(') { if (strcmp(e->u.s, "exit") == 0) return 1; ps_exec(e->u.s); return 0; } /* fall thru */ case VAL: case BRK: e = elt_dup(e); if (!e) return 1; push(e); return 0; } /* (e->type == SEQ) */ e = e->u.e; while (e) { switch (e->type) { case STR: if (strcmp(e->u.s, "exit") == 0) return 1; if (e->u.s[0] != '(' && e->u.s[0] != '/') { ps_exec(e->u.s); break; } /* fall thru */ default: e2 = elt_dup(e); if (!e2) return 1; push(e2); break; } e = e->next; } return 0; } /* execute a command */ /* (in case of error, a string may be not freed, but this is not important!) */ static void ps_exec(char *op) { struct ps_sym_s *sym; struct elt_s *e, *e2; float x, y, w, h; int n; char *s; if (ps_error) return; #if 0 fprintf(stderr, "%s ", op); stack_dump(); #endif sym = ps_sym_lookup(op); if (sym) { if (++sym->exec > 2) { fprintf(stderr, "svg: Too many recursions of '%s'\n", op); ps_error = 1; return; } seq_exec(sym->e); sym->exec--; return; } if (*op == ' ') /* load */ op++; switch (*op) { case '!': /* def */ if (op[1] == '\0') { if (!stack) { fprintf(stderr, "svg def: Stack empty\n"); ps_error = 1; return; } e = pop(stack->type); /* value */ s = pop_free_str(); /* symbol */ if (!s || *s != '/') { fprintf(stderr, "svg def: No / bad symbol\n"); if (s) free(s); ps_error = 1; return; } ps_sym_def(&s[1], e); free(s); return; } break; case 'a': if (strcmp(op, "accent") == 0) { xysym(op, D_accent); return; } if (strcmp(op, "abs") == 0) { if (!stack || stack->type != VAL) { fprintf(stderr, "svg abs: Bad value\n"); ps_error = 1; return; } if (stack->u.v < 0) stack->u.v = -stack->u.v; return; } if (strcmp(op, "add") == 0) { x = pop_free_val(); if (!stack || stack->type != VAL) { fprintf(stderr, "svg add: Bad value\n"); ps_error = 1; return; } stack->u.v += x; return; } if (strcmp(op, "and") == 0) { x = pop_free_val(); if (!stack || stack->type != VAL) { fprintf(stderr, "svg and: Bad value\n"); ps_error = 1; return; } stack->u.v = (int) x & (int) stack->u.v; return; } if (strcmp(op, "anshow") == 0) { show('s'); return; } if (strcmp(op, "arc") == 0 || strcmp(op, "arcn") == 0) { float r, a1, a2, x1, y1, x2, y2; a2 = pop_free_val(); a1 = pop_free_val(); r = pop_free_val(); if (r < 0) { fprintf(stderr, "svg arc: Bad value\n"); ps_error = 1; return; } if (a1 >= 360) a1 -= 360; if (a2 >= 360) a2 -= 360; y = pop_free_val(); x = pop_free_val(); x1 = x + r * cos(a1 * M_PI / 180); y1 = y + r * sinf(a1 * M_PI / 180); if (gcur.cx != NaN) { // if no newpath if (path) { path_print("\n\t%c%.2f %.2f", x1 != gcur.cx || y1 != gcur.cy ? 'l' : 'm', x1 - gcur.cx, -(y1 - gcur.cy)); } else { gcur.cx = x1; gcur.cy = y1; path_def(); } } else { gcur.cx = x1; gcur.cy = y1; path_def(); } if (a1 == a2) { /* circle */ a2 = 180 - a1; x2 = x + r * cosf(a2 * M_PI / 180); y2 = y + r * sinf(a2 * M_PI / 180); path_print("\n\ta%.2f %.2f 0 0 %d %.2f %.2f " "%.2f %.2f 0 0 %d %.2f %.2f\n", r, r, op[3] == 'n', x2 - x1, -(y2 - y1), r, r, op[3] == 'n', x1 - x2, -(y1 - y2)); gcur.cx = x1; gcur.cy = y1; } else { x2 = x + r * cosf(a2 * M_PI / 180); y2 = y + r * sinf(a2 * M_PI / 180); path_print("\n\ta%.2f %.2f 0 0 %d %.2f %.2f\n", r, r, op[3] == 'n', x2 - x1, -(y2 - y1)); gcur.cx = x2; gcur.cy = y2; } return; } if (strcmp(op, "arp") == 0) { arp_ltr('a'); return; } if (strcmp(op, "atan") == 0) { x = pop_free_val(); /* den */ if (!stack || stack->type != VAL || x == 0) { fprintf(stderr, "svg atan: Bad value\n"); ps_error = 1; return; } y = stack->u.v; /* num */ stack->u.v = atan(y / x) / M_PI * 180; return; } break; case 'b': if (strcmp(op, "bar") == 0) { setg(1); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); h = pop_free_val(); fprintf(fout, "\n", x, y, -h); return; } if (strcmp(op, "bclef") == 0) { xysym(op, D_bclef); return; } if (strcmp(op, "bdef") == 0) { ps_exec("!"); return; } if (strcmp(op, "bind") == 0) { return; } if (strcmp(op, "bitshift") == 0) { int shift; shift = pop_free_val(); if (!stack || stack->type != VAL || shift >= 32 || shift < -32) { fprintf(stderr, "svg: Bad value for bitshift\n"); ps_error = 1; return; } if (shift > 0) n = (int) stack->u.v << shift; else n = (int) stack->u.v >> -shift; stack->u.v = n; return; } if (strcmp(op, "bm") == 0) { float dx, dy; setg(1); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); dy = pop_free_val(); dx = pop_free_val(); h = pop_free_val(); fprintf(fout, "\n", x, y, dx, -dy, h,-dx, dy); return; } if (strcmp(op, "bnum") == 0 || strcmp(op, "bnumb") == 0) { setg(1); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); s = pop_free_str(); if (!s) { fprintf(stderr, "svg: No string\n"); ps_error = 1; return; } if (op[4] == 'b') { w = 7 * strlen(s); fprintf(fout, "\n", x - w / 2, y - 10, w); } fprintf(fout, "%s\n", x, y, s + 1); free(s); return; } if (strcmp(op, "box") == 0) { setg(1); h = pop_free_val(); w = pop_free_val(); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); fprintf(fout, "\n", x, y - h, w, h); return; } if (strcmp(op, "boxdraw") == 0) { setg(1); h = pop_free_val(); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); fprintf(fout, "\n", x, y - h, boxend - (x - gcur.xoffs) + 2, h); return; } if (strcmp(op, "boxmark") == 0) { if (gcur.cx > boxend) boxend = gcur.cx; return; } if (strcmp(op, "boxend") == 0) { boxend = gcur.cx; return; } if (strcmp(op, "brace") == 0) { def_use(D_brace); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); h = pop_free_val() * 0.01; fprintf(fout, "\n" " \n" "\n", x, y, h); return; } if (strcmp(op, "bracket") == 0) { setg(1); y = gcur.yoffs - pop_free_val() - 3; x = gcur.xoffs + pop_free_val() - 5; h = pop_free_val() + 2; fprintf(fout, "\n", x, y, h); return; } if (strcmp(op, "breve") == 0) { setxysym(op, D_breve); return; } if (strcmp(op, "brth") == 0) { setg(1); y = gcur.yoffs - pop_free_val() - 6; x = gcur.xoffs + pop_free_val(); fprintf(fout, "" ",\n", x, y); return; } break; case 'C': if (strcmp(op, "C") == 0) { float c1, c2, c3, c4; curveto: path_def(); y = pop_free_val(); x = pop_free_val(); c4 = gcur.yoffs - pop_free_val(); c3 = gcur.xoffs + pop_free_val(); c2 = gcur.yoffs - pop_free_val(); c1 = gcur.xoffs + pop_free_val(); path_print("\tC%.2f %.2f %.2f %.2f %.2f %.2f\n", c1, c2, c3, c4, gcur.xoffs + x, gcur.yoffs - y); gcur.cx = x; gcur.cy = y; return; } break; case 'c': if (strcmp(op, "cclef") == 0) { xysym(op, D_cclef); return; } if (strcmp(op, "csig") == 0) { xysym(op, D_csig); return; } if (strcmp(op, "ctsig") == 0) { xysym(op, D_ctsig); return; } if (strcmp(op, "coda") == 0) { xysym(op, D_coda); return; } if (strcmp(op, "closepath") == 0) { if (path) { // path_def(); path_print("\tz"); } return; } if (strcmp(op, "composefont") == 0) { pop(BRK); pop(STR); return; } if (strcmp(op, "copy") == 0) { struct elt_s *e3; n = pop_free_val(); if ((unsigned) n > 10) { fprintf(stderr, "svg copy: Too wide\n"); ps_error = 1; return; } e = stack; e2 = NULL; while (--n >= 0) { if (!e) break; e3 = elt_dup(e); if (!e3) return; e3->next = e2; e2 = e3; e = e->next; } if (n >= 0) { fprintf(stderr, "svg copy: Stack empty\n"); ps_error = 1; return; } while (e2) { e3 = e2->next; push(e2); e2 = e3; } return; } if (strcmp(op, "cos") == 0) { if (!stack || stack->type != VAL) { fprintf(stderr, "svg cos: Bad value\n"); ps_error = 1; return; } stack->u.v = cos(stack->u.v * M_PI / 180); return; } if (strcmp(op, "cpu") == 0) { xysym(op, D_cpu); return; } if (strcmp(op, "crdc") == 0) { setg(1); y = gcur.yoffs - pop_free_val() - 5; x = gcur.xoffs + pop_free_val(); s = pop_free_str(); if (!s) { fprintf(stderr, "svg crdc: No string\n"); ps_error = 1; return; } fprintf(fout, "%s\n", x, y, s + 1); free(s); return; } if (strcmp(op, "cresc") == 0) { setg(1); y = gcur.yoffs - pop_free_val() - 5; x = gcur.xoffs + pop_free_val(); w = pop_free_val(); sym = ps_sym_lookup("defl"); x += w; if ((int) sym->e->u.v & 1) fprintf(fout, "\n", x, y, -w, w); else fprintf(fout, "\n", x, y, -w, w); return; } if (strcmp(op, "custos") == 0) { xysym(op, D_custos); return; } if (strcmp(op, "currentgray") == 0) { e = elt_new(); if (!e) return; e->type = VAL; e->u.v = (float) gcur.rgb / 0xffffff; push(e); return; } if (strcmp(op, "currentpoint") == 0) { e = elt_new(); if (!e) return; e->type = VAL; e->u.v = gcur.cx; push(e); e = elt_new(); if (!e) return; e->type = VAL; e->u.v = gcur.cy; push(e); return; } if (strcmp(op, "curveto") == 0) goto curveto; if (strcmp(op, "cvi") == 0) { if (!stack || stack->type != VAL) { fprintf(stderr, "svg cvi: Bad value\n"); ps_error = 1; return; } n = stack->u.v; stack->u.v = n; return; } if (strcmp(op, "cvx") == 0) { s = pop_free_str(); if (!s || ((*s != '/') && (*s != '('))) { fprintf(stderr, "svg cvx: No / bad string\n"); if (s) free(s); ps_error = 1; return; } *s = '{'; svg_write(s, strlen(s)); svg_write("}", 1); free(s); return; } break; case 'd': if (strcmp(op, "dacs") == 0) { setg(1); y = gcur.yoffs - pop_free_val() - 3; x = gcur.xoffs + pop_free_val(); s = pop_free_str(); if (!s) { fprintf(stderr, "svg dacs: No string\n"); ps_error = 1; return; } fprintf(fout, "%s\n", x, y, s + 1); free(s); return; } if (strcmp(op, "dacoda") == 0) { setg(1); e = elt_dup(stack); y = gcur.yoffs - pop_free_val() - 7; e2 = elt_dup(stack); e2->u.v += 10; x = gcur.xoffs + pop_free_val() - 10; fprintf(fout, "Da\n", x, y); push(e2); push(e); xysym("coda", D_coda); return; } if (strcmp(op, "def") == 0) { ps_exec("!"); return; } if (strcmp(op, "dim") == 0) { setg(1); y = gcur.yoffs - pop_free_val() - 5; x = gcur.xoffs + pop_free_val(); w = pop_free_val(); sym = ps_sym_lookup("defl"); if ((int) sym->e->u.v & 2) fprintf(fout, "\n", x, y, w, -w); else fprintf(fout, "\n", x, y, w, -w); return; } if (strcmp(op, "div") == 0) { x = pop_free_val(); if (!stack || stack->type != VAL || x == 0) { fprintf(stderr, "svg: Bad value for div\n"); ps_error = 1; return; } stack->u.v /= x; return; } if (strcmp(op, "dnb") == 0) { xysym(op, D_dnb); return; } if (strcmp(op, "dplus") == 0) { xysym(op, D_dplus); return; } if (strcmp(op, "dSL") == 0) { float a1, a2, a3, a4, a5, a6, m1, m2; setg(1); m2 = gcur.yoffs - pop_free_val(); m1 = gcur.xoffs + pop_free_val(); a6 = pop_free_val(); a5 = pop_free_val(); a4 = pop_free_val(); a3 = pop_free_val(); a2 = pop_free_val(); a1 = pop_free_val(); fprintf(fout, "\n", m1, m2, a1, -a2, a3, -a4, a5, -a6); return; } if (strcmp(op, "dt") == 0) { setg(1); sym = ps_sym_lookup("x"); x = gcur.xoffs + sym->e->u.v; sym = ps_sym_lookup("y"); y = gcur.yoffs - sym->e->u.v; y -= pop_free_val(); x += pop_free_val(); fprintf(fout, "\n", x, y); return; } if (strcmp(op, "dotbar") == 0) { setg(1); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); h = pop_free_val(); fprintf(fout, "\n", x, y, -h); return; } if (strcmp(op, "dup") == 0) { if (!stack) { fprintf(stderr, "svg dup: Stack empty\n"); ps_error = 1; return; } e = elt_dup(stack); if (e) push(e); return; } if (strcmp(op, "dft0") == 0) { xysym(op, D_dft0); return; } if (strcmp(op, "dsh0") == 0) { xysym(op, D_dsh0); return; } break; case 'e': if (strcmp(op, "emb") == 0) { xysym(op, D_emb); return; } if (strcmp(op, "eofill") == 0) { if (!path) { fprintf(stderr, "svg eofill: No path\n"); ps_error = 1; return; } path_end(); fprintf(fout, "\t\" fill-rule=\"evenodd\" class=\"fill\"/>\n"); return; } if (strcmp(op, "eq") == 0) { cond(C_EQ); return; } if (strcmp(op, "exch") == 0) { if (!stack || !stack->next) { fprintf(stderr, "svg exch: Stack empty\n"); ps_error = 1; return; } e = stack->next; stack->next = e->next; e->next = stack; stack = e; return; } if (strcmp(op, "exec") == 0) { e = pop(SEQ); if (!e) return; seq_exec(e); elt_free(e); return; } break; case 'F': if (sscanf(op, "F%d", &n) == 1) { h = pop_free_val(); if (!fontnames[n]) break; if (gcur.font_s != h || strcmp(fontnames[n], gcur.font_n) != 0) { free(gcur.font_n_old); gcur.font_n_old = gcur.font_n; gcur.font_n = strdup(fontnames[n]); gcur.font_s = h; gold.font_n = NULL; } return; } break; case 'f': if (strcmp(op, "false") == 0) { e = elt_new(); if (!e) return; e->type = VAL; e->u.v = 0; push(e); return; } if (strcmp(op, "fill") == 0) { if (!path) { fprintf(stderr, "svg fill: No path\n"); // ps_error = 1; return; } path_end(); fprintf(fout, "\t\" class=\"fill\"/>\n"); return; } if (strcmp(op, "findfont") == 0) { s = pop_free_str(); if (!s || *s != '/') { fprintf(stderr, "svg findfont: No / bad font\n"); if (s) free(s); ps_error = 1; return; } if (strcmp(s, gcur.font_n) != 0) { free(gcur.font_n_old); gcur.font_n_old = gcur.font_n; gcur.font_n = s; gold.font_n = NULL; } else { free(s); } return; } if (strcmp(op, "fng") == 0) { setg(1); y = gcur.yoffs - pop_free_val() - 1; x = gcur.xoffs + pop_free_val() - 3; s = pop_free_str(); if (!s) { fprintf(stderr, "svg fng: No string\n"); ps_error = 1; return; } fprintf(fout, "%s\n", x, y, s + 1); free(s); return; } if (strcmp(op, "for") == 0) { float init, incr, limit; e = pop(SEQ); /* proc */ if (!e) return; limit = pop_free_val(); incr = pop_free_val(); init = pop_free_val(); if (incr == 0 || (limit - init) / incr > 100) { fprintf(stderr, "svg for: Bad values\n"); ps_error = 1; return; } if (incr > 0) { while (init <= limit) { e2 = elt_new(); if (!e2) break; e2->type = VAL; e2->u.v = init; push(e2); if (seq_exec(e) != 0) break; init += incr; } } else { while (init >= limit) { e2 = elt_new(); if (!e2) break; e2->type = VAL; e2->u.v = init; push(e2); if (seq_exec(e) != 0) break; init += incr; } } elt_free(e); return; } if (strcmp(op, "forall") == 0) { struct elt_s *e3; unsigned char *p; e = pop(SEQ); /* proc */ if (!e) return; e2 = stack; /* array/string */ if (!e2) { fprintf(stderr, "svg forall: Stack empty\n"); ps_error = 1; return; } stack = e2->next; switch (e2->type) { case STR: p = (unsigned char *) &e2->u.s[1]; while (*p != '\0') { e3 = elt_new(); if (!e3) return; e3->u.v = *p++; push(e3); if (seq_exec(e) != 0) break; } break; case BRK: for (e3 = e2->u.e; e3; e3 = e3->next) { struct elt_s *e4; e4 = elt_dup(e3); push(e4); if (seq_exec(e) != 0) break; } break; default: fprintf(stderr, "svg forall: Bad any\n"); ps_error = 1; return; } elt_free(e); elt_free(e2); return; } if (strcmp(op, "ft0") == 0) { xysym(op, D_ft0); return; } if (strcmp(op, "ft1") == 0) { xysym(op, D_ft1); return; } if (strcmp(op, "ft4") == 0) { n = pop_free_val(); switch (n) { case 1: xysym("ft1", D_ft1); break; case 2: xysym("ft0", D_ft0); break; case 3: xysym("ft513", D_ft513); break; default: xysym("dft0", D_dft0); break; } return; } if (strcmp(op, "ft513") == 0) { xysym(op, D_ft513); return; } break; case 'g': if (strcmp(op, "gcshow") == 0) { show('s'); return; } if (strcmp(op, "ge") == 0) { cond(C_GE); return; } if (strcmp(op, "get") == 0) { n = pop_free_val(); if (!stack) { fprintf(stderr, "svg get: Stack empty\n"); ps_error = 1; return; } switch (stack->type) { case VAL: if (n != 0) { fprintf(stderr, "svg get: Out of bounds\n"); ps_error = 1; return; } return; case STR: s = stack->u.s; if (!s || *s != '(') { fprintf(stderr, "svg get: Not a string\n"); if (s) free(s); ps_error = 1; return; } if ((unsigned) n >= strlen(s) - 1) { fprintf(stderr, "svg get: Out of bounds\n"); ps_error = 1; return; } stack->type = VAL; stack->u.v = s[n + 1]; free(s); return; } e = stack->u.e; e2 = NULL; while (--n >= 0) { if (!e) break; e2 = e; e = e->next; } if (!e) { fprintf(stderr, "svg get: Out of bounds\n"); ps_error = 1; return; } if (!e2) stack->u.e = e->next; else e2->next = e->next; e->next = stack->next; elt_free(stack); stack = e; return; } if (strcmp(op, "getinterval") == 0) { int count; count = pop_free_val(); n = pop_free_val(); s = pop_free_str(); if (!s || *s != '(') { fprintf(stderr, "svg getinterval: No string\n"); if (s) free(s); ps_error = 1; return; } if ((unsigned) n >= strlen(s) || (unsigned) count >= strlen(s) - n) { fprintf(stderr, "svg getinterval: Out of bounds\n"); ps_error = 1; return; } e = elt_new(); if (!e) return; e->type = STR; e->u.s = malloc(count + 2); e->u.s[0] = '('; memcpy(&e->u.s[1], &s[n + 1], count); e->u.s[count + 1] = '\0'; push(e); free(s); return; } if (strcmp(op, "ghd") == 0) { setxysym(op, D_ghd); return; } if (strcmp(op, "ghl") == 0) { xysym(op, D_ghl); return; } if (strcmp(op, "glisq") == 0 || strcmp(op, "gliss") == 0) { gliss(op[4] == 'q'); return; } if (strcmp(op, "gt") == 0) { cond(C_GT); return; } if (strcmp(op, "gu") == 0 || strcmp(op, "gd") == 0) { stem(op); return; } if (strcmp(op, "gua") == 0 || strcmp(op, "gda") == 0) { acciac(op); return; } if (strcmp(op, "grestore") == 0) { if (nsave <= 0) { fprintf(stderr, "svg grestore: No gsave\n"); ps_error = 1; return; } setg(1); free(gcur.font_n); free(gcur.font_n_old); memcpy(&gcur, &gsave[--nsave], sizeof gcur); return; } if (strcmp(op, "grm") == 0) { xysym(op, D_grm); return; } if (strcmp(op, "gsave") == 0) { if (nsave >= (int) (sizeof gsave / sizeof gsave[0])) { fprintf(stderr, "svg grestore: Too many gsave's\n"); ps_error = 1; return; } // setg(1); memcpy(&gsave[nsave++], &gcur, sizeof gsave[0]); gcur.font_n = strdup(gcur.font_n); gcur.font_n_old = strdup(gcur.font_n_old); return; } if (strcmp(op, "gsl") == 0) { float a1, a2, a3, a4, a5, a6, m1, m2; setg(1); m2 = gcur.yoffs - pop_free_val(); m1 = gcur.xoffs + pop_free_val(); a6 = pop_free_val(); a5 = pop_free_val(); a4 = pop_free_val(); a3 = pop_free_val(); a2 = pop_free_val(); a1 = pop_free_val(); fprintf(fout, "\n", m1, m2, a1, -a2, a3, -a4, a5, -a6); return; } if (strcmp(op, "gxshow") == 0) { show('x'); return; } break; case 'H': if (strcmp(op, "Hd") == 0) { setxysym(op, D_Hd); return; } if (strcmp(op, "HD") == 0) { setxysym(op, D_HD); return; } if (strcmp(op, "HDD") == 0) { setxysym(op, D_HDD); return; } break; case 'h': if (strcmp(op, "hd") == 0) { setxysym(op, D_hd); return; } if (strcmp(op, "hl") == 0) { xysym(op, D_hl); return; } if (strcmp(op, "hl1") == 0) { xysym(op, D_hl1); return; } if (strcmp(op, "hl2") == 0) { xysym(op, D_hl2); return; } if (strcmp(op, "hld") == 0) { xysym(op, D_hld); return; } if (strcmp(op, "hyph") == 0) { int d; setg(1); y = pop_free_val(); x = pop_free_val(); w = pop_free_val(); d = 25 + (int) w / 20 * 3; n = (w - 15.) / d; x += (w - d * n - 5) / 2; fprintf(fout, "\n", d - 5, gcur.xoffs + x, gcur.yoffs - y - gcur.font_s * 0.3, d * n + 5); return; } break; case 'i': if (strcmp(op, "idiv") == 0) { n = pop_free_val(); if (!stack || stack->type != VAL || n == 0) { fprintf(stderr, "svg idiv: Bad value\n"); ps_error = 1; return; } n = (int) stack->u.v / n; stack->u.v = n; return; } if (strcmp(op, "if") == 0) { e = pop(SEQ); /* sequence */ if (!e) return; n = pop_free_val(); /* condition */ if (n != 0) seq_exec(e); elt_free(e); return; } if (strcmp(op, "ifelse") == 0) { e2 = pop(SEQ); /* sequence 2 */ e = pop(SEQ); /* sequence 1 */ if (!e || !e2) return; n = pop_free_val(); /* condition */ if (n != 0) seq_exec(e); else seq_exec(e2); elt_free(e); elt_free(e2); return; } if (strcmp(op, "imsig") == 0) { xysym(op, D_imsig); return; } if (strcmp(op, "iMsig") == 0) { xysym(op, D_iMsig); return; } if (strcmp(op, "index") == 0) { n = pop_free_val(); e = stack; while (--n >= 0) { if (!e) break; e = e->next; } if (!e) { fprintf(stderr, "svg index: Stack empty\n"); ps_error = 1; return; } e = elt_dup(e); if (!e) return; push(e); return; } break; case 'j': if (strcmp(op, "jshow") == 0) { show('j'); return; } break; case 'L': if (strcmp(op, "L") == 0) { lineto: path_def(); y = pop_free_val(); x = pop_free_val(); if (x == gcur.cx) path_print("\tv%.2f\n", gcur.cy - y); else if (y == gcur.cy) path_print("\th%.2f\n", x - gcur.cx); else path_print("\tl%.2f %.2f\n", x - gcur.cx, gcur.cy - y); gcur.cx = x; gcur.cy = y; return; } case 'l': if (strcmp(op, "le") == 0) { cond(C_LE); return; } if (strcmp(op, "lt") == 0) { cond(C_LT); return; } if (strcmp(op, "length") == 0) { s = pop_free_str(); if (!s || *s != '(') { fprintf(stderr, "svg length: No string\n"); if (s) free(s); ps_error = 1; return; } e = elt_new(); if (!e) return; e->type = VAL; e->u.v = strlen(s + 1); push(e); free(s); return; } if (strcmp(op, "lineto") == 0) goto lineto; if (strcmp(op, "lmrd") == 0) { xysym(op, D_lmrd); return; } if (strcmp(op, "load") == 0) { s = pop_free_str(); if (!s || *s != '/') { fprintf(stderr, "svg load: No / bad symbol\n"); if (s) free(s); ps_error = 1; return; } sym = ps_sym_lookup(s + 1); if (!sym) { e = elt_new(); if (!e) return; e->type = STR; e->u.s = strdup(s); e->u.s[0] = ' '; /* internal */ } else { e = elt_dup(sym->e); if (!e) return; } free(s); push(e); return; } if (strcmp(op, "longa") == 0) { setxysym(op, D_longa); return; } if (strcmp(op, "lphr") == 0) { xysym(op, D_lphr); return; } if (strcmp(op, "ltr") == 0) { arp_ltr('l'); return; } if (strcmp(op, "lyshow") == 0) { show('s'); return; } break; case 'M': if (strcmp(op, "M") == 0) { moveto: gcur.cy = pop_free_val(); gcur.cx = pop_free_val(); if (path) { path_print("\tM%.2f %.2f\n", gcur.xoffs + gcur.cx, gcur.yoffs - gcur.cy); } else if (g == 2) { fputs("\n", fout); g = 1; } return; } break; case 'm': if (strcmp(op, "marcato") == 0) { xysym(op, D_marcato); return; } if (strcmp(op, "moveto") == 0) goto moveto; if (strcmp(op, "mphr") == 0) { xysym(op, D_mphr); return; } if (strcmp(op, "mod") == 0) { x = pop_free_val(); if (!stack || stack->type != VAL || x == 0) { fprintf(stderr, "svg: Bad value for mod\n"); ps_error = 1; return; } n = (int) stack->u.v % (int) x; stack->u.v = n; return; } if (strcmp(op, "mrep") == 0) { xysym(op, D_mrep); return; } if (strcmp(op, "mrep2") == 0) { xysym(op, D_mrep2); return; } if (strcmp(op, "mrest") == 0) { #if 1 xysym(op, D_mrest); return; #else def_use(D_mrest); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); s = pop_free_str(); if (!s) { fprintf(stderr, "svg: No string\n"); ps_error = 1; return; } fprintf(fout, "\n" "%s\n", x, y, x, y - 28, s + 1); free(s); #endif return; } if (strcmp(op, "mul") == 0) { x = pop_free_val(); if (!stack || stack->type != VAL) { fprintf(stderr, "svg: Bad value for mul\n"); ps_error = 1; return; } stack->u.v *= x; return; } break; case 'n': if (strcmp(op, "ne") == 0) { cond(C_NE); return; } if (strcmp(op, "neg") == 0) { if (!stack || stack->type != VAL) { fprintf(stderr, "svg: Bad value for neg\n"); ps_error = 1; return; } stack->u.v = -stack->u.v; return; } if (strcmp(op, "newpath") == 0) { // path_def(); gcur.cx = NaN; return; } if (strcmp(op, "nt0") == 0) { xysym(op, D_nt0); return; } break; case 'o': if (strcmp(op, "o8va") == 0) { setg(1); y = gcur.yoffs - pop_free_val() - 5; x = gcur.xoffs + pop_free_val(); w = pop_free_val(); sym = ps_sym_lookup("defl"); if (!((int) sym->e->u.v & 1)) { fprintf(fout, "8" "va\n", x - 5, y); x += 14; w -= 14; } else { w -= 5; } y -= 6; fprintf(fout, "\n", x, y, w); if (!((int) sym->e->u.v & 2)) fprintf(fout, "\n", x + w, y); return; } if (strcmp(op, "o8vb") == 0) { setg(1); y = gcur.yoffs - pop_free_val() - 5; x = gcur.xoffs + pop_free_val(); w = pop_free_val(); sym = ps_sym_lookup("defl"); if (!((int) sym->e->u.v & 1)) { fprintf(fout, "8" "vb\n", x - 5, y); x += 8; w -= 8; } else { w -= 5; } fprintf(fout, "\n", x, y, w); if (!((int) sym->e->u.v & 2)) fprintf(fout, "\n", x + w, y); return; } if (strcmp(op, "oct") == 0) { setg(1); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); fprintf(fout, "8\n", x, y); return; } if (strcmp(op, "opend") == 0) { xysym(op, D_opend); return; } if (strcmp(op, "or") == 0) { x = pop_free_val(); if (!stack || stack->type != VAL) { fprintf(stderr, "svg or: Bad value\n"); ps_error = 1; return; } stack->u.v = (int) x & (int) stack->u.v; return; } break; case 'p': if (strcmp(op, "pclef") == 0) { xysym(op, D_pclef); return; } if (strcmp(op, "ped") == 0) { xysym(op, D_ped); return; } if (strcmp(op, "pedoff") == 0) { xysym(op, D_pedoff); return; } if (strcmp(op, "pf") == 0) { setg(1); y = gcur.yoffs - pop_free_val() - 5; x = gcur.xoffs + pop_free_val(); s = pop_free_str(); if (!s) { fprintf(stderr, "svg pf: No string\n"); ps_error = 1; return; } fprintf(fout, "%s\n", x, y, s + 1); free(s); return; } if (strcmp(op, "pmsig") == 0) { xysym(op, D_pmsig); return; } if (strcmp(op, "pMsig") == 0) { xysym(op, D_pMsig); return; } if (strcmp(op, "pop") == 0) { if (!stack) { fprintf(stderr, "svg pop: Stack empty\n"); ps_error = 1; return; } e = pop(stack->type); elt_free(e); return; } if (strcmp(op, "pshhd") == 0) { setxysym(op, D_pshhd); return; } if (strcmp(op, "pdshhd") == 0) { setxysym("pshhd", D_pshhd); return; } if (strcmp(op, "pfthd") == 0) { setxysym(op, D_pfthd); return; } if (strcmp(op, "pdfthd") == 0) { setxysym("pfthd", D_pfthd); return; } #if 0 //fixme: cannot work because duplication... if (strcmp(op, "put") == 0) { int v; v = pop_free_val(); n = pop_free_val(); if (!stack) { fprintf(stderr, "svg put: Stack empty\n"); ps_error = 1; return; } s = pop_free_str(); if (!s || *s != '(') { fprintf(stderr, "svg put: No string\n"); if (s) free(s); ps_error = 1; return; } if ((unsigned) n >= strlen(s) - 1) { fprintf(stderr, "svg put: Out of bounds\n"); if (s) free(s); ps_error = 1; return; } //fixme: should keep the original string... s[n + 1] = v; free(s); return; } #endif break; case 'R': if (strcmp(op, "RC") == 0) { float c1, c2, c3, c4; rcurveto: path_def(); y = pop_free_val(); x = pop_free_val(); c4 = pop_free_val(); c3 = pop_free_val(); c2 = pop_free_val(); c1 = pop_free_val(); path_print("\tc%.2f %.2f %.2f %.2f %.2f %.2f\n", c1, -c2, c3, -c4, x, -y); gcur.cx += x; gcur.cy += y; return; } if (strcmp(op, "RL") == 0) { rlineto: path_def(); y = pop_free_val(); x = pop_free_val(); if (x == 0) path_print("\tv%.2f\n", -y); else if (y == 0) path_print("\th%.2f\n", x); else path_print("\tl%.2f %.2f\n", x, -y); gcur.cx += x; gcur.cy += y; return; } if (strcmp(op, "RM") == 0) { rmoveto: y = pop_free_val(); x = pop_free_val(); if (path) { path_print("\tm%.2f %.2f\n", x, -y); } else if (g == 2) { fputs("\n", fout); g = 1; } gcur.cx += x; gcur.cy += y; return; } break; case 'r': if (strcmp(op, "r00") == 0) { setxysym(op, D_r00); return; } if (strcmp(op, "r0") == 0) { setxysym(op, D_r0); return; } if (strcmp(op, "r1") == 0) { setxysym(op, D_r1); return; } if (strcmp(op, "r2") == 0) { setxysym(op, D_r2); return; } if (strcmp(op, "r4") == 0) { setxysym(op, D_r4); return; } if (strcmp(op, "r8") == 0) { setxysym(op, D_r8); return; } if (strcmp(op, "r16") == 0) { setxysym(op, D_r16); return; } if (strcmp(op, "r32") == 0) { setxysym(op, D_r32); return; } if (strcmp(op, "r64") == 0) { setxysym(op, D_r64); return; } if (strcmp(op, "r128") == 0) { setxysym(op, D_r128); return; } if (strcmp(op, "rdots") == 0) { xysym(op, D_rdots); return; } if (strcmp(op, "rcurveto") == 0) goto rcurveto; if (strcmp(op, "rlineto") == 0) goto rlineto; if (strcmp(op, "rmoveto") == 0) goto rmoveto; if (strcmp(op, "roll") == 0) { int i, j; j = pop_free_val(); n = pop_free_val(); if (n <= 0) { fprintf(stderr, "svg roll: Invalid value\n"); ps_error = 1; return; } if (j > 0) { j = j % n; if (j > n / 2) j -= n; } else if (j < 0) { j = -(-j % n); if (j < -n / 2) j += n; } if (j == 0) return; e2 = stack; /* check the stack */ i = n; for (;;) { if (!e2) { fprintf(stderr, "svg roll: Stack empty\n"); ps_error = 1; return; } if (--i <= 0) break; e2 = e2->next; } if (j > 0) { while (j-- > 0) { e = stack; stack = e->next; e->next = e2->next; e2->next = e; e2 = e; } return; } while (j++ < 0) { e = stack; for (i = 0; i < n - 2; i++) e = e->next; e2 = e->next; e->next = e2->next; e2->next = stack; stack = e2; } return; } if (strcmp(op, "repbra") == 0) { int i; setg(1); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); w = pop_free_val(); i = pop_free_val(); h = pop_free_val(); s = pop_free_str(); if (!s) { fprintf(stderr, "svg repbra: No string\n"); ps_error = 1; return; } fprintf(fout, "", x + 4, y - h); xml_str_out(s + 1); fprintf(fout, "\n" "\n"); free(s); return; } if (strcmp(op, "repeat") == 0) { e = pop(SEQ); /* sequence */ if (!e) return; n = pop_free_val(); /* n times */ if ((unsigned) n >= 100) { fprintf(stderr, "svg repeat: Too high value\n"); ps_error = 1; } while (--n >= 0) { if (seq_exec(e)) break; /* exit */ if (ps_error) break; } elt_free(e); return; } if (strcmp(op, "rotate") == 0) { float x, y, _sin, _cos; setg(0); // convert orig and currentpoint coord to absolute coord x = gcur.xoffs; y = -gcur.yoffs; _sin = gcur.sin; _cos = gcur.cos; gcur.xoffs = x * _cos + y * _sin; gcur.yoffs = -x * _sin + y * _cos; // PS orientation x = gcur.cx * _cos + gcur.cy * _sin; y = -gcur.cx * _sin + gcur.cy * _cos; // rotate gcur.rotate -= pop_free_val(); if (gcur.rotate > 180) gcur.rotate -= 360; else if (gcur.rotate <= -180) gcur.rotate += 360; h = gcur.rotate * M_PI / 180; gcur.sin = _sin = sin(h); gcur.cos = _cos = cos(h); gcur.cx = x * _cos - y * _sin; gcur.cy = x * _sin + y * _cos; x = gcur.xoffs; y = gcur.yoffs; gcur.xoffs = x * _cos - y * _sin; gcur.yoffs = -(x * _sin + y * _cos); // SVG orientation return; } break; case 'S': if (strcmp(op, "SL") == 0) { float c1, c2, c3, c4, c5, c6, l2; float a1, a2, a3, a4, a5, a6, m1, m2; setg(1); m2 = gcur.yoffs - pop_free_val(); m1 = gcur.xoffs + pop_free_val(); a6 = pop_free_val(); a5 = pop_free_val(); a4 = pop_free_val(); a3 = pop_free_val(); a2 = pop_free_val(); a1 = pop_free_val(); l2 = pop_free_val(); pop_free_val(); // always '0' c6 = pop_free_val(); c5 = pop_free_val(); c4 = pop_free_val(); c3 = pop_free_val(); c2 = pop_free_val(); c1 = pop_free_val(); fprintf(fout, "\n", m1, m2, a1, -a2, a3, -a4, a5, -a6, -l2, c1, -c2, c3, -c4, c5, -c6); return; } if (strcmp(op, "SLW") == 0) { gcur.linewidth = pop_free_val(); return; } break; case 's': if (strcmp(op, "scale") == 0) { y = pop_free_val(); x = pop_free_val(); gcur.xoffs /= x; gcur.yoffs /= y; gcur.cx /= x; gcur.cy /= y; gcur.xscale *= x; gcur.yscale *= y; return; } if (strcmp(op, "scalefont") == 0) { gcur.font_s = pop_free_val(); return; } if (strcmp(op, "search") == 0) { char *p; e = pop(STR); /* seek */ e2 = pop(STR); /* string */ if (!e || !e2 || e->u.s[0] != '(' || e2->u.s[0] != '(') { fprintf(stderr, "svg search: No string\n"); ps_error = 1; return; } p = strstr(&e2->u.s[1], &e->u.s[1]); if (p) { struct elt_s *e3; int l1, l2, l3; l1 = p - e2->u.s; l2 = strlen(e->u.s); l3 = strlen(e2->u.s) - l2 - l1 + 2; e3 = elt_new(); if (!e3) return; e3->type = STR; e3->u.s = malloc(l3); e3->u.s[0] = '('; memcpy(&e3->u.s[1], &e2->u.s[l1 + l2 - 2], l3 - 1); e3->u.s[l1 + l2 - 1] = '\0'; push(e3); push(e); e2->u.s[l1] = '\0'; push (e2); e = elt_new(); if (!e) return; e->type = VAL; e->u.v = 1; } else { push(e2); free(e->u.s); e->type = VAL; e->u.v = 0; } push(e); return; } if (strcmp(op, "selectfont") == 0) { h = pop_free_val(); s = pop_free_str(); if (!s || *s != '/') { fprintf(stderr, "svg selectfont: No / bad font\n"); if (s) free(s); ps_error = 1; return; } if (gcur.font_s != h || strcmp(s, gcur.font_n) != 0) { free(gcur.font_n_old); gcur.font_n_old = gcur.font_n; gcur.font_n = strdup(s); gcur.font_s = h; gold.font_n = NULL; } else { free(s); } return; } if (strcmp(op, "sep0") == 0) { x = pop_free_val(); w = pop_free_val(); fprintf(fout, "\n", gcur.xoffs + x, gcur.yoffs, w); return; } if (strcmp(op, "setdash") == 0) { char *p; n = pop_free_val(); e = pop(BRK); if (!e) { fprintf(stderr, "svg setdash: Bad pattern\n"); ps_error = 1; return; } e = e->u.e; if (!e) { gcur.dash[0] = '\0'; return; } p = gcur.dash; if (n != 0) p += sprintf(p, " stroke-dashoffset=\"%d\"", n); p += sprintf(p, " stroke-dasharray=\""); do { if (e->type != VAL) { fprintf(stderr, "svg setdash: Bad pattern type\n"); ps_error = 1; return; } if (p >= &gcur.dash[sizeof gcur.dash] - 10) { fprintf(stderr, "svg setdash: Pattern too wide\n"); ps_error = 1; return; } p += sprintf(p, "%d,", (int) e->u.v); e = e->next; } while (e); p--; sprintf(p, "\""); return; } if (strcmp(op, "setfont") == 0) { return; } if (strcmp(op, "setgray") == 0) { n = pop_free_val() * 255; gcur.rgb = (n << 16) | (n << 8) | n; return; } if (strcmp(op, "setlinewidth") == 0) { gcur.linewidth = pop_free_val(); return; } //fixme: use 'use' for flags if (strcmp(op, "sfu") == 0) { setg(1); h = pop_free_val(); n = pop_free_val(); sym = ps_sym_lookup("x"); x = gcur.xoffs + sym->e->u.v + 3.5; sym = ps_sym_lookup("y"); y = gcur.yoffs - sym->e->u.v; fprintf(fout, "\n" "= 0) { fprintf(fout, "M%.2f %.2fc0.9 3.7 9.1 6.4 6 12.4\n" " 1 -5.4 -4.2 -8.4 -6 -8.4\n", x, y); y += 5.4; } } fprintf(fout, "\"/>\n"); return; } if (strcmp(op, "sfd") == 0) { setg(1); h = pop_free_val(); n = pop_free_val(); sym = ps_sym_lookup("x"); x = gcur.xoffs + sym->e->u.v - 3.5; sym = ps_sym_lookup("y"); y = gcur.yoffs - sym->e->u.v; fprintf(fout, "\n" "= 0) { fprintf(fout, "M%.2f %.2fc0.9 -3.7 9.1 -6.4 6 -12.4\n" " 1 5.4 -4.2 8.4 -6 8.4\n", x, y); y -= 5.4; } } fprintf(fout, "\"/>\n"); return; } if (strcmp(op, "sfs") == 0) { setg(1); h = pop_free_val(); n = pop_free_val(); sym = ps_sym_lookup("x"); x = gcur.xoffs + sym->e->u.v; sym = ps_sym_lookup("y"); y = gcur.yoffs - sym->e->u.v - 1; if (h > 0) { x += 3.5; y -= 1; fprintf(fout, "\n" "= 0) { fprintf(fout, "M%.2f %.2fl7 3.2 0 3.2 -7 -3.2z\n", x, y); y += 5.4; } } else { x -= 3.5; y += 1; fprintf(fout, "\n" "= 0) { fprintf(fout, "M%.2f %.2fl7 -3.2 0 -3.2 -7 3.2z\n", x, y); y -= 5.4; } } fprintf(fout, "\"/>\n"); return; } if (strcmp(op, "sgu") == 0) { setg(1); h = pop_free_val(); n = pop_free_val(); sym = ps_sym_lookup("x"); x = gcur.xoffs + sym->e->u.v + GSTEM_XOFF; sym = ps_sym_lookup("y"); y = gcur.yoffs - sym->e->u.v; fprintf(fout, "\n" "= 0) { fprintf(fout, "M%.2f %.2fc1 3.2 5.6 2.8 3.2 8\n" " 1.4 -4.8 -2.4 -5.4 -3.2 -5.2\n", x, y); y += 3.5; } } fprintf(fout, "\"/>\n"); return; } if (strcmp(op, "sgd") == 0) { setg(1); h = pop_free_val(); n = pop_free_val(); sym = ps_sym_lookup("x"); x = gcur.xoffs + sym->e->u.v - GSTEM_XOFF; sym = ps_sym_lookup("y"); y = gcur.yoffs - sym->e->u.v; fprintf(fout, "\n" "= 0) { fprintf(fout, "M%.2f %.2fc1 -3.2 5.6 -2.8 3.2 -8\n" " 1.4 4.8 -2.4 5.4 -3.2 5.2\n", x, y); y -= 3.5; } } fprintf(fout, "\"/>\n"); return; } if (strcmp(op, "sgs") == 0) { setg(1); h = pop_free_val(); n = pop_free_val(); sym = ps_sym_lookup("x"); x = gcur.xoffs + sym->e->u.v + GSTEM_XOFF; sym = ps_sym_lookup("y"); y = gcur.yoffs - sym->e->u.v; fprintf(fout, "\n" "= 0) { fprintf(fout, "M%.2f %.2fl3 1.5 0 2 -3 -1.5z\n", x, y); y += 3; } fprintf(fout, "\"/>\n"); return; } if (strcmp(op, "sfz") == 0) { xysym(op, D_sfz); s = pop_free_str(); if (s) free(s); return; } if (strcmp(op, "sgno") == 0) { xysym(op, D_sgno); return; } if (strcmp(op, "show") == 0) { show('s'); return; } if (strcmp(op, "showb") == 0) { show('b'); return; } if (strcmp(op, "showc") == 0) { show('c'); return; } if (strcmp(op, "showr") == 0) { show('r'); return; } if (strcmp(op, "showerror") == 0) { xysym(op, D_showerror); return; } if (strcmp(op, "sld") == 0) { xysym(op, D_sld); return; } if (strcmp(op, "snap") == 0) { xysym(op, D_snap); return; } if (strcmp(op, "sphr") == 0) { xysym(op, D_sphr); return; } if (strcmp(op, "spclef") == 0) { xysym(op + 1, D_pclef); // same as 'pclef' return; } if (strcmp(op, "setrgbcolor") == 0) { int rgb; rgb = pop_free_val() * 255; rgb += (int) (pop_free_val() * 255) << 8; rgb += (int) (pop_free_val() * 255) << 16; gcur.rgb = rgb; return; } if (strcmp(op, "stc") == 0) { xysym(op, D_stc); return; } if (strcmp(op, "stroke") == 0) { if (!path) { fprintf(stderr, "svg: 'stroke' with no path\n"); // ps_error = 1; return; } path_end(); fprintf(fout, "\t\" class=\"stroke\"%s/>\n", gcur.dash); return; } if (strcmp(op, "su") == 0 || strcmp(op, "sd") == 0) { stem(op); return; } if (strcmp(op, "stsig") == 0) { setg(1); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); s = pop_free_str(); if (!s) { fprintf(stderr, "svg: No string\n"); ps_error = 1; return; } fprintf(fout, "\n" " %s\n" "\n", x, y, s + 1); free(s); return; } if (strcmp(op, "sub") == 0) { x = pop_free_val(); if (!stack || stack->type != VAL) { fprintf(stderr, "svg: Bad value for sub\n"); ps_error = 1; return; } stack->u.v -= x; return; } if (strcmp(op, "sbclef") == 0) { xysym(op, D_sbclef); return; } if (strcmp(op, "scclef") == 0) { xysym(op, D_scclef); return; } if (strcmp(op, "sh0") == 0) { xysym(op, D_sh0); return; } if (strcmp(op, "sh1") == 0) { xysym(op, D_sh1); return; } if (strcmp(op, "sh4") == 0) { n = pop_free_val(); switch (n) { case 1: xysym("sh1", D_sh1); break; case 2: xysym("sh0", D_sh0); break; case 3: xysym("sh513", D_sh513); break; default: xysym("dsh0", D_dsh0); break; } return; } if (strcmp(op, "sh513") == 0) { xysym(op, D_sh513); return; } if (strcmp(op, "srep") == 0) { xysym(op, D_srep); return; } if (strcmp(op, "stclef") == 0) { xysym(op, D_stclef); return; } if (strcmp(op, "stringwidth") == 0) { s = pop_free_str(); if (!s || *s != '(') { fprintf(stderr, "svg stringwidth: No string\n"); ps_error = 1; return; } e = elt_new(); if (!e) return; e->type = VAL; e->u.v = strw(s + 1); push(e); e = elt_new(); if (!e) return; e->type = VAL; e->u.v = gcur.font_s; push(e); return; } if (strcmp(op, "svg") == 0) { e = elt_new(); if (!e) return; e->type = VAL; e->u.v = 1; push(e); return; } break; case 'T': if (strcmp(op, "T") == 0) { translate: //fixme:test // setg(1); y = pop_free_val(); x = pop_free_val(); gcur.xoffs += x; gcur.yoffs -= y; gcur.cx -= x; gcur.cy -= y; return; } break; case 't': if (strcmp(op, "tclef") == 0) { xysym(op, D_tclef); return; } if (strcmp(op, "thbar") == 0) { setg(1); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val() + 1.5; h = pop_free_val(); fprintf(fout, "\n", x, y, -h); return; } if (strcmp(op, "thumb") == 0) { xysym(op, D_thumb); return; } if (strcmp(op, "translate") == 0) goto translate; if (strcmp(op, "trem") == 0) { setg(1); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val() - 4.5; n = pop_free_val(); fprintf(fout, "", fout); return; } if (strcmp(op, "trl") == 0) { xysym(op, D_trl); return; } if (strcmp(op, "true") == 0) { e = elt_new(); if (!e) return; e->type = VAL; e->u.v = 1; push(e); return; } if (strcmp(op, "tsig") == 0) { char *d; setg(1); y = gcur.yoffs - pop_free_val() - 0.5; x = gcur.xoffs + pop_free_val(); d = pop_free_str(); s = pop_free_str(); if (!d || !s) { fprintf(stderr, "svg: No string\n"); if (d) free(d); if (s) free(s); ps_error = 1; return; } fprintf(fout, "\n" " %s\n" " %s\n" "\n", x, y, d + 1, s + 1); free(d); free(s); return; } if (strcmp(op, "tubr") == 0 || strcmp(op, "tubrl") == 0) { float dx, dy; int h; setg(1); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); dy = pop_free_val(); dx = pop_free_val(); if (op[4] == 'l') { h = 3; y -= 3; } else { h = -3; y += 3; } fprintf(fout, "\n", x, y, h, dx, -dy, -h); return; } if (strcmp(op, "turn") == 0) { xysym(op, D_turn); return; } if (strcmp(op, "turnx") == 0) { xysym(op, D_turnx); return; } break; case 'u': if (strcmp(op, "upb") == 0) { xysym(op, D_upb); return; } if (strcmp(op, "umrd") == 0) { xysym(op, D_umrd); return; } break; case 'w': if (strcmp(op, "wedge") == 0) { xysym(op, D_wedge); return; } if (strcmp(op, "wln") == 0) { setg(1); y = pop_free_val(); x = pop_free_val(); w = pop_free_val(); fprintf(fout, "\n", gcur.xoffs + x, gcur.yoffs - y, w); return; } if (strcmp(op, "where") == 0) { s = pop_free_str(); /* symbol */ if (!s || *s != '/') { fprintf(stderr, "svg where: No / bad symbol\n"); if (s) free(s); ps_error = 1; return; } e = elt_new(); if (!e) return; e->type = VAL; sym = ps_sym_lookup(&s[1]); if (!sym) { e->u.v = 0; } else { e->u.v = 1; e2 = elt_new(); /* dictionnary */ if (!e2) return; e2->type = VAL; e2->u.v = 0; push(e2); } free(s); push(e); return; } break; case 'x': if (strcmp(op, "xydef") == 0) { y = pop_free_val(); x = pop_free_val(); setxory("x", x); setxory("y", y); return; } if (strcmp(op, "xymove") == 0) { gcur.cy = pop_free_val(); gcur.cx = pop_free_val(); setxory("x", gcur.cx); setxory("y", gcur.cy); return; } break; } // check if already a SVG definition from %%beginsvg if (defs) { s = strstr(defs, op); if (s && s[-1] == '"' && s[strlen(op)] == '"') { xysym(op, -1); return; } } fprintf(stderr, "svg: Symbol '%s' not defined\n", op); ps_error = 1; } void svg_write(char *buf, int len) { int l; struct elt_s *e, *e2; unsigned char c, *p, *q, *r; if (ps_error) return; p = (unsigned char *) buf; #if 0 if (strncmp((char *) p, "%svg ", 5) == 0) { /* %%beginsvg */ fwrite(p + 5, 1, len - 5, fout); fputs("\n", fout); return; } #endif /* scan the string */ while (--len >= 0) { c = *p++; switch (c) { case ' ': case '\t': case '\n': continue; case '{': case '[': /* treat '[' as '{' */ e = elt_new(); if (!e) return; in_cnt++; e->type = STR; e->u.s = strdup(c == '{' ? "{" : "["); push(e); break; case '}': case ']': in_cnt--; if (in_cnt < 0) { if (c == '}') fprintf(stderr, "svg: '}' without '{'\n"); else fprintf(stderr, "svg: ']' without '['\n"); ps_error = 1; return; } e = elt_new(); if (!e) return; /* create a container with elements in direct order */ e->u.e = NULL; if (c == '}') { e->type = SEQ; c = '{'; } else { e->type = BRK; c = '['; } for (;;) { e2 = stack; stack = stack->next; if (e2->type == STR && (e2->u.s[0] == '[' || e2->u.s[0] == '{')) break; e2->next = e->u.e; e->u.e = e2; } if (e2->u.s[0] != c) { fprintf(stderr, "svg: '%c' found before '%c'\n", e2->u.s[0], c); ps_error = 1; return; } elt_free(e2); push(e); break; case '%': q = p; while (--len >= 0) { c = *p++; if (c == '\n') break; } if ((char *) q != &buf[1] && q[-2] != '\n') break; if (strncmp((char *) q, "A ", 2) == 0) { /* annotation */ char type; int row , col, h; float x, y, w; q += 2; type = *q++; if (type != 'b' && type != 'e') { /* if not beam */ sscanf((char *) q + 1, "%d %d %f %f %f %d", &row, &col, &x, &y, &w, &h); } else { sscanf((char *) q + 1, "%d %d %f %f", &row, &col, &x, &y); w = h = 6; } fprintf(fout, "\n", type, row, col, gcur.xoffs + x, gcur.yoffs - y - h, w, h); break; } if (strncmp((char *) q, " --- title", 10) == 0) { /* title info */ if (strstr((char *) q + 10, "--") < (char *) p) break; // cannmot have '--' in comments setg(1); if (q[10] == 's') { /* subtitle */ q += 14; fprintf(fout, "\n", (int) (p - q - 1), q); break; } q += 11; fprintf(fout, "\n", (int) (p - q -1), q); break; } break; case '(': q = p - 1; l = 1; for (;;) { switch (*p++) { case '\\': p++; l--; continue; default: continue; case ')': break; } break; } len -= p - q - 1; l += p - q - 1; p = q; e = elt_new(); if (!e) return; e->type = STR; r = malloc(l); e->u.s = (char *) r; for (;;) { c = *p++; switch (c) { case '\\': *r++ = *p++; continue; default: *r++ = c; continue; case ')': break; } break; } *r = '\0'; push(e); break; default: q = p - 1; while (--len >= 0) { c = *p++; switch (c) { case '(': case ' ': case '\t': case '\n': case '{': case '}': case '[': case ']': case '%': case '/': break; default: continue; } break; } if (len >= 0) { p--; len++; } if (isdigit((unsigned) *q) || *q == '-' || *q == '.') { int i; float v; e = elt_new(); if (!e) return; e->type = VAL; c = *p; *p = '\0'; if (q[1] == '#') { i = strtol((char *) q + 2, 0, 8); e->u.v = i; } else if (q[2] == '#') { i = strtol((char *) q + 3, 0, 16); e->u.v = i; } else { if (sscanf((char *) q, "%f", &v) != 1) { fprintf(stderr, "svg: Bad numeric value in '%s'\n", buf); v = 0; } e->u.v = v; } *p = c; } else { if (!in_cnt) { if (*q != '/') { /* operator */ c = *p; *p = '\0'; ps_exec((char *) q); if (ps_error) return; *p = c; break; } } else if (strncmp((char *) q, "pdfmark", 7) == 0) { in_cnt--; for (;;) { e = pop(stack->type); if (e->type == STR && (e->u.s[0] == '[' || e->u.s[0] == '{')) break; elt_free(e); } elt_free(e); break; } l = p - q; r = malloc(l + 1); memcpy(r, q, l); r[l] = '\0'; e = elt_new(); if (!e) { free(r); return; } e->type = STR; e->u.s = (char *) r; } push(e); break; } } } int svg_output(FILE *out, const char *fmt, ...) { va_list args; char tmp[128]; va_start(args, fmt); vsnprintf(tmp, sizeof tmp, fmt, args); va_end(args); svg_write(tmp, strlen(tmp)); return 0; } void svg_close(void) { struct elt_s *e, *e2; setg(0); fputs("\n", fout); e = stack; if (e) { stack = NULL; fprintf(stderr, "svg close: stack not empty "); elt_lst_dump(e); fprintf(stderr, "\n"); do { e2 = e->next; elt_free(e); e = e2; } while (e); } } abcm2ps-8.14.11/syms.c000066400000000000000000000723651376266546700144170ustar00rootroot00000000000000/* * Postscript definitions. * * This file is part of abcm2ps. * * Copyright (C) 1998-2019 Jean-François Moine (http://moinejf.free.fr) * Adapted from abc2ps, Copyright (C) 1996-1998 Michael Methfessel * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. */ #include #include "abcm2ps.h" static char ps_head[] = "/xydef{/y exch def/x exch def}!\n" "/xymove{/x 2 index def/y 1 index def M}!\n" /* str showc - center at current pt */ "/showc{dup stringwidth pop .5 mul neg 0 RM show}!\n" /* str showr - show right-aligned */ "/showr{dup stringwidth pop neg 0 RM show}!\n" /* str showb - show in box */ "/showb{ dup currentpoint 3 -1 roll show\n" " .6 SLW\n" " exch 2 sub exch 3 sub 3 -1 roll\n" " stringwidth pop 4 add\n" " currentfont/ScaleMatrix get 0 get .8 mul\n" " 4 add rectstroke}!\n" /* x y oct - upper/lower clef '8' */ "/oct{/Times-Roman 12 selectfont M(8)show}!\n" /* x y octu - upper '8' - compatibility */ "/octu{oct}!\n" /* x y octl - lower '8' - compatibility */ "/octl{oct}!\n" /* t dx dy x y bm - beam, depth t */ "/bm{ M 3 copy RL neg 0 exch RL neg exch neg exch RL 0 exch RL fill}!\n" /* str x y bnum - tuplet number / ratio */ "/bnum{M/Times-Italic 12 selectfont showc}!\n" /* same with clearing below the number */ "/bnumb{ currentgray/Times-Italic 12 selectfont\n" " 3 index stringwidth pop 4 add\n" " dup .5 mul neg 4 index add 3 index 3 -1 roll 8\n" " 1.0 setgray rectfill setgray M showc}!\n" /* dx dy x y tubr - tuplet bracket */ "/tubr{3 sub M 0 3 RL RL 0 -3 RL dlw stroke}!\n" "/tubrl{3 add M 0 -3 RL RL 0 3 RL dlw stroke}!\n" /* dx dy dt - dot relative to head */ "/dt{x y M RM currentpoint 1.2 0 360 arc fill}!\n" /* x y dnb - down bow */ "/dnb{ dlw M -3.2 2 RM\n" " 0 7.2 RL\n" " 6.4 0 RM\n" " 0 -7.2 RL\n" " currentpoint stroke M\n" " -6.4 4.8 RM\n" " 0 2.4 RL\n" " 6.4 0 RL\n" " 0 -2.4 RL\n" " fill}!\n" /* x y upb - up bow */ "/upb{ dlw M -2.6 9.4 RM\n" " 2.6 -8.8 RL\n" " 2.6 8.8 RL\n" " stroke}!\n" /* x y grm - gracing mark */ "/grm{ M -5 2.5 RM\n" " 5 8.5 5.5 -4.5 10 2 RC\n" " -5 -8.5 -5.5 4.5 -10 -2 RC fill}!\n" /* x y stc - staccato mark */ "/stc{ 3 add M currentpoint 1.2 0 360 arc fill}!\n" /* x y emb - emphasis bar */ "/emb{ 1.2 SLW 1 setlinecap M -2.5 3 RM 5 0 RL stroke 0 setlinecap}!\n" /* x y cpu - roll sign above head */ "/cpu{ M -6 0 RM\n" " 0.4 7.3 11.3 7.3 11.7 0 RC\n" " -1.3 6 -10.4 6 -11.7 0 RC fill}!\n" /* x y sld - slide */ "/sld{ M -7.2 -4.8 RM\n" " 1.8 -0.7 4.5 0.2 7.2 4.8 RC\n" " -2.1 -5 -5.4 -6.8 -7.6 -6 RC fill}!\n" /* x y trl - trill sign */ "/trl{ /Times-BoldItalic 16 selectfont M -4 2 RM(tr)show}!\n" /* str x y fng - finger (0-5) */ "/fng{/Bookman-Demi 8 selectfont M -3 1 RM show}!\n" /* str x y dacs - D.C. / D.S. */ "/dacs{/Times-Roman 16 selectfont 3 add M showc}!\n" /* str x y crdc - italic annotations */ "/crdc{/Times-Italic 14 selectfont 5 add M show}!\n" /* x y brth - breath */ "/brth{/Times-BoldItalic 30 selectfont 6 add M(,)show}!\n" /* str x y pf - p, f, pp, .. */ "/pf{/Times-BoldItalic 16 selectfont 5 add M show}!\n" /* str x y sfz */ "/sfz{ M -7 5 RM pop\n" " /Times-Italic 14 selectfont(s)show\n" " /Times-BoldItalic 16 selectfont(f)show\n" " /Times-Italic 14 selectfont(z)show}!\n" /* w x y cresc - crescendo */ "/cresc{ 1 SLW M dup 5 RM\n" " defl 1 and 0 eq\n" " {dup neg 4 RL 4 RL}\n" " {dup neg 2.2 RL 0 3.6 RM 2.2 RL}\n" " ifelse stroke}!\n" /* w x y dim - diminuendo */ "/dim{ 1 SLW 5 add M\n" " defl 2 and 0 eq\n" " {dup 4 RL neg 4 RL}\n" " {dup 2.2 RL 0 3.6 RM neg 2.2 RL}\n" " ifelse stroke}!\n" // w x y o8va - ottava "/o8va{ M\n" " defl 1 and 0 eq\n" " {14 sub currentpoint\n" " -5 0 RM /Times-BoldItalic 12 selectfont(8)show\n" " 0 4 RM /Times-BoldItalic 10 selectfont(va)show\n" " M 14 0 RM}if 0 6 RM\n" " [6] 0 setdash 5 sub 0 RL currentpoint stroke [] 0 setdash\n" " M defl 2 and 0 eq\n" " {0 -6 RL stroke}if}!\n" // w x y o8vb - ottava bassa "/o8vb{ M\n" " defl 1 and 0 eq\n" " {8 sub currentpoint\n" " -5 0 RM /Times-BoldItalic 12 selectfont(8)show\n" " 0 4 RM /Times-BoldItalic 10 selectfont(vb)show\n" " M 8 0 RM}if\n" " [6] 0 setdash 5 sub 0 RL currentpoint stroke [] 0 setdash\n" " M defl 2 and 0 eq\n" " {0 6 RL stroke}if}!\n" /* x y dplus - plus */ "/dplus{ 1.2 SLW 0.5 add M 0 6 RL -3 -3 RM 6 0 RL stroke}!\n" /* x y trnx - turn with line through it */ "/turnx{ 2 copy turn .6 SLW 1.5 add M 0 9 RL stroke}!\n" /* x y lphr - longphrase */ "/lphr{1.2 SLW M 0 -18 RL stroke}!\n" /* x y mphr - mediumphrase */ "/mphr{1.2 SLW M 0 -12 RL stroke}!\n" /* x y sphr - shortphrase */ "/sphr{1.2 SLW M 0 -6 RL stroke}!\n" /* w x y ltr - long trill */ "/ltr{ gsave 4 add T\n" " 0 6 3 -1 roll{\n" /* % first loop draws left half of squiggle; second draws right\n*/ " 2{\n" " 0 0.4 M\n" " 2 1.9 3.4 2.3 3.9 0 C\n" " 2.1 0 L\n" " 1.9 0.8 1.4 0.7 0 -0.4 C\n" " fill\n" " 180 rotate -6 0 T\n" " }repeat\n" /* % shift axes right one squiggle*/ " pop 6 0 T\n" " }for\n" " grestore}!\n" /* h x ylow arp - arpeggio */ "/arp{gsave 90 rotate exch neg ltr grestore}!\n" /* x2 y2 x1 y1 gliss - line glissando */ "/gliss{ gsave 2 copy T\n" "exch 4 -1 roll exch sub 3 1 roll sub\n" // dx dy "2 copy exch atan dup rotate\n" // dx dy alpha "exch pop cos div\n" // len "8 0 M 14 sub 0 RL stroke " "grestore}!\n" /* x2 y2 x1 y1 glisq - squiggly glissando */ "/glisq{ gsave 2 copy T\n" "exch 4 -1 roll exch sub 3 1 roll sub\n" // dx dy "2 copy exch atan dup rotate\n" // dx dy alpha "exch pop cos div\n" // len "17 sub 8 -4 ltr " "grestore}!\n" /* x y wedge - wedge */ "/wedge{1 add M -1.5 5 RL 3 0 RL -1.5 -5 RL fill}!\n" /* x y opend - 'open' sign */ "/opend{dlw M currentpoint 3 add 2.5 -90 270 arc stroke}!\n" /* x y snap - 'snap' sign */ "/snap{ dlw 2 copy M -3 6 RM\n" " 0 5 6 5 6 0 RC\n" " 0 -5 -6 -5 -6 0 RC\n" " 5 add M 0 -6 RL stroke}!\n" /* x y thumb - 'thumb' sign */ "/thumb{ dlw 2 copy M -2.5 7 RM\n" " 0 6 5 6 5 0 RC\n" " 0 -6 -5 -6 -5 0 RC\n" " 2 add M 0 -4 RL stroke}!\n" /* n x y trem - tremolo on one note */ "/trem{ M -4.5 0 RM{\n" " currentpoint\n" " 9 3 RL 0 -3 RL -9 -3 RL 0 3 RL\n" " fill 5.4 sub M\n" " }repeat}!\n" /* x y hl - ledger line */ "/hl{ .8 SLW M -6 0 RM 12 0 RL stroke}!\n" /* x y hl1 - longer ledger line */ "/hl1{ .8 SLW M -7 0 RM 14 0 RL stroke}!\n" /* x y hl2 - more longer ledger line */ "/hl2{ .7 SLW M -9 0 RM 18 0 RL stroke}!\n" /* ancillary function for grace note accidentals */ "/gsc{gsave y T .8 dup scale 0 0}!\n" // accidentals for text "/uflat{<95200028\n" /* width 400 */ " 0064000001b802ee\n" " 006402ea\n" " 008402ea\n" " 0084000c\n" " 00640008\n" " 00840154\n" " 00b2019c011c01ae01540168\n" " 01b800fa00dc00220084000c\n" " 00840028\n" " 00ba0028014c00f60106014a\n" " 00d401860084014e00840128\n" " ><0b00010303030a0105050105050a>}cvlit def\n" "/unat{<95200022\n" /* width 380 */ " 003cff42013602ee\n" " 006002ee\n" " 004002ee\n" " 00400022\n" " 0060002a\n" " 01160060\n" " 0116ff46\n" " 0136ff46\n" " 01360208\n" " 011401fe\n" " 006001cc\n" " 006002ee\n" " 0060009e\n" " 0060015c\n" " 01160190\n" " 011600d4\n" " ><0b00012a030a0123030a>}cvlit def\n" "/usharp{<95200024\n" /* width 460 */ " 003cff42019a02ee\n" " 008802be\n" " 0088ff44\n" " 00a8ff44\n" " 00a802be\n" " 0128ff76\n" " 0148ff76\n" " 014802ee\n" " 012802ee\n" " 004001d0\n" " 0040015c\n" " 019201bc\n" " 01920230\n" " 00400076\n" " 00400002\n" " 01920064\n" " 019200d6\n" " ><0b000123030a0123030a0123030a0123030a>}cvlit def\n" "/udblesharp{<95200046\n" /* width 460 */ " 003c006e019001c2\n" " 00f0011a\n" " 01180140013a015e018e015e\n" " 018e01be\n" " 012e01be\n" " 012e016a0110014800ea0122\n" " 00c2014800a4016a00a401be\n" " 004401be\n" " 0044015e\n" " 009a015e00bc014000e2011a\n" " 00bc00f4009a00d6004400d6\n" " 00440076\n" " 00a40076\n" " 00a400ca00c200ec00ea0112\n" " 011000ec012e00ca012e0076\n" " 018e0076\n" " 018e00d6\n" " 013a00d6011800f400f0011a\n" " ><0b0001050303050503030505030305050303050a>}cvlit def\n" "/udbleflat{<9520004c\n" /* width 500 */ " 00140000022602ee\n" " 001402ea\n" " 002c02ea\n" " 002c000c\n" " 00140008\n" " 002c0154\n" " 004e019c009e01ae00c80168\n" " 011300fa00660022002c000c\n" " 002c0028\n" " 0054002800c200f6008d014a\n" " 00680186002c014e002c0128\n" " 010e02ea\n" " 012602ea\n" " 0126000c\n" " 010e0008\n" " 01260154\n" " 0148019c019801ae01c20168\n" " 020d00fa016000220126000c\n" " 01260028\n" " 014e002801bc00f60187014a\n" " 016201860126014e01260128\n" " ><0b000123030a0105050105050a0123030a0105050105050a>}cvlit def\n" /* some microtone accidentals */ /* 1/4 ton sharp */ "/sh1{ gsave T .9 SLW\n" " 0 -7.8 M 0 15.4 RL stroke\n" " -1.8 -2.7 M 3.6 1.1 RL 0 -2.2 RL -3.6 -1.1 RL 0 2.2 RL fill\n" " -1.8 3.7 M 3.6 1.1 RL 0 -2.2 RL -3.6 -1.1 RL 0 2.2 RL fill\n" " grestore}!\n" /* 3/4 ton sharp */ "/sh513{ gsave T .8 SLW\n" " -2.5 -8.7 M 0 15.4 RL\n" " 0 -7.8 M 0 15.4 RL\n" " 2.5 -6.9 M 0 15.4 RL stroke\n" " -3.7 -3.1 M 7.4 2.2 RL 0 -2.2 RL -7.4 -2.2 RL 0 2.2 RL fill\n" " -3.7 3.2 M 7.4 2.2 RL 0 -2.2 RL -7.4 -2.2 RL 0 2.2 RL fill\n" " grestore}!\n" /* 1/4 ton flat */ "/ft1{gsave -1 1 scale exch neg exch ft0 grestore}!\n" /* x y ftx - narrow flat sign */ "/ftx{ -1.4 2.7 RM\n" " 5.7 3.1 5.7 -3.6 0 -6.7 RC\n" " 3.9 4 4 7.6 0 5.8 RC\n" " currentpoint fill 7.1 add M\n" " dlw 0 -12.4 RL stroke}!\n" /* 3/4 ton flat */ "/ft513{2 copy gsave -1 1 scale exch neg 3 add exch M ftx grestore\n" " M 1.5 0 RM ftx}!\n" /* microscale= 4 */ "/sh4tb[/.notdef/sh1/sh0/sh513/dsh0]def\n" "/sh4{sh4tb exch get cvx exec}!\n" "/ft4tb[/.notdef/ft1/ft0/ft513/dft0]def\n" "/ft4{ft4tb exch get cvx exec}!\n" /* -- bars -- */ /* h x y bar - thin bar */ "/bar{M 1 SLW 0 exch RL stroke}!\n" /* h x y dotbar - dotted bar */ "/dotbar{[5] 0 setdash bar [] 0 setdash}!\n" /* h x y thbar - thick bar */ "/thbar{3 -1 roll 3 exch rectfill}!\n" /* x y rdots - repeat dots */ "/rdots{ 2 copy 9 add M currentpoint 1.2 0 360 arc\n" " 15 add M currentpoint 1.2 0 360 arc fill}!\n" /* x y xxsig - old time signatures ('o', 'o.', 'c' 'c.') */ "/pmsig{0.3 SLW 12 add M currentpoint 5 0 360 arc stroke}!\n" "/pMsig{2 copy pmsig 12 add M currentpoint 1.3 0 360 arc fill}!\n" "/imsig{0.3 SLW 12 add 2 copy 5 add M 5 60 300 arc stroke}!\n" "/iMsig{2 copy imsig 12 add M currentpoint 1.3 0 360 arc fill}!\n" /* (top) (bot) x y tsig - time signature */ "/tsig{ 1 add M gsave/Times-Bold 16 selectfont 1.2 1 scale\n" " currentpoint 3 -1 roll showc\n" " 12 add M showc grestore}!\n" /* (meter) x y stsig - single time signature */ "/stsig{ 7 add M gsave/Times-Bold 18 selectfont 1.2 1 scale\n" " showc grestore}!\n" /* l x sep0 - hline separator */ "/sep0{ dlw 0 M 0 RL stroke}!\n" /* h x y bracket */ "/bracket{M -5 2 RM currentpoint\n" " -1.7 2 RM 10.5 -1 12 4.5 12 3.5 RC\n" " 0 -1 -3.5 -5.5 -8.5 -5.5 RC fill\n" " 3 SLW 2 add M\n" " 0 exch neg 8 sub RL currentpoint stroke\n" " M -1.7 0 RM\n" " 10.5 1 12 -4.5 12 -3.5 RC\n" " 0 1 -3.5 5.5 -8.5 5.5 RC fill}!\n" /* x y srep - sequence repeat */ "/srep{ M -1 -6 RM 11 12 RL 3 0 RL -11 -12 RL -3 0 RL fill}!\n" /* str dy bracket_type dx x y repbra - repeat bracket */ "/repbra{gsave dlw T 0 -20 M\n" " 0 20 3 index 1 and 1 eq{RL}{RM}ifelse 0\n" " RL 2 and 2 eq{0 -20 RL}if stroke\n" " 4 exch M show grestore}!\n" /* pp2x pp1x p1 pp1 pp2 p2 p1 SL - slur / tie */ "/SL{M RC RL RC closepath fill}!\n" /* pp2x pp1x p1 pp1 pp2 p2 p1 dSL - dotted slur / tie */ "/dSL{ M [4] 0 setdash .8 SLW RC stroke [] 0 setdash}!\n" /* -- text -- */ "/strw{ gsave 0 -2000 M/strop/show load def 1 setgray str\n" " 0 setgray currentpoint pop/w exch def grestore}!\n" "/jshow{w 0 32 4 -1 roll widthshow}!\n" "/strop/show load def\n" "/arrayshow{{dup type/stringtype eq{strop}{glyphshow}ifelse}forall}def\n" /* str gcshow - guitar chord */ "/gcshow{show}!\n" "/agcshow{arrayshow}!\n" /* x y w h box - draw a box */ "/box{.6 SLW rectstroke}!\n" /* set the end of a box */ "/boxend{currentpoint pop/x exch def}!\n" /* mark the right most end of a box */ "/boxmark{currentpoint pop dup x gt\n" " {/x exch def}{pop}ifelse}!\n" /* x y dy boxdraw - draw a box around a guitar chord */ "/boxdraw{x 3 index sub 2 add exch box}!\n" /* w str gxshow - expand a guitar chord */ "/gxshow{0 9 3 -1 roll widthshow}!\n" /* str anshow - annotation */ "/anshow{show}!\n" "/aanshow{arrayshow}!\n" /* -- lyrics under notes -- */ /* w x y wln - underscore line */ "/wln{M .8 SLW 0 RL stroke}!\n" /* w x y hyph - hyphen */ "/hyph{ .8 SLW 3 add M\n" " dup cvi 20 idiv 3 mul 25 add\n" /* w d */ " 1 index cvi exch idiv 1 add " /* w n */ "exch " /* n w */ "1 index div\n" /* n dx */ " dup 4 sub " /* n dx (dx-4) */ "3 1 roll " /* (dx-4) n dx */ ".5 mul 2 sub 0 RM\n" /* (dx / 2 - 4) rmoveto */ " {4 0 RL dup 0 RM}repeat stroke pop}!\n" /* str lyshow - lyrics */ "/lyshow{show}!\n" "/alyshow{arrayshow}!\n" /* -- default percussion heads -- */ /* x y pfthd - percussion flat head */ "/pfthd{/x 2 index def/y 1 index def dsh0\n" " .7 SLW x y M x y 4 0 360 arc stroke}!\n" /* same for dble sharp/flat */ "/pdshhd{pshhd}!\n" "/pdfthd{pfthd}!\n" /* x y ghd - grace note head */ "/ghd{ xymove\n" " 1.7 1.5 RM\n" " -1.32 2.31 -5.94 -0.33 -4.62 -2.64 RC\n" " 1.32 -2.31 5.94 0.33 4.62 2.64 RC fill}!\n" /* dx dy gua / gda - acciaccatura */ "/gua{x y M -1 4 RM RL stroke}!\n" "/gda{x y M -5 -4 RM RL stroke}!\n" /* x y ghl - grace note ledger line */ "/ghl{ .6 SLW M -3.5 0 RM 7 0 RL stroke}!\n" /* x1 y2 x2 y2 x3 y3 x0 y0 gsl - grace note slur */ "/gsl{dlw M RC stroke}!\n" /* x y custos */ "/custos{2 copy M -4 0 RM 2 2.5 RL 2 -2.5 RL 2 2.5 RL 2 -2.5 RL\n" " -2 -2.5 RL -2 2.5 RL -2 -2.5 RL -2 2.5 RL fill\n" " M 3.5 0 RM 5 7 RL dlw stroke}!\n" #ifdef HAVE_PANGO "/glypharray{{glyphshow}forall}!\n" #endif /* x y showerror */ "/showerror{gsave 1 0.7 0.7 setrgbcolor 2.5 SLW newpath\n" " 30 0 360 arc stroke grestore}!\n" "/pdfmark where{pop}{userdict/pdfmark/cleartomark load put}ifelse\n" "0 setlinecap 0 setlinejoin\n"; /* PS direct glyphs */ static char psdgl[] = "/hbrce{ -2.5 1 RM\n" " -4.5 -4.6 -7.5 -12.2 -4.4 -26.8 RC\n" " 3.5 -14.3 3.2 -21.7 -2.1 -24.2 RC\n" " 7.4 2.4 7.3 14.2 3.5 29.5 RC\n" " -2.7 9.5 -1.5 16.2 3 21.5 RC\n" " fill}!\n" /* h x y brace */ "/brace{ gsave T 0 0 M .01 mul 1 exch scale hbrce\n" " 0 -100 M 1 -1 scale hbrce grestore}!\n" /* x y sgno - segno */ "/sgno{ 3 add M currentpoint currentpoint currentpoint\n" " 1.5 -1.7 6.4 0.3 3 3.7 RC\n" " -10.4 7.8 -8 10.6 -6.5 11.9 RC\n" " 4 1.9 5.9 -1.7 4.2 -2.6 RC\n" " -1.3 -0.7 -2.9 1.3 -0.7 2 RC\n" " -1.5 1.7 -6.4 -0.3 -3 -3.7 RC\n" " 10.4 -7.8 8 -10.6 6.5 -11.9 RC\n" " -4 -1.9 -5.9 1.7 -4.2 2.6 RC\n" " 1.3 0.7 2.9 -1.3 0.7 -2 RC\n" " fill\n" " M 0.8 SLW -6 1.2 RM 12.6 12.6 RL stroke\n" " 7 add exch 6 sub exch 1.2 0 360 arc fill\n" " 8 add exch 6 add exch 1.2 0 360 arc fill}!\n" /* x y dacoda - Da Coda */ "/dacoda{2 1 roll 10 add 2 1 roll dup 3 -1 roll dup 3 1 roll -23 add 4 1 roll\n" " 1 SLW 2 add 2 copy M 0 20 RL\n" " 2 copy M -10 10 RM 20 0 RL stroke\n" " 10 add 6 0 360 arc 1.7 SLW stroke\n" " /Times-Roman 16 selectfont 7 add M (Da) showc}!\n" /* x y coda - coda */ "/coda{ 1 SLW 2 add 2 copy M 0 20 RL\n" " 2 copy M -10 10 RM 20 0 RL 1.1 SLW stroke\n" " exch -7 add exch (O) 3 1 roll /Times-Bold 18 selectfont 4 add M show}!\n" /* x y tclef - treble clef */ "/utclef{<95200072\n" " 0000ff2e01c2030c\n" " 00ac0056\n" " 0064007f006400f400e00112\n" " 0176011c01bc0056013a0012\n" " 00c8ffde002700120015009a\n" " 0006014f0072017f00f101e8\n" " 0149023f0140026d012f02ba\n" " 00fc029900d1025100d60200\n" " 00e700f500fa008a0107ffc2\n" " 010dff6200f4ff3c00baff3b\n" " 006aff3a003cff98007dffc0\n" " 00d2ffe90102ff5b009cff57\n" " 00b3ff4600f8ff3200f6ffb3\n" " 00ec009200cf010900c4021c\n" " 00c4027600c402be01240304\n" " 015c02bc0163021e013a01e3\n" " 00f001790039013b003b00a7\n" " 0044000e00cfffee01370022\n" " 018d0063015400e200e700d2\n" " 00a000c6007e008f00ac0056\n" " ><0b000132050a>}cvlit def\n" "/tclef{gsave T -10 -6 T .045 dup scale utclef ufill grestore}!\n" /* x y cclef */ "/ucclef{<95200066\n" " 006effbe01e70256\n" " 00d10108\n" " 00d10002\n" " 00c40002\n" " 00c40213\n" " 00d10213\n" " 00d10113\n" " 00ea012700fa013701100180\n" " 011e0161011d014d0148013a\n" " 01a2011801a80244011f01f3\n" " 015301e0013a01a3011401a6\n" " 00ba01cc01350256019f01eb\n" " 01e7019c01a000fa01190131\n" " 0109010a\n" " 011900e4\n" " 01a0011b01e70079019f002a\n" " 0135ffbe00ba00490114006f\n" " 013a007201530035011f0022\n" " 01a8ffd101a200fd014800db\n" " 011d00c8011b00bd0110009b\n" " 00fa00e400ea00f400d10108\n" " 006e0213\n" " 00a70213\n" " 00a70002\n" " 006e0002\n" " 006e0213\n" " ><0b000125032605220326050a0124030a>}cvlit def\n" "/cclef{gsave T -12 -12 T .045 dup scale ucclef ufill grestore}!\n" /* x y bclef - bass clef */ "/ubclef{<95200046\n" " 00000050019a0244\n" " 00010057\n" " 007d007a00df00a500ff0143\n" " 012a022700580239003f01aa\n" " 007a01fa00dc0194009b015c\n" " 005d012d00280172003101b4\n" " 00460241013f023c01430180\n" " 014200d100d9007800010057\n" " 01660151\n" " 016601750199017301990151\n" " 0199012c0166012d01660151\n" " 016401d2\n" " 016401f6019701f4019701d2\n" " 019701ac016401ad016401d2\n" " ><0b000126050a0122050a0122050a>}cvlit def\n" "/bclef{gsave T -10 -18 T .045 dup scale ubclef ufill grestore}!\n" /* x y pclef */ "/pclef{ exch 2.7 sub exch -9 add 5.4 18 1.4 SLW rectstroke}!\n" "/spclef{pclef}!\n" "/stclef{gsave T -10 -6 T .037 dup scale utclef ufill grestore}!\n" "/scclef{gsave T -12 -10 T .037 dup scale ucclef ufill grestore}!\n" "/sbclef{gsave T -10 -15 T .037 dup scale ubclef ufill grestore}!\n" /* x y csig - C timesig */ "/csig{ M\n" " 6 5.3 RM\n" " 0.9 0 2.3 -0.7 2.4 -2.2 RC\n" " -1.2 2 -3.6 -0.1 -1.6 -1.7 RC\n" " 2 -1 3.8 3.5 -0.8 4.7 RC\n" " -2 0.4 -6.4 -1.3 -5.8 -7 RC\n" " 0.4 -6.4 7.9 -6.8 9.1 -0.7 RC\n" " -2.3 -5.6 -6.7 -5.1 -6.8 0 RC\n" " -0.5 4.4 0.7 7.5 3.5 6.9 RC\n" " fill}!\n" /* x y ctsig - C| timesig */ "/ctsig{dlw 2 copy csig M 5 -8 RM 0 16 RL stroke}!\n" /* x y HDD - round breve */ "/HDD{ dlw HD\n" " x y M -6 -4 RM 0 8 RL\n" " 12 0 RM 0 -8 RL stroke}!\n" /* x y breve - square breve */ "/breve{ xymove\n" " 2.5 SLW -6 -2.7 RM 12 0 RL\n" " 0 5.4 RM -12 0 RL stroke\n" " dlw x y M -6 -5 RM 0 10 RL\n" " 12 0 RM 0 -10 RL stroke}!\n" /* x y HD - open head for whole */ "/HD{ xymove\n" " -2.7 1.4 RM\n" " 1.5 2.8 6.9 0 5.3 -2.7 RC\n" " -1.5 -2.8 -6.9 0 -5.3 2.7 RC\n" " 8.3 -1.4 RM\n" " 0 1.5 -2.2 3 -5.6 3 RC\n" " -3.4 0 -5.6 -1.5 -5.6 -3 RC\n" " 0 -1.5 2.2 -3 5.6 -3 RC\n" " 3.4 0 5.6 1.5 5.6 3 RC fill}!\n" /* x y Hd - open head for half */ "/Hd{ xymove\n" " 3 1.6 RM\n" " -1 1.8 -7 -1.4 -6 -3.2 RC\n" " 1 -1.8 7 1.4 6 3.2 RC\n" " 0.5 0.3 RM\n" " 2 -3.8 -5 -7.6 -7 -3.8 RC\n" " -2 3.8 5 7.6 7 3.8 RC fill}!\n" /* x y hd - full head */ "/uhd{{ 100 -270 640 280\n" " 560 82\n" " 474 267 105 105 186 -80\n" " 267 -265 636 -102 555 82\n" " }<0b000122050a>}cvlit def\n" "/hd{ /x 2 index def/y 1 index def\n" " gsave T -7.4 0 T .02 dup scale uhd ufill grestore}!\n" /* x y ft0 - flat sign */ "/ft0{ gsave T -3.5 -3.5 T .018 dup scale uflat ufill grestore}!\n" /* x y nt0 - natural sign */ "/nt0{ gsave T -3 -5 T .018 dup scale unat ufill grestore}!\n" /* x y sh0 - sharp sign */ "/sh0{ gsave T -4 -5 T .018 dup scale usharp ufill grestore}!\n" /* x y dsh0 - double sharp */ "/dsh0{ gsave T -4 -5 T .018 dup scale udblesharp ufill grestore}!\n" /* x y pshhd - percussion sharp head */ "/pshhd{/x 2 index def/y 1 index def dsh0}!\n" /* x y dft0 - double flat sign */ "/dft0{ gsave T -4 -3.5 T .018 dup scale udbleflat ufill grestore}!\n" /* x y accent - accent */ "/accent{1.2 SLW M -4 1 RM 8 2 RL -8 2 RL stroke}!\n" /* x y marcato - accent */ "/marcato{M -3 0 RM 3 7 RL 3 -7 RL -1.5 0 RL -1.8 4.2 RL -1.7 -4.2 RL fill}!\n" /* x y hld - fermata */ "/hld{ 1.5 add 2 copy 1.5 add M currentpoint 1.3 0 360 arc\n" " M -7.5 0 RM\n" " 0 11.5 15 11.5 15 0 RC\n" " -0.25 0 RL\n" " -1.25 9 -13.25 9 -14.5 0 RC\n" " fill}!\n" /* x y r00 - longa rest */ "/r00{ xymove -1.5 -6 RM currentpoint 3 12 rectfill}!\n" /* x y r0 - breve rest */ "/r0{ xymove -1.5 0 RM currentpoint 3 6 rectfill}!\n" /* x y r1 - rest */ "/r1{ xymove -3.5 3 RM currentpoint 7 3 rectfill}!\n" /* x y r2 - half rest */ "/r2{ xymove -3.5 0 RM currentpoint 7 3 rectfill}!\n" /* x y r4 - quarter rest */ "/r4{ xymove\n" " -1 8.5 RM\n" " 3.6 -5.1 RL\n" " -2.1 -5.2 RL\n" " 2.2 -4.3 RL\n" " -2.6 2.3 -5.1 0 -2.4 -2.6 RC\n" " -4.8 3 -1.5 6.9 1.4 4.1 RC\n" " -3.1 4.5 RL\n" " 1.9 5.1 RL\n" " -1.5 3.5 RL\n" " fill}!\n" /* 1/8 .. 1/64 rest element */ "/r8e{ -1.5 -1.5 -2.4 -2 -3.6 -2 RC\n" " 2.4 2.8 -2.8 4 -2.8 1.2 RC\n" " 0 -2.7 4.3 -2.4 5.9 -0.6 RC\n" " fill}!\n" /* x y r8 - eighth rest */ "/r8{ xymove\n" " .5 SLW 3.3 4 RM\n" " -3.4 -9.6 RL stroke\n" " x y M 3.4 4 RM r8e}!\n" /* x y r16 - 16th rest */ "/r16{ xymove\n" " .5 SLW 3.3 4 RM\n" " -4 -15.6 RL stroke\n" " x y M 3.4 4 RM r8e\n" " x y M 1.9 -2 RM r8e}!\n" /* x y r32 - 32th rest */ "/r32{ xymove\n" " .5 SLW 4.8 10 RM\n" " -5.5 -21.6 RL stroke\n" " x y M 4.9 10 RM r8e\n" " x y M 3.4 4 RM r8e\n" " x y M 1.9 -2 RM r8e}!\n" /* x y r64 - 64th rest */ "/r64{ xymove\n" " .5 SLW 4.8 10 RM\n" " -7 -27.6 RL stroke\n" " x y M 4.9 10 RM r8e\n" " x y M 3.4 4 RM r8e\n" " x y M 1.9 -2 RM r8e\n" " x y M 0.4 -8 RM r8e}!\n" /* x y r128 - 128th rest */ "/r128{ xymove\n" " .5 SLW 5.8 16 RM\n" " -8.5 -33.6 RL stroke\n" " x y M 5.9 16 RM r8e\n" " x y M 4.4 10 RM r8e\n" " x y M 2.9 4 RM r8e\n" " x y M 1.4 -2 RM r8e\n" " x y M -0.1 -8 RM r8e}!\n" /* x y mrest */ "/mrest{ M currentpoint 1 SLW\n" " -20 -6 RM 0 12 RL 40 0 RM 0 -12 RL stroke\n" " M 5 SLW -20 0 RM 40 0 RL stroke}!\n" /* x y mrep - measure repeat */ "/mrep{ 2 copy 2 copy\n" " M -5 3 RM currentpoint 1.4 0 360 arc\n" " M 5 -3 RM currentpoint 1.4 0 360 arc\n" " M -7 -6 RM 11 12 RL 3 0 RL -11 -12 RL -3 0 RL fill}!\n" /* x y mrep2 - measure repeat 2 times */ "/mrep2{ 2 copy 2 copy\n" " M -5 6 RM currentpoint 1.4 0 360 arc\n" " M 5 -6 RM currentpoint 1.4 0 360 arc fill\n" " M 1.8 SLW\n" " -7 -8 RM 14 10 RL -14 -4 RM 14 10 RL stroke}!\n" /* x y turn - turn */ "/turn{ M 5.2 8 RM\n" " 1.4 -0.5 0.9 -4.8 -2.2 -2.8 RC\n" " -4.8 3.5 RL\n" " -3 2 -5.8 -1.8 -3.6 -4.4 RC\n" " 1 -1.1 2 -0.8 2.1 0.1 RC\n" " 0.1 0.9 -0.7 1.2 -1.9 0.6 RC\n" " -1.4 0.5 -0.9 4.8 2.2 2.8 RC\n" " 4.8 -3.5 RL\n" " 3 -2 5.8 1.8 3.6 4.4 RC\n" " -1 1.1 -2 0.8 -2.1 -0.1 RC\n" " -0.1 -0.9 0.7 -1.2 1.9 -0.6 RC\n" " fill}!\n" /* x y umrd - upper mordent */ "/umrd{ 4 add M\n" " 2.2 2.2 RL 2.1 -2.9 RL 0.7 0.7 RL\n" " -2.2 -2.2 RL -2.1 2.9 RL -0.7 -0.7 RL\n" " -2.2 -2.2 RL -2.1 2.9 RL -0.7 -0.7 RL\n" " 2.2 2.2 RL 2.1 -2.9 RL 0.7 0.7 RL fill}!\n" /* x y lmrd - lower mordent */ "/lmrd{ 2 copy umrd M .6 SLW 0 8 RL stroke}!\n" // dummy !ped! and !ped-up! "/ped{ /Times-BoldItalic 16 selectfont M -10 2 RM(Ped)show}!\n" "/pedoff{ /Times-BoldItalic 16 selectfont M -4 2 RM(*)show}!\n" /* x y longa */ "/longa{ xymove\n" " 2.5 SLW -6 -2.7 RM 12 0 RL\n" " 0 5.4 RM -12 0 RL stroke\n" " dlw x y M -6 -5 RM 0 10 RL\n" " 12 0 RM 0 -16 RL stroke}!\n"; /* PS font glyphs */ static char psfgl[] = "/musgly{music 24 selectfont glyphshow}!\n" "/brace{gsave\n" " T -7.5 0 M -.042 mul 3 exch scale/uniE000 musgly\n" " grestore}!\n" "/sgno{ M -6 4 RM/uniE047 musgly}!\n" "/coda{ M -12 6 RM/uniE048 musgly}!\n" "/tclef{M -8 0 RM/uniE050 musgly}!\n" "/cclef{M -8 0 RM/uniE05C musgly}!\n" "/bclef{M -8 0 RM/uniE062 musgly}!\n" "/pclef{M -6 0 RM/uniE069 musgly}!\n" "/stclef{M -8 0 RM/uniE07A musgly}!\n" "/scclef{M -8 0 RM/uniE07B musgly}!\n" "/sbclef{M -7 0 RM/uniE07C musgly}!\n" "/csig{ M 0 0 RM/uniE08A musgly}!\n" "/ctsig{M 0 0 RM/uniE08B musgly}!\n" "/HDD{ xymove -7 0 RM/uniE0A0 musgly}!\n" "/breve{xymove -6 0 RM/uniE0A1 musgly}!\n" "/HD{ xymove -5.2 0 RM/uniE0A2 musgly}!\n" "/Hd{ xymove -3.8 0 RM/uniE0A3 musgly}!\n" "/hd{ xymove -3.7 0 RM/uniE0A4 musgly}!\n" "/ft0{ M -3 0 RM/uniE260 musgly}!\n" "/nt0{ M -2 0 RM/uniE261 musgly}!\n" "/sh0{ M -3 0 RM/uniE262 musgly}!\n" "/dsh0{ M -3 0 RM/uniE263 musgly}!\n" "/pshhd{xymove -3 0 RM/uniE263 musgly}!\n" "/dft0{ M -3 0 RM/uniE264 musgly}!\n" "/accent{M -3 0 RM/uniE4A0 musgly}!\n" "/marcato{M -3 0 RM/uniE4AC musgly}!\n" "/hld{ M -7 0 RM/uniE4C0 musgly}!\n" "/r00{ xymove -1.5 0 RM/uniE4E1 musgly}!\n" "/r0{ xymove -1.5 0 RM/uniE4E2 musgly}!\n" "/r1{ xymove -3.5 6 RM/uniE4E3 musgly}!\n" "/r2{ xymove -3.2 0 RM/uniE4E4 musgly}!\n" "/r4{ xymove -3 0 RM/uniE4E5 musgly}!\n" "/r8{ xymove -3 0 RM/uniE4E6 musgly}!\n" "/r16{ xymove -4 0 RM/uniE4E7 musgly}!\n" "/r32{ xymove -4 0 RM/uniE4E8 musgly}!\n" "/r64{ xymove -4 0 RM/uniE4E9 musgly}!\n" "/r128{ xymove -4 0 RM/uniE4EA musgly}!\n" "/mrest{M -10 0 RM/uniE4EE musgly}!\n" "/mrep{ M -6 0 RM/uniE500 musgly}!\n" "/mrep2{M -9 0 RM/uniE501 musgly}!\n" "/turn{ M -4 0 RM/uniE567 musgly}!\n" "/umrd{ M -7 2 RM/uniE56C musgly}!\n" "/lmrd{ M -7 2 RM/uniE56D musgly}!\n" "/ped{ M -10 0 RM/uniE650 musgly}!\n" "/pedoff{M -6 0 RM/uniE655 musgly}!\n" "/longa{xymove -6 0 RM/uniE95C musgly}!\n"; /* -- define a font -- */ void define_font(char name[], int num, int enc) { if (enc == 0) /* utf-8 */ fprintf(fout, "/%s-utf8/%s mkfont\n" "/F%d{/%s-utf8 exch selectfont}!\n", name, name, num, name); else /* native encoding */ fprintf(fout, "/F%d{/%s exch selectfont}!\n", num, name); } /* -- output the symbol definitions -- */ void define_symbols(void) { char *p, *q, *r; p = cfmt.musicfont; fputs(ps_head, fout); fputs(p ? psfgl : psdgl, fout); // if a music font, give it a name if (p) { q = strchr(p, '('); if (q) { // hope "url(...)" q++; r = strrchr(q, '.'); // remove the file type if (!r) r = q + strlen(p) - 1; // ')' p = strrchr(q, DIRSEP); // and the directory path if (p) q = p + 1; fprintf(fout, "/music/%.*s def\n", (int) (r - q), q); } else { fprintf(fout, "/music/%s def\n", p); } } /* len su - up stem */ fprintf(fout, "/su{dlw x y M %.1f %.1f RM %.1f sub 0 exch RL stroke}!\n", STEM_XOFF, STEM_YOFF, STEM_YOFF); /* len sd - down stem */ fprintf(fout, "/sd{dlw x y M %.1f %.1f RM %.1f add 0 exch RL stroke}!\n", -STEM_XOFF, -STEM_YOFF, STEM_YOFF); /* n len sfu - stem and n flags up */ fprintf(fout, "/sfu{ dlw x y M %.1f %.1f RM\n" " %.1f sub 0 exch RL currentpoint stroke\n" " M dup 1 eq{\n" " pop\n" " 0.6 -5.6 9.6 -9 5.6 -18.4 RC\n" " 1.6 6 -1.3 11.6 -5.6 12.8 RC fill\n" " }{\n" " 1 sub{ currentpoint\n" " 0.9 -3.7 9.1 -6.4 6 -12.4 RC\n" " 1 5.4 -4.2 8.4 -6 8.4 RC\n" " fill 5.4 sub M\n" " }repeat\n" " 1.2 -3.2 9.6 -5.7 5.6 -14.6 RC\n" " 1.6 5.4 -1 10.2 -5.6 11.4 RC fill\n" " }ifelse}!\n", STEM_XOFF, STEM_YOFF, STEM_YOFF); /* n len sfd - stem and n flags down */ fprintf(fout, "/sfd{ dlw x y M %.1f %.1f RM\n" " %.1f add 0 exch RL currentpoint stroke\n" " M dup 1 eq{\n" " pop\n" " 0.6 5.6 9.6 9 5.6 18.4 RC\n" " 1.6 -6 -1.3 -11.6 -5.6 -12.8 RC fill\n" " }{\n" " 1 sub{ currentpoint\n" " 0.9 3.7 9.1 6.4 6 12.4 RC\n" " 1 -5.4 -4.2 -8.4 -6 -8.4 RC\n" " fill 5.4 add M\n" " }repeat\n" " 1.2 3.2 9.6 5.7 5.6 14.6 RC\n" " 1.6 -5.4 -1 -10.2 -5.6 -11.4 RC fill\n" " }ifelse}!\n", -STEM_XOFF, -STEM_YOFF, STEM_YOFF); /* n len sfs - stem and n straight flag down */ fprintf(fout, "/sfs{ dup 0 lt{\n" " dlw x y M -%.1f -%.1f RM\n" " %.1f add 0 exch RL currentpoint stroke\n" " M{ currentpoint\n" " 7 %.1f RL\n" " 0 %.1f RL\n" " -7 -%.1f RL\n" " fill 5.4 add M\n" " }repeat\n" " }{\n" " dlw x y M %.1f %.1f RM\n" " %.1f sub 0 exch RL currentpoint stroke\n" " M{ currentpoint\n" " 7 -%.1f RL\n" " 0 -%.1f RL\n" " -7 %.1f RL\n" " fill 5.4 sub M\n" " }repeat\n" " }ifelse}!\n", STEM_XOFF, STEM_YOFF, STEM_YOFF, BEAM_DEPTH, BEAM_DEPTH, BEAM_DEPTH, STEM_XOFF, STEM_YOFF, STEM_YOFF, BEAM_DEPTH, BEAM_DEPTH, BEAM_DEPTH); /* len gu - grace note stem up */ fprintf(fout, "/gu{ .6 SLW x y M\n" " %.1f 0 RM 0 exch RL stroke}!\n" /* len gd - grace note stem down */ "/gd{ .6 SLW x y M\n" " %.1f 0 RM 0 exch RL stroke}!\n", GSTEM_XOFF, -GSTEM_XOFF); /* n len sgu - gnote stem and n flag up */ fprintf(fout, "/sgu{ .6 SLW x y M %.1f 0 RM\n" " 0 exch RL currentpoint stroke\n" " M dup 1 eq{\n" " pop\n" " 0.6 -3.4 5.6 -3.8 3 -10 RC\n" " 1.2 4.4 -1.4 7 -3 7 RC fill\n" " }{\n" " { currentpoint\n" " 1 -3.2 5.6 -2.8 3.2 -8 RC\n" " 1.4 4.8 -2.4 5.4 -3.2 5.2 RC\n" " fill 3.5 sub M\n" " }repeat\n" " }ifelse}!\n", GSTEM_XOFF); /* n len sgd - gnote stem and n flag down */ fprintf(fout, "/sgd{ .6 SLW x y M %.1f 0 RM\n" " 0 exch RL currentpoint stroke\n" " M dup 1 eq{\n" " pop\n" " 0.6 3.4 5.6 3.8 3 10 RC\n" " 1.2 -4.4 -1.4 -7 -3 -7 RC fill\n" " }{\n" " { currentpoint\n" " 1 3.2 5.6 2.8 3.2 8 RC\n" " 1.4 -4.8 -2.4 -5.4 -3.2 -5.2 RC\n" " fill 3.5 add M\n" " }repeat\n" " }ifelse}!\n", -GSTEM_XOFF); /* n len sgs - gnote stem and n straight flag up */ fprintf(fout, "/sgs{ .6 SLW x y M %.1f 0 RM\n" " 0 exch RL currentpoint stroke\n" " M{ currentpoint\n" " 3 -1.5 RL 0 -2 RL -3 1.5 RL\n" " closepath fill 3 sub M\n" " }repeat}!\n", GSTEM_XOFF); } abcm2ps-8.14.11/voices.abc000066400000000000000000000057561376266546700152170ustar00rootroot00000000000000% here are only some first lines of sample tunes %%deco fp 6 pf 20 2 5 fp % --- piano --- X:1 T:Song for a guy C:Elton John M:C L:1/8 Q:1/4=128 %%staves {RH LH} K:C % most of piano tunes have only 2 voices .. V:RH [C8E8]|zE FG- GEC2|[B,3E3][B,D]- [B,4D4]|zD EF- FED2|D8| V:LH [C,3G,3][C,G,]- [C,4G,4]|[C,3G,3][C,G,]- [C,4G,4]|[B,,3G,3][B,,G,]- [B,,4G,4]|\ [B,,3G,3][B,,G,]- [B,,4G,4]|[_B,,3F,3][B,,F,]- [B,,4F,4]| X:2 T:8th Sonata for piano C:L. van Beethoven M:C L:1/16 Q:1/8=66 %%staves {1 2} K:Cm % .. even when there are a lot of notes V:1 !fp![E,4G,4C4]- [E,3/G,3/C3/]!3![G,/C/]!4![G,3/=B,3/D3/]!5![G,/C/E/] ([=A,4C4E4]!4![=B,2D2])z2|\ !fp!!3![=B,4D4F4]- [B,3/D3/F3/][B,/D/F/][B,3/D3/G3/][B,/D/A/] ([B,4D4A4]!3![C2E2G2])z2| V:2 [C,,4E,,4G,,4C,4]- [C,,3/E,,3/G,,3/C,3/]!2!E,/!3!D,3/!4!C,/ (!2!^F,4G,2)z _A,,|\ _A,4-A,3/!2!A,/!1!G,3/=F,/ E,4-E,2z3/ E,/| X:3 T: Praeludium II (WT II) C: J.S. Bach M: C L: 1/16 Q:1/4=66 %%staves {RH LH} %%MIDI program 6 K:Cm % same as bach.abc (abc2ps-1.3.0) but rewritten in a more standard way V:RH zGFG AFEF GEDE FDCD | E2c2F2c2 E2c2D2=B2 | V:LH C,2C2F,2C2 E,2C2D,2=B,2 | C,G,F,G, A,F,E,F, G,E,D,E, F,D,C,D, | X:4 T:Allegro grazioso C:Schumann M:C L:1/8 Q:1/4=66 %%staves {1 (2 3)} K:G V:1 (!p!B2AB G3)(A |Bcd[Ge]) ([F3A3][^GB])|([A2c2][^GB][Ac] AF=GA)|[G2B2][F2A2] {/G}G3B| V:2 % this voice is not complete, but the measure must be respected G,2C2 xD3- |D2x2 x4 | A,2D2 C2x2 |x8 | V:3 (G,DCD B,D2)(F,|G,A,B,C (D2)CB,) |(A,EDE CDB,C)|D2[D,2C2] [G,3B,3]D| % --- vocal --- X:5 T:Tridal a ra va c'halon T:(Mor Fawr Wyt Ti!) M:4/4 L:1/8 Q:1/4=48 %%staves [(S A) (T B)] K:A % Breton words on a Wales choral V:S EEE |C3E EEFF |(D2F3) FFF |E3C EEDD |C4 z:| w:Ka-na a |rin, ka-na a rin be-|pred,* rag an Ao-|trou en-eus va zan-tel-|let. w:Eñ eo va|nerz, eñ eo i-vez va|Zad,* eñ eo va|han, ka-na a rin e |hloar. V:A CCC |A,3A, B,CA,A, |(B,2A,3) DDB, |C3A, B,A,A,G, |A,4 z:| V:T A,A,A, |E,3E, G,A,F,F, |(F,2F,3) A,A,G, |A,3A, G,A,F,E, |E,4 z:| V:B A,,A,,A,, |A,,3C, B,,A,,D,D, |(B,,2D,3) D,D,B,,|E,3F, E,C,B,,E,, |A,,4 z:| % --- organ --- X:6 T:Wär Gott nicht mit uns diese Zeit C:Johann Nicolaus Hanff M:C L:1/8 Q:1/4=66 %%staves [1 (2 3) 4] V:1 nm="Rückpos" V:2 nm="Organo" K:Am V:1 %%MIDI program 53 A3B c2c2 |d2e2 de/f/P ^c3/d/|d8 |z8 | V:2 %%MIDI program 73 z2E2- E2AG |F2E2 F2E2 |F6 F2|E2CD E3F/G/| V:3 %%MIDI program 73 z2C2- CB,A,2 |A,8 |A,6 D2|C2A,B, C3D/E/| V:4 %%MIDI program 73 z2A,2- A,G,F,E,|D,2^C,2 D,2A,,2 |D,,8 |z4 z2A,2 | X:7 T:Qui Tolis (Trio) C:André Raison M:3/4 L:1/4 Q:1/4=92 %%staves {(Pos1 Pos2) Trompette} K:F % V:Pos1 %%MIDI program 78 "^Positif"x3 |x3 |c'>ba|Pga/g/f|:g2a |ba2 |g2c- |c2P=B |c>de |fga | V:Pos2 %%MIDI program 78 Mf>ed|cd/c/B|PA2d |ef/e/d |:e2f |ef2 |c>BA |GA/G/F |E>FG |ABc- | V:Trompette %%MIDI program 56 "^Trompette"z3|z3 |z3 |z3 |:Mc>BA|PGA/G/F|PE>EF|PEF/E/D|C>CPB,|A,G,F,-|