--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
--- /dev/null
+
+--------------------------------------------------------------------------------
+
+
+ Giada - Your Hardcore Loopmachine.
+
+ Developed by Monocasual Laboratories
+
+ www.giadamusic.com
+
+ CHANGELOG
+--------------------------------------------------------------------------------
+
+
+
+0.9.6 --- 2015 . 05 . 11
+- Keyboard binding for MIDI channels
+- Support for multiple files in drag-n-drop operations
+- Different color for wait/end statuses
+- Small improvements to Keyboard grabber widget
+- Fix random crashes with Jack enabled
+- Fix weird behavior with multiple drag and drop
+- Code refactoring
+
+
+0.9.5 --- 2015 . 03 . 28
+- Better column resize algorithm
+- New patch loading system with permanent MIDI mapping
+- Ability to clear assigned keys (keyboard mode)
+- Improved zoom icons in editors
+- Fix deprecation warning in configure.ac
+
+
+0.9.4 --- 2015 . 02 . 24
+- Drag-n-drop now works also in existing channels
+- Store 'resize recordings' flag in giada.conf
+- Better management of duplicate samples
+- Add more VST debug information
+- Minor fixes and tweaks
+
+
+0.9.3 --- 2015 . 02 . 01
+- New GUI improvement: responsive and resizable columns
+- Upgrade to FLTK 1.3.3
+- More robust column handling mechanism
+- Support for MIDI devices without note-off message (@blablack)
+- Fix segfaults when saving a patch with missing plugins
+- Fix many minor graphical bugs
+- Fix wrong vector assignment in MIDI send event
+- Fix reloaded patches with no right tempo/beats displayed
+- Fix random odd frames when adding/moving events in Piano Roll
+- Minor internal cleanup
+
+
+0.9.2 --- 2014 . 11 . 29
+- New grid layout in Sample Editor
+- Load samples via drag n drop
+- Add new utility functions: gTrim and gStripFileUrl
+- Fix "normalize" button position in Sample Editor
+- Minor waveform drawing optimizations
+- Add missing files for RtAudio-mod compilation
+- All one-shot mode, if fired manually, get the first frame truncated (fixed)
+
+
+0.9.1 --- 2014 . 09 . 24
+- Bring back custom version of rtAudio in source package
+- Automatically turn up volume when adding new channel
+- Updated 'misc' tab in configuration panel
+- Fix startup crash on OS X
+- Fix missing jack headers
+
+
+0.9.0 --- 2014 . 08 . 18
+- New full-screen GUI
+- Multi-column support
+- Advanced logging system
+- Upgrade to RtAudio 4.1.1 and RtMidi 2.1.0
+- Removed embedded RtAudio (thanks to Arty)
+- Fix wrong processing of VST MIDI events on 64 bit version
+- Fix stretched buttons when resizing sample editor window
+- "Clear all samples" destroys channels (fixed)
+- "Free channel" messes up loop / mute buttons (fixes)
+- Fix potential recordings with odd frames
+
+
+0.8.4 --- 2014 . 03 . 27
+- New mode 'Loop Bar Once'
+- Several small improvements and cleanups to internal utils functions
+- Fixed missing title in several subwindows
+- (win) Fix runtime error when loading a new project
+- Fix chan reset when clicking on waveform
+- Properly close subwindows after a channel has been deleted
+- Fix 'reload' button not working for samples with updated names
+
+
+0.8.3 --- 2014 . 02 . 14
+- Experimental MIDI timing output with MTC and MIDI clock
+- Expose Sequencer x2 and /2 via MIDI
+- New pitch operators x2 and /2
+- Internal xfade process restored
+- "set key..." becomes "setup keyboard input" for sample channels
+- MIDI events are now saved as unsigned int in patch
+- Same expression on both sides of '|' in recorder.cpp (fixed)
+- Muted channels leak some glitches on 'kill' event (fixed)
+- Piano roll can't be edited anymore if beats == 32 (fixed)
+- Noise when adding new MIDI channel (fixed)
+- Boost and Normalize not working (fixed)
+- Multiple copies of every file used by the patch (fixed)
+- Samples with -1, -2, ... -n suffix are not included in patch (fixed)
+- Segfaults when quantizing samples (fixed)
+
+
+0.8.2 --- 2014 . 01 . 13
+- Pitch control exposed via MIDI
+- New tools in Sample Editor (linear fade in/out, smooth edges)
+- Implemented vstEvent->deltaFrames, gaining more precision with vst
+ MIDI events
+- Add Fl::lock/Fl::unlock dynamics to glue_ calls where needed
+- Avoid pitch sliding when changing pitch of a sample in status OFF
+- Update copyright info in source files
+- Internal fade in and fade out restored
+- Add 'Giada' keyword to desktop file
+- Fix annoying glitches when playing very short samples
+- Fix random crashes when controlling giada via MIDI
+- Fix missing MIDI mapping for read-actions button
+
+
+0.8.1 --- 2013 . 12 . 09
+- New, high-quality pitch control based on libsamplerate
+- New set of functions 'spread sample to beat/song'
+[known issues]
+- Internal crossfades have been temporarily disabled. Some clicks may
+ occur
+
+
+0.8.0 --- 2013 . 11 . 03
+- Initial MIDI input support
+- Fix freeze when recording audio inputs on a second channel
+- Fix 'R' button to show up even if the channel has no actions
+- Fix weird drawings of keypress actions in action editor
+- Free channel: delete 'R' button as well
+- Shift+key does not kill loop mode channels in a wait status
+- Fix issue with 'R' button and newly added actions
+- Remove "left"/"right" labels from main buttons
+
+
+0.7.3 --- 2013 . 09 . 14
+- Experimental 64 bit compilation (Linux only)
+- Massive internal cleanup of channel/gui channel layers
+- Set default mode to full volume on sample load
+- Set default mode to oneshot basic
+- Faster drawings in piano roll
+- Visual aids in piano roll
+- Scroll to pointer in piano roll
+- Several minor improvements in piano roll's usability
+- Revised VST Carbon window popup system
+- Minor improvements in startInputRec/stopInputRec procedure
+- Fix compile error using local type Plugin* in Channel's constructor
+- Fix segfault in OSX when working with VST windows
+
+
+0.7.2 --- 2013 . 07 . 27
+- Initial MIDI output support
+- Mute now affects channels with VSTi signals
+- Lots of deb package improvements
+- Complete rewrite of VST GUI part on OS X
+- Don't send MIDI mute on sample channels
+- Send MIDI mute for MIDI channels in play mode
+- Fix wrong looping due to VST processing in mixer::masterPlay
+- Fix jack crashes when using Giada with ALSA
+- Fix VST random crashes on OSX, bus error
+- Fix input device set to -1 after a system change
+
+
+0.7.1 --- 2013 . 06 . 27
+- Initial Jack Transport support
+- Send global note off when sequencer is being stopped
+- Send note off when deleting notes in Piano Roll
+- Store position and size of Piano Roll in conf file
+- Avoid overlap MIDI notes in Piano Roll
+- MIDI channel refactoring
+- MIDI channels now behave like loop-mode ones
+- Fix graphical bugs in Action Editor, sample mode
+- Fix refresh issue in Piano Roll when deleting items
+- Lots of invisible cleanups and improvements
+
+
+0.7.0 --- 2013 . 06 . 05
+- Initial MIDI output implementation
+- Initial VSTi (instrument) support
+- New piano roll widget in action editor
+- New chan mode: MIDI vs SAMPLE
+- Fix E-MU Tracker Pre not correctly listed in audio in/output
+
+
+0.6.4 --- 2013 . 05 . 07
+- Resizable plugin parameter window
+- New and standard package name format <name>-<version>.<ext>
+- Implement RtAudio::getCompiledApi() to fetch compiled APIs
+- Implement audioMasterGetSampleRate, audioMasterGetLanguage VST opcodes
+- Add drop-down menu for buffer size values in config panel
+- Enhance project portability between OSes
+- Lots of fixes and improvements for VST strings and parameters
+- Avoid segfault when loading recs from a patch with files not found
+- Always remember selected program when shifting up/down plugins
+- Fix wrong size of single_press displayed in action editor
+- Fix volume actions resized with value set to zero
+- Fix volume envelope always over the cover area
+- Fix src package extracts to current dir
+- Fix segfault in loadpatch process if plugin GUIs are open
+- Fix segfault when closing patch with plugins in BAD status
+
+
+0.6.3 --- 2013 . 04 . 23
+- New 'solo' button
+- Portable project system
+- New 'Single Endless' channel mode
+- GUI enhancements for channels in WAIT or ENDING status
+- Minor fixes & cleanups
+
+
+0.6.2 --- 2013 . 04 . 05
+- New volume envelope widget
+- Zoom with mouse wheel in the action editor
+- Graphical enhancements & speedups for the action editor
+- Loop-repeat doesn't stop when put in ending mode (fixed)
+- Fix draw errors when zooming too much the action editor
+- Set silence in wave editor messes up the waveform (fixed)
+- Wrong slashes in file path when saving a patch in Windows (fixed)
+- Many, many code improvements and bugs fixed
+
+
+0.6.1 --- 2013 . 03 . 21
+- Unlimited number of channels
+- Deep internal refactoring, mixer/GUI layers
+- Fix random crashes on exit
+- Fix crashes when closing Giada with VST windows opened
+- Always free Master In plugin stack on exit
+- Lots of other minor bugs fixed and small enhancements
+
+
+0.6.0 --- 2013 . 03 . 02
+- New, full-screen, redesigned sample editor
+- Zoom with mouse wheel in sample editor
+- Use kernelAudio::defaultIn/defaultOut for DEFAULT_SOUNDDEV_OUT
+- Volume knob in main window now updates the editor
+- Sound system issues in OS X (fixed)
+- Output device info dialog refers to wrong device (fixed)
+
+
+0.5.8 --- 2013 . 02 . 07
+- Internal samplerate conversion (with libsamplerate)
+- Bring channels automatically to full volume on sample load
+- Ability to set the audio device frequency
+- New "internal mute" feature
+- fix for deprecated VST opcode 14
+- fix deb package issues on Ubuntu 12.10 / KXStudio
+
+
+0.5.7 --- 2013 . 01 . 21
+- visual grid + snapping in the action editor
+- implement more audioMasterCanDo's in pluginHost
+- limit zoom in actionEditor
+- revise zoom behavior in actionEditor, now more comfortable
+- fix forward declaration & inclusion of several headers
+- implemented VST opcode 32
+- implemented VST opcode 33
+- implemented VST opcode 34
+- update website link in tar files
+- update copyright info for 2013
+
+
+0.5.6 --- 2013 . 01 . 03
+- New overdub mode for live recording
+- Support for VST programs, aka presets
+- Lots of VST opcodes implemented
+- Fix crash when removing a plugin from the stack
+- Fix pops when going to beat 0
+- Fix compilation issues without --enable-vst
+- Many invisible optimizations and small bugs fixed
+
+
+0.5.5 --- 2012 . 12 . 15
+- "Hear what you're playing" feature
+- Fx processing on the input side
+- Ability to add different action types (Action Editor)
+- Desktop integration on Linux (via deb package)
+- Upgrade to FLTK 1.3.2
+- Remove "the action might stop the channel" when loading new samples
+- Fix wrong positioning of zoom tools (Action Editor)
+- Fix unwanted interactions on the grey area (Action Editor)
+- Fix wrong memory alloc during the VST processing
+- VST don't show up in OS X (fixed)
+- Minor internal refactoring + bugfixing
+
+
+0.5.4 --- 2012 . 11 . 24
+- VST GUI support
+- Better subwindow management
+- Implemented many other VST opcodes
+- Missing plugins are now shown in the list with a 'dead' state
+- Refresh action editor when changing beats (via beat operator or
+ beat window)
+- Graphical improvements in the action editor
+- Resizable action editor doesn't work well (fixed)
+- Fix auto fadeout for SINGLE_PRESS channels
+- Fix compilation without --enable-vst
+- Fix for a wrong prototype definition of the VST hostCallback
+
+
+0.5.3 --- 2012 . 10 . 26
+- Live beat manipulators (x2)(/2)
+- New sub-windows management, faster and more comfortable
+- New optional hard limiter on the output side
+- Action Editor window recalls x,y,w,h zoom and position
+- Usability improvements while handling an action (action editor)
+- Refresh actionEditor window when switching channel mode or delete
+ actions
+- Unable to delete a killchan action (action editor) (fixed)
+- Don't show ACTION_KILLCHAN in a singlepress channel (action editor)
+- Libsndfile no longer statically linked in Linux
+- Fixed a typo in config: "when the sequeCer is halted"
+- redefinition of DEFAULT_PITCH in wingdi.h (windows) (fixed)
+- Upgrade to FLTK 1.3.0
+- Other internal optimizations
+- Other small bugs fixed
+
+
+0.5.2 --- 2012 . 10 . 05
+- Add ability to handle actions for loop-mode channels
+- Add ability to record live mute actions for loop-mode channels
+- Lots of live action recording improvements
+- Enhanced usability for the action editor
+- More verbose output if kernel audio fails to start
+- Several internal optimizations
+
+
+0.5.1 --- 2012 . 09 . 13
+- First implementation of the Action Editor
+- Added compatibility with Ubuntu >= 10.04
+
+
+0.5.0 --- 2012 . 07 . 23
+- New custom project folder (.gprj)
+- Sample names are now made unique
+- Fixed unwanted time stretching while exporting a mono sample
+- Lots of minor internal improvements
+
+
+0.4.12 --- 2012 . 07 . 01
+- VST parameters and stacks are now stored in patch file
+- Upgrade to RtAudio 0.4.11
+- PulseAudio support in Linux (thanks to RtAudio 0.4.11)
+- Revised .deb package
+- Enhanced "normalize" function in wave editor
+- Several memory issues fixed
+- Internal enhancements and minor bugs fixed
+
+
+0.4.11 --- 2012 . 06 . 10
+- VST stack for each channel
+- Custom paths for plugins, samples and patches
+- Crash in config panel if device is busy (fixed)
+- Graphical bug in the input meter (fixed)
+- ParamLabel added in the VST parameter list
+
+
+0.4.10 --- 2012 . 05 . 30
+- Ability to shift up an down VST plugins
+- Enhanced patch/conf architecture
+- Ability to edit a sample while playing
+- Mutex controls in VST processing
+- Lots of security issues fixed while changing pitch dinamically
+- Enhanced sub-window system
+- Several minor bugs fixed
+
+
+0.4.9 --- 2012 . 05 . 12
+- No more mandatory inputs
+- Pitch value properly stored inside the patch
+- Several small VST host improvements
+- Enhanced window management
+- Ability to browse files while playing with main GUI (non-modal browser)
+- Improved error checking in KernelAudio
+- Wrong style for lower scrollbar in Browser (fixed)
+- Fixed compilation on 64 bit systems (thanks to Speps@Archlinux)
+- Samplerate no longer hardcoded, auto-detected with JACK
+- Minor internal improvements and bugfixing
+
+
+0.4.8 --- 2012 . 04 . 21
+- Initial VST support (experimental)
+- Pitch controller (experimental, no filtering)
+- OSX bundles are now correctly handled by the file browser
+- Fixed several memory leaks
+- Minor internal improvements
+
+
+0.4.7 --- 2012 . 03 . 31
+- Cut, trim & silence operations in sample editor
+- New "Reload sample" button added
+- Lots of optimizations in the waveform drawing routines
+- The sample is no longer editable while in play mode
+- Fixed potential startup crashes while using Giada with Jack Audio
+- Other minor fixes applied to the configuration panel
+- Fixed compilation on 64 bit systems (thanks to Speps@Archlinux)
+
+
+0.4.6 --- 2012 . 03 . 11
+- New device information panel
+- The device configuration now shows only active and available devices
+- Channel panel no longer pops up during a recording process
+- GUI beautifications and other minor graphical fixes
+- Program icon added in all subwindows
+- Action records no longer available during a take, and vice versa
+- Fixed a serious bug that swapped input and output devices
+- Fixed loop behavior in ending mode
+- Fixed clicks when stopping a muted channel in loop
+
+
+0.4.5 --- 2012 . 02 . 25
+- Complete GUI redesign
+- New "start/stop action recs" button
+- Lots of internal cleanups and micro refactorings
+- Small drawing glithes in Editor and status box (fixed)
+- An invalid patch puts Giada to init state (fixed)
+- Fixed button repeat on start/stop, action rec, input rec
+- Checks against takes with unique name
+- Message "this action may stop the channel" always shown (fixed)
+- Channel no longer freeable while a take is in progress
+
+
+0.4.4 --- 2012 . 02 . 04
+- New input/output channel selector
+- Rewind bypasses the quantizer if triggered via mouse (fixed)
+- Fixed library paths in configure and makefile (thanks to Yann C.)
+- Added AUTHORS and NEWS files to the source package (thanks to Yann C.)
+- More robust sample export procedure
+- Issues with mute buttons when opening a patch (fixed)
+- Several usability improvements
+- Minor code cleanups and optimizations
+
+
+0.4.3 --- 2012 . 01 . 21
+- New "save project" feature
+- Ability to export a single sample to disk
+- More feedback when removing/clearing actions and samples
+- Sequencer starts automatically when action-rec button is pressed
+- Alert if patch name is empty while saving it
+- Channels now store internally the name of the samples
+- Missing "--no devices found--" in input devices menu (fixed)
+- Alert added if there are no empty channels for recording
+- "Edit->Clear all actions" no longer works (fixed)
+- END button could be used as a channel trigger (fixed)
+- Recorders are available even if device status is wrong (fixed)
+- Missing sample rewind if channel is muted (fixed)
+- Quantizer doesn't work if framesize is odd (fixed)
+- Random segfault when closing Giada (fixed)
+- Lots of code cleanups
+- Other minor improvements and optimizations
+
+
+0.4.2 --- 2012 . 01 . 09
+- Live sampling from external input with meter and delay compensation
+- Check against uneven values and overflow in buffersize field
+- Wrong normalized values if volume level is 0.0 (fixed)
+- Boost dial goes crazy if normalized > 20.0 dB (fixed)
+- Boost dial goes crazy if normalized < 0.0 dB (fixed)
+- Unwanted noise click if a muted channel is being rewinded (fixed)
+- Mute doesn't work well for single-shot samples (fixed)
+- Wrong FLTK headers (fixed, thanks to Yann C.)
+- Moving chanStart/chanEnd swaps stereo image (fixed)
+- Reset to init state doesn't reset mute buttons (fixed)
+- Wrong chanStart value if > 0 (fixed)
+
+
+0.4.1 --- 2011 . 12 . 07
+- Complete mixer engine refactoring
+- Faster audio buffer allocation
+- Global beat system revisited
+- Autocrossfade between samples is now enabled by default
+- No more recorded actions on odd frames
+- Unintentional channel swapping fixed
+- Unable to list all sound systems and sound devs under OSX (fixed)
+- Missing graceful stop of audio streaming under OSX (fixed)
+
+
+0.4.0 --- 2011 . 11 . 16
+- Support for all major uncompressed file formats (with libsndfile)
+- Enhanced mono > stereo conversion
+- Fixed drawing issues for the start/stop labels inside the waveform
+- Enhanced backward compatibility with old patches
+- Support for compilation on OS X and Windows
+
+
+0.3.6 --- 2011 . 11 . 02
+- Initial Mac OS X release
+- (Windows) Ability to list and browse all active drives
+- Change some internal routines plus minor optimizations
+- Added -pedantic and -Werror flag to the compiler
+- Crash if clicking on mute in an empty channel (fixed)
+- Chan status changes if an empty channel is being muted (fixed)
+
+
+0.3.5 --- 2011 . 10 . 22
+- Pan controller added
+- New GNU-style source code packaging
+- Revamped .deb package
+- Program icon missing under Windows (fixed)
+- Crash if a sample in patch is missing from the filesystem (fixed)
+- Unable to rewind to beat 1 if quantizer is on and seq stopped (fixed)
+- Several minor glitches fixed
+
+
+0.3.4 --- 2011 . 10 . 10
+- Full source code released under GPL license
+- Autosmooth is now toggleable via setup
+- Faster loading process of patch files
+- Various internal cleanups and optimizations
+- Fixed incorrect reading of boost values from patch
+- Fixed a potential bug that prevented the config panel to appear
+- Fixed stereo swap bug
+- Minor graphical revisions
+
+
+0.3.3 --- 2011 . 09 . 28
+- New "normalize" function
+- More editing tools added inside the sample editor
+- Waveform beautifications
+- Fixed interaction bugs for boost and volume controls
+
+
+0.3.2 --- 2011 . 09 . 19
+- New "mute" button inside the main window
+- Waveform is now updated when the boost value changes
+- Zoomin/zoomout relative to the scrollbar position
+- Fixed garbage output if the volume was "-inf" (windows version)
+- Fixed several rendering issues for short waveforms
+
+
+0.3.1 --- 2011 . 09 . 12
+- Boost volume + fine volume control in sample editor
+- Start/End handles inside the editor are now draggable via mouse
+- Fixed scrollbar issues in sample editor
+- Start/end points are now always drawn in the foreground
+- Waveform no longer overflow if a value is greater than the window
+- (linux) giada.conf is saved inside the hidden folder /home/.giada
+- patch loading process is now faster and cleaner
+- Update to rtAudio 4.0.10
+
+
+0.3.0 --- 2011 . 09 . 01
+- New sample editor window
+- Ability to set start/end points within a sample
+- Update to rtAudio 4.0.9
+- Fixed an string overflow inside a patch
+- Fixed a missing memory free if a sample is unreadable
+- Several internal updates and optimizations
+
+
+0.2.7 --- 2011 . 07. 22
+- New way to handle recorded channels as loops
+- Fixed retrig for backspace key (rewind)
+- Enhanced rewind with quantization support
+- Main and alert windows now appear centered on screen
+- Sanity check against old patches without metronome information
+- Rewind now affects loops in rec-reading mode
+
+
+0.2.6 --- 2011 . 07 . 11
+- Internal metronome
+- Fixed some glitches in config panel
+- Minor cleanups
+
+
+0.2.5 --- 2011 . 06 . 20
+- Configuration panel redesign
+- Several new control options
+- Progress feedback when loading patches
+- Internal optimizations
+- Updated docs
+
+
+0.2.4 --- 2011 . 06 . 08
+- New loop repeat mode
+- Ability to save patches anywhere in the filesystem
+- Sub-beat management
+- Sound meter has been revisited and improved
+- Several patch enhancements
+- Core audio optimizations
+
+
+0.2.3 --- 2011 . 05 . 18
+- ASIO support for Windows version
+- Enhanced security when reading values from a patch
+- Ability to disable the recordings when the sequencer is paused
+- Master volume and rec status are now saved inside the patch
+- Device selection fixed and improved
+- Sequencer flickering in Windows has been fixed
+- Feedback added if a sample from a patch is unreadable or corrupted
+- Minor internal optimizations
+
+
+0.2.2 --- 2011 . 05 . 04
+- New open-source patch system
+- A patch can now be loaded from any location of the filesystem
+- Enhanced file browser coords system
+- Lots of minor improvements to the sample loading/unloading procedure
+- (win) Init path of file browser now starts from %userProfile%/Desktop
+- Wrong handling of "/" chars fixed in config menu
+- Fixed potential hangs on quit
+- Fixed clicks when stopping sequencer/sample
+- Minor gui beautifications
+
+
+0.2.1 --- 2011 . 04 . 26
+- Windows version
+
+
+0.2.0 --- 2011 . 04 . 19
+- Full JACK and ALSA support with RtAudio
+- New list of sound devices in menu window
+- Enhanced shutdown procedure to prevent potential crashes
+- Some GUI glitches fixed
+- Fixed random locks when the screensaver is active
+
+
+0.1.8 --- 2011 . 04 . 13
+- new functions: free al samples/recordings, reset to init patch
+- main menu redesign
+- the file browser is now resizable
+- GUI feedback for samples in play mode
+- some fixes when unloading a sample
+
+
+0.1.7 --- 2011 . 04 . 07
+- Ability to remove only action recordings or mute recordings
+- Shift+key now stops the sample if the master play is deactivated
+- Frame 0 was always processed at the end of the sequencer
+- Minor internal improvements
+
+
+0.1.6 --- 2011 . 03 . 29
+- Autocrossfade to prevent clicks
+- Internal improvements and bugfixing
+
+
+0.1.5 --- 2011 . 03 . 10
+- decimal bpm adjustment
+- ability to shrink/expand actions when changing the global beats
+- improved GUI for beats and bpm controllers
+- improved routines for action management
+- actions are now updated when you change bpm
+
+
+0.1.4 --- 2011 . 03 . 04
+- ability to save recorded actions
+- status box now shows if a recorded chan is deactivated
+- recorder is reset correctly when you load a new patch
+- minor improvements
+
+
+0.1.3 --- 2011 . 02 . 26
+- action recorder (first implementation)
+- quantization procedure slightly optimized
+- minor graphical adjustments
+- expanded documentation
+
+
+0.1.2 --- 2011 . 02 . 08
+- master volume controller
+- improved sound meter with more accuracy
+- improved verifications when reading or writing a patch
+- beat counter is now always reset to 1 after a patch is loaded
+- made loading wave files more robust, plus memory optimizations
+- minor crashes fixed
+
+
+0.1.1 --- 2011 . 01 . 26
+- expansion to 32 channels
+- GUI restyling
+- live quantizer
+- fixed wrong handling of "mute" value when loading a patch
+- minor internal improvements
+
+
+0.1.0 --- 2011 . 01 . 18
+- ability to mute channels
+- stop and rewind buttons now affect only channels in loop mode
+- undo for ending loops
+- internal patch improvements to provide backward compatibility
+- better behaviour when exceeding the total amount of available memory
+- fixed random reversals of stereo field at the end of the beat bar
+- fixed a potential segmentation fault when freeing a sample
+
+
+0.0.12 --- 2011 . 01 . 11
+- ability to free a channel
+- "stop" button to suspend the general program
+- new "stop-to-end" mode for looped channels
+- new "full stop" key combination
+- enhanced mouse interaction
+- minor bugfixing
+
+
+0.0.11 --- 2010 . 12 . 28
+- customizable keys
+- GUI layer optimizations and improvements
+- overwrite confirmation when saving a patch
+- the browser always displays the patch folder when loading a new patch
+- browser url is now read-only to prevent manipulations
+
+
+0.0.10 --- 2010 . 12 . 16
+- new "single-mode retrig" mode added
+- expansion to 16 channels
+- new advanced file browser with the ability to navigate the filesystem
+- audio configuration now uses the "default" device, if not changed
+- graphical restyling for audio channels
+- fixed a random crash on startup, due to a wrong thread synch
+
+
+0.0.9 --- 2010 . 12 . 08
+- new loop once mode
+- new graphical beat meter
+- rewind-program button added
+- heavy buttons and controls restyling
+- reinforced header verification when a new patch is opened for reading
+- some bugfixing for the loading procedure of a patch
+- fixed a potential crash while a new sample is being loaded
+
+
+0.0.8 --- 2010 . 11 . 28
+- fixed a critical crash while loading a sample
+- GUI warning when loading a sample or a patch into an active channel
+- little optimization during the search for data into waves
+- all popup windows are now modal (always on top)
+- fixed a potential crash in case of malformed wave files
+
+
+0.0.7 --- 2010 . 11 . 18
+- new peak meter with clip warning and system status report
+- any "ok" button is associated to the "return" key (for fast inputs)
+- graphical improvements for checkboxes, buttons, smaller fonts in browsers
+- graphical feedback for missing samples
+- internal optimizations
+
+
+0.0.6 --- 2010 . 11 . 01
+- new 32 bit floating point audio engine
+- support for any wave bit-rate, from 8 bit pcm to 32 float
+- Giada now prompts when a sound card error occurs
+- removed the hard-limiting system, now useless
+- the "save patch" panel now shows the actual patchname in use
+- alphabetic sort into the file browser
+- fixed an annoying gui flickering
+- patch volume information are now handled correctly
+- minor internal optimizations
+- fixed a memory leak when loading a new patch
+- other memory optimizations
+
+
+0.0.5 --- 2010 . 10 . 21
+- Patch-based system: load/save your setup from/to a binary file
+- New audio configuration panel
+- New configuration file (giada.conf) where to store data
+- Complete implementation of the double click startup
+- Fixed a bug related to the confirm-on-quit window
+- Minor GUI beautifications
+- Extended documentation
+
+
+0.0.4 --- 2010 . 10 . 11
+- New internal sample-accurate loop engine
+- Ability to configure the period size through ini file
+- First implementation of the double click startup
+- Debug information are now properly tagged, reporting the interested layer
+
+
+0.0.3 --- 2010 . 10 . 02
+- (giada) New official logo
+- (giada) Ability to load single-channel samples
+- (giada) Capital letter consistency between GUI buttons
+- (giada) Added "cancel" button to the browser window
+- (giada) Endianness verification
+- (giada) Cleanup of the audio initialization procedure
+- (giada) Several internal optimization for audio playback
+- (giada) ALSA layer now tells if an underrun occurs
+- (giada) Internal memory allocation improvements
+- (giada) Fixed an unallocated hardware parameter into ALSA configuration
+- (wa) Information about wave endianness
+- Added a "Requirements" section to the readme file
+
+
+0.0.2 --- 2010 . 09 . 17
+- (giada) More visual feedbacks if a key is pressed
+- (giada) Added a graphical alert if a sample is in an incorrect format
+- (giada) Confirm on exit
+- (giada) Graphical improvements for the browser window
+- (giada) Browser window doesn't close itself anymore if a sample format is incorrect
+- (giada) Added "-- no sample --" for empty channels
+- (giada) Startup no longer fails if a sample from the ini file is not found
+- (giada) Internal optimization for the sample loading routine
+- (giada) More graphical consistency between subwindows
+- (giada) The sample name is now trucated to fit into its box, preventing overflow
+- (giada) Other minor GUI tweaks
+- (giada) Internal memory improvements to prevent a bad bug of allocation with malformed wave files
+- (wa) More information about sample size
+- (wa) Added calculations and comparison between data sizes
+
+
+0.0.1 --- 2010 . 09 . 06
+(initial release)
--- /dev/null
+SUBDIRS=src
\ No newline at end of file
--- /dev/null
+
+
+Giada - Your Hardcore Loopmachine
+=================================
+
+http://www.giadamusic.com
+
+
+
+What is Giada?
+--------------
+Giada is a free, minimal, hardcore audio tool for DJs, live performers
+and electronic musicians. How does it work? Just pick up your channel,
+fill it with samples or MIDI events and start the show by using this
+tiny piece of software as a loop machine, drum machine, sequencer, live
+sampler or yet as a plugin/effect host. Giada aims to be a compact and
+portable virtual device for Linux, Mac OS X and Windows for production
+use and live sets.
+
+
+
+Features:
+
+* Ultra-lightweight internal design.
+* Multi-thread/multi-core support.
+* 32-bit floating point audio engine.
+* ALSA, JACK + Transport, CoreAudio, ASIO and DirectSound full support.
+* Unlimited number of channels controlled via computer keyboard.
+* Different playback modes and combinations.
+* BPM and beat sync with sample-accurate loop engine.
+* Built-in wave editor.
+* VST and VSTi (instrument) plugin support.
+* MIDI input and output support.
+* Live sampler from external inputs.
+* Live quantizer.
+* Piano Roll editor.
+* Action recorder.
+* Patch-based system.
+* Support for all major uncompressed file formats.
+* A constant stage of development.
+* 100% open-source GPL.
+
+
+
+
+License
+-------
+Giada is available under the terms of the GNU General Public License.
+Take a look at the COPYING file for further informations.
+
+
+
+Documentation
+-------------
+Docs are available online on the official website
+
+http://www.giadamusic.com/documentation
+
+
+
+Bugs, requests and questions for non-developers
+-----------------------------------------------
+End users forum
+
+http://www.giadamusic.com/forum
+
+
+
+Copyright
+---------
+Giada is Copyright (C) 2010-2015 by Giovanni A. Zuliani | Monocasual
+
+Giada - Your Hardcore Loopmachine 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.
+
+Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+<http://www.gnu.org/licenses/>.
--- /dev/null
+#!/bin/sh
+# a u t o g e n . s h
+#
+# Copyright (c) 2005-2009 United States Government as represented by
+# the U.S. Army Research Laboratory.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#
+# 3. The name of the author may not be used to endorse or promote
+# products derived from this software without specific prior written
+# permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+###
+#
+# Script for automatically preparing the sources for compilation by
+# performing the myriad of necessary steps. The script attempts to
+# detect proper version support, and outputs warnings about particular
+# systems that have autotool peculiarities.
+#
+# Basically, if everything is set up and installed correctly, the
+# script will validate that minimum versions of the GNU Build System
+# tools are installed, account for several common configuration
+# issues, and then simply run autoreconf for you.
+#
+# If autoreconf fails, which can happen for many valid configurations,
+# this script proceeds to run manual preparation steps effectively
+# providing a POSIX shell script (mostly complete) reimplementation of
+# autoreconf.
+#
+# The AUTORECONF, AUTOCONF, AUTOMAKE, LIBTOOLIZE, ACLOCAL, AUTOHEADER
+# environment variables and corresponding _OPTIONS variables (e.g.
+# AUTORECONF_OPTIONS) may be used to override the default automatic
+# detection behaviors. Similarly the _VERSION variables will override
+# the minimum required version numbers.
+#
+# Examples:
+#
+# To obtain help on usage:
+# ./autogen.sh --help
+#
+# To obtain verbose output:
+# ./autogen.sh --verbose
+#
+# To skip autoreconf and prepare manually:
+# AUTORECONF=false ./autogen.sh
+#
+# To verbosely try running with an older (unsupported) autoconf:
+# AUTOCONF_VERSION=2.50 ./autogen.sh --verbose
+#
+# Author:
+# Christopher Sean Morrison <morrison@brlcad.org>
+#
+# Patches:
+# Sebastian Pipping <sebastian@pipping.org>
+#
+######################################################################
+
+# set to minimum acceptable version of autoconf
+if [ "x$AUTOCONF_VERSION" = "x" ] ; then
+ AUTOCONF_VERSION=2.52
+fi
+# set to minimum acceptable version of automake
+if [ "x$AUTOMAKE_VERSION" = "x" ] ; then
+ AUTOMAKE_VERSION=1.6.0
+fi
+# set to minimum acceptable version of libtool
+if [ "x$LIBTOOL_VERSION" = "x" ] ; then
+ LIBTOOL_VERSION=1.4.2
+fi
+
+
+##################
+# ident function #
+##################
+ident ( ) {
+ # extract copyright from header
+ __copyright="`grep Copyright $AUTOGEN_SH | head -${HEAD_N}1 | awk '{print $4}'`"
+ if [ "x$__copyright" = "x" ] ; then
+ __copyright="`date +%Y`"
+ fi
+
+ # extract version from CVS Id string
+ __id="$Id: autogen.sh 33925 2009-03-01 23:27:06Z brlcad $"
+ __version="`echo $__id | sed 's/.*\([0-9][0-9][0-9][0-9]\)[-\/]\([0-9][0-9]\)[-\/]\([0-9][0-9]\).*/\1\2\3/'`"
+ if [ "x$__version" = "x" ] ; then
+ __version=""
+ fi
+
+ echo "autogen.sh build preparation script by Christopher Sean Morrison"
+ echo " + config.guess download patch by Sebastian Pipping (2008-12-03)"
+ echo "revised 3-clause BSD-style license, copyright (c) $__copyright"
+ echo "script version $__version, ISO/IEC 9945 POSIX shell script"
+}
+
+
+##################
+# USAGE FUNCTION #
+##################
+usage ( ) {
+ echo "Usage: $AUTOGEN_SH [-h|--help] [-v|--verbose] [-q|--quiet] [-d|--download] [--version]"
+ echo " --help Help on $NAME_OF_AUTOGEN usage"
+ echo " --verbose Verbose progress output"
+ echo " --quiet Quiet suppressed progress output"
+ echo " --download Download the latest config.guess from gnulib"
+ echo " --version Only perform GNU Build System version checks"
+ echo
+ echo "Description: This script will validate that minimum versions of the"
+ echo "GNU Build System tools are installed and then run autoreconf for you."
+ echo "Should autoreconf fail, manual preparation steps will be run"
+ echo "potentially accounting for several common preparation issues. The"
+
+ echo "AUTORECONF, AUTOCONF, AUTOMAKE, LIBTOOLIZE, ACLOCAL, AUTOHEADER,"
+ echo "PROJECT, & CONFIGURE environment variables and corresponding _OPTIONS"
+ echo "variables (e.g. AUTORECONF_OPTIONS) may be used to override the"
+ echo "default automatic detection behavior."
+ echo
+
+ ident
+
+ return 0
+}
+
+
+##########################
+# VERSION_ERROR FUNCTION #
+##########################
+version_error ( ) {
+ if [ "x$1" = "x" ] ; then
+ echo "INTERNAL ERROR: version_error was not provided a version"
+ exit 1
+ fi
+ if [ "x$2" = "x" ] ; then
+ echo "INTERNAL ERROR: version_error was not provided an application name"
+ exit 1
+ fi
+ $ECHO
+ $ECHO "ERROR: To prepare the ${PROJECT} build system from scratch,"
+ $ECHO " at least version $1 of $2 must be installed."
+ $ECHO
+ $ECHO "$NAME_OF_AUTOGEN does not need to be run on the same machine that will"
+ $ECHO "run configure or make. Either the GNU Autotools will need to be installed"
+ $ECHO "or upgraded on this system, or $NAME_OF_AUTOGEN must be run on the source"
+ $ECHO "code on another system and then transferred to here. -- Cheers!"
+ $ECHO
+}
+
+##########################
+# VERSION_CHECK FUNCTION #
+##########################
+version_check ( ) {
+ if [ "x$1" = "x" ] ; then
+ echo "INTERNAL ERROR: version_check was not provided a minimum version"
+ exit 1
+ fi
+ _min="$1"
+ if [ "x$2" = "x" ] ; then
+ echo "INTERNAL ERROR: version check was not provided a comparison version"
+ exit 1
+ fi
+ _cur="$2"
+
+ # needed to handle versions like 1.10 and 1.4-p6
+ _min="`echo ${_min}. | sed 's/[^0-9]/./g' | sed 's/\.\././g'`"
+ _cur="`echo ${_cur}. | sed 's/[^0-9]/./g' | sed 's/\.\././g'`"
+
+ _min_major="`echo $_min | cut -d. -f1`"
+ _min_minor="`echo $_min | cut -d. -f2`"
+ _min_patch="`echo $_min | cut -d. -f3`"
+
+ _cur_major="`echo $_cur | cut -d. -f1`"
+ _cur_minor="`echo $_cur | cut -d. -f2`"
+ _cur_patch="`echo $_cur | cut -d. -f3`"
+
+ if [ "x$_min_major" = "x" ] ; then
+ _min_major=0
+ fi
+ if [ "x$_min_minor" = "x" ] ; then
+ _min_minor=0
+ fi
+ if [ "x$_min_patch" = "x" ] ; then
+ _min_patch=0
+ fi
+ if [ "x$_cur_minor" = "x" ] ; then
+ _cur_major=0
+ fi
+ if [ "x$_cur_minor" = "x" ] ; then
+ _cur_minor=0
+ fi
+ if [ "x$_cur_patch" = "x" ] ; then
+ _cur_patch=0
+ fi
+
+ $VERBOSE_ECHO "Checking if ${_cur_major}.${_cur_minor}.${_cur_patch} is greater than ${_min_major}.${_min_minor}.${_min_patch}"
+
+ if [ $_min_major -lt $_cur_major ] ; then
+ return 0
+ elif [ $_min_major -eq $_cur_major ] ; then
+ if [ $_min_minor -lt $_cur_minor ] ; then
+ return 0
+ elif [ $_min_minor -eq $_cur_minor ] ; then
+ if [ $_min_patch -lt $_cur_patch ] ; then
+ return 0
+ elif [ $_min_patch -eq $_cur_patch ] ; then
+ return 0
+ fi
+ fi
+ fi
+ return 1
+}
+
+
+######################################
+# LOCATE_CONFIGURE_TEMPLATE FUNCTION #
+######################################
+locate_configure_template ( ) {
+ _pwd="`pwd`"
+ if test -f "./configure.ac" ; then
+ echo "./configure.ac"
+ elif test -f "./configure.in" ; then
+ echo "./configure.in"
+ elif test -f "$_pwd/configure.ac" ; then
+ echo "$_pwd/configure.ac"
+ elif test -f "$_pwd/configure.in" ; then
+ echo "$_pwd/configure.in"
+ elif test -f "$PATH_TO_AUTOGEN/configure.ac" ; then
+ echo "$PATH_TO_AUTOGEN/configure.ac"
+ elif test -f "$PATH_TO_AUTOGEN/configure.in" ; then
+ echo "$PATH_TO_AUTOGEN/configure.in"
+ fi
+}
+
+
+##################
+# argument check #
+##################
+ARGS="$*"
+PATH_TO_AUTOGEN="`dirname $0`"
+NAME_OF_AUTOGEN="`basename $0`"
+AUTOGEN_SH="$PATH_TO_AUTOGEN/$NAME_OF_AUTOGEN"
+
+LIBTOOL_M4="${PATH_TO_AUTOGEN}/misc/libtool.m4"
+
+if [ "x$HELP" = "x" ] ; then
+ HELP=no
+fi
+if [ "x$QUIET" = "x" ] ; then
+ QUIET=no
+fi
+if [ "x$VERBOSE" = "x" ] ; then
+ VERBOSE=no
+fi
+if [ "x$VERSION_ONLY" = "x" ] ; then
+ VERSION_ONLY=no
+fi
+if [ "x$DOWNLOAD" = "x" ] ; then
+ DOWNLOAD=no
+fi
+if [ "x$AUTORECONF_OPTIONS" = "x" ] ; then
+ AUTORECONF_OPTIONS="-i -f"
+fi
+if [ "x$AUTOCONF_OPTIONS" = "x" ] ; then
+ AUTOCONF_OPTIONS="-f"
+fi
+if [ "x$AUTOMAKE_OPTIONS" = "x" ] ; then
+ AUTOMAKE_OPTIONS="-a -c -f"
+fi
+ALT_AUTOMAKE_OPTIONS="-a -c"
+if [ "x$LIBTOOLIZE_OPTIONS" = "x" ] ; then
+ LIBTOOLIZE_OPTIONS="--automake -c -f"
+fi
+ALT_LIBTOOLIZE_OPTIONS="--automake --copy --force"
+if [ "x$ACLOCAL_OPTIONS" = "x" ] ; then
+ ACLOCAL_OPTIONS=""
+fi
+if [ "x$AUTOHEADER_OPTIONS" = "x" ] ; then
+ AUTOHEADER_OPTIONS=""
+fi
+if [ "x$CONFIG_GUESS_URL" = "x" ] ; then
+ CONFIG_GUESS_URL="http://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=blob_plain;f=build-aux/config.guess;hb=HEAD"
+fi
+for arg in $ARGS ; do
+ case "x$arg" in
+ x--help) HELP=yes ;;
+ x-[hH]) HELP=yes ;;
+ x--quiet) QUIET=yes ;;
+ x-[qQ]) QUIET=yes ;;
+ x--verbose) VERBOSE=yes ;;
+ x-[dD]) DOWNLOAD=yes ;;
+ x--download) DOWNLOAD=yes ;;
+ x-[vV]) VERBOSE=yes ;;
+ x--version) VERSION_ONLY=yes ;;
+ *)
+ echo "Unknown option: $arg"
+ echo
+ usage
+ exit 1
+ ;;
+ esac
+done
+
+
+#####################
+# environment check #
+#####################
+
+# sanity check before recursions potentially begin
+if [ ! -f "$AUTOGEN_SH" ] ; then
+ echo "INTERNAL ERROR: $AUTOGEN_SH does not exist"
+ if [ ! "x$0" = "x$AUTOGEN_SH" ] ; then
+ echo "INTERNAL ERROR: dirname/basename inconsistency: $0 != $AUTOGEN_SH"
+ fi
+ exit 1
+fi
+
+# force locale setting to C so things like date output as expected
+LC_ALL=C
+
+# commands that this script expects
+for __cmd in echo head tail pwd ; do
+ echo "test" | $__cmd > /dev/null 2>&1
+ if [ $? != 0 ] ; then
+ echo "INTERNAL ERROR: '${__cmd}' command is required"
+ exit 2
+ fi
+done
+echo "test" | grep "test" > /dev/null 2>&1
+if test ! x$? = x0 ; then
+ echo "INTERNAL ERROR: grep command is required"
+ exit 1
+fi
+echo "test" | sed "s/test/test/" > /dev/null 2>&1
+if test ! x$? = x0 ; then
+ echo "INTERNAL ERROR: sed command is required"
+ exit 1
+fi
+
+
+# determine the behavior of echo
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+# determine the behavior of head
+case "x`echo 'head' | head -n 1 2>&1`" in
+ *xhead*) HEAD_N="n " ;;
+ *) HEAD_N="" ;;
+esac
+
+# determine the behavior of tail
+case "x`echo 'tail' | tail -n 1 2>&1`" in
+ *xtail*) TAIL_N="n " ;;
+ *) TAIL_N="" ;;
+esac
+
+VERBOSE_ECHO=:
+ECHO=:
+if [ "x$QUIET" = "xyes" ] ; then
+ if [ "x$VERBOSE" = "xyes" ] ; then
+ echo "Verbose output quelled by quiet option. Further output disabled."
+ fi
+else
+ ECHO=echo
+ if [ "x$VERBOSE" = "xyes" ] ; then
+ echo "Verbose output enabled"
+ VERBOSE_ECHO=echo
+ fi
+fi
+
+
+# allow a recursive run to disable further recursions
+if [ "x$RUN_RECURSIVE" = "x" ] ; then
+ RUN_RECURSIVE=yes
+fi
+
+
+################################################
+# check for help arg and bypass version checks #
+################################################
+if [ "x`echo $ARGS | sed 's/.*[hH][eE][lL][pP].*/help/'`" = "xhelp" ] ; then
+ HELP=yes
+fi
+if [ "x$HELP" = "xyes" ] ; then
+ usage
+ $ECHO "---"
+ $ECHO "Help was requested. No preparation or configuration will be performed."
+ exit 0
+fi
+
+
+#######################
+# set up signal traps #
+#######################
+untrap_abnormal ( ) {
+ for sig in 1 2 13 15; do
+ trap - $sig
+ done
+}
+
+# do this cleanup whenever we exit.
+trap '
+ # start from the root
+ if test -d "$START_PATH" ; then
+ cd "$START_PATH"
+ fi
+
+ # restore/delete backup files
+ if test "x$PFC_INIT" = "x1" ; then
+ recursive_restore
+ fi
+' 0
+
+# trap SIGHUP (1), SIGINT (2), SIGPIPE (13), SIGTERM (15)
+for sig in 1 2 13 15; do
+ trap '
+ $ECHO ""
+ $ECHO "Aborting $NAME_OF_AUTOGEN: caught signal '$sig'"
+
+ # start from the root
+ if test -d "$START_PATH" ; then
+ cd "$START_PATH"
+ fi
+
+ # clean up on abnormal exit
+ $VERBOSE_ECHO "rm -rf autom4te.cache"
+ rm -rf autom4te.cache
+
+ if test -f "acinclude.m4.$$.backup" ; then
+ $VERBOSE_ECHO "cat acinclude.m4.$$.backup > acinclude.m4"
+ chmod u+w acinclude.m4
+ cat acinclude.m4.$$.backup > acinclude.m4
+
+ $VERBOSE_ECHO "rm -f acinclude.m4.$$.backup"
+ rm -f acinclude.m4.$$.backup
+ fi
+
+ { (exit 1); exit 1; }
+' $sig
+done
+
+
+#############################
+# look for a configure file #
+#############################
+if [ "x$CONFIGURE" = "x" ] ; then
+ CONFIGURE="`locate_configure_template`"
+ if [ ! "x$CONFIGURE" = "x" ] ; then
+ $VERBOSE_ECHO "Found a configure template: $CONFIGURE"
+ fi
+else
+ $ECHO "Using CONFIGURE environment variable override: $CONFIGURE"
+fi
+if [ "x$CONFIGURE" = "x" ] ; then
+ if [ "x$VERSION_ONLY" = "xyes" ] ; then
+ CONFIGURE=/dev/null
+ else
+ $ECHO
+ $ECHO "A configure.ac or configure.in file could not be located implying"
+ $ECHO "that the GNU Build System is at least not used in this directory. In"
+ $ECHO "any case, there is nothing to do here without one of those files."
+ $ECHO
+ $ECHO "ERROR: No configure.in or configure.ac file found in `pwd`"
+ exit 1
+ fi
+fi
+
+####################
+# get project name #
+####################
+if [ "x$PROJECT" = "x" ] ; then
+ PROJECT="`grep AC_INIT $CONFIGURE | grep -v '.*#.*AC_INIT' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_INIT(\([^,)]*\).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ if [ "x$PROJECT" = "xAC_INIT" ] ; then
+ # projects might be using the older/deprecated arg-less AC_INIT .. look for AM_INIT_AUTOMAKE instead
+ PROJECT="`grep AM_INIT_AUTOMAKE $CONFIGURE | grep -v '.*#.*AM_INIT_AUTOMAKE' | tail -${TAIL_N}1 | sed 's/^[ ]*AM_INIT_AUTOMAKE(\([^,)]*\).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ fi
+ if [ "x$PROJECT" = "xAM_INIT_AUTOMAKE" ] ; then
+ PROJECT="project"
+ fi
+ if [ "x$PROJECT" = "x" ] ; then
+ PROJECT="project"
+ fi
+else
+ $ECHO "Using PROJECT environment variable override: $PROJECT"
+fi
+$ECHO "Preparing the $PROJECT build system...please wait"
+$ECHO
+
+
+########################
+# check for autoreconf #
+########################
+HAVE_AUTORECONF=no
+if [ "x$AUTORECONF" = "x" ] ; then
+ for AUTORECONF in autoreconf ; do
+ $VERBOSE_ECHO "Checking autoreconf version: $AUTORECONF --version"
+ $AUTORECONF --version > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ HAVE_AUTORECONF=yes
+ break
+ fi
+ done
+else
+ HAVE_AUTORECONF=yes
+ $ECHO "Using AUTORECONF environment variable override: $AUTORECONF"
+fi
+
+
+##########################
+# autoconf version check #
+##########################
+_acfound=no
+if [ "x$AUTOCONF" = "x" ] ; then
+ for AUTOCONF in autoconf ; do
+ $VERBOSE_ECHO "Checking autoconf version: $AUTOCONF --version"
+ $AUTOCONF --version > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ _acfound=yes
+ break
+ fi
+ done
+else
+ _acfound=yes
+ $ECHO "Using AUTOCONF environment variable override: $AUTOCONF"
+fi
+
+_report_error=no
+if [ ! "x$_acfound" = "xyes" ] ; then
+ $ECHO "ERROR: Unable to locate GNU Autoconf."
+ _report_error=yes
+else
+ _version="`$AUTOCONF --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`"
+ if [ "x$_version" = "x" ] ; then
+ _version="0.0.0"
+ fi
+ $ECHO "Found GNU Autoconf version $_version"
+ version_check "$AUTOCONF_VERSION" "$_version"
+ if [ $? -ne 0 ] ; then
+ _report_error=yes
+ fi
+fi
+if [ "x$_report_error" = "xyes" ] ; then
+ version_error "$AUTOCONF_VERSION" "GNU Autoconf"
+ exit 1
+fi
+
+
+##########################
+# automake version check #
+##########################
+_amfound=no
+if [ "x$AUTOMAKE" = "x" ] ; then
+ for AUTOMAKE in automake ; do
+ $VERBOSE_ECHO "Checking automake version: $AUTOMAKE --version"
+ $AUTOMAKE --version > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ _amfound=yes
+ break
+ fi
+ done
+else
+ _amfound=yes
+ $ECHO "Using AUTOMAKE environment variable override: $AUTOMAKE"
+fi
+
+
+_report_error=no
+if [ ! "x$_amfound" = "xyes" ] ; then
+ $ECHO
+ $ECHO "ERROR: Unable to locate GNU Automake."
+ _report_error=yes
+else
+ _version="`$AUTOMAKE --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`"
+ if [ "x$_version" = "x" ] ; then
+ _version="0.0.0"
+ fi
+ $ECHO "Found GNU Automake version $_version"
+ version_check "$AUTOMAKE_VERSION" "$_version"
+ if [ $? -ne 0 ] ; then
+ _report_error=yes
+ fi
+fi
+if [ "x$_report_error" = "xyes" ] ; then
+ version_error "$AUTOMAKE_VERSION" "GNU Automake"
+ exit 1
+fi
+
+
+########################
+# check for libtoolize #
+########################
+HAVE_LIBTOOLIZE=yes
+HAVE_ALT_LIBTOOLIZE=no
+_ltfound=no
+if [ "x$LIBTOOLIZE" = "x" ] ; then
+ LIBTOOLIZE=libtoolize
+ $VERBOSE_ECHO "Checking libtoolize version: $LIBTOOLIZE --version"
+ $LIBTOOLIZE --version > /dev/null 2>&1
+ if [ ! $? = 0 ] ; then
+ HAVE_LIBTOOLIZE=no
+ $ECHO
+ if [ "x$HAVE_AUTORECONF" = "xno" ] ; then
+ $ECHO "Warning: libtoolize does not appear to be available."
+ else
+ $ECHO "Warning: libtoolize does not appear to be available. This means that"
+ $ECHO "the automatic build preparation via autoreconf will probably not work."
+ $ECHO "Preparing the build by running each step individually, however, should"
+ $ECHO "work and will be done automatically for you if autoreconf fails."
+ fi
+
+ # look for some alternates
+ for tool in glibtoolize libtoolize15 libtoolize14 libtoolize13 ; do
+ $VERBOSE_ECHO "Checking libtoolize alternate: $tool --version"
+ _glibtoolize="`$tool --version > /dev/null 2>&1`"
+ if [ $? = 0 ] ; then
+ $VERBOSE_ECHO "Found $tool --version"
+ _glti="`which $tool`"
+ if [ "x$_glti" = "x" ] ; then
+ $VERBOSE_ECHO "Cannot find $tool with which"
+ continue;
+ fi
+ if test ! -f "$_glti" ; then
+ $VERBOSE_ECHO "Cannot use $tool, $_glti is not a file"
+ continue;
+ fi
+ _gltidir="`dirname $_glti`"
+ if [ "x$_gltidir" = "x" ] ; then
+ $VERBOSE_ECHO "Cannot find $tool path with dirname of $_glti"
+ continue;
+ fi
+ if test ! -d "$_gltidir" ; then
+ $VERBOSE_ECHO "Cannot use $tool, $_gltidir is not a directory"
+ continue;
+ fi
+ HAVE_ALT_LIBTOOLIZE=yes
+ LIBTOOLIZE="$tool"
+ $ECHO
+ $ECHO "Fortunately, $tool was found which means that your system may simply"
+ $ECHO "have a non-standard or incomplete GNU Autotools install. If you have"
+ $ECHO "sufficient system access, it may be possible to quell this warning by"
+ $ECHO "running:"
+ $ECHO
+ sudo -V > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ $ECHO " sudo ln -s $_glti $_gltidir/libtoolize"
+ $ECHO
+ else
+ $ECHO " ln -s $_glti $_gltidir/libtoolize"
+ $ECHO
+ $ECHO "Run that as root or with proper permissions to the $_gltidir directory"
+ $ECHO
+ fi
+ _ltfound=yes
+ break
+ fi
+ done
+ else
+ _ltfound=yes
+ fi
+else
+ _ltfound=yes
+ $ECHO "Using LIBTOOLIZE environment variable override: $LIBTOOLIZE"
+fi
+
+
+############################
+# libtoolize version check #
+############################
+_report_error=no
+if [ ! "x$_ltfound" = "xyes" ] ; then
+ $ECHO
+ $ECHO "ERROR: Unable to locate GNU Libtool."
+ _report_error=yes
+else
+ _version="`$LIBTOOLIZE --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`"
+ if [ "x$_version" = "x" ] ; then
+ _version="0.0.0"
+ fi
+ $ECHO "Found GNU Libtool version $_version"
+ version_check "$LIBTOOL_VERSION" "$_version"
+ if [ $? -ne 0 ] ; then
+ _report_error=yes
+ fi
+fi
+if [ "x$_report_error" = "xyes" ] ; then
+ version_error "$LIBTOOL_VERSION" "GNU Libtool"
+ exit 1
+fi
+
+
+#####################
+# check for aclocal #
+#####################
+if [ "x$ACLOCAL" = "x" ] ; then
+ for ACLOCAL in aclocal ; do
+ $VERBOSE_ECHO "Checking aclocal version: $ACLOCAL --version"
+ $ACLOCAL --version > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ break
+ fi
+ done
+else
+ $ECHO "Using ACLOCAL environment variable override: $ACLOCAL"
+fi
+
+
+########################
+# check for autoheader #
+########################
+if [ "x$AUTOHEADER" = "x" ] ; then
+ for AUTOHEADER in autoheader ; do
+ $VERBOSE_ECHO "Checking autoheader version: $AUTOHEADER --version"
+ $AUTOHEADER --version > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ break
+ fi
+ done
+else
+ $ECHO "Using AUTOHEADER environment variable override: $AUTOHEADER"
+fi
+
+
+#########################
+# check if version only #
+#########################
+$VERBOSE_ECHO "Checking whether to only output version information"
+if [ "x$VERSION_ONLY" = "xyes" ] ; then
+ $ECHO
+ ident
+ $ECHO "---"
+ $ECHO "Version requested. No preparation or configuration will be performed."
+ exit 0
+fi
+
+
+#################################
+# PROTECT_FROM_CLOBBER FUNCTION #
+#################################
+protect_from_clobber ( ) {
+ PFC_INIT=1
+
+ # protect COPYING & INSTALL from overwrite by automake. the
+ # automake force option will (inappropriately) ignore the existing
+ # contents of a COPYING and/or INSTALL files (depending on the
+ # version) instead of just forcing *missing* files like it does
+ # for AUTHORS, NEWS, and README. this is broken but extremely
+ # prevalent behavior, so we protect against it by keeping a backup
+ # of the file that can later be restored.
+
+ for file in COPYING INSTALL ; do
+ if test -f ${file} ; then
+ if test -f ${file}.$$.protect_from_automake.backup ; then
+ $VERBOSE_ECHO "Already backed up ${file} in `pwd`"
+ else
+ $VERBOSE_ECHO "Backing up ${file} in `pwd`"
+ $VERBOSE_ECHO "cp -p ${file} ${file}.$$.protect_from_automake.backup"
+ cp -p ${file} ${file}.$$.protect_from_automake.backup
+ fi
+ fi
+ done
+}
+
+
+##############################
+# RECURSIVE_PROTECT FUNCTION #
+##############################
+recursive_protect ( ) {
+
+ # for projects using recursive configure, run the build
+ # preparation steps for the subdirectories. this function assumes
+ # START_PATH was set to pwd before recursion begins so that
+ # relative paths work.
+
+ # git 'r done, protect COPYING and INSTALL from being clobbered
+ protect_from_clobber
+
+ if test -d autom4te.cache ; then
+ $VERBOSE_ECHO "Found an autom4te.cache directory, deleting it"
+ $VERBOSE_ECHO "rm -rf autom4te.cache"
+ rm -rf autom4te.cache
+ fi
+
+ # find configure template
+ _configure="`locate_configure_template`"
+ if [ "x$_configure" = "x" ] ; then
+ return
+ fi
+ # $VERBOSE_ECHO "Looking for configure template found `pwd`/$_configure"
+
+ # look for subdirs
+ # $VERBOSE_ECHO "Looking for subdirs in `pwd`"
+ _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $_configure | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ CHECK_DIRS=""
+ for dir in $_det_config_subdirs ; do
+ if test -d "`pwd`/$dir" ; then
+ CHECK_DIRS="$CHECK_DIRS \"`pwd`/$dir\""
+ fi
+ done
+
+ # process subdirs
+ if [ ! "x$CHECK_DIRS" = "x" ] ; then
+ $VERBOSE_ECHO "Recursively scanning the following directories:"
+ $VERBOSE_ECHO " $CHECK_DIRS"
+ for dir in $CHECK_DIRS ; do
+ $VERBOSE_ECHO "Protecting files from automake in $dir"
+ cd "$START_PATH"
+ eval "cd $dir"
+
+ # recursively git 'r done
+ recursive_protect
+ done
+ fi
+} # end of recursive_protect
+
+
+#############################
+# RESTORE_CLOBBERED FUNCION #
+#############################
+restore_clobbered ( ) {
+
+ # The automake (and autoreconf by extension) -f/--force-missing
+ # option may overwrite COPYING and INSTALL even if they do exist.
+ # Here we restore the files if necessary.
+
+ spacer=no
+
+ for file in COPYING INSTALL ; do
+ if test -f ${file}.$$.protect_from_automake.backup ; then
+ if test -f ${file} ; then
+ # compare entire content, restore if needed
+ if test "x`cat ${file}`" != "x`cat ${file}.$$.protect_from_automake.backup`" ; then
+ if test "x$spacer" = "xno" ; then
+ $VERBOSE_ECHO
+ spacer=yes
+ fi
+ # restore the backup
+ $VERBOSE_ECHO "Restoring ${file} from backup (automake -f likely clobbered it)"
+ $VERBOSE_ECHO "rm -f ${file}"
+ rm -f ${file}
+ $VERBOSE_ECHO "mv ${file}.$$.protect_from_automake.backup ${file}"
+ mv ${file}.$$.protect_from_automake.backup ${file}
+ fi # check contents
+ elif test -f ${file}.$$.protect_from_automake.backup ; then
+ $VERBOSE_ECHO "mv ${file}.$$.protect_from_automake.backup ${file}"
+ mv ${file}.$$.protect_from_automake.backup ${file}
+ fi # -f ${file}
+
+ # just in case
+ $VERBOSE_ECHO "rm -f ${file}.$$.protect_from_automake.backup"
+ rm -f ${file}.$$.protect_from_automake.backup
+ fi # -f ${file}.$$.protect_from_automake.backup
+ done
+
+ CONFIGURE="`locate_configure_template`"
+ if [ "x$CONFIGURE" = "x" ] ; then
+ return
+ fi
+
+ _aux_dir="`grep AC_CONFIG_AUX_DIR $CONFIGURE | grep -v '.*#.*AC_CONFIG_AUX_DIR' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_CONFIG_AUX_DIR(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ if test ! -d "$_aux_dir" ; then
+ _aux_dir=.
+ fi
+
+ for file in config.guess config.sub ltmain.sh ; do
+ if test -f "${_aux_dir}/${file}" ; then
+ $VERBOSE_ECHO "rm -f \"${_aux_dir}/${file}.backup\""
+ rm -f "${_aux_dir}/${file}.backup"
+ fi
+ done
+} # end of restore_clobbered
+
+
+##############################
+# RECURSIVE_RESTORE FUNCTION #
+##############################
+recursive_restore ( ) {
+
+ # restore COPYING and INSTALL from backup if they were clobbered
+ # for each directory recursively.
+
+ # git 'r undone
+ restore_clobbered
+
+ # find configure template
+ _configure="`locate_configure_template`"
+ if [ "x$_configure" = "x" ] ; then
+ return
+ fi
+
+ # look for subdirs
+ _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $_configure | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ CHECK_DIRS=""
+ for dir in $_det_config_subdirs ; do
+ if test -d "`pwd`/$dir" ; then
+ CHECK_DIRS="$CHECK_DIRS \"`pwd`/$dir\""
+ fi
+ done
+
+ # process subdirs
+ if [ ! "x$CHECK_DIRS" = "x" ] ; then
+ $VERBOSE_ECHO "Recursively scanning the following directories:"
+ $VERBOSE_ECHO " $CHECK_DIRS"
+ for dir in $CHECK_DIRS ; do
+ $VERBOSE_ECHO "Checking files for automake damage in $dir"
+ cd "$START_PATH"
+ eval "cd $dir"
+
+ # recursively git 'r undone
+ recursive_restore
+ done
+ fi
+} # end of recursive_restore
+
+
+#######################
+# INITIALIZE FUNCTION #
+#######################
+initialize ( ) {
+
+ # this routine performs a variety of directory-specific
+ # initializations. some are sanity checks, some are preventive,
+ # and some are necessary setup detection.
+ #
+ # this function sets:
+ # CONFIGURE
+ # SEARCH_DIRS
+ # CONFIG_SUBDIRS
+
+ ##################################
+ # check for a configure template #
+ ##################################
+ CONFIGURE="`locate_configure_template`"
+ if [ "x$CONFIGURE" = "x" ] ; then
+ $ECHO
+ $ECHO "A configure.ac or configure.in file could not be located implying"
+ $ECHO "that the GNU Build System is at least not used in this directory. In"
+ $ECHO "any case, there is nothing to do here without one of those files."
+ $ECHO
+ $ECHO "ERROR: No configure.in or configure.ac file found in `pwd`"
+ exit 1
+ fi
+
+ #####################
+ # detect an aux dir #
+ #####################
+ _aux_dir="`grep AC_CONFIG_AUX_DIR $CONFIGURE | grep -v '.*#.*AC_CONFIG_AUX_DIR' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_CONFIG_AUX_DIR(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ if test ! -d "$_aux_dir" ; then
+ _aux_dir=.
+ else
+ $VERBOSE_ECHO "Detected auxillary directory: $_aux_dir"
+ fi
+
+ ################################
+ # detect a recursive configure #
+ ################################
+ CONFIG_SUBDIRS=""
+ _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $CONFIGURE | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ for dir in $_det_config_subdirs ; do
+ if test -d "`pwd`/$dir" ; then
+ $VERBOSE_ECHO "Detected recursive configure directory: `pwd`/$dir"
+ CONFIG_SUBDIRS="$CONFIG_SUBDIRS `pwd`/$dir"
+ fi
+ done
+
+ ###########################################################
+ # make sure certain required files exist for GNU projects #
+ ###########################################################
+ _marker_found=""
+ _marker_found_message_intro='Detected non-GNU marker "'
+ _marker_found_message_mid='" in '
+ for marker in foreign cygnus ; do
+ _marker_found_message=${_marker_found_message_intro}${marker}${_marker_found_message_mid}
+ _marker_found="`grep 'AM_INIT_AUTOMAKE.*'${marker} $CONFIGURE`"
+ if [ ! "x$_marker_found" = "x" ] ; then
+ $VERBOSE_ECHO "${_marker_found_message}`basename \"$CONFIGURE\"`"
+ break
+ fi
+ if test -f "`dirname \"$CONFIGURE\"/Makefile.am`" ; then
+ _marker_found="`grep 'AUTOMAKE_OPTIONS.*'${marker} Makefile.am`"
+ if [ ! "x$_marker_found" = "x" ] ; then
+ $VERBOSE_ECHO "${_marker_found_message}Makefile.am"
+ break
+ fi
+ fi
+ done
+ if [ "x${_marker_found}" = "x" ] ; then
+ _suggest_foreign=no
+ for file in AUTHORS COPYING ChangeLog INSTALL NEWS README ; do
+ if [ ! -f $file ] ; then
+ $VERBOSE_ECHO "Touching ${file} since it does not exist"
+ _suggest_foreign=yes
+ touch $file
+ fi
+ done
+
+ if [ "x${_suggest_foreign}" = "xyes" ] ; then
+ $ECHO
+ $ECHO "Warning: Several files expected of projects that conform to the GNU"
+ $ECHO "coding standards were not found. The files were automatically added"
+ $ECHO "for you since you do not have a 'foreign' declaration specified."
+ $ECHO
+ $ECHO "Considered adding 'foreign' to AM_INIT_AUTOMAKE in `basename \"$CONFIGURE\"`"
+ if test -f "`dirname \"$CONFIGURE\"/Makefile.am`" ; then
+ $ECHO "or to AUTOMAKE_OPTIONS in your top-level Makefile.am file."
+ fi
+ $ECHO
+ fi
+ fi
+
+ ##################################################
+ # make sure certain generated files do not exist #
+ ##################################################
+ for file in config.guess config.sub ltmain.sh ; do
+ if test -f "${_aux_dir}/${file}" ; then
+ $VERBOSE_ECHO "mv -f \"${_aux_dir}/${file}\" \"${_aux_dir}/${file}.backup\""
+ mv -f "${_aux_dir}/${file}" "${_aux_dir}/${file}.backup"
+ fi
+ done
+
+ ############################
+ # search alternate m4 dirs #
+ ############################
+ SEARCH_DIRS=""
+ for dir in m4 ; do
+ if [ -d $dir ] ; then
+ $VERBOSE_ECHO "Found extra aclocal search directory: $dir"
+ SEARCH_DIRS="$SEARCH_DIRS -I $dir"
+ fi
+ done
+
+ ######################################
+ # remove any previous build products #
+ ######################################
+ if test -d autom4te.cache ; then
+ $VERBOSE_ECHO "Found an autom4te.cache directory, deleting it"
+ $VERBOSE_ECHO "rm -rf autom4te.cache"
+ rm -rf autom4te.cache
+ fi
+# tcl/tk (and probably others) have a customized aclocal.m4, so can't delete it
+# if test -f aclocal.m4 ; then
+# $VERBOSE_ECHO "Found an aclocal.m4 file, deleting it"
+# $VERBOSE_ECHO "rm -f aclocal.m4"
+# rm -f aclocal.m4
+# fi
+
+} # end of initialize()
+
+
+##############
+# initialize #
+##############
+
+# stash path
+START_PATH="`pwd`"
+
+# Before running autoreconf or manual steps, some prep detection work
+# is necessary or useful. Only needs to occur once per directory, but
+# does need to traverse the entire subconfigure hierarchy to protect
+# files from being clobbered even by autoreconf.
+recursive_protect
+
+# start from where we started
+cd "$START_PATH"
+
+# get ready to process
+initialize
+
+
+#########################################
+# DOWNLOAD_GNULIB_CONFIG_GUESS FUNCTION #
+#########################################
+
+# TODO - should make sure wget/curl exist and/or work before trying to
+# use them.
+
+download_gnulib_config_guess () {
+ # abuse gitweb to download gnulib's latest config.guess via HTTP
+ config_guess_temp="config.guess.$$.download"
+ ret=1
+ for __cmd in wget curl fetch ; do
+ $VERBOSE_ECHO "Checking for command ${__cmd}"
+ ${__cmd} --version > /dev/null 2>&1
+ ret=$?
+ if [ ! $ret = 0 ] ; then
+ continue
+ fi
+
+ __cmd_version=`${__cmd} --version | head -n 1 | sed -e 's/^[^0-9]\+//' -e 's/ .*//'`
+ $VERBOSE_ECHO "Found ${__cmd} ${__cmd_version}"
+
+ opts=""
+ case ${__cmd} in
+ wget)
+ opts="-O"
+ ;;
+ curl)
+ opts="-o"
+ ;;
+ fetch)
+ opts="-t 5 -f"
+ ;;
+ esac
+
+ $VERBOSE_ECHO "Running $__cmd \"${CONFIG_GUESS_URL}\" $opts \"${config_guess_temp}\""
+ eval "$__cmd \"${CONFIG_GUESS_URL}\" $opts \"${config_guess_temp}\"" > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ mv -f "${config_guess_temp}" ${_aux_dir}/config.guess
+ ret=0
+ break
+ fi
+ done
+
+ if [ ! $ret = 0 ] ; then
+ $ECHO "Warning: config.guess download failed from: $CONFIG_GUESS_URL"
+ rm -f "${config_guess_temp}"
+ fi
+}
+
+
+##############################
+# LIBTOOLIZE_NEEDED FUNCTION #
+##############################
+libtoolize_needed () {
+ ret=1 # means no, don't need libtoolize
+ for feature in AC_PROG_LIBTOOL AM_PROG_LIBTOOL LT_INIT ; do
+ $VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
+ found="`grep \"^$feature.*\" $CONFIGURE`"
+ if [ ! "x$found" = "x" ] ; then
+ ret=0 # means yes, need to run libtoolize
+ break
+ fi
+ done
+ return ${ret}
+}
+
+
+
+############################################
+# prepare build via autoreconf or manually #
+############################################
+reconfigure_manually=no
+if [ "x$HAVE_AUTORECONF" = "xyes" ] ; then
+ $ECHO
+ $ECHO $ECHO_N "Automatically preparing build ... $ECHO_C"
+
+ $VERBOSE_ECHO "$AUTORECONF $SEARCH_DIRS $AUTORECONF_OPTIONS"
+ autoreconf_output="`$AUTORECONF $SEARCH_DIRS $AUTORECONF_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$autoreconf_output"
+
+ if [ ! $ret = 0 ] ; then
+ if [ "x$HAVE_ALT_LIBTOOLIZE" = "xyes" ] ; then
+ if [ ! "x`echo \"$autoreconf_output\" | grep libtoolize | grep \"No such file or directory\"`" = "x" ] ; then
+ $ECHO
+ $ECHO "Warning: autoreconf failed but due to what is usually a common libtool"
+ $ECHO "misconfiguration issue. This problem is encountered on systems that"
+ $ECHO "have installed libtoolize under a different name without providing a"
+ $ECHO "symbolic link or without setting the LIBTOOLIZE environment variable."
+ $ECHO
+ $ECHO "Restarting the preparation steps with LIBTOOLIZE set to $LIBTOOLIZE"
+
+ export LIBTOOLIZE
+ RUN_RECURSIVE=no
+ export RUN_RECURSIVE
+ untrap_abnormal
+
+ $VERBOSE_ECHO sh $AUTOGEN_SH "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+ sh "$AUTOGEN_SH" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+ exit $?
+ fi
+ fi
+
+ $ECHO "Warning: $AUTORECONF failed"
+
+ if test -f ltmain.sh ; then
+ $ECHO "libtoolize being run by autoreconf is not creating ltmain.sh in the auxillary directory like it should"
+ fi
+
+ $ECHO "Attempting to run the preparation steps individually"
+ reconfigure_manually=yes
+ else
+ if [ "x$DOWNLOAD" = "xyes" ] ; then
+ if libtoolize_needed ; then
+ download_gnulib_config_guess
+ fi
+ fi
+ fi
+else
+ reconfigure_manually=yes
+fi
+
+
+############################
+# LIBTOOL_FAILURE FUNCTION #
+############################
+libtool_failure ( ) {
+
+ # libtool is rather error-prone in comparison to the other
+ # autotools and this routine attempts to compensate for some
+ # common failures. the output after a libtoolize failure is
+ # parsed for an error related to AC_PROG_LIBTOOL and if found, we
+ # attempt to inject a project-provided libtool.m4 file.
+
+ _autoconf_output="$1"
+
+ if [ "x$RUN_RECURSIVE" = "xno" ] ; then
+ # we already tried the libtool.m4, don't try again
+ return 1
+ fi
+
+ if test -f "$LIBTOOL_M4" ; then
+ found_libtool="`$ECHO $_autoconf_output | grep AC_PROG_LIBTOOL`"
+ if test ! "x$found_libtool" = "x" ; then
+ if test -f acinclude.m4 ; then
+ rm -f acinclude.m4.$$.backup
+ $VERBOSE_ECHO "cat acinclude.m4 > acinclude.m4.$$.backup"
+ cat acinclude.m4 > acinclude.m4.$$.backup
+ fi
+ $VERBOSE_ECHO "cat \"$LIBTOOL_M4\" >> acinclude.m4"
+ chmod u+w acinclude.m4
+ cat "$LIBTOOL_M4" >> acinclude.m4
+
+ # don't keep doing this
+ RUN_RECURSIVE=no
+ export RUN_RECURSIVE
+ untrap_abnormal
+
+ $ECHO
+ $ECHO "Restarting the preparation steps with libtool macros in acinclude.m4"
+ $VERBOSE_ECHO sh $AUTOGEN_SH "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+ sh "$AUTOGEN_SH" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+ exit $?
+ fi
+ fi
+}
+
+
+###########################
+# MANUAL_AUTOGEN FUNCTION #
+###########################
+manual_autogen ( ) {
+
+ ##################################################
+ # Manual preparation steps taken are as follows: #
+ # aclocal [-I m4] #
+ # libtoolize --automake -c -f #
+ # aclocal [-I m4] #
+ # autoconf -f #
+ # autoheader #
+ # automake -a -c -f #
+ ##################################################
+
+ ###########
+ # aclocal #
+ ###########
+ $VERBOSE_ECHO "$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS"
+ aclocal_output="`$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$aclocal_output"
+ if [ ! $ret = 0 ] ; then $ECHO "ERROR: $ACLOCAL failed" && exit 2 ; fi
+
+ ##############
+ # libtoolize #
+ ##############
+ if libtoolize_needed ; then
+ if [ "x$HAVE_LIBTOOLIZE" = "xyes" ] ; then
+ $VERBOSE_ECHO "$LIBTOOLIZE $LIBTOOLIZE_OPTIONS"
+ libtoolize_output="`$LIBTOOLIZE $LIBTOOLIZE_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$libtoolize_output"
+
+ if [ ! $ret = 0 ] ; then $ECHO "ERROR: $LIBTOOLIZE failed" && exit 2 ; fi
+ else
+ if [ "x$HAVE_ALT_LIBTOOLIZE" = "xyes" ] ; then
+ $VERBOSE_ECHO "$LIBTOOLIZE $ALT_LIBTOOLIZE_OPTIONS"
+ libtoolize_output="`$LIBTOOLIZE $ALT_LIBTOOLIZE_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$libtoolize_output"
+
+ if [ ! $ret = 0 ] ; then $ECHO "ERROR: $LIBTOOLIZE failed" && exit 2 ; fi
+ fi
+ fi
+
+ ###########
+ # aclocal #
+ ###########
+ # re-run again as instructed by libtoolize
+ $VERBOSE_ECHO "$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS"
+ aclocal_output="`$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$aclocal_output"
+
+ # libtoolize might put ltmain.sh in the wrong place
+ if test -f ltmain.sh ; then
+ if test ! -f "${_aux_dir}/ltmain.sh" ; then
+ $ECHO
+ $ECHO "Warning: $LIBTOOLIZE is creating ltmain.sh in the wrong directory"
+ $ECHO
+ $ECHO "Fortunately, the problem can be worked around by simply copying the"
+ $ECHO "file to the appropriate location (${_aux_dir}/). This has been done for you."
+ $ECHO
+ $VERBOSE_ECHO "cp -p ltmain.sh \"${_aux_dir}/ltmain.sh\""
+ cp -p ltmain.sh "${_aux_dir}/ltmain.sh"
+ $ECHO $ECHO_N "Continuing build preparation ... $ECHO_C"
+ fi
+ fi # ltmain.sh
+
+ if [ "x$DOWNLOAD" = "xyes" ] ; then
+ download_gnulib_config_guess
+ fi
+ fi # libtoolize_needed
+
+ ############
+ # autoconf #
+ ############
+ $VERBOSE_ECHO
+ $VERBOSE_ECHO "$AUTOCONF $AUTOCONF_OPTIONS"
+ autoconf_output="`$AUTOCONF $AUTOCONF_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$autoconf_output"
+
+ if [ ! $ret = 0 ] ; then
+ # retry without the -f and check for usage of macros that are too new
+ ac2_59_macros="AC_C_RESTRICT AC_INCLUDES_DEFAULT AC_LANG_ASSERT AC_LANG_WERROR AS_SET_CATFILE"
+ ac2_55_macros="AC_COMPILER_IFELSE AC_FUNC_MBRTOWC AC_HEADER_STDBOOL AC_LANG_CONFTEST AC_LANG_SOURCE AC_LANG_PROGRAM AC_LANG_CALL AC_LANG_FUNC_TRY_LINK AC_MSG_FAILURE AC_PREPROC_IFELSE"
+ ac2_54_macros="AC_C_BACKSLASH_A AC_CONFIG_LIBOBJ_DIR AC_GNU_SOURCE AC_PROG_EGREP AC_PROG_FGREP AC_REPLACE_FNMATCH AC_FUNC_FNMATCH_GNU AC_FUNC_REALLOC AC_TYPE_MBSTATE_T"
+
+ macros_to_search=""
+ ac_major="`echo ${AUTOCONF_VERSION}. | cut -d. -f1 | sed 's/[^0-9]//g'`"
+ ac_minor="`echo ${AUTOCONF_VERSION}. | cut -d. -f2 | sed 's/[^0-9]//g'`"
+
+ if [ $ac_major -lt 2 ] ; then
+ macros_to_search="$ac2_59_macros $ac2_55_macros $ac2_54_macros"
+ else
+ if [ $ac_minor -lt 54 ] ; then
+ macros_to_search="$ac2_59_macros $ac2_55_macros $ac2_54_macros"
+ elif [ $ac_minor -lt 55 ] ; then
+ macros_to_search="$ac2_59_macros $ac2_55_macros"
+ elif [ $ac_minor -lt 59 ] ; then
+ macros_to_search="$ac2_59_macros"
+ fi
+ fi
+
+ configure_ac_macros=__none__
+ for feature in $macros_to_search ; do
+ $VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
+ found="`grep \"^$feature.*\" $CONFIGURE`"
+ if [ ! "x$found" = "x" ] ; then
+ if [ "x$configure_ac_macros" = "x__none__" ] ; then
+ configure_ac_macros="$feature"
+ else
+ configure_ac_macros="$feature $configure_ac_macros"
+ fi
+ fi
+ done
+ if [ ! "x$configure_ac_macros" = "x__none__" ] ; then
+ $ECHO
+ $ECHO "Warning: Unsupported macros were found in $CONFIGURE"
+ $ECHO
+ $ECHO "The `basename \"$CONFIGURE\"` file was scanned in order to determine if any"
+ $ECHO "unsupported macros are used that exceed the minimum version"
+ $ECHO "settings specified within this file. As such, the following macros"
+ $ECHO "should be removed from configure.ac or the version numbers in this"
+ $ECHO "file should be increased:"
+ $ECHO
+ $ECHO "$configure_ac_macros"
+ $ECHO
+ $ECHO $ECHO_N "Ignorantly continuing build preparation ... $ECHO_C"
+ fi
+
+ ###################
+ # autoconf, retry #
+ ###################
+ $VERBOSE_ECHO
+ $VERBOSE_ECHO "$AUTOCONF"
+ autoconf_output="`$AUTOCONF 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$autoconf_output"
+
+ if [ ! $ret = 0 ] ; then
+ # test if libtool is busted
+ libtool_failure "$autoconf_output"
+
+ # let the user know what went wrong
+ cat <<EOF
+$autoconf_output
+EOF
+ $ECHO "ERROR: $AUTOCONF failed"
+ exit 2
+ else
+ # autoconf sans -f and possibly sans unsupported options succeed so warn verbosely
+ $ECHO
+ $ECHO "Warning: autoconf seems to have succeeded by removing the following options:"
+ $ECHO " AUTOCONF_OPTIONS=\"$AUTOCONF_OPTIONS\""
+ $ECHO
+ $ECHO "Removing those options should not be necessary and indicate some other"
+ $ECHO "problem with the build system. The build preparation is highly suspect"
+ $ECHO "and may result in configuration or compilation errors. Consider"
+ if [ "x$VERBOSE_ECHO" = "x:" ] ; then
+ $ECHO "rerunning the build preparation with verbose output enabled."
+ $ECHO " $AUTOGEN_SH --verbose"
+ else
+ $ECHO "reviewing the minimum GNU Autotools version settings contained in"
+ $ECHO "this script along with the macros being used in your `basename \"$CONFIGURE\"` file."
+ fi
+ $ECHO
+ $ECHO $ECHO_N "Continuing build preparation ... $ECHO_C"
+ fi # autoconf ret = 0
+ fi # autoconf ret = 0
+
+ ##############
+ # autoheader #
+ ##############
+ need_autoheader=no
+ for feature in AM_CONFIG_HEADER AC_CONFIG_HEADER ; do
+ $VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
+ found="`grep \"^$feature.*\" $CONFIGURE`"
+ if [ ! "x$found" = "x" ] ; then
+ need_autoheader=yes
+ break
+ fi
+ done
+ if [ "x$need_autoheader" = "xyes" ] ; then
+ $VERBOSE_ECHO "$AUTOHEADER $AUTOHEADER_OPTIONS"
+ autoheader_output="`$AUTOHEADER $AUTOHEADER_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$autoheader_output"
+ if [ ! $ret = 0 ] ; then $ECHO "ERROR: $AUTOHEADER failed" && exit 2 ; fi
+ fi # need_autoheader
+
+ ############
+ # automake #
+ ############
+ need_automake=no
+ for feature in AM_INIT_AUTOMAKE ; do
+ $VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
+ found="`grep \"^$feature.*\" $CONFIGURE`"
+ if [ ! "x$found" = "x" ] ; then
+ need_automake=yes
+ break
+ fi
+ done
+
+ if [ "x$need_automake" = "xyes" ] ; then
+ $VERBOSE_ECHO "$AUTOMAKE $AUTOMAKE_OPTIONS"
+ automake_output="`$AUTOMAKE $AUTOMAKE_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$automake_output"
+
+ if [ ! $ret = 0 ] ; then
+
+ ###################
+ # automake, retry #
+ ###################
+ ALT_AUTOMAKE_OPTIONS="$ALT_AUTOMAKE_OPTIONS --add-missing"
+ $VERBOSE_ECHO
+ $VERBOSE_ECHO "$AUTOMAKE $ALT_AUTOMAKE_OPTIONS"
+ # retry without the -f
+ automake_output="`$AUTOMAKE $ALT_AUTOMAKE_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$automake_output"
+
+ if [ ! $ret = 0 ] ; then
+ # test if libtool is busted
+ libtool_failure "$automake_output"
+
+ # let the user know what went wrong
+ cat <<EOF
+$automake_output
+EOF
+ $ECHO "ERROR: $AUTOMAKE failed"
+ exit 2
+ fi # automake retry
+ fi # automake ret = 0
+ fi # need_automake
+} # end of manual_autogen
+
+
+#####################################
+# RECURSIVE_MANUAL_AUTOGEN FUNCTION #
+#####################################
+recursive_manual_autogen ( ) {
+
+ # run the build preparation steps manually for this directory
+ manual_autogen
+
+ # for projects using recursive configure, run the build
+ # preparation steps for the subdirectories.
+ if [ ! "x$CONFIG_SUBDIRS" = "x" ] ; then
+ $VERBOSE_ECHO "Recursively configuring the following directories:"
+ $VERBOSE_ECHO " $CONFIG_SUBDIRS"
+ for dir in $CONFIG_SUBDIRS ; do
+ $VERBOSE_ECHO "Processing recursive configure in $dir"
+ cd "$START_PATH"
+ cd "$dir"
+
+ # new directory, prepare
+ initialize
+
+ # run manual steps for the subdir and any others below
+ recursive_manual_autogen
+ done
+ fi
+}
+
+
+################################
+# run manual preparation steps #
+################################
+if [ "x$reconfigure_manually" = "xyes" ] ; then
+ $ECHO
+ $ECHO $ECHO_N "Preparing build ... $ECHO_C"
+
+ recursive_manual_autogen
+fi
+
+
+#########################
+# restore and summarize #
+#########################
+cd "$START_PATH"
+
+# restore COPYING and INSTALL from backup if necessary
+recursive_restore
+
+# make sure we end up with a configure script
+config_ac="`locate_configure_template`"
+config="`echo $config_ac | sed 's/\.ac$//' | sed 's/\.in$//'`"
+if [ "x$config" = "x" ] ; then
+ $VERBOSE_ECHO "Could not locate the configure template (from `pwd`)"
+fi
+
+# summarize
+$ECHO "done"
+$ECHO
+if test "x$config" = "x" -o ! -f "$config" ; then
+ $ECHO "WARNING: The $PROJECT build system should now be prepared but there"
+ $ECHO "does not seem to be a resulting configure file. This is unexpected"
+ $ECHO "and likely the result of an error. You should run $NAME_OF_AUTOGEN"
+ $ECHO "with the --verbose option to get more details on a potential"
+ $ECHO "misconfiguration."
+else
+ $ECHO "The $PROJECT build system is now prepared. To build here, run:"
+ $ECHO " $config"
+ $ECHO " make"
+fi
+
+
+# Local Variables:
+# mode: sh
+# tab-width: 8
+# sh-basic-offset: 4
+# sh-indentation: 4
+# indent-tabs-mode: t
+# End:
+# ex: shiftwidth=4 tabstop=8
--- /dev/null
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+# prereq & init
+
+AC_PREREQ(2.60)
+AC_INIT([giada], [0.9], [giadaloopmachine@gmail.com])
+AC_CONFIG_SRCDIR([src/main.cpp])
+AM_INIT_AUTOMAKE
+
+# ----------------------------------------------------------------------
+
+# test the build environment. These vars are used in Makefile.am during
+# the linking of the libraries.
+# Usage: ./configure --target=[windows | linux | osx]
+
+if test "$target" = ""; then
+ AC_MSG_ERROR(["target OS not specified. Please run ./configure --target=<windows | linux | osx>"])
+fi
+
+case "$target" in
+ linux)
+ os=linux
+ ;;
+ windows)
+ os=windows
+ ;;
+ osx)
+ os=osx
+ ;;
+ *)
+ AC_MSG_ERROR(["Unrecognised target OS: $target"])
+ ;;
+esac
+AM_CONDITIONAL(LINUX, test "x$os" = "xlinux")
+AM_CONDITIONAL(WINDOWS, test "x$os" = "xwindows")
+AM_CONDITIONAL(OSX, test "x$os" = "xosx")
+
+# ----------------------------------------------------------------------
+
+# --enable-vst. VST compilation is disabled by default
+#
+# WITH_VST will be passed to gcc as -DWITH_VST
+#
+# AC_ARG_ENABLE (
+# feature, [--enable-] + [feature], eg --enable-vst
+# help-string,
+# [action-if-given], == gcc ... -DWITH_VST
+# [action-if-not-given]) not used here
+
+AC_ARG_ENABLE(
+ vst,
+ AS_HELP_STRING([--enable-vst], [enable vst support]),
+ [AC_DEFINE(WITH_VST)]
+)
+
+# ----------------------------------------------------------------------
+
+# Check for programs.
+
+AC_PROG_CXX
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+
+# ----------------------------------------------------------------------
+
+# Check for libraries.
+
+AC_CHECK_LIB(
+ [pthread],
+ [pthread_exit],
+ [],
+ [AC_MSG_ERROR([error: library 'pthread' not found!])]
+)
+
+# ----------------------------------------------------------------------
+
+# Check for generic headers (fltk, rtaudio and libsndfile are static,
+# we ask if headers are available)
+
+AC_LANG_PUSH([C++])
+AC_CHECK_HEADER(
+ [FL/Fl.H],
+ [],
+ [AC_MSG_ERROR([library 'fltk' not found!])]
+)
+AC_LANG_POP
+
+AC_LANG_PUSH([C++])
+AC_CHECK_HEADER(
+ [RtMidi.h],
+ [],
+ [AC_MSG_ERROR([library 'rtMidi' not found!])]
+)
+AC_LANG_POP
+
+AC_LANG_PUSH([C++])
+AC_CHECK_HEADER(
+ [sndfile.h],
+ [],
+ [AC_MSG_ERROR([library 'libsndfile' not found!])]
+)
+AC_LANG_POP
+
+#~ AC_LANG_PUSH([C++])
+#~ AC_CHECK_HEADER(
+ #~ [RtAudio.h],
+ #~ [],
+ #~ [AC_MSG_ERROR([library 'RtAudio' not found!])]
+#~ )
+#~ AC_LANG_POP
+
+# brutal and temporary hack for OS X: don't use pkg-config
+
+if test "x$os" = "xosx"; then
+ AC_LANG_PUSH([C++])
+ AC_CHECK_HEADER(
+ [samplerate.h],
+ [],
+ [AC_MSG_ERROR([library 'samplerate' not found!])]
+ )
+ AC_LANG_POP
+else
+# PKG_CHECK_MODULES(
+# SAMPLERATE,
+# samplerate >= 0.1.8,
+# [],
+# AC_MSG_ERROR([library 'libsamplerate' not found!])
+# )
+ AC_LANG_PUSH([C++])
+ AC_CHECK_HEADER(
+ [samplerate.h],
+ [],
+ [AC_MSG_ERROR([library 'samplerate' not found!])]
+ )
+ AC_LANG_POP
+fi
+
+
+# ----------------------------------------------------------------------
+
+# Check for linux header files.
+
+if test "x$os" = "xlinux"; then
+
+# TODO
+
+# AC_LANG_PUSH([C++])
+# AC_CHECK_HEADER(
+# [X11/extensions/Xext.h],
+# [],
+# [AC_MSG_ERROR([missing Xext.h, maybe you need to install the libxext-dev package?])]
+# )
+# AC_LANG_POP
+
+# AC_LANG_PUSH([C++])
+# AC_CHECK_HEADER(
+# [X11/Xft.h],
+# [],
+# [AC_MSG_ERROR([missing Xft.h, maybe you need to install the libxft-dev package?])]
+# )
+# AC_LANG_POP
+
+ AC_LANG_PUSH([C++])
+ AC_CHECK_HEADER(
+ [X11/xpm.h],
+ [],
+ [AC_MSG_ERROR([missing xpm.h, maybe you need to install the libxpm-dev package?])]
+ )
+ AC_LANG_POP
+fi
+
+# ----------------------------------------------------------------------
+
+# finalizing
+
+AC_CONFIG_FILES([Makefile src/Makefile])
+AC_OUTPUT
--- /dev/null
+AUTOMAKE_OPTIONS = foreign
+
+
+
+# aeffect.h (header from VST SDK) uses 'long long' which is not supported
+# in ISO C++ 1998 and -Werror flag breaks the compilation.
+# This is a workaround, fixes needed.
+
+if WINDOWS
+AM_CXXFLAGS = -Wall -pedantic
+else
+AM_CXXFLAGS = -Wall -pedantic -Werror
+endif
+
+bin_PROGRAMS = giada
+
+giada_SOURCES = \
+const.h gd_keyGrabber.h glue.h mixerHandler.cpp \
+gd_about.cpp gd_mainWindow.cpp graphics.cpp mixerHandler.h \
+gd_about.h gd_mainWindow.h graphics.h patch.cpp \
+gd_beatsInput.cpp gd_warnings.cpp ge_mixed.cpp patch.h \
+gd_beatsInput.h gd_warnings.h ge_mixed.h recorder.cpp \
+gd_bpmInput.cpp ge_waveform.cpp gui_utils.cpp recorder.h \
+gd_bpmInput.h ge_waveform.h gui_utils.h utils.cpp \
+gd_browser.cpp init.cpp channel.h utils.h \
+gd_browser.h init.h gd_config.cpp channel.cpp \
+gg_keyboard.cpp kernelAudio.cpp wave.cpp gd_config.h \
+gg_keyboard.h kernelAudio.h waveFx.cpp gd_editor.cpp \
+gg_waveTools.cpp main.cpp waveFx.h gd_editor.h \
+gg_waveTools.h mixer.cpp wave.h gd_keyGrabber.cpp \
+glue.cpp mixer.h ge_browser.h ge_browser.cpp \
+gd_devInfo.cpp gd_devInfo.h plugin.h plugin.cpp \
+pluginHost.h pluginHost.cpp gd_pluginList.h gd_pluginList.cpp \
+gd_pluginWindow.h gd_pluginWindow.cpp ge_window.h ge_window.cpp \
+dataStorage.h dataStorage.cpp conf.h conf.cpp \
+gd_actionEditor.h gd_actionEditor.cpp ge_muteChannel.h ge_muteChannel.cpp \
+ge_actionChannel.h ge_actionChannel.cpp gd_pluginWindowGUI.h gd_pluginWindowGUI.cpp \
+ge_actionWidget.h ge_actionWidget.cpp ge_envelopeChannel.h ge_envelopeChannel.cpp \
+ge_pianoRoll.h ge_pianoRoll.cpp kernelMidi.h kernelMidi.cpp \
+gd_midiOutputSetup.h gd_midiOutputSetup.cpp gd_midiGrabber.h gd_midiGrabber.cpp \
+sampleChannel.h sampleChannel.cpp midiChannel.cpp midiChannel.h \
+ge_channel.h ge_channel.cpp log.h log.cpp \
+ge_column.h ge_column.cpp ge_sampleChannel.h ge_sampleChannel.cpp \
+ge_midiChannel.h ge_midiChannel.cpp
+
+
+
+# Check for environment: these vars are defined via AM_CONDITIONAL
+# inside configure.ac
+
+if LINUX
+giada_LDADD = -lsndfile -lfltk -lXext -lX11 -lXft -lXpm -lm \
+ rtaudio-mod/librtaudio.a -ljack -lasound -lpthread -ldl \
+ -lpulse-simple -lpulse -lsamplerate -lrtmidi
+endif
+if WINDOWS
+giada_LDADD = -lrtaudio -ldsound -lwsock32 -lm -lpthread \
+ -lfltk -lwininet -lgdi32 -lshell32 -lvfw32 -lrpcrt4 \
+ -luuid -lcomctl32 -lole32 -lws2_32 -lsndfile \
+ -lsamplerate -lrtmidi -lwinmm -lsetupapi -lksuser
+giada_LDFLAGS = -mwindows -mno-cygwin -static
+giada_SOURCES += resource.rc
+endif
+if OSX
+giada_LDADD = -lsndfile -lm -lpthread -lfltk -lrtmidi -lrtaudio \
+ -lsamplerate
+giada_LDFLAGS = -framework CoreAudio -framework Cocoa -framework Carbon \
+ -framework CoreMIDI -framework CoreFoundation
+endif
+
+
+# used only under MinGW to compile the resource.rc file (program icon)
+
+.rc.o:
+ windres $^ -o $@
+%.o : %.rc
+ windres $^ -o $@
+
+
+
+#compile libraries
+
+libs:
+if LINUX
+ @cd rtaudio-mod; echo "Building RtAudio for Linux..."; \
+ ./configure --with-jack --with-alsa --with-pulse; \
+ make;
+endif
+if WINDOWS
+ @cd rtaudio-mod; echo "Building RtAudio for Windows..."; \
+ ./configure --with-asio --with-ds; \
+ make;
+endif
+if OSX
+ @cd rtaudio-mod; echo "Building RtAudio for OS X..."; \
+ ./configure --with-core; \
+ make;
+endif
+
+
+
+# rename the binaries
+
+if LINUX
+rename:
+ mv giada giada_lin
+endif
+if WINDOWS
+rename:
+ mv giada giada_win.exe
+endif
+if OSX
+rename:
+ mv giada giada_osx
+endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * channel
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "channel.h"
+#include "ge_channel.h"
+#include "pluginHost.h"
+#include "kernelMidi.h"
+#include "patch.h"
+#include "wave.h"
+#include "mixer.h"
+#include "mixerHandler.h"
+#include "conf.h"
+#include "waveFx.h"
+#include "log.h"
+
+
+extern Patch G_Patch;
+extern Mixer G_Mixer;
+extern Conf G_Conf;
+#ifdef WITH_VST
+extern PluginHost G_PluginHost;
+#endif
+
+
+Channel::Channel(int type, int status, int bufferSize)
+ : bufferSize(bufferSize),
+ type (type),
+ status (status),
+ key (0),
+ volume (DEFAULT_VOL),
+ volume_i (1.0f),
+ volume_d (0.0f),
+ panLeft (1.0f),
+ panRight (1.0f),
+ mute_i (false),
+ mute_s (false),
+ mute (false),
+ solo (false),
+ hasActions(false),
+ recStatus (REC_STOPPED),
+ vChan (NULL),
+ guiChannel(NULL),
+ midiIn (true),
+ midiInKeyPress(0x0),
+ midiInKeyRel (0x0),
+ midiInKill (0x0),
+ midiInVolume (0x0),
+ midiInMute (0x0),
+ midiInSolo (0x0)
+{
+ vChan = (float *) malloc(bufferSize * sizeof(float));
+ if (!vChan)
+ gLog("[Channel] unable to alloc memory for vChan\n");
+ memset(vChan, 0, bufferSize * sizeof(float));
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Channel::~Channel()
+{
+ status = STATUS_OFF;
+ if (vChan)
+ free(vChan);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Channel::readPatchMidiIn(int i)
+{
+ midiIn = G_Patch.getMidiValue(i, "In");
+ midiInKeyPress = G_Patch.getMidiValue(i, "InKeyPress");
+ midiInKeyRel = G_Patch.getMidiValue(i, "InKeyRel");
+ midiInKill = G_Patch.getMidiValue(i, "InKill");
+ midiInVolume = G_Patch.getMidiValue(i, "InVolume");
+ midiInMute = G_Patch.getMidiValue(i, "InMute");
+ midiInSolo = G_Patch.getMidiValue(i, "InSolo");
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool Channel::isPlaying()
+{
+ return status & (STATUS_PLAY | STATUS_ENDING);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Channel::writePatch(FILE *fp, int i, bool isProject)
+{
+ fprintf(fp, "chanType%d=%d\n", i, type);
+ fprintf(fp, "chanIndex%d=%d\n", i, index);
+ fprintf(fp, "chanColumn%d=%d\n", i, guiChannel->getColumnIndex());
+ fprintf(fp, "chanMute%d=%d\n", i, mute);
+ fprintf(fp, "chanMute_s%d=%d\n", i, mute_s);
+ fprintf(fp, "chanSolo%d=%d\n", i, solo);
+ fprintf(fp, "chanvol%d=%f\n", i, volume);
+ fprintf(fp, "chanPanLeft%d=%f\n", i, panLeft);
+ fprintf(fp, "chanPanRight%d=%f\n", i, panRight);
+
+ fprintf(fp, "chanMidiIn%d=%u\n", i, midiIn);
+ fprintf(fp, "chanMidiInKeyPress%d=%u\n", i, midiInKeyPress);
+ fprintf(fp, "chanMidiInKeyRel%d=%u\n", i, midiInKeyRel);
+ fprintf(fp, "chanMidiInKill%d=%u\n", i, midiInKill);
+ fprintf(fp, "chanMidiInVolume%d=%u\n", i, midiInVolume);
+ fprintf(fp, "chanMidiInMute%d=%u\n", i, midiInMute);
+ fprintf(fp, "chanMidiInSolo%d=%u\n", i, midiInSolo);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * channel
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef CHANNEL_H
+#define CHANNEL_H
+
+
+#include "utils.h"
+#include "const.h"
+#include "recorder.h"
+
+
+#ifdef WITH_VST
+
+/* before including aeffetx(x).h we must define __cdecl, otherwise VST
+ * headers can't be compiled correctly. In windows __cdecl is already
+ * defined. */
+
+ #ifdef __GNUC__
+ #ifndef _WIN32
+ #define __cdecl
+ #endif
+ #endif
+ #include "vst/aeffectx.h"
+
+#endif
+
+
+class Channel {
+
+protected:
+
+ /* bufferSize
+ * size of every buffer in this channel (vChan, pChan) */
+
+ int bufferSize;
+
+public:
+
+ Channel(int type, int status, int bufferSize);
+ virtual ~Channel();
+
+ /* writePatch
+ * store values in patch, writing to *fp. */
+
+ virtual void writePatch(FILE *fp, int i, bool isProject);
+
+ /* loadByPatch
+ * load a sample inside a patch. */
+
+ virtual int loadByPatch(const char *file, int i) = 0;
+
+ /* process
+ * merge vChannels into buffer, plus plugin processing (if any). */
+
+ virtual void process(float *buffer) = 0;
+
+ /* start
+ * action to do when channel starts. doQuantize = false (don't
+ * quantize) when Mixer is reading actions from Recorder::. */
+
+ virtual void start(int frame, bool doQuantize) = 0;
+
+ /* stop
+ * action to do when channel is stopped normally (via key or MIDI). */
+
+ virtual void stop() = 0;
+
+ /* kill
+ * action to do when channel stops abruptly. */
+
+ virtual void kill(int frame) = 0;
+
+ /* mute
+ * action to do when channel is muted. If internal == true, set
+ * internal mute without altering main mute. */
+
+ virtual void setMute (bool internal) = 0;
+ virtual void unsetMute(bool internal) = 0;
+
+ /* empty
+ * free any associated resources (e.g. waveform for SAMPLE). */
+
+ virtual void empty() = 0;
+
+ /* stopBySeq
+ * action to do when channel is stopped by sequencer. */
+
+ virtual void stopBySeq() = 0;
+
+ /* quantize
+ * start channel according to quantizer. Index = array index of
+ * mixer::channels, used by recorder. LocalFrame = frame within buffer.
+ * GloalFrame = actual frame from mixer. */
+
+ virtual void quantize(int index, int localFrame, int globalFrame) = 0;
+
+ /* onZero
+ * action to do when frame goes to zero, i.e. sequencer restart. */
+
+ virtual void onZero(int frame) = 0;
+
+ /* onBar
+ * action to do when a bar has passed. */
+
+ virtual void onBar(int frame) = 0;
+
+ /* parseAction
+ * do something on a recorded action. Parameters:
+ * action *a - action to parse
+ * localFrame - frame number of the processed buffer
+ * globalFrame - actual frame in Mixer */
+
+ virtual void parseAction(recorder::action *a, int localFrame, int globalFrame) = 0;
+
+ /* rewind
+ * rewind channel when rewind button is pressed. */
+
+ virtual void rewind() = 0;
+
+ /* ------------------------------------------------------------------------ */
+
+ int index; // unique id
+ int type; // midi or sample
+ int status; // status: see const.h
+ int key; // keyboard button
+ float volume; // global volume
+ float volume_i; // internal volume
+ float volume_d; // delta volume (for envelope)
+ float panLeft;
+ float panRight;
+ bool mute_i; // internal mute
+ bool mute_s; // previous mute status after being solo'd
+ bool mute; // global mute
+ bool solo;
+ bool hasActions; // has something recorded
+ int recStatus; // status of recordings (waiting, ending, ...)
+ float *vChan; // virtual channel
+ class gChannel *guiChannel; // pointer to a gChannel object, part of the GUI
+
+ bool midiIn; // enable midi output
+ uint32_t midiInKeyPress;
+ uint32_t midiInKeyRel;
+ uint32_t midiInKill;
+ uint32_t midiInVolume;
+ uint32_t midiInMute;
+ uint32_t midiInSolo;
+
+#ifdef WITH_VST
+ gVector <class Plugin *> plugins;
+#endif
+
+
+ /* ------------------------------------------------------------------------ */
+
+ /* isPlaying
+ * tell wether the channel is playing or is stopped. */
+
+ bool isPlaying();
+
+ /* readPatchMidiIn
+ * read from patch all midi-related parameters such as keypress, mute
+ * and so on. */
+
+ void readPatchMidiIn(int i);
+};
+
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * conf
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include <stdlib.h>
+#include "const.h"
+#include "conf.h"
+#include "utils.h"
+#include "log.h"
+
+
+int Conf::openFileForReading()
+{
+ char path[PATH_MAX];
+
+#if defined(__linux__)
+ snprintf(path, PATH_MAX, "%s/.giada/%s", getenv("HOME"), CONF_FILENAME);
+#elif defined(_WIN32)
+ snprintf(path, PATH_MAX, "%s", CONF_FILENAME);
+#elif defined(__APPLE__)
+ struct passwd *p = getpwuid(getuid());
+ if (p == NULL) {
+ gLog("[Conf::openFile] unable to fetch user infos\n");
+ return 0;
+ }
+ else {
+ const char *home = p->pw_dir;
+ snprintf(path, PATH_MAX, "%s/Library/Application Support/Giada/giada.conf", home);
+ }
+#endif
+
+ fp = fopen(path, "r");
+ if (fp == NULL) {
+ gLog("[Conf::openFile] unable to open conf file for reading\n");
+ return 0;
+ }
+ return 1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Conf::createConfigFolder(const char *path)
+{
+ if (gDirExists(path))
+ return 1;
+
+ gLog("[Conf] .giada folder not present. Updating...\n");
+
+ if (gMkdir(path)) {
+ gLog("[Conf] status: ok\n");
+ return 1;
+ }
+ else {
+ gLog("[Conf] status: error!\n");
+ return 0;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Conf::openFileForWriting()
+{
+ /* writing config file. In windows is in the same dir of the .exe,
+ * in Linux and OS X in home */
+
+#if defined(__linux__)
+
+ char giadaPath[PATH_MAX];
+ sprintf(giadaPath, "%s/.giada", getenv("HOME"));
+
+ if (!createConfigFolder(giadaPath))
+ return 0;
+
+ char path[PATH_MAX];
+ sprintf(path, "%s/%s", giadaPath, CONF_FILENAME);
+
+#elif defined(_WIN32)
+
+ const char *path = CONF_FILENAME;
+
+#elif defined(__APPLE__)
+
+ struct passwd *p = getpwuid(getuid());
+ const char *home = p->pw_dir;
+ char giadaPath[PATH_MAX];
+ snprintf(giadaPath, PATH_MAX, "%s/Library/Application Support/Giada", home);
+
+ if (!createConfigFolder(giadaPath))
+ return 0;
+
+ char path[PATH_MAX];
+ sprintf(path, "%s/%s", giadaPath, CONF_FILENAME);
+
+#endif
+
+ fp = fopen(path, "w");
+ if (fp == NULL)
+ return 0;
+ return 1;
+
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Conf::setDefault()
+{
+ logMode = LOG_MODE_MUTE;
+
+ soundSystem = DEFAULT_SOUNDSYS;
+ soundDeviceOut = DEFAULT_SOUNDDEV_OUT;
+ soundDeviceIn = DEFAULT_SOUNDDEV_IN;
+ samplerate = DEFAULT_SAMPLERATE;
+ buffersize = DEFAULT_BUFSIZE;
+ delayComp = DEFAULT_DELAYCOMP;
+ limitOutput = false;
+ rsmpQuality = 0;
+
+ midiPortIn = DEFAULT_MIDI_PORT_IN;
+ noNoteOff = false;
+ midiPortOut = DEFAULT_MIDI_PORT_OUT;
+ midiSync = MIDI_SYNC_NONE;
+ midiTCfps = 25.0f;
+
+ midiInRewind = 0x0;
+ midiInStartStop = 0x0;
+ midiInActionRec = 0x0;
+ midiInInputRec = 0x0;
+ midiInVolumeIn = 0x0;
+ midiInVolumeOut = 0x0;
+ midiInBeatDouble = 0x0;
+ midiInBeatHalf = 0x0;
+ midiInMetronome = 0x0;
+
+ pluginPath[0] = '\0';
+ patchPath [0] = '\0';
+ samplePath[0] = '\0';
+
+ recsStopOnChanHalt = false;
+ chansStopOnSeqHalt = false;
+ treatRecsAsLoops = false;
+
+ resizeRecordings = true;
+
+ actionEditorZoom = 100;
+ actionEditorGridOn = false;
+ actionEditorGridVal = 1;
+
+ mainWindowX = 0;
+ mainWindowY = 0;
+ mainWindowW = GUI_WIDTH;
+ mainWindowH = GUI_HEIGHT;
+
+ pianoRollY = -1;
+ pianoRollH = 422;
+}
+
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Conf::read()
+{
+ setDefault();
+
+ if (!openFileForReading()) {
+ gLog("[Conf] unreadable .conf file, using default parameters\n");
+ return 0;
+ }
+
+ if (getValue("header") != "GIADACFG") {
+ gLog("[Conf] corrupted .conf file, using default parameters\n");
+ return -1;
+ }
+
+ logMode = atoi(getValue("logMode").c_str());
+
+ soundSystem = atoi(getValue("soundSystem").c_str());
+ if (!soundSystem & (SYS_API_ANY)) soundSystem = DEFAULT_SOUNDSYS;
+
+ soundDeviceOut = atoi(getValue("soundDeviceOut").c_str());
+ if (soundDeviceOut < 0) soundDeviceOut = DEFAULT_SOUNDDEV_OUT;
+
+ soundDeviceIn = atoi(getValue("soundDeviceIn").c_str());
+ if (soundDeviceIn < -1) soundDeviceIn = DEFAULT_SOUNDDEV_IN;
+
+ channelsOut = atoi(getValue("channelsOut").c_str());
+ channelsIn = atoi(getValue("channelsIn").c_str());
+ if (channelsOut < 0) channelsOut = 0;
+ if (channelsIn < 0) channelsIn = 0;
+
+ buffersize = atoi(getValue("buffersize").c_str());
+ if (buffersize < 8) buffersize = DEFAULT_BUFSIZE;
+
+ delayComp = atoi(getValue("delayComp").c_str());
+ if (delayComp < 0) delayComp = DEFAULT_DELAYCOMP;
+
+ midiSystem = atoi(getValue("midiSystem").c_str());
+ if (midiPortOut < -1) midiPortOut = DEFAULT_MIDI_SYSTEM;
+
+ midiPortOut = atoi(getValue("midiPortOut").c_str());
+ if (midiPortOut < -1) midiPortOut = DEFAULT_MIDI_PORT_OUT;
+
+ midiPortIn = atoi(getValue("midiPortIn").c_str());
+ if (midiPortIn < -1) midiPortIn = DEFAULT_MIDI_PORT_IN;
+
+ noNoteOff = atoi(getValue("noNoteOff").c_str());
+
+ midiSync = atoi(getValue("midiSync").c_str());
+ midiTCfps = atof(getValue("midiTCfps").c_str());
+
+ midiInRewind = strtoul(getValue("midiInRewind").c_str(), NULL, 10);
+ midiInStartStop = strtoul(getValue("midiInStartStop").c_str(), NULL, 10);
+ midiInActionRec = strtoul(getValue("midiInActionRec").c_str(), NULL, 10);
+ midiInInputRec = strtoul(getValue("midiInInputRec").c_str(), NULL, 10);
+ midiInMetronome = strtoul(getValue("midiInMetronome").c_str(), NULL, 10);
+ midiInVolumeIn = strtoul(getValue("midiInVolumeIn").c_str(), NULL, 10);
+ midiInVolumeOut = strtoul(getValue("midiInVolumeOut").c_str(), NULL, 10);
+ midiInBeatDouble = strtoul(getValue("midiInBeatDouble").c_str(), NULL, 10);
+ midiInBeatHalf = strtoul(getValue("midiInBeatHalf").c_str(), NULL, 10);
+
+ mainWindowX = atoi(getValue("mainWindowX").c_str());
+ mainWindowY = atoi(getValue("mainWindowY").c_str());
+ mainWindowW = atoi(getValue("mainWindowW").c_str());
+ mainWindowH = atoi(getValue("mainWindowH").c_str());
+
+ browserX = atoi(getValue("browserX").c_str());
+ browserY = atoi(getValue("browserY").c_str());
+ browserW = atoi(getValue("browserW").c_str());
+ browserH = atoi(getValue("browserH").c_str());
+ if (browserX < 0) browserX = 0;
+ if (browserY < 0) browserY = 0;
+ if (browserW < 396) browserW = 396;
+ if (browserH < 302) browserH = 302;
+
+ actionEditorX = atoi(getValue("actionEditorX").c_str());
+ actionEditorY = atoi(getValue("actionEditorY").c_str());
+ actionEditorW = atoi(getValue("actionEditorW").c_str());
+ actionEditorH = atoi(getValue("actionEditorH").c_str());
+ actionEditorZoom = atoi(getValue("actionEditorZoom").c_str());
+ actionEditorGridVal = atoi(getValue("actionEditorGridVal").c_str());
+ actionEditorGridOn = atoi(getValue("actionEditorGridOn").c_str());
+ if (actionEditorX < 0) actionEditorX = 0;
+ if (actionEditorY < 0) actionEditorY = 0;
+ if (actionEditorW < 640) actionEditorW = 640;
+ if (actionEditorH < 176) actionEditorH = 176;
+ if (actionEditorZoom < 100) actionEditorZoom = 100;
+ if (actionEditorGridVal < 0) actionEditorGridVal = 0;
+ if (actionEditorGridOn < 0) actionEditorGridOn = 0;
+
+ pianoRollY = atoi(getValue("pianoRollY").c_str());
+ pianoRollH = atoi(getValue("pianoRollH").c_str());
+ if (pianoRollH <= 0)
+ pianoRollH = 422;
+
+ sampleEditorX = atoi(getValue("sampleEditorX").c_str());
+ sampleEditorY = atoi(getValue("sampleEditorY").c_str());
+ sampleEditorW = atoi(getValue("sampleEditorW").c_str());
+ sampleEditorH = atoi(getValue("sampleEditorH").c_str());
+ sampleEditorGridVal = atoi(getValue("sampleEditorGridVal").c_str());
+ sampleEditorGridOn = atoi(getValue("sampleEditorGridOn").c_str());
+ if (sampleEditorX < 0) sampleEditorX = 0;
+ if (sampleEditorY < 0) sampleEditorY = 0;
+ if (sampleEditorW < 500) sampleEditorW = 500;
+ if (sampleEditorH < 292) sampleEditorH = 292;
+ if (sampleEditorGridVal < 0) sampleEditorGridVal = 0;
+ if (sampleEditorGridOn < 0) sampleEditorGridOn = 0;
+
+ configX = atoi(getValue("configX").c_str());
+ configY = atoi(getValue("configY").c_str());
+ if (configX < 0) configX = 0;
+ if (configY < 0) configY = 0;
+
+ pluginListX = atoi(getValue("pluginListX").c_str());
+ pluginListY = atoi(getValue("pluginListY").c_str());
+ if (pluginListX < 0) pluginListX = 0;
+ if (pluginListY < 0) pluginListY = 0;
+
+ bpmX = atoi(getValue("bpmX").c_str());
+ bpmY = atoi(getValue("bpmY").c_str());
+ if (bpmX < 0) bpmX = 0;
+ if (bpmY < 0) bpmY = 0;
+
+ beatsX = atoi(getValue("beatsX").c_str());
+ beatsY = atoi(getValue("beatsY").c_str());
+ if (beatsX < 0) beatsX = 0;
+ if (beatsY < 0) beatsY = 0;
+
+ aboutX = atoi(getValue("aboutX").c_str());
+ aboutY = atoi(getValue("aboutY").c_str());
+ if (aboutX < 0) aboutX = 0;
+ if (aboutY < 0) aboutY = 0;
+
+ samplerate = atoi(getValue("samplerate").c_str());
+ if (samplerate < 8000) samplerate = DEFAULT_SAMPLERATE;
+
+ limitOutput = atoi(getValue("limitOutput").c_str());
+ rsmpQuality = atoi(getValue("rsmpQuality").c_str());
+
+ std::string p = getValue("pluginPath");
+ strncpy(pluginPath, p.c_str(), p.size());
+ pluginPath[p.size()] = '\0'; // strncpy doesn't add '\0'
+
+ p = getValue("patchPath");
+ strncpy(patchPath, p.c_str(), p.size());
+ patchPath[p.size()] = '\0'; // strncpy doesn't add '\0'
+
+ p = getValue("samplePath");
+ strncpy(samplePath, p.c_str(), p.size());
+ samplePath[p.size()] = '\0'; // strncpy doesn't add '\0'
+
+ recsStopOnChanHalt = atoi(getValue("recsStopOnChanHalt").c_str());
+ chansStopOnSeqHalt = atoi(getValue("chansStopOnSeqHalt").c_str());
+ treatRecsAsLoops = atoi(getValue("treatRecsAsLoops").c_str());
+
+ resizeRecordings = atoi(getValue("resizeRecordings").c_str());
+
+ close();
+ return 1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Conf::write()
+{
+ if (!openFileForWriting())
+ return 0;
+
+ fprintf(fp, "# --- Giada configuration file --- \n");
+ fprintf(fp, "header=GIADACFG\n");
+ fprintf(fp, "version=%s\n", VERSIONE);
+
+ fprintf(fp, "logMode=%d\n", logMode);
+
+ fprintf(fp, "soundSystem=%d\n", soundSystem);
+ fprintf(fp, "soundDeviceOut=%d\n", soundDeviceOut);
+ fprintf(fp, "soundDeviceIn=%d\n", soundDeviceIn);
+ fprintf(fp, "channelsOut=%d\n", channelsOut);
+ fprintf(fp, "channelsIn=%d\n", channelsIn);
+ fprintf(fp, "buffersize=%d\n", buffersize);
+ fprintf(fp, "delayComp=%d\n", delayComp);
+ fprintf(fp, "samplerate=%d\n", samplerate);
+ fprintf(fp, "limitOutput=%d\n", limitOutput);
+ fprintf(fp, "rsmpQuality=%d\n", rsmpQuality);
+
+ fprintf(fp, "midiSystem=%d\n", midiSystem);
+ fprintf(fp, "midiPortOut=%d\n", midiPortOut);
+ fprintf(fp, "midiPortIn=%d\n", midiPortIn);
+ fprintf(fp, "noNoteOff=%d\n", noNoteOff);
+ fprintf(fp, "midiSync=%d\n", midiSync);
+ fprintf(fp, "midiTCfps=%f\n", midiTCfps);
+
+ fprintf(fp, "midiInRewind=%u\n", midiInRewind);
+ fprintf(fp, "midiInStartStop=%u\n", midiInStartStop);
+ fprintf(fp, "midiInActionRec=%u\n", midiInActionRec);
+ fprintf(fp, "midiInInputRec=%u\n", midiInInputRec);
+ fprintf(fp, "midiInMetronome=%u\n", midiInMetronome);
+ fprintf(fp, "midiInVolumeIn=%u\n", midiInVolumeIn);
+ fprintf(fp, "midiInVolumeOut=%u\n", midiInVolumeOut);
+ fprintf(fp, "midiInBeatDouble=%u\n", midiInBeatDouble);
+ fprintf(fp, "midiInBeatHalf=%u\n", midiInBeatHalf);
+
+ fprintf(fp, "pluginPath=%s\n", pluginPath);
+ fprintf(fp, "patchPath=%s\n", patchPath);
+ fprintf(fp, "samplePath=%s\n", samplePath);
+
+ fprintf(fp, "mainWindowX=%d\n", mainWindowX);
+ fprintf(fp, "mainWindowY=%d\n", mainWindowY);
+ fprintf(fp, "mainWindowW=%d\n", mainWindowW);
+ fprintf(fp, "mainWindowH=%d\n", mainWindowH);
+
+ fprintf(fp, "browserX=%d\n", browserX);
+ fprintf(fp, "browserY=%d\n", browserY);
+ fprintf(fp, "browserW=%d\n", browserW);
+ fprintf(fp, "browserH=%d\n", browserH);
+
+ fprintf(fp, "actionEditorX=%d\n", actionEditorX);
+ fprintf(fp, "actionEditorY=%d\n", actionEditorY);
+ fprintf(fp, "actionEditorW=%d\n", actionEditorW);
+ fprintf(fp, "actionEditorH=%d\n", actionEditorH);
+ fprintf(fp, "actionEditorZoom=%d\n", actionEditorZoom);
+ fprintf(fp, "actionEditorGridOn=%d\n", actionEditorGridOn);
+ fprintf(fp, "actionEditorGridVal=%d\n", actionEditorGridVal);
+
+ fprintf(fp, "pianoRollY=%d\n", pianoRollY);
+ fprintf(fp, "pianoRollH=%d\n", pianoRollH);
+
+ fprintf(fp, "sampleEditorX=%d\n", sampleEditorX);
+ fprintf(fp, "sampleEditorY=%d\n", sampleEditorY);
+ fprintf(fp, "sampleEditorW=%d\n", sampleEditorW);
+ fprintf(fp, "sampleEditorH=%d\n", sampleEditorH);
+ fprintf(fp, "sampleEditorGridOn=%d\n", sampleEditorGridOn);
+ fprintf(fp, "sampleEditorGridVal=%d\n", sampleEditorGridVal);
+
+ fprintf(fp, "configX=%d\n", configX);
+ fprintf(fp, "configY=%d\n", configY);
+
+ fprintf(fp, "pluginListX=%d\n", pluginListX);
+ fprintf(fp, "pluginListY=%d\n", pluginListY);
+
+ fprintf(fp, "bpmX=%d\n", bpmX);
+ fprintf(fp, "bpmY=%d\n", bpmY);
+
+ fprintf(fp, "beatsX=%d\n", beatsX);
+ fprintf(fp, "beatsY=%d\n", beatsY);
+
+ fprintf(fp, "aboutX=%d\n", aboutX);
+ fprintf(fp, "aboutY=%d\n", aboutY);
+
+ fprintf(fp, "recsStopOnChanHalt=%d\n", recsStopOnChanHalt);
+ fprintf(fp, "chansStopOnSeqHalt=%d\n", chansStopOnSeqHalt);
+ fprintf(fp, "treatRecsAsLoops=%d\n", treatRecsAsLoops);
+
+ fprintf(fp, "resizeRecordings=%d\n", resizeRecordings);
+
+ close();
+ return 1;
+}
+
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Conf::close()
+{
+ if (fp != NULL)
+ fclose(fp);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Conf::setPath(char *path, const char *p)
+{
+ path[0] = '\0';
+ strncpy(path, p, strlen(p));
+ path[strlen(p)] = '\0';
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * conf
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef __CONF_H__
+#define __CONF_H__
+
+
+#include <limits.h>
+#include <stdint.h>
+#include "dataStorage.h"
+
+
+#if defined(__APPLE__)
+ #include <pwd.h>
+#endif
+
+
+class Conf : public DataStorage
+{
+private:
+
+ int openFileForReading();
+ int openFileForWriting();
+ int createConfigFolder(const char *path);
+
+public:
+
+ int logMode;
+
+ int soundSystem;
+ int soundDeviceOut;
+ int soundDeviceIn;
+ int channelsOut;
+ int channelsIn;
+ int samplerate;
+ int buffersize;
+ int delayComp;
+ bool limitOutput;
+ int rsmpQuality;
+
+ int midiSystem;
+ int midiPortOut;
+ int midiPortIn;
+ bool noNoteOff;
+ int midiSync; // see const.h
+ float midiTCfps;
+
+ uint32_t midiInRewind;
+ uint32_t midiInStartStop;
+ uint32_t midiInActionRec;
+ uint32_t midiInInputRec;
+ uint32_t midiInMetronome;
+ uint32_t midiInVolumeIn;
+ uint32_t midiInVolumeOut;
+ uint32_t midiInBeatDouble;
+ uint32_t midiInBeatHalf;
+
+ bool recsStopOnChanHalt;
+ bool chansStopOnSeqHalt;
+ bool treatRecsAsLoops;
+
+ bool resizeRecordings;
+
+ char pluginPath[FILENAME_MAX];
+ char patchPath [FILENAME_MAX];
+ char samplePath[FILENAME_MAX];
+
+ int mainWindowX, mainWindowY, mainWindowW, mainWindowH;
+ int browserX, browserY, browserW, browserH;
+ int actionEditorX, actionEditorY, actionEditorW, actionEditorH, actionEditorZoom;
+ int actionEditorGridVal;
+ int actionEditorGridOn;
+ int sampleEditorX, sampleEditorY, sampleEditorW, sampleEditorH;
+ int sampleEditorGridVal;
+ int sampleEditorGridOn;
+ int pianoRollY, pianoRollH;
+ int pluginListX, pluginListY;
+ int configX, configY;
+ int bpmX, bpmY;
+ int beatsX, beatsY;
+ int aboutX, aboutY;
+
+ int read();
+ int write();
+ void setDefault();
+
+ /* setPath
+ * updates one of the following values: plugin, patch or sample.
+ * Pass it a pointer to one of these (path) and the string to save (p). */
+
+ void setPath(char *path, const char *p);
+
+ void close();
+};
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * const.h
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#ifndef CONST_H
+#define CONST_H
+
+
+/* -- version --------------------------------------------------------------- */
+#define VERSIONE "0.9.6"
+#define VERSIONE_STR "Giada"
+#define VERSIONE_FLOAT 0.96f
+
+#define CONF_FILENAME "giada.conf"
+
+
+
+/* -- GUI ------------------------------------------------------------------- */
+#ifdef _WIN32
+ #define GUI_SLEEP 1000/24
+#else
+ #define GUI_SLEEP 1000000/24 // == 1.000.000 / 24 == 1/24 sec == 24 Hz
+#endif
+#define GUI_WIDTH 810
+#define GUI_HEIGHT 510
+
+#define COLOR_BD_0 fl_rgb_color(78, 78, 78) // border off
+#define COLOR_BD_1 fl_rgb_color(188, 188, 188) // border on
+#define COLOR_BG_0 fl_rgb_color(37, 37, 37) // bg off
+#define COLOR_BG_1 fl_rgb_color(78, 78, 78) // bg on (clicked)
+#define COLOR_BG_2 fl_rgb_color(177, 142, 142) // bg active (play, for some widgets)
+#define COLOR_BG_3 fl_rgb_color(28, 32, 80) // bg input rec
+#define COLOR_BG_4 fl_rgb_color(113, 31, 31) // bg action rec
+#define COLOR_ALERT fl_rgb_color(239, 75, 53) // peak meter alert
+#define COLOR_TEXT_0 fl_rgb_color(200, 200, 200)
+#define COLOR_TEXT_1 fl_rgb_color(25, 25, 25) // TODO - duplicate!
+#define COLOR_BG_MAIN fl_rgb_color(25, 25, 25) // windows background
+#define COLOR_BG_DARK fl_rgb_color(0, 0, 0) // inputs background
+
+
+
+/* -- MIN/MAX values -------------------------------------------------------- */
+#define MAX_BEATS 32
+#define MAX_BARS 32
+#define MAX_PATCHNAME_LEN 32
+#define DB_MIN_SCALE 60.0f
+#define MAX_VST_EVENTS 32
+
+
+
+/* -- kernel audio ---------------------------------------------------------- */
+#define SYS_API_JACK 0x01 // 0000 0001
+#define SYS_API_ALSA 0x02 // 0000 0010
+#define SYS_API_DS 0x04 // 0000 0100
+#define SYS_API_ASIO 0x08 // 0000 1000
+#define SYS_API_CORE 0x10 // 0001 0000
+#define SYS_API_PULSE 0x20 // 0010 0000
+#define SYS_API_ANY 0x3F // 0011 1111
+
+#define KERNEL_OK 0
+#define KERNEL_UNDERRUN -1
+#define KERNEL_CRITICAL -2
+
+
+
+/* -- kernel midi ----------------------------------------------------------- */
+#define MIDI_API_JACK 0x01 // 0000 0001
+#define MIDI_API_ALSA 0x02 // 0000 0010
+
+
+
+/* -- default system -------------------------------------------------------- */
+#if defined(__linux__)
+ #define DEFAULT_SOUNDSYS SYS_API_ALSA
+#elif defined(_WIN32)
+ #define DEFAULT_SOUNDSYS SYS_API_DS
+#elif defined(__APPLE__)
+ #define DEFAULT_SOUNDSYS SYS_API_CORE
+#endif
+
+#define DEFAULT_SOUNDDEV_OUT 0 /// FIXME - please override with rtAudio::getDefaultDevice (or similar)
+#define DEFAULT_SOUNDDEV_IN -1 // no recording by default: input disabled
+#define DEFAULT_MIDI_SYSTEM 0
+#define DEFAULT_MIDI_PORT_IN -1
+#define DEFAULT_MIDI_PORT_OUT -1
+#define DEFAULT_SAMPLERATE 44100
+#define DEFAULT_BUFSIZE 1024
+#define DEFAULT_DELAYCOMP 0
+#define DEFAULT_VOL 0.0f
+#define DEFAULT_BOOST 0.0f
+#define gDEFAULT_PITCH 1.0f // ugly and temporary fix to avoid conflicts with wingdi.h (Windows only).
+#define DEFAULT_OUT_VOL 1.0f
+#define DEFAULT_IN_VOL 0.0f
+#define DEFAULT_CHANMODE SINGLE_BASIC
+#define DEFAULT_BPM 120.0f
+#define DEFAULT_BEATS 4
+#define DEFAULT_BARS 1
+#define DEFAULT_QUANTIZE 0 // quantizer off
+#define DEFAULT_FADEOUT_STEP 0.01f // micro-fadeout speed
+
+
+
+/* -- mixer statuses and modes ---------------------------------------------- */
+#define LOOP_BASIC 0x01 // 0000 0001 chanMode
+#define LOOP_ONCE 0x02 // 0000 0010 chanMode
+#define SINGLE_BASIC 0x04 // 0000 0100 chanMode
+#define SINGLE_PRESS 0x08 // 0000 1000 chanMode
+#define SINGLE_RETRIG 0x10 // 0001 0000 chanMode
+#define LOOP_REPEAT 0x20 // 0010 0000 chanMode
+#define SINGLE_ENDLESS 0x40 // 0100 0000 chanMode
+#define LOOP_ONCE_BAR 0x80 // 1000 0000 chanMode
+
+#define LOOP_ANY 0xA3 // 1010 0011 chanMode - any loop mode
+#define SINGLE_ANY 0x5C // 0101 1100 chanMode - any single mode
+
+#define STATUS_ENDING 0x01 // 0000 0001 chanStatus - ending (loop mode only)
+#define STATUS_WAIT 0x02 // 0000 0010 chanStatus - waiting for start (loop mode only)
+#define STATUS_PLAY 0x04 // 0000 0100 chanStatus - playing
+#define STATUS_OFF 0x08 // 0000 1000 chanStatus - off
+#define STATUS_EMPTY 0x10 // 0001 0000 chanStatus - not loaded (empty chan)
+#define STATUS_MISSING 0x20 // 0010 0000 chanStatus - not found
+#define STATUS_WRONG 0x40 // 0100 0000 chanStatus - something wrong (freq, bitrate, ...)
+
+#define REC_WAITING 0x01 // 0000 0001
+#define REC_ENDING 0x02 // 0000 0010
+#define REC_READING 0x04 // 0000 0100
+#define REC_STOPPED 0x08 // 0000 1000
+
+
+
+/* -- actions --------------------------------------------------------------- */
+#define ACTION_KEYPRESS 0x01 // 0000 0001
+#define ACTION_KEYREL 0x02 // 0000 0010
+#define ACTION_KILLCHAN 0x04 // 0000 0100
+#define ACTION_MUTEON 0x08 // 0000 1000
+#define ACTION_MUTEOFF 0x10 // 0001 0000
+#define ACTION_VOLUME 0x20 // 0010 0000
+#define ACTION_MIDI 0x40 // 0100 0000
+
+#define ACTION_KEYS 0x03 // 0000 0011 any key
+#define ACTION_MUTES 0x24 // 0001 1000 any mute
+
+#define RANGE_CHAR 0x01 // range for MIDI (0-127)
+#define RANGE_FLOAT 0x02 // range for volumes and VST params (0.0-1.0)
+
+
+
+/* -- mixerHandler signals -------------------------------------------------- */
+#define SAMPLE_LOADED_OK 0x01
+#define SAMPLE_LEFT_EMPTY 0x02
+#define SAMPLE_NOT_VALID 0x04
+#define SAMPLE_MULTICHANNEL 0x08
+#define SAMPLE_WRONG_BIT 0x10
+#define SAMPLE_WRONG_ENDIAN 0x20
+#define SAMPLE_WRONG_FORMAT 0x40
+#define SAMPLE_READ_ERROR 0x80
+#define SAMPLE_PATH_TOO_LONG 0x100
+
+/** FIXME - add to SAMPLE_ series those for when exporting */
+
+
+
+/* -- log modes ------------------------------------------------------------- */
+#define LOG_MODE_STDOUT 0x01
+#define LOG_MODE_FILE 0x02
+#define LOG_MODE_MUTE 0x04
+
+
+
+/* -- browser types --------------------------------------------------------- */
+#define BROWSER_LOAD_PATCH 0x00
+#define BROWSER_LOAD_SAMPLE 0x01
+#define BROWSER_SAVE_PATCH 0x02
+#define BROWSER_SAVE_SAMPLE 0x04
+#define BROWSER_SAVE_PROJECT 0x08
+#define BROWSER_LOAD_PLUGIN 0x10
+
+
+
+/* -- channel types --------------------------------------------------------- */
+#define CHANNEL_SAMPLE 0x01
+#define CHANNEL_MIDI 0x02
+
+
+
+/* -- unique IDs of mainWin's subwindows ------------------------------------ */
+/* -- wid > 0 are reserved by gg_keyboard ----------------------------------- */
+#define WID_BEATS -1
+#define WID_BPM -2
+#define WID_ABOUT -3
+#define WID_FILE_BROWSER -4
+#define WID_CONFIG -5
+#define WID_FX_LIST -6
+#define WID_ACTION_EDITOR -7
+#define WID_SAMPLE_EDITOR -8
+#define WID_FX -9
+#define WID_KEY_GRABBER -10
+
+
+/* -- patch signals --------------------------------------------------------- */
+#define PATCH_UNREADABLE 0
+#define PATCH_INVALID -1
+#define PATCH_OPEN_OK 1
+
+/** TODO - addo to PATCH_ serie the signals for saving/loading */
+
+
+
+/* -- MIDI signals -------------------------------------------------------------
+ * all signals are set to channel 0 (where channels are considered).
+ * It's up to the caller to bitmask them with the proper channel number. */
+
+/* channel voices messages - controller (0xB0) is a special subset of
+ * this family: it drives knobs, volume, faders and such. */
+
+#define MIDI_CONTROLLER 0xB0 << 24
+#define MIDI_NOTE_ON 0x90 << 24
+#define MIDI_NOTE_OFF 0x80 << 24
+#define MIDI_ALL_NOTES_OFF (MIDI_CONTROLLER) | (0x7B << 16)
+#define MIDI_VOLUME (MIDI_CONTROLLER) | (0x07 << 16)
+
+/* system common / real-time messages. Single bytes */
+
+#define MIDI_SYSEX 0xF0
+#define MIDI_MTC_QUARTER 0xF1
+#define MIDI_POSITION_PTR 0xF2
+#define MIDI_CLOCK 0xF8
+#define MIDI_START 0xFA
+#define MIDI_CONTINUE 0xFB
+#define MIDI_STOP 0xFC
+#define MIDI_EOX 0xF7 // end of sysex
+
+/* channels */
+
+#define MIDI_CHAN_0 0x00 << 24
+#define MIDI_CHAN_1 0x01 << 24
+#define MIDI_CHAN_2 0x02 << 24
+#define MIDI_CHAN_3 0x03 << 24
+#define MIDI_CHAN_4 0x04 << 24
+#define MIDI_CHAN_5 0x05 << 24
+#define MIDI_CHAN_6 0x06 << 24
+#define MIDI_CHAN_7 0x07 << 24
+#define MIDI_CHAN_8 0x08 << 24
+#define MIDI_CHAN_9 0x09 << 24
+#define MIDI_CHAN_10 0x0A << 24
+#define MIDI_CHAN_11 0x0B << 24
+#define MIDI_CHAN_12 0x0C << 24
+#define MIDI_CHAN_13 0x0D << 24
+#define MIDI_CHAN_14 0x0E << 24
+#define MIDI_CHAN_15 0x0F << 24
+
+const int MIDI_CHANS[16] = {
+ MIDI_CHAN_0, MIDI_CHAN_1, MIDI_CHAN_2, MIDI_CHAN_3,
+ MIDI_CHAN_4, MIDI_CHAN_5, MIDI_CHAN_6, MIDI_CHAN_7,
+ MIDI_CHAN_8, MIDI_CHAN_9, MIDI_CHAN_10, MIDI_CHAN_11,
+ MIDI_CHAN_12, MIDI_CHAN_13, MIDI_CHAN_14, MIDI_CHAN_15
+};
+
+/* midi sync constants */
+
+#define MIDI_SYNC_NONE 0x00
+#define MIDI_SYNC_CLOCK_M 0x01
+#define MIDI_SYNC_CLOCK_S 0x02
+#define MIDI_SYNC_MTC_M 0x04
+#define MIDI_SYNC_MTC_S 0x08
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * dataStorage
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include "dataStorage.h"
+#include "log.h"
+
+
+std::string DataStorage::getValue(const char *in) {
+
+ /* on each call reset the pointe to the beginning of the file. Not so
+ * good but necessary if you want to pick up random values from the
+ * file. */
+
+ fseek(fp, 0L, SEEK_SET);
+ std::string out = "";
+
+ while (!feof(fp)) {
+
+ char buffer[MAX_LINE_LEN];
+ if (fgets(buffer, MAX_LINE_LEN, fp) == NULL) {
+ gLog("[PATCH] get_value error (key=%s)\n", in);
+ return "";
+ }
+
+ if (buffer[0] == '#')
+ continue;
+
+ unsigned len = strlen(in);
+ if (strncmp(buffer, in, len) == 0) {
+
+ for (unsigned i=len+1; i<MAX_LINE_LEN; i++) {
+ if (buffer[i] == '\0' || buffer[i] == '\n' || buffer[i] == '\r')
+ break;
+ out += buffer[i];
+ }
+
+ break; // string found
+ }
+ }
+ return out;
+}
+
+
+
+
+
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * dataStorage
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef __DATA_STORAGE_H__
+#define __DATA_STORAGE_H__
+
+#include <stdio.h>
+#include <string>
+#include <string.h>
+
+#define MAX_LINE_LEN 1024
+
+
+class DataStorage {
+
+protected:
+
+ FILE *fp;
+ std::string getValue(const char *in);
+
+};
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_about
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include "gd_about.h"
+#include "conf.h"
+#include "const.h"
+#include "kernelAudio.h"
+#include "kernelMidi.h"
+#include "ge_mixed.h"
+#include "graphics.h"
+#include "gui_utils.h"
+
+
+extern Conf G_Conf;
+
+
+gdAbout::gdAbout()
+#ifdef WITH_VST
+: gWindow(340, 405, "About Giada") {
+#else
+: gWindow(340, 320, "About Giada") {
+#endif
+
+ if (G_Conf.aboutX)
+ resize(G_Conf.aboutX, G_Conf.aboutY, w(), h());
+
+ set_modal();
+
+ logo = new gBox(8, 10, 324, 86);
+ text = new gBox(8, 120, 324, 145);
+ close = new gClick(252, h()-28, 80, 20, "Close");
+#ifdef WITH_VST
+ vstLogo = new gBox(8, 265, 324, 50);
+ vstText = new gBox(8, 315, 324, 46);
+#endif
+ end();
+
+ logo->image(new Fl_Pixmap(giada_logo_xpm));
+ text->align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_TOP);
+
+ char message[512];
+ sprintf(
+ message,
+ "Version " VERSIONE " (" __DATE__ ")\n\n"
+ "Developed by Monocasual\n"
+ "Based on FLTK (%d.%d.%d), RtAudio (%s),\n"
+ "RtMidi (%s), libsamplerate and libsndfile\n\n"
+ "Released under the terms of the GNU General\n"
+ "Public License (GPL v3)\n\n"
+ "News, infos, contacts and documentation:\n"
+ "www.giadamusic.com",
+ FL_MAJOR_VERSION, FL_MINOR_VERSION, FL_PATCH_VERSION,
+ kernelAudio::getRtAudioVersion().c_str(),
+ kernelMidi::getRtMidiVersion().c_str());
+
+ int tw = 0;
+ int th = 0;
+ fl_measure(message, tw, th);
+ text->copy_label(message);
+ text->size(text->w(), th);
+
+#ifdef WITH_VST
+ vstLogo->image(new Fl_Pixmap(vstLogo_xpm));
+ vstLogo->position(vstLogo->x(), text->y()+text->h()+8);
+ vstText->label(
+ "VST Plug-In Technology by Steinberg\n"
+ "VST is a trademark of Steinberg\nMedia Technologies GmbH"
+ );
+ vstText->position(vstText->x(), vstLogo->y()+vstLogo->h());
+
+#endif
+
+ close->callback(cb_close, (void*)this);
+ gu_setFavicon(this);
+ setId(WID_ABOUT);
+ show();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+gdAbout::~gdAbout() {
+ G_Conf.aboutX = x();
+ G_Conf.aboutY = y();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdAbout::cb_close(Fl_Widget *w, void *p) { ((gdAbout*)p)->__cb_close(); }
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdAbout::__cb_close() {
+ do_callback();
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_about
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef GD_ABOUT_H
+#define GD_ABOUT_H
+
+#include <FL/Fl.H>
+#include <FL/Fl_Window.H>
+#include "ge_window.h"
+
+
+class gdAbout : public gWindow {
+private:
+ class gBox *logo;
+ class gBox *text;
+ class gClick *close;
+
+#ifdef WITH_VST
+ class gBox *vstText;
+ class gBox *vstLogo;
+#endif
+
+public:
+ gdAbout();
+ ~gdAbout();
+
+ static void cb_close(Fl_Widget *w, void *p);
+ inline void __cb_close();
+
+};
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_actionEditor
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include <math.h>
+#include "gd_actionEditor.h"
+#include "ge_actionChannel.h"
+#include "ge_muteChannel.h"
+#include "ge_envelopeChannel.h"
+#include "ge_pianoRoll.h"
+#include "gui_utils.h"
+#include "graphics.h"
+#include "mixer.h"
+#include "recorder.h"
+#include "conf.h"
+#include "ge_mixed.h"
+#include "channel.h"
+#include "sampleChannel.h"
+
+
+extern Mixer G_Mixer;
+extern Conf G_Conf;
+
+
+gdActionEditor::gdActionEditor(Channel *chan)
+ : gWindow(640, 284),
+ chan (chan),
+ zoom (100),
+ coverX (0)
+{
+ if (G_Conf.actionEditorW) {
+ resize(G_Conf.actionEditorX, G_Conf.actionEditorY, G_Conf.actionEditorW, G_Conf.actionEditorH);
+ zoom = G_Conf.actionEditorZoom;
+ }
+
+ totalWidth = (int) ceilf(G_Mixer.framesInSequencer / (float) zoom);
+
+ /* container with zoom buttons and the action type selector. Scheme of
+ * the resizable boxes: |[--b1--][actionType][--b2--][+][-]| */
+
+ Fl_Group *upperArea = new Fl_Group(8, 8, w()-16, 20);
+
+ upperArea->begin();
+
+ if (chan->type == CHANNEL_SAMPLE) {
+ actionType = new gChoice(8, 8, 80, 20);
+ gridTool = new gGridTool(actionType->x()+actionType->w()+4, 8, this);
+ actionType->add("key press");
+ actionType->add("key release");
+ actionType->add("kill chan");
+ actionType->value(0);
+
+ SampleChannel *ch = (SampleChannel*) chan;
+ if (ch->mode == SINGLE_PRESS || ch->mode & LOOP_ANY)
+ actionType->deactivate();
+ }
+ else {
+ gridTool = new gGridTool(8, 8, this);
+ }
+
+ gBox *b1 = new gBox(gridTool->x()+gridTool->w()+4, 8, 300, 20); // padding actionType - zoomButtons
+ zoomIn = new gClick(w()-8-40-4, 8, 20, 20, "", zoomInOff_xpm, zoomInOn_xpm);
+ zoomOut = new gClick(w()-8-20, 8, 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm);
+ upperArea->end();
+ upperArea->resizable(b1);
+
+ zoomIn->callback(cb_zoomIn, (void*)this);
+ zoomOut->callback(cb_zoomOut, (void*)this);
+
+ /* main scroller: contains all widgets */
+
+ scroller = new gScroll(8, 36, w()-16, h()-44);
+
+ if (chan->type == CHANNEL_SAMPLE) {
+
+ SampleChannel *ch = (SampleChannel*) chan;
+
+ ac = new gActionChannel (scroller->x(), upperArea->y()+upperArea->h()+8, this, ch);
+ mc = new gMuteChannel (scroller->x(), ac->y()+ac->h()+8, this);
+ vc = new gEnvelopeChannel (scroller->x(), mc->y()+mc->h()+8, this, ACTION_VOLUME, RANGE_FLOAT, "volume");
+ scroller->add(ac);
+ //scroller->add(new gResizerBar(ac->x(), ac->y()+ac->h(), scroller->w(), 8));
+ scroller->add(mc);
+ //scroller->add(new gResizerBar(mc->x(), mc->y()+mc->h(), scroller->w(), 8));
+ scroller->add(vc);
+ //scroller->add(new gResizerBar(vc->x(), vc->y()+vc->h(), scroller->w(), 8));
+
+ /* fill volume envelope with actions from recorder */
+
+ vc->fill();
+
+ /* if channel is LOOP_ANY, deactivate it: a loop mode channel cannot
+ * hold keypress/keyrelease actions */
+
+ if (ch->mode & LOOP_ANY)
+ ac->deactivate();
+ }
+ else {
+ pr = new gPianoRollContainer(scroller->x(), upperArea->y()+upperArea->h()+8, this);
+ scroller->add(pr);
+ scroller->add(new gResizerBar(pr->x(), pr->y()+pr->h(), scroller->w(), 8));
+ }
+
+ end();
+
+ /* compute values */
+
+ update();
+ gridTool->calc();
+
+ gu_setFavicon(this);
+
+ char buf[256];
+ sprintf(buf, "Edit Actions in Channel %d", chan->index+1);
+ label(buf);
+
+ set_non_modal();
+ size_range(640, 284);
+ resizable(scroller);
+
+ show();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+gdActionEditor::~gdActionEditor() {
+ G_Conf.actionEditorX = x();
+ G_Conf.actionEditorY = y();
+ G_Conf.actionEditorW = w();
+ G_Conf.actionEditorH = h();
+ G_Conf.actionEditorZoom = zoom;
+
+ /** CHECKME - missing clear() ? */
+
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdActionEditor::cb_zoomIn(Fl_Widget *w, void *p) { ((gdActionEditor*)p)->__cb_zoomIn(); }
+void gdActionEditor::cb_zoomOut(Fl_Widget *w, void *p) { ((gdActionEditor*)p)->__cb_zoomOut(); }
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdActionEditor::__cb_zoomIn() {
+
+ /* zoom 50: empirical value, to avoid a totalWidth > 16 bit signed
+ * (32767 max), unsupported by FLTK 1.3.x */
+
+ if (zoom <= 50)
+ return;
+
+ zoom /= 2;
+
+ update();
+
+ if (chan->type == CHANNEL_SAMPLE) {
+ ac->size(totalWidth, ac->h());
+ mc->size(totalWidth, mc->h());
+ vc->size(totalWidth, vc->h());
+ ac->updateActions();
+ mc->updateActions();
+ vc->updateActions();
+ }
+ else {
+ pr->size(totalWidth, pr->h());
+ pr->updateActions();
+ }
+
+ /* scroll to pointer */
+
+ int shift = Fl::event_x() + scroller->xposition();
+ scroller->scroll_to(scroller->xposition() + shift, scroller->yposition());
+
+ /* update all underlying widgets */
+
+ gridTool->calc();
+ scroller->redraw();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdActionEditor::__cb_zoomOut() {
+
+ zoom *= 2;
+
+ update();
+
+ if (chan->type == CHANNEL_SAMPLE) {
+ ac->size(totalWidth, ac->h());
+ mc->size(totalWidth, mc->h());
+ vc->size(totalWidth, vc->h());
+ ac->updateActions();
+ mc->updateActions();
+ vc->updateActions();
+ }
+ else {
+ pr->size(totalWidth, pr->h());
+ pr->updateActions();
+ }
+
+ /* scroll to pointer */
+
+ int shift = (Fl::event_x() + scroller->xposition()) / -2;
+ if (scroller->xposition() + shift < 0)
+ shift = 0;
+ scroller->scroll_to(scroller->xposition() + shift, scroller->yposition());
+
+ /* update all underlying widgets */
+
+ gridTool->calc();
+ scroller->redraw();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdActionEditor::update() {
+ totalWidth = (int) ceilf(G_Mixer.framesInSequencer / (float) zoom);
+ if (totalWidth < scroller->w()) {
+ totalWidth = scroller->w();
+ zoom = (int) ceilf(G_Mixer.framesInSequencer / (float) totalWidth);
+ scroller->scroll_to(0, scroller->yposition());
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gdActionEditor::handle(int e) {
+ int ret = Fl_Group::handle(e);
+ switch (e) {
+ case FL_MOUSEWHEEL: {
+ Fl::event_dy() == -1 ? __cb_zoomIn() : __cb_zoomOut();
+ ret = 1;
+ break;
+ }
+ }
+ return ret;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gdActionEditor::getActionType() {
+ if (actionType->value() == 0)
+ return ACTION_KEYPRESS;
+ else
+ if (actionType->value() == 1)
+ return ACTION_KEYREL;
+ else
+ if (actionType->value() == 2)
+ return ACTION_KILLCHAN;
+ else
+ return -1;
+}
+
+
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+
+
+gGridTool::gGridTool(int x, int y, gdActionEditor *parent)
+ : Fl_Group(x, y, 80, 20), parent(parent)
+{
+ gridType = new gChoice(x, y, 40, 20);
+ gridType->add("1");
+ gridType->add("2");
+ gridType->add("3");
+ gridType->add("4");
+ gridType->add("6");
+ gridType->add("8");
+ gridType->add("16");
+ gridType->add("32");
+ gridType->value(0);
+ gridType->callback(cb_changeType, (void*)this);
+
+ active = new gCheck (x+44, y+4, 12, 12);
+
+ gridType->value(G_Conf.actionEditorGridVal);
+ active->value(G_Conf.actionEditorGridOn);
+
+ end();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+gGridTool::~gGridTool() {
+ G_Conf.actionEditorGridVal = gridType->value();
+ G_Conf.actionEditorGridOn = active->value();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gGridTool::cb_changeType(Fl_Widget *w, void *p) { ((gGridTool*)p)->__cb_changeType(); }
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gGridTool::__cb_changeType() {
+ calc();
+ parent->redraw();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool gGridTool::isOn() {
+ return active->value();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gGridTool::getValue() {
+ switch (gridType->value()) {
+ case 0: return 1;
+ case 1: return 2;
+ case 2: return 3;
+ case 3: return 4;
+ case 4: return 6;
+ case 5: return 8;
+ case 6: return 16;
+ case 7: return 32;
+ }
+ return 0;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gGridTool::calc() {
+
+ points.clear();
+ frames.clear();
+ bars.clear();
+ beats.clear();
+
+ /* find beats, bars and grid. The method is the same of the waveform in sample
+ * editor. Take totalwidth (the width in pixel of the area to draw), knowing
+ * that totalWidth = totalFrames / zoom. Then, for each pixel of totalwidth,
+ * put a concentrate of each block (which is totalFrames / zoom) */
+
+ int j = 0;
+ int fpgc = floor(G_Mixer.framesPerBeat / getValue()); // frames per grid cell
+
+ for (int i=1; i<parent->totalWidth; i++) { // if i=0, step=0 -> useless cycle
+ int step = parent->zoom*i;
+ while (j < step && j < G_Mixer.totalFrames) {
+ if (j % fpgc == 0) {
+ points.add(i);
+ frames.add(j);
+ }
+ if (j % G_Mixer.framesPerBeat == 0)
+ beats.add(i);
+ if (j % G_Mixer.framesPerBar == 0 && i != 1)
+ bars.add(i);
+ if (j == G_Mixer.totalFrames-1)
+ parent->coverX = i;
+ j++;
+ }
+ j = step;
+ }
+
+ /* fix coverX if == 0, which means G_Mixer.beats == 32 */
+
+ if (G_Mixer.beats == 32)
+ parent->coverX = parent->totalWidth;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gGridTool::getSnapPoint(int v) {
+
+ if (v == 0) return 0;
+
+ for (int i=0; i<(int)points.size; i++) {
+
+ if (i == (int) points.size-1)
+ return points.at(i);
+
+ int gp = points.at(i);
+ int gpn = points.at(i+1);
+
+ if (v >= gp && v < gpn)
+ return gp;
+ }
+ return v; // default value
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gGridTool::getSnapFrame(int v) {
+
+ v *= parent->zoom; // transformation pixel -> frame
+
+ for (int i=0; i<(int)frames.size; i++) {
+
+ if (i == (int) frames.size-1)
+ return frames.at(i);
+
+ int gf = frames.at(i); // grid frame
+ int gfn = frames.at(i+1); // grid frame next
+
+ if (v >= gf && v < gfn) {
+
+ /* which one is the closest? gf < v < gfn */
+
+ if ((gfn - v) < (v - gf))
+ return gfn;
+ else
+ return gf;
+ }
+ }
+ return v; // default value
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gGridTool::getCellSize() {
+ return (parent->coverX - parent->ac->x()) / G_Mixer.beats / getValue();
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_actionEditor
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef GD_ACTIONEDITOR_H
+#define GD_ACTIONEDITOR_H
+
+#include <FL/Fl.H>
+#include <FL/Fl_Double_Window.H>
+#include <FL/Fl_Scroll.H>
+#include "ge_window.h"
+
+
+/* gActionEditor
+ * main window which contains the tools for dealing with actions.
+ * This class calculates chan, zoom, frames per beat, and so on. Each
+ * sub-widget contains a pointer to this window to query those data. */
+
+class gdActionEditor : public gWindow {
+
+private:
+
+ /* update
+ * compute total width, in pixel. */
+
+ void update();
+
+public:
+
+ gdActionEditor(class Channel *chan);
+ ~gdActionEditor();
+
+ int handle(int e);
+
+ int getActionType();
+
+ static void cb_zoomIn(Fl_Widget *w, void *p);
+ static void cb_zoomOut(Fl_Widget *w, void *p);
+ inline void __cb_zoomIn();
+ inline void __cb_zoomOut();
+
+ class gChoice *actionType;
+ class gGridTool *gridTool;
+ class gClick *zoomIn;
+ class gClick *zoomOut;
+ class gScroll *scroller; // widget container
+
+ class gActionChannel *ac;
+ class gMuteChannel *mc;
+ class gEnvelopeChannel *vc;
+ class gPianoRollContainer *pr;
+
+ gVector <class gActionWidget*> widgets;
+
+ class Channel *chan;
+
+ int zoom;
+ int totalWidth; // total width of the widget, in pixel (zoom affected)
+ int coverX; // x1 of the unused area (x2 = totalWidth)
+};
+
+
+/* ------------------------------------------------------------------ */
+
+
+class gGridTool : public Fl_Group {
+
+private:
+ class gChoice *gridType;
+ class gCheck *active;
+
+ class gdActionEditor *parent;
+
+ static void cb_changeType(Fl_Widget *w, void *p);
+ inline void __cb_changeType();
+
+public:
+
+ gGridTool(int x, int y, gdActionEditor *parent);
+ ~gGridTool();
+
+ int getValue();
+ bool isOn();
+ void calc();
+
+ /* getSnapPoint
+ * given a cursor position in input, return the x coordinates of the
+ * nearest snap point (in pixel, clean, ie. not x()-shifted) */
+
+ int getSnapPoint(int v);
+ int getSnapFrame(int v);
+
+ /* getCellSize
+ * return the size in pixel of a single cell of the grid. */
+
+ int getCellSize();
+
+ gVector<int> points; // points of the grid
+ gVector<int> frames; // frames of the grid
+
+ gVector<int> bars;
+ gVector<int> beats;
+};
+
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_beatsInput
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include "gd_beatsInput.h"
+#include "gd_mainWindow.h"
+#include "gui_utils.h"
+#include "patch.h"
+#include "glue.h"
+#include "mixer.h"
+#include "conf.h"
+
+
+extern Mixer G_Mixer;
+extern Conf G_Conf;
+extern gdMainWindow *mainWin;
+
+
+gdBeatsInput::gdBeatsInput()
+ : gWindow(164, 60, "Beats")
+{
+ if (G_Conf.beatsX)
+ resize(G_Conf.beatsX, G_Conf.beatsY, w(), h());
+
+ set_modal();
+
+ beats = new gInput(8, 8, 35, 20);
+ bars = new gInput(47, 8, 35, 20);
+ ok = new gClick(86, 8, 70, 20, "Ok");
+ resizeRec = new gCheck(8, 40, 12, 12, "resize recorded actions");
+ end();
+
+ char buf_bars[3]; sprintf(buf_bars, "%d", G_Mixer.bars);
+ char buf_beats[3]; sprintf(buf_beats, "%d", G_Mixer.beats);
+ beats->maximum_size(2);
+ beats->value(buf_beats);
+ beats->type(FL_INT_INPUT);
+ bars->maximum_size(2);
+ bars->value(buf_bars);
+ bars->type(FL_INT_INPUT);
+ ok->shortcut(FL_Enter);
+ ok->callback(cb_update_batt, (void*)this);
+ resizeRec->value(G_Conf.resizeRecordings);
+
+ gu_setFavicon(this);
+ setId(WID_BEATS);
+ show();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+gdBeatsInput::~gdBeatsInput()
+{
+ G_Conf.beatsX = x();
+ G_Conf.beatsY = y();
+ G_Conf.resizeRecordings = resizeRec->value();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdBeatsInput::cb_update_batt(Fl_Widget *w, void *p) { ((gdBeatsInput*)p)->__cb_update_batt(); }
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdBeatsInput::__cb_update_batt()
+{
+ if (!strcmp(beats->value(), "") || !strcmp(bars->value(), ""))
+ return;
+ glue_setBeats(atoi(beats->value()), atoi(bars->value()), resizeRec->value());
+ do_callback();
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_beatsInput
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+#ifndef GD_BEATSINPUT_H
+#define GD_BEATSINPUT_H
+
+#include <FL/Fl.H>
+#include <FL/Fl_Window.H>
+#include "ge_window.h"
+
+
+class gdBeatsInput : public gWindow
+{
+private:
+
+ static void cb_update_batt(Fl_Widget *w, void *p);
+ inline void __cb_update_batt();
+
+ class gInput *beats;
+ class gInput *bars;
+ class gClick *ok;
+ class gCheck *resizeRec;
+
+public:
+
+ gdBeatsInput();
+ ~gdBeatsInput();
+};
+
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_bpmInput
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include "gd_bpmInput.h"
+#include "gd_mainWindow.h"
+#include "conf.h"
+#include "ge_mixed.h"
+#include "mixer.h"
+#include "gui_utils.h"
+#include "glue.h"
+
+
+extern Mixer G_Mixer;
+extern Conf G_Conf;
+extern gdMainWindow *mainWin;
+
+
+gdBpmInput::gdBpmInput(const char *label)
+: gWindow(144, 36, "Bpm") {
+
+ if (G_Conf.bpmX)
+ resize(G_Conf.bpmX, G_Conf.bpmY, w(), h());
+
+ set_modal();
+
+ input_a = new gInput(8, 8, 30, 20);
+ input_b = new gInput(42, 8, 20, 20);
+ ok = new gClick(66, 8, 70, 20, "Ok");
+ end();
+
+ char a[4];
+ snprintf(a, 4, "%d", (int) G_Mixer.bpm);
+ char b[2];
+ for (unsigned i=0; i<strlen(label); i++) // looking for the dot
+ if (label[i] == '.') {
+ snprintf(b, 2, "%c", label[i+1]);
+ break;
+ }
+
+ input_a->maximum_size(3);
+ input_a->type(FL_INT_INPUT);
+ input_a->value(a);
+ input_b->maximum_size(1);
+ input_b->type(FL_INT_INPUT);
+ input_b->value(b);
+
+ ok->shortcut(FL_Enter);
+ ok->callback(cb_update_bpm, (void*)this);
+
+ gu_setFavicon(this);
+ setId(WID_BPM);
+ show();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+gdBpmInput::~gdBpmInput() {
+ G_Conf.bpmX = x();
+ G_Conf.bpmY = y();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdBpmInput::cb_update_bpm(Fl_Widget *w, void *p) { ((gdBpmInput*)p)->__cb_update_bpm(); }
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdBpmInput::__cb_update_bpm() {
+ if (strcmp(input_a->value(), "") == 0)
+ return;
+ glue_setBpm(input_a->value(), input_b->value());
+ do_callback();
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_bpmInput
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+#ifndef GD_BPMINPUT_H
+#define GD_BPMINPUT_H
+
+#include <FL/Fl.H>
+#include <FL/Fl_Window.H>
+#include "ge_window.h"
+
+
+class gdBpmInput : public gWindow {
+private:
+ static void cb_update_bpm(Fl_Widget *w, void *p);
+ inline void __cb_update_bpm();
+
+ class gInput *input_a;
+ class gInput *input_b;
+ class gClick *ok;
+
+public:
+ gdBpmInput(const char *label); // pointer to mainWin->timing->bpm->label()
+ ~gdBpmInput();
+};
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_browser
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "gd_browser.h"
+#include "ge_browser.h"
+#include "gd_pluginList.h"
+#include "gd_mainWindow.h"
+#include "gg_keyboard.h"
+#include "gd_warnings.h"
+#include "ge_channel.h"
+#include "mixer.h"
+#include "graphics.h"
+#include "wave.h"
+#include "glue.h"
+#include "pluginHost.h"
+#include "channel.h"
+#include "sampleChannel.h"
+#include "patch.h"
+#include "conf.h"
+
+
+extern Patch G_Patch;
+extern Conf G_Conf;
+extern Mixer G_Mixer;
+#ifdef WITH_VST
+extern PluginHost G_PluginHost;
+#endif
+extern gdMainWindow *mainWin;
+
+
+gdBrowser::gdBrowser(const char *title, const char *initPath, Channel *ch, int type, int stackType)
+ : gWindow (396, 302, title),
+ ch (ch),
+ type (type),
+ stackType(stackType)
+{
+ set_non_modal();
+
+ browser = new gBrowser(8, 36, 380, 230);
+ Fl_Group *group_btn = new Fl_Group(8, 274, 380, 20);
+ gBox *b = new gBox(8, 274, 204, 20); // spacer window border <-> buttons
+ ok = new gClick(308, 274, 80, 20);
+ cancel = new gClick(220, 274, 80, 20, "Cancel");
+ status = new gProgress(8, 274, 204, 20);
+ status->minimum(0);
+ status->maximum(1);
+ status->hide(); // show the bar only if necessary
+ group_btn->resizable(b);
+ group_btn->end();
+
+ Fl_Group *group_upd = new Fl_Group(8, 8, 380, 25);
+ if (type == BROWSER_SAVE_PATCH || type == BROWSER_SAVE_SAMPLE || type == BROWSER_SAVE_PROJECT) /// bitmask please!
+ name = new gInput(208, 8, 152, 20);
+ if (type == BROWSER_SAVE_PATCH || type == BROWSER_SAVE_SAMPLE || type == BROWSER_SAVE_PROJECT) /// bitmask please!
+ where = new gInput(8, 8, 192, 20);
+ else
+ where = new gInput(8, 8, 352, 20);
+ updir = new gClick(368, 8, 20, 20, "", updirOff_xpm, updirOn_xpm);
+ group_upd->resizable(where);
+ group_upd->end();
+
+ end();
+
+ resizable(browser);
+ size_range(w(), h(), 0, 0);
+
+ where->readonly(true);
+ where->cursor_color(COLOR_BG_DARK);
+
+ if (type == BROWSER_SAVE_PATCH || type == BROWSER_SAVE_SAMPLE || type == BROWSER_SAVE_PROJECT) /// bitmask please!
+ ok->label("Save");
+ else
+ ok->label("Load");
+
+ if (type == BROWSER_LOAD_PATCH)
+ ok->callback(cb_load_patch, (void*)this);
+ else
+ if (type == BROWSER_LOAD_SAMPLE)
+ ok->callback(cb_load_sample, (void*)this);
+ else
+ if (type == BROWSER_SAVE_PATCH) {
+ ok->callback(cb_save_patch, (void*)this);
+ name->value(G_Patch.name[0] == '\0' ? "my_patch.gptc" : G_Patch.name);
+ name->maximum_size(MAX_PATCHNAME_LEN+5); // +5 for ".gptc"
+ }
+ else
+ if (type == BROWSER_SAVE_SAMPLE) {
+ ok->callback(cb_save_sample, (void*)this);
+ name->value(((SampleChannel*)ch)->wave->name.c_str());
+ }
+ else
+ if (type == BROWSER_SAVE_PROJECT) {
+ ok->callback(cb_save_project, (void*)this);
+ name->value(gStripExt(G_Patch.name).c_str());
+ }
+#ifdef WITH_VST
+ else
+ if (type == BROWSER_LOAD_PLUGIN) {
+ ok->callback(cb_loadPlugin, (void*)this);
+ }
+#endif
+
+ ok->shortcut(FL_Enter);
+
+ updir->callback(cb_up, (void*)this);
+ cancel->callback(cb_close, (void*)this);
+ browser->callback(cb_down, this);
+ browser->path_obj = where;
+ browser->init(initPath);
+
+ if (G_Conf.browserW)
+ resize(G_Conf.browserX, G_Conf.browserY, G_Conf.browserW, G_Conf.browserH);
+
+ gu_setFavicon(this);
+ show();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gdBrowser::~gdBrowser() {
+ G_Conf.browserX = x();
+ G_Conf.browserY = y();
+ G_Conf.browserW = w();
+ G_Conf.browserH = h();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowser::cb_load_patch (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_load_patch(); }
+void gdBrowser::cb_load_sample (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_load_sample(); }
+void gdBrowser::cb_save_sample (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_save_sample(); }
+void gdBrowser::cb_save_patch (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_save_patch(); }
+void gdBrowser::cb_save_project(Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_save_project(); }
+void gdBrowser::cb_down (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_down(); }
+void gdBrowser::cb_up (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_up(); }
+void gdBrowser::cb_close (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_close(); }
+#ifdef WITH_VST
+void gdBrowser::cb_loadPlugin (Fl_Widget *v, void *p) { ((gdBrowser*)p)->__cb_loadPlugin(); }
+#endif
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowser::__cb_load_patch() {
+
+ if (browser->text(browser->value()) == NULL)
+ return;
+
+ /* patchFile is the file to open.
+ * For patches: browser->get_selected_item()
+ * for projects: browser->get_selected_item() without extention +
+ * patch name appended */
+
+ std::string patchFile = browser->get_selected_item();;
+ bool isProject;
+
+ if (gIsProject(browser->get_selected_item())) {
+ std::string patchName = gGetProjectName(browser->get_selected_item());
+#if defined(__linux__) || defined(__APPLE__)
+ patchFile = patchFile+"/"+patchName+".gptc";
+#elif defined(_WIN32)
+ patchFile = patchFile+"\\"+patchName+".gptc";
+#endif
+ isProject = true;
+ }
+ else
+ isProject = false;
+
+ int res = glue_loadPatch(patchFile.c_str(), browser->path_obj->value(), status, isProject);
+
+ if (res == PATCH_UNREADABLE) {
+ status->hide();
+ if (isProject)
+ gdAlert("This project is unreadable.");
+ else
+ gdAlert("This patch is unreadable.");
+ }
+ else if (res == PATCH_INVALID) {
+ status->hide();
+ if (isProject)
+ gdAlert("This project is not valid.");
+ else
+ gdAlert("This patch is not valid.");
+ }
+ else
+ do_callback();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowser::__cb_save_sample() {
+
+ if (strcmp(name->value(), "") == 0) { /// FIXME glue business
+ gdAlert("Please choose a file name.");
+ return;
+ }
+
+ /* bruteforce check extension. */
+
+ std::string filename = gStripExt(name->value());
+ char fullpath[PATH_MAX];
+ sprintf(fullpath, "%s/%s.wav", where->value(), filename.c_str());
+
+ if (gFileExists(fullpath))
+ if (!gdConfirmWin("Warning", "File exists: overwrite?"))
+ return;
+
+ if (((SampleChannel*)ch)->save(fullpath))
+ do_callback();
+ else
+ gdAlert("Unable to save this sample!");
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowser::__cb_load_sample() {
+ if (browser->text(browser->value()) == NULL)
+ return;
+
+ int res = glue_loadChannel((SampleChannel*) ch, browser->get_selected_item());
+
+ if (res == SAMPLE_LOADED_OK) {
+ do_callback();
+ mainWin->delSubWindow(WID_SAMPLE_EDITOR); // if editor is open
+ }
+ else
+ mainWin->keyboard->printChannelMessage(res);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowser::__cb_down() {
+ const char *path = browser->get_selected_item();
+ if (!path) // when click on an empty area
+ return;
+ if (!gIsDir(path)) {
+
+ /* set the name of the patch/sample/project as the selected item */
+
+ if (type == BROWSER_SAVE_PATCH || type == BROWSER_SAVE_SAMPLE || type == BROWSER_SAVE_PROJECT) {
+ if (gIsProject(path)) {
+ std::string tmp = browser->text(browser->value());
+ tmp.erase(0, 4);
+ name->value(tmp.c_str());
+ }
+ else
+ name->value(browser->text(browser->value()));
+ }
+ return;
+ }
+ browser->clear();
+ browser->down_dir(path);
+ browser->sort();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowser::__cb_up() {
+ browser->clear();
+ browser->up_dir();
+ browser->sort();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowser::__cb_save_patch() {
+
+ if (strcmp(name->value(), "") == 0) { /// FIXME glue business
+ gdAlert("Please choose a file name.");
+ return;
+ }
+
+ /* if name->value() contains ".gptc" */
+
+ char ext[6] = ".gptc";
+ if (strstr(name->value(), ".gptc") != NULL)
+ ext[0] = '\0';
+
+ char fullpath[PATH_MAX];
+ sprintf(fullpath, "%s/%s%s", where->value(), name->value(), ext);
+ if (gFileExists(fullpath))
+ if (!gdConfirmWin("Warning", "File exists: overwrite?"))
+ return;
+
+ if (glue_savePatch(fullpath, name->value(), false)) // false == not a project
+ do_callback();
+ else
+ gdAlert("Unable to save the patch!");
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowser::__cb_save_project() {
+
+ if (strcmp(name->value(), "") == 0) { /// FIXME glue business
+ gdAlert("Please choose a project name.");
+ return;
+ }
+
+ /* check if name->value() contains ".gprj" */
+
+ char ext[6] = ".gprj";
+ if (strstr(name->value(), ".gprj") != NULL)
+ ext[0] = '\0';
+
+ char fullpath[PATH_MAX];
+#if defined(_WIN32)
+ sprintf(fullpath, "%s\\%s%s", where->value(), name->value(), ext);
+#else
+ sprintf(fullpath, "%s/%s%s", where->value(), name->value(), ext);
+#endif
+
+ if (gIsProject(fullpath) && !gdConfirmWin("Warning", "Project exists: overwrite?"))
+ return;
+
+ if (glue_saveProject(fullpath, name->value()))
+ do_callback();
+ else
+ gdAlert("Unable to save the project!");
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+void gdBrowser::__cb_loadPlugin() {
+
+ if (browser->text(browser->value()) == NULL)
+ return;
+
+ int res = G_PluginHost.addPlugin(browser->get_selected_item(), stackType, ch);
+
+ /* store the folder path inside G_Conf, in order to reuse it the
+ * next time. */
+
+ G_Conf.setPath(G_Conf.pluginPath, where->value());
+
+ if (res)
+ do_callback();
+ else
+ gdAlert("Unable to load the selected plugin!");
+}
+#endif
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdBrowser::__cb_close() {
+ do_callback();
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_browser
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef GD_BROWSER_H
+#define GD_BROWSER_H
+
+
+#include <FL/Fl.H>
+#include <FL/Fl_Double_Window.H>
+#include "ge_window.h"
+
+
+/* TODO - this class must be subclassed into gdPluginBrowser, gdFileBrowser,
+ * and so on. It's a real mess right now. */
+
+class gdBrowser : public gWindow {
+
+private:
+ static void cb_down(Fl_Widget *v, void *p);
+ static void cb_up (Fl_Widget *v, void *p);
+ static void cb_load_sample (Fl_Widget *v, void *p);
+ static void cb_save_sample (Fl_Widget *v, void *p);
+ static void cb_load_patch (Fl_Widget *v, void *p);
+ static void cb_save_patch (Fl_Widget *v, void *p);
+ static void cb_save_project(Fl_Widget *v, void *p);
+ static void cb_close (Fl_Widget *w, void *p);
+#ifdef WITH_VST
+ static void cb_loadPlugin (Fl_Widget *v, void *p);
+#endif
+
+ inline void __cb_down();
+ inline void __cb_up();
+ inline void __cb_load_sample();
+ inline void __cb_save_sample();
+ inline void __cb_save_project();
+ inline void __cb_load_patch();
+ inline void __cb_save_patch();
+ inline void __cb_close();
+#ifdef WITH_VST
+ inline void __cb_loadPlugin();
+#endif
+
+ class gBrowser *browser;
+ class gClick *ok;
+ class gClick *cancel;
+ class gInput *where;
+ class gInput *name;
+ class gClick *updir;
+ class gProgress *status;
+
+ class Channel *ch;
+
+ /* browser type: see const.h */
+
+ /** FIXME internal enum:
+ * enum browserType {
+ * TYPE_A,
+ * TYPE_B,
+ * ....
+ * }; */
+ int type;
+
+ /* PluginHost stack type. Used only when loading plugins */
+
+ int stackType;
+
+public:
+ gdBrowser(const char *title, const char *initPath, class Channel *ch, int type, int stackType=0);
+ ~gdBrowser();
+};
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_config
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include "gd_config.h"
+#include "gd_keyGrabber.h"
+#include "gd_devInfo.h"
+#include "ge_mixed.h"
+#include "conf.h"
+#include "log.h"
+#include "gui_utils.h"
+#include "patch.h"
+#include "kernelAudio.h"
+#include "kernelMidi.h"
+
+
+extern Patch G_Patch;
+extern Conf G_Conf;
+extern bool G_audio_status;
+
+
+/* ------------------------------------------------------------------ */
+
+
+gTabMisc::gTabMisc(int X, int Y, int W, int H)
+ : Fl_Group(X, Y, W, H, "Misc")
+{
+ begin();
+ debugMsg = new gChoice(x()+92, y()+9, 253, 20, "Debug messages");
+ end();
+
+ debugMsg->add("(disabled)");
+ debugMsg->add("To standard output");
+ debugMsg->add("To file");
+
+ labelsize(11);
+
+ switch (G_Conf.logMode) {
+ case LOG_MODE_MUTE:
+ debugMsg->value(0);
+ break;
+ case LOG_MODE_STDOUT:
+ debugMsg->value(1);
+ break;
+ case LOG_MODE_FILE:
+ debugMsg->value(2);
+ break;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTabMisc::save()
+{
+ switch(debugMsg->value()) {
+ case 0:
+ G_Conf.logMode = LOG_MODE_MUTE;
+ break;
+ case 1:
+ G_Conf.logMode = LOG_MODE_STDOUT;
+ break;
+ case 2:
+ G_Conf.logMode = LOG_MODE_FILE;
+ break;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+
+
+gTabAudio::gTabAudio(int X, int Y, int W, int H)
+ : Fl_Group(X, Y, W, H, "Sound System")
+{
+ begin();
+ soundsys = new gChoice(x()+92, y()+9, 253, 20, "System");
+ buffersize = new gChoice(x()+92, y()+37, 55, 20, "Buffer size");
+ samplerate = new gChoice(x()+290, y()+37, 55, 20, "Sample rate");
+ sounddevOut = new gChoice(x()+92, y()+65, 225, 20, "Output device");
+ devOutInfo = new gClick (x()+325, y()+65, 20, 20, "?");
+ channelsOut = new gChoice(x()+92, y()+93, 55, 20, "Output channels");
+ limitOutput = new gCheck (x()+155, y()+97, 55, 20, "Limit output");
+ sounddevIn = new gChoice(x()+92, y()+121, 225, 20, "Input device");
+ devInInfo = new gClick (x()+325, y()+121, 20, 20, "?");
+ channelsIn = new gChoice(x()+92, y()+149, 55, 20, "Input channels");
+ delayComp = new gInput (x()+290, y()+149, 55, 20, "Rec delay comp.");
+ rsmpQuality = new gChoice(x()+92, y()+177, 253, 20, "Resampling");
+ new gBox(x(), rsmpQuality->y()+rsmpQuality->h()+8, w(), 92, "Restart Giada for the changes to take effect.");
+ end();
+ labelsize(11);
+
+#if defined(__linux__)
+
+ if (kernelAudio::hasAPI(RtAudio::LINUX_ALSA))
+ soundsys->add("ALSA");
+ if (kernelAudio::hasAPI(RtAudio::UNIX_JACK))
+ soundsys->add("Jack");
+ if (kernelAudio::hasAPI(RtAudio::LINUX_PULSE))
+ soundsys->add("PulseAudio");
+
+ switch (G_Conf.soundSystem) {
+ case SYS_API_ALSA:
+ soundsys->show("ALSA");
+ break;
+ case SYS_API_JACK:
+ soundsys->show("Jack");
+ buffersize->deactivate();
+ samplerate->deactivate();
+ break;
+ case SYS_API_PULSE:
+ soundsys->show("PulseAudio");
+ break;
+ }
+ soundsysInitValue = soundsys->value();
+
+#elif defined(_WIN32)
+
+ if (kernelAudio::hasAPI(RtAudio::WINDOWS_DS))
+ soundsys->add("DirectSound");
+ if (kernelAudio::hasAPI(RtAudio::WINDOWS_ASIO))
+ soundsys->add("ASIO");
+ soundsys->show(G_Conf.soundSystem == SYS_API_DS ? "DirectSound" : "ASIO");
+ soundsysInitValue = soundsys->value();
+
+#elif defined (__APPLE__)
+
+ if (kernelAudio::hasAPI(RtAudio::MACOSX_CORE))
+ soundsys->add("CoreAudio");
+ soundsys->show("CoreAudio");
+ soundsysInitValue = soundsys->value();
+
+#endif
+
+ sounddevIn->callback(cb_fetchInChans, this);
+ sounddevOut->callback(cb_fetchOutChans, this);
+
+ devOutInfo->callback(cb_showOutputInfo, this);
+ devInInfo->callback(cb_showInputInfo, this);
+
+ fetchSoundDevs();
+
+ fetchOutChans(sounddevOut->value());
+ fetchInChans(sounddevIn->value());
+
+ buffersize->add("8");
+ buffersize->add("16");
+ buffersize->add("32");
+ buffersize->add("64");
+ buffersize->add("128");
+ buffersize->add("256");
+ buffersize->add("512");
+ buffersize->add("1024");
+ buffersize->add("2048");
+ buffersize->add("4096");
+
+ char buf[8];
+ sprintf(buf, "%d", G_Conf.buffersize);
+ buffersize->show(buf);
+
+ /* fill frequency dropdown menu */
+
+ int nfreq = kernelAudio::getTotalFreqs(sounddevOut->value());
+ for (int i=0; i<nfreq; i++) {
+ char buf[16];
+ int freq = kernelAudio::getFreq(sounddevOut->value(), i);
+ sprintf(buf, "%d", freq);
+ samplerate->add(buf);
+ if (freq == G_Conf.samplerate)
+ samplerate->value(i);
+ }
+
+ rsmpQuality->add("Sinc best quality (very slow)");
+ rsmpQuality->add("Sinc medium quality (slow)");
+ rsmpQuality->add("Sinc basic quality (medium)");
+ rsmpQuality->add("Zero Order Hold (fast)");
+ rsmpQuality->add("Linear (very fast)");
+ rsmpQuality->value(G_Conf.rsmpQuality);
+
+ buf[0] = '\0';
+ sprintf(buf, "%d", G_Conf.delayComp);
+ delayComp->value(buf);
+ delayComp->type(FL_INT_INPUT);
+ delayComp->maximum_size(5);
+
+ limitOutput->value(G_Conf.limitOutput);
+ soundsys->callback(cb_deactivate_sounddev, (void*)this);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTabAudio::cb_deactivate_sounddev(Fl_Widget *w, void *p) { ((gTabAudio*)p)->__cb_deactivate_sounddev(); }
+void gTabAudio::cb_fetchInChans(Fl_Widget *w, void *p) { ((gTabAudio*)p)->__cb_fetchInChans(); }
+void gTabAudio::cb_fetchOutChans(Fl_Widget *w, void *p) { ((gTabAudio*)p)->__cb_fetchOutChans(); }
+void gTabAudio::cb_showInputInfo(Fl_Widget *w, void *p) { ((gTabAudio*)p)->__cb_showInputInfo(); }
+void gTabAudio::cb_showOutputInfo(Fl_Widget *w, void *p) { ((gTabAudio*)p)->__cb_showOutputInfo(); }
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTabAudio::__cb_fetchInChans()
+{
+ fetchInChans(sounddevIn->value());
+ channelsIn->value(0);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTabAudio::__cb_fetchOutChans()
+{
+ fetchOutChans(sounddevOut->value());
+ channelsOut->value(0);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTabAudio::__cb_showInputInfo()
+{
+ unsigned dev = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value()));
+ new gdDevInfo(dev);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTabAudio::__cb_showOutputInfo()
+{
+ unsigned dev = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value()));
+ new gdDevInfo(dev);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTabAudio::__cb_deactivate_sounddev()
+{
+ /* if the user changes sound system (eg ALSA->JACK) device menu deactivates.
+ * If it returns to the original sound system, we re-fill the list by
+ * querying kernelAudio. */
+
+ if (soundsysInitValue == soundsys->value()) {
+ sounddevOut->clear();
+ sounddevIn->clear();
+
+ fetchSoundDevs();
+
+ /* the '?' button is added by fetchSoundDevs */
+
+ fetchOutChans(sounddevOut->value());
+ sounddevOut->activate();
+ channelsOut->activate();
+
+ /* chan menus and '?' button are activated by fetchInChans(...) */
+
+ fetchInChans(sounddevIn->value());
+ sounddevIn->activate();
+ }
+ else {
+ sounddevOut->deactivate();
+ sounddevOut->clear();
+ sounddevOut->add("-- restart to fetch device(s) --");
+ sounddevOut->value(0);
+ channelsOut->deactivate();
+ devOutInfo->deactivate();
+
+ sounddevIn->deactivate();
+ sounddevIn->clear();
+ sounddevIn->add("-- restart to fetch device(s) --");
+ sounddevIn->value(0);
+ channelsIn->deactivate();
+ devInInfo->deactivate();
+ }
+}
+
+/* ------------------------------------------------------------------ */
+
+
+void gTabAudio::fetchInChans(int menuItem)
+{
+ /* if menuItem==0 device in input is disabled. */
+
+ if (menuItem == 0) {
+ devInInfo ->deactivate();
+ channelsIn->deactivate();
+ delayComp ->deactivate();
+ return;
+ }
+
+ devInInfo ->activate();
+ channelsIn->activate();
+ delayComp ->activate();
+
+ channelsIn->clear();
+
+ unsigned dev = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value()));
+ unsigned chs = kernelAudio::getMaxInChans(dev);
+
+ if (chs == 0) {
+ channelsIn->add("none");
+ channelsIn->value(0);
+ return;
+ }
+ for (unsigned i=0; i<chs; i+=2) {
+ char str[16];
+ sprintf(str, "%d-%d", (i+1), (i+2));
+ channelsIn->add(str);
+ }
+ channelsIn->value(G_Conf.channelsIn);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTabAudio::fetchOutChans(int menuItem)
+{
+ channelsOut->clear();
+
+ unsigned dev = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value()));
+ unsigned chs = kernelAudio::getMaxOutChans(dev);
+
+ if (chs == 0) {
+ channelsOut->add("none");
+ channelsOut->value(0);
+ return;
+ }
+ for (unsigned i=0; i<chs; i+=2) {
+ char str[16];
+ sprintf(str, "%d-%d", (i+1), (i+2));
+ channelsOut->add(str);
+ }
+ channelsOut->value(G_Conf.channelsOut);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gTabAudio::findMenuDevice(gChoice *m, int device)
+{
+ if (device == -1)
+ return 0;
+
+ if (G_audio_status == false)
+ return 0;
+
+ for (int i=0; i<m->size(); i++) {
+ if (kernelAudio::getDeviceName(device) == NULL)
+ continue;
+ if (m->text(i) == NULL)
+ continue;
+ if (strcmp(m->text(i), kernelAudio::getDeviceName(device))==0)
+ return i;
+ }
+
+ return 0;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTabAudio::fetchSoundDevs()
+{
+ if (kernelAudio::numDevs == 0) {
+ sounddevOut->add("-- no devices found --");
+ sounddevOut->value(0);
+ sounddevIn->add("-- no devices found --");
+ sounddevIn->value(0);
+ devInInfo ->deactivate();
+ devOutInfo->deactivate();
+ }
+ else {
+
+ devInInfo ->activate();
+ devOutInfo->activate();
+
+ /* input device may be disabled: now device number -1 is the disabled
+ * one. KernelAudio knows how to handle it. */
+
+ sounddevIn->add("(disabled)");
+
+ for (unsigned i=0; i<kernelAudio::numDevs; i++) {
+
+ /* escaping '/', very dangerous in FLTK (it creates a submenu) */
+
+ std::string tmp = kernelAudio::getDeviceName(i);
+ for (unsigned k=0; k<tmp.size(); k++)
+ if (tmp[k] == '/' || tmp[k] == '|' || tmp[k] == '&' || tmp[k] == '_')
+ tmp[k] = '-';
+
+ /* add to list devices with at least 1 channel available. In this
+ * way we can filter devices only for input or output, e.g. an input
+ * devices has 0 output channels. */
+
+ if (kernelAudio::getMaxOutChans(i) > 0)
+ sounddevOut->add(tmp.c_str());
+
+ if (kernelAudio::getMaxInChans(i) > 0)
+ sounddevIn->add(tmp.c_str());
+ }
+
+ /* we show the device saved in the configuration file. */
+
+ if (sounddevOut->size() == 0) {
+ sounddevOut->add("-- no devices found --");
+ sounddevOut->value(0);
+ devOutInfo->deactivate();
+ }
+ else {
+ int outMenuValue = findMenuDevice(sounddevOut, G_Conf.soundDeviceOut);
+ sounddevOut->value(outMenuValue);
+ }
+
+ if (sounddevIn->size() == 0) {
+ sounddevIn->add("-- no devices found --");
+ sounddevIn->value(0);
+ devInInfo->deactivate();
+ }
+ else {
+ int inMenuValue = findMenuDevice(sounddevIn, G_Conf.soundDeviceIn);
+ sounddevIn->value(inMenuValue);
+ }
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTabAudio::save()
+{
+ /** FIXME - wrong, if API is missing! Right way in gTabMidi::save */
+
+#ifdef __linux__
+ if (soundsys->value() == 0) G_Conf.soundSystem = SYS_API_ALSA;
+ else if (soundsys->value() == 1) G_Conf.soundSystem = SYS_API_JACK;
+ else if (soundsys->value() == 2) G_Conf.soundSystem = SYS_API_PULSE;
+#else
+#ifdef _WIN32
+ if (soundsys->value() == 0) G_Conf.soundSystem = SYS_API_DS;
+ else if (soundsys->value() == 1) G_Conf.soundSystem = SYS_API_ASIO;
+#endif
+#endif
+
+ /* use the device name to search into the drop down menu's */
+
+ G_Conf.soundDeviceOut = kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value()));
+ G_Conf.soundDeviceIn = kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value()));
+ G_Conf.channelsOut = channelsOut->value();
+ G_Conf.channelsIn = channelsIn->value();
+ G_Conf.limitOutput = limitOutput->value();
+ G_Conf.rsmpQuality = rsmpQuality->value();
+
+ /* if sounddevOut is disabled (because of system change e.g. alsa ->
+ * jack) its value is equal to -1. Change it! */
+
+ if (G_Conf.soundDeviceOut == -1)
+ G_Conf.soundDeviceOut = 0;
+
+ int bufsize = atoi(buffersize->text());
+ if (bufsize % 2 != 0) bufsize++;
+ if (bufsize < 8) bufsize = 8;
+ if (bufsize > 8192) bufsize = 8192;
+ G_Conf.buffersize = bufsize;
+
+ const Fl_Menu_Item *i = NULL;
+ i = samplerate->mvalue(); // mvalue() returns a pointer to the last menu item that was picked
+ if (i)
+ G_Conf.samplerate = atoi(i->label());
+
+ int _delayComp = atoi(delayComp->value());
+ if (_delayComp < 0) _delayComp = 0;
+ G_Conf.delayComp = _delayComp;
+}
+
+
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+
+
+gTabMidi::gTabMidi(int X, int Y, int W, int H)
+ : Fl_Group(X, Y, W, H, "MIDI")
+{
+ begin();
+ system = new gChoice(x()+92, y()+9, 253, 20, "System");
+ portOut = new gChoice(x()+92, system->y()+system->h()+8, 253, 20, "Output port");
+ portIn = new gChoice(x()+92, portOut->y()+portOut->h()+8, 253, 20, "Input port");
+ noNoteOff = new gCheck (x()+92, portIn->y()+portIn->h()+8, 253, 20, "Device does not send NoteOff");
+ sync = new gChoice(x()+92, noNoteOff->y()+noNoteOff->h(), 253, 20, "Sync");
+ new gBox(x(), sync->y()+sync->h()+8, w(), h()-125, "Restart Giada for the changes to take effect.");
+ end();
+
+ labelsize(11);
+
+ system->callback(cb_changeSystem, (void*)this);
+
+ fetchSystems();
+ fetchOutPorts();
+ fetchInPorts();
+
+ noNoteOff->value(G_Conf.noNoteOff);
+
+ sync->add("(disabled)");
+ sync->add("MIDI Clock (master)");
+ sync->add("MTC (master)");
+ if (G_Conf.midiSync == MIDI_SYNC_NONE)
+ sync->value(0);
+ else if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M)
+ sync->value(1);
+ else if (G_Conf.midiSync == MIDI_SYNC_MTC_M)
+ sync->value(2);
+
+ systemInitValue = system->value();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTabMidi::fetchOutPorts() {
+
+ if (kernelMidi::numOutPorts == 0) {
+ portOut->add("-- no ports found --");
+ portOut->value(0);
+ portOut->deactivate();
+ }
+ else {
+
+ portOut->add("(disabled)");
+
+ for (unsigned i=0; i<kernelMidi::numOutPorts; i++) {
+ char *t = (char*) kernelMidi::getOutPortName(i);
+ for (int k=0; t[k] != '\0'; k++)
+ if (t[k] == '/' || t[k] == '|' || t[k] == '&' || t[k] == '_')
+ t[k] = '-';
+ portOut->add(t);
+ }
+
+ portOut->value(G_Conf.midiPortOut+1); // +1 because midiPortOut=-1 is '(disabled)'
+ }
+}
+
+/* ------------------------------------------------------------------ */
+
+
+void gTabMidi::fetchInPorts()
+{
+ if (kernelMidi::numInPorts == 0) {
+ portIn->add("-- no ports found --");
+ portIn->value(0);
+ portIn->deactivate();
+ }
+ else {
+
+ portIn->add("(disabled)");
+
+ for (unsigned i=0; i<kernelMidi::numInPorts; i++) {
+ char *t = (char*) kernelMidi::getInPortName(i);
+ for (int k=0; t[k] != '\0'; k++)
+ if (t[k] == '/' || t[k] == '|' || t[k] == '&' || t[k] == '_')
+ t[k] = '-';
+ portIn->add(t);
+ }
+
+ portIn->value(G_Conf.midiPortIn+1); // +1 because midiPortIn=-1 is '(disabled)'
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTabMidi::save()
+{
+ if (!strcmp("ALSA", system->text(system->value())))
+ G_Conf.midiSystem = RtMidi::LINUX_ALSA;
+ else if (!strcmp("Jack", system->text(system->value())))
+ G_Conf.midiSystem = RtMidi::UNIX_JACK;
+ else if (!strcmp("Multimedia MIDI", system->text(system->value())))
+ G_Conf.midiSystem = RtMidi::WINDOWS_MM;
+ //else if (!strcmp("Kernel Streaming MIDI", system->text(system->value())))
+ // G_Conf.midiSystem = RtMidi::WINDOWS_KS;
+ else if (!strcmp("OSX Core MIDI", system->text(system->value())))
+ G_Conf.midiSystem = RtMidi::MACOSX_CORE;
+
+ G_Conf.midiPortOut = portOut->value()-1; // -1 because midiPortOut=-1 is '(disabled)'
+ G_Conf.midiPortIn = portIn->value()-1; // -1 because midiPortIn=-1 is '(disabled)'
+
+ G_Conf.noNoteOff = noNoteOff->value();
+
+ if (sync->value() == 0)
+ G_Conf.midiSync = MIDI_SYNC_NONE;
+ else if (sync->value() == 1)
+ G_Conf.midiSync = MIDI_SYNC_CLOCK_M;
+ else if (sync->value() == 2)
+ G_Conf.midiSync = MIDI_SYNC_MTC_M;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTabMidi::fetchSystems()
+{
+#if defined(__linux__)
+
+ if (kernelMidi::hasAPI(RtMidi::LINUX_ALSA))
+ system->add("ALSA");
+ if (kernelMidi::hasAPI(RtMidi::UNIX_JACK))
+ system->add("Jack");
+
+#elif defined(_WIN32)
+
+ if (kernelMidi::hasAPI(RtMidi::WINDOWS_MM))
+ system->add("Multimedia MIDI");
+ //if (kernelMidi::hasAPI(RtMidi::WINDOWS_KS))
+ // system->add("Kernel Streaming MIDI");
+
+#elif defined (__APPLE__)
+
+ system->add("OSX Core MIDI");
+
+#endif
+
+ switch (G_Conf.midiSystem) {
+ case RtMidi::LINUX_ALSA: system->show("ALSA"); break;
+ case RtMidi::UNIX_JACK: system->show("Jack"); break;
+ case RtMidi::WINDOWS_MM: system->show("Multimedia MIDI"); break;
+ //case RtMidi::WINDOWS_KS: system->show("Kernel Streaming MIDI"); break;
+ case RtMidi::MACOSX_CORE: system->show("OSX Core MIDI"); break;
+ default: system->value(0); break;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTabMidi::cb_changeSystem(Fl_Widget *w, void *p) { ((gTabMidi*)p)->__cb_changeSystem(); }
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTabMidi::__cb_changeSystem()
+{
+ /* if the user changes MIDI device (eg ALSA->JACK) device menu deactivates.
+ * If it returns to the original system, we re-fill the list by
+ * querying kernelMidi. */
+
+ if (systemInitValue == system->value()) {
+ portOut->clear();
+ fetchOutPorts();
+ portOut->activate();
+ }
+ else {
+ portOut->deactivate();
+ portOut->clear();
+ portOut->add("-- restart to fetch device(s) --");
+ portOut->value(0);
+ }
+
+}
+
+
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+
+
+gTabBehaviors::gTabBehaviors(int X, int Y, int W, int H)
+ : Fl_Group(X, Y, W, H, "Behaviors")
+{
+ begin();
+ Fl_Group *radioGrp_1 = new Fl_Group(x(), y()+10, w(), 70); // radio group for the mutex
+ new gBox(x(), y()+10, 70, 25, "When a channel with recorded actions is halted:", FL_ALIGN_LEFT);
+ recsStopOnChanHalt_1 = new gRadio(x()+25, y()+35, 280, 20, "stop it immediately");
+ recsStopOnChanHalt_0 = new gRadio(x()+25, y()+55, 280, 20, "play it until finished");
+ radioGrp_1->end();
+
+ Fl_Group *radioGrp_2 = new Fl_Group(x(), y()+70, w(), 70); // radio group for the mutex
+ new gBox(x(), y()+80, 70, 25, "When the sequencer is halted:", FL_ALIGN_LEFT);
+ chansStopOnSeqHalt_1 = new gRadio(x()+25, y()+105, 280, 20, "stop immediately all dynamic channels");
+ chansStopOnSeqHalt_0 = new gRadio(x()+25, y()+125, 280, 20, "play all dynamic channels until finished");
+ radioGrp_2->end();
+
+ treatRecsAsLoops = new gCheck(x(), y()+155, 280, 20, "Treat one shot channels with actions as loops");
+
+ end();
+ labelsize(11);
+
+ G_Conf.recsStopOnChanHalt == 1 ? recsStopOnChanHalt_1->value(1) : recsStopOnChanHalt_0->value(1);
+ G_Conf.chansStopOnSeqHalt == 1 ? chansStopOnSeqHalt_1->value(1) : chansStopOnSeqHalt_0->value(1);
+ G_Conf.treatRecsAsLoops == 1 ? treatRecsAsLoops->value(1) : treatRecsAsLoops->value(0);
+
+ recsStopOnChanHalt_1->callback(cb_radio_mutex, (void*)this);
+ recsStopOnChanHalt_0->callback(cb_radio_mutex, (void*)this);
+ chansStopOnSeqHalt_1->callback(cb_radio_mutex, (void*)this);
+ chansStopOnSeqHalt_0->callback(cb_radio_mutex, (void*)this);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTabBehaviors::cb_radio_mutex(Fl_Widget *w, void *p) { ((gTabBehaviors*)p)->__cb_radio_mutex(w); }
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTabBehaviors::__cb_radio_mutex(Fl_Widget *w)
+{
+ ((Fl_Button *)w)->type(FL_RADIO_BUTTON);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTabBehaviors::save()
+{
+ G_Conf.recsStopOnChanHalt = recsStopOnChanHalt_1->value() == 1 ? 1 : 0;
+ G_Conf.chansStopOnSeqHalt = chansStopOnSeqHalt_1->value() == 1 ? 1 : 0;
+ G_Conf.treatRecsAsLoops = treatRecsAsLoops->value() == 1 ? 1 : 0;
+}
+
+
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+
+
+gdConfig::gdConfig(int w, int h) : gWindow(w, h, "Configuration")
+{
+ set_modal();
+
+ if (G_Conf.configX)
+ resize(G_Conf.configX, G_Conf.configY, this->w(), this->h());
+
+ Fl_Tabs *tabs = new Fl_Tabs(8, 8, w-16, h-44);
+ tabAudio = new gTabAudio(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
+ tabMidi = new gTabMidi(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
+ tabBehaviors = new gTabBehaviors(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
+ tabMisc = new gTabMisc(tabs->x()+10, tabs->y()+20, tabs->w()-20, tabs->h()-40);
+ tabs->end();
+
+ save = new gClick (w-88, h-28, 80, 20, "Save");
+ cancel = new gClick (w-176, h-28, 80, 20, "Cancel");
+
+ end();
+
+ tabs->box(FL_FLAT_BOX); // TODO - G_BOX crashes FLTK 1.3.3
+
+ tabs->labelcolor(COLOR_TEXT_0);
+
+ save->callback(cb_save_config, (void*)this);
+ cancel->callback(cb_cancel, (void*)this);
+
+ gu_setFavicon(this);
+ setId(WID_CONFIG);
+ show();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+gdConfig::~gdConfig()
+{
+ G_Conf.configX = x();
+ G_Conf.configY = y();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdConfig::cb_save_config(Fl_Widget *w, void *p) { ((gdConfig*)p)->__cb_save_config(); }
+void gdConfig::cb_cancel (Fl_Widget *w, void *p) { ((gdConfig*)p)->__cb_cancel(); }
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdConfig::__cb_save_config()
+{
+ tabAudio->save();
+ tabBehaviors->save();
+ tabMidi->save();
+ tabMisc->save();
+ do_callback();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdConfig::__cb_cancel()
+{
+ do_callback();
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_config
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+#ifndef GD_CONFIG_H
+#define GD_CONFIG_H
+
+#include <FL/Fl.H>
+#include <FL/Fl_Double_Window.H>
+#include <FL/Fl_Tabs.H>
+#include "ge_window.h"
+
+
+class gdConfig : public gWindow
+{
+private:
+ static void cb_save_config (Fl_Widget *w, void *p);
+ static void cb_cancel (Fl_Widget *w, void *p);
+ inline void __cb_save_config();
+ inline void __cb_cancel();
+
+public:
+ gdConfig(int w, int h);
+ ~gdConfig();
+
+ class gTabAudio *tabAudio;
+ class gTabBehaviors *tabBehaviors;
+ class gTabMidi *tabMidi;
+ class gTabMisc *tabMisc;
+ class gClick *save;
+ class gClick *cancel;
+};
+
+
+/* ------------------------------------------------------------------ */
+
+
+class gTabMidi : public Fl_Group
+{
+private:
+ void fetchSystems();
+ void fetchOutPorts();
+ void fetchInPorts();
+
+ static void cb_changeSystem (Fl_Widget *w, void *p);
+ inline void __cb_changeSystem();
+
+ int systemInitValue;
+
+public:
+ class gChoice *system;
+ class gChoice *portOut;
+ class gChoice *portIn;
+ class gCheck *noNoteOff;
+ class gChoice *sync;
+
+ gTabMidi(int x, int y, int w, int h);
+
+ void save();
+};
+
+
+/* ------------------------------------------------------------------ */
+
+
+class gTabAudio : public Fl_Group
+{
+private:
+ static void cb_deactivate_sounddev(Fl_Widget *w, void *p);
+ static void cb_fetchInChans (Fl_Widget *w, void *p);
+ static void cb_fetchOutChans (Fl_Widget *w, void *p);
+ static void cb_showInputInfo (Fl_Widget *w, void *p);
+ static void cb_showOutputInfo (Fl_Widget *w, void *p);
+ inline void __cb_deactivate_sounddev();
+ inline void __cb_fetchInChans();
+ inline void __cb_fetchOutChans();
+ inline void __cb_showInputInfo();
+ inline void __cb_showOutputInfo();
+
+ void fetchSoundDevs();
+ void fetchInChans(int menuItem);
+ void fetchOutChans(int menuItem);
+ int findMenuDevice(class gChoice *m, int device);
+
+ int soundsysInitValue;
+
+public:
+ class gChoice *soundsys;
+ class gChoice *samplerate;
+ class gChoice *rsmpQuality;
+ class gChoice *sounddevIn;
+ class gClick *devInInfo;
+ class gChoice *channelsIn;
+ class gChoice *sounddevOut;
+ class gClick *devOutInfo;
+ class gChoice *channelsOut;
+ class gCheck *limitOutput;
+ class gChoice *buffersize;
+ class gInput *delayComp;
+
+ gTabAudio(int x, int y, int w, int h);
+
+ void save();
+};
+
+
+/* ------------------------------------------------------------------ */
+
+
+class gTabBehaviors : public Fl_Group
+{
+private:
+ static void cb_radio_mutex (Fl_Widget *w, void *p);
+ inline void __cb_radio_mutex(Fl_Widget *w);
+
+public:
+ class gRadio *recsStopOnChanHalt_1;
+ class gRadio *recsStopOnChanHalt_0;
+ class gRadio *chansStopOnSeqHalt_1;
+ class gRadio *chansStopOnSeqHalt_0;
+ class gCheck *treatRecsAsLoops;
+
+ gTabBehaviors(int x, int y, int w, int h);
+
+ void save();
+};
+
+
+/* ------------------------------------------------------------------ */
+
+
+class gTabMisc : public Fl_Group
+{
+public:
+ class gChoice *debugMsg;
+
+ gTabMisc(int x, int y, int w, int h);
+
+ void save();
+};
+
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_devInfo
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include "gd_devInfo.h"
+#include "ge_mixed.h"
+#include "kernelAudio.h"
+#include "gui_utils.h"
+
+
+gdDevInfo::gdDevInfo(unsigned dev)
+: Fl_Window(340, 300, "Device information") {
+ set_modal();
+
+ text = new gBox(8, 8, 320, 200, "", (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
+ close = new gClick(252, h()-28, 80, 20, "Close");
+ end();
+
+ std::string bufTxt;
+ char bufNum[128];
+ int lines = 0;
+
+ bufTxt = "Device name: ";
+ bufTxt += +kernelAudio::getDeviceName(dev);
+ bufTxt += "\n";
+ lines++;
+
+ bufTxt += "Total output(s): ";
+ sprintf(bufNum, "%d\n", kernelAudio::getMaxOutChans(dev));
+ bufTxt += bufNum;
+ lines++;
+
+ bufTxt += "Total intput(s): ";
+ sprintf(bufNum, "%d\n", kernelAudio::getMaxInChans(dev));
+ bufTxt += bufNum;
+ lines++;
+
+ bufTxt += "Duplex channel(s): ";
+ sprintf(bufNum, "%d\n", kernelAudio::getDuplexChans(dev));
+ bufTxt += bufNum;
+ lines++;
+
+ bufTxt += "Default output: ";
+ sprintf(bufNum, "%s\n", kernelAudio::isDefaultOut(dev) ? "yes" : "no");
+ bufTxt += bufNum;
+ lines++;
+
+ bufTxt += "Default input: ";
+ sprintf(bufNum, "%s\n", kernelAudio::isDefaultIn(dev) ? "yes" : "no");
+ bufTxt += bufNum;
+ lines++;
+
+ int totalFreq = kernelAudio::getTotalFreqs(dev);
+ bufTxt += "Supported frequencies: ";
+ sprintf(bufNum, "%d", totalFreq);
+ bufTxt += bufNum;
+ lines++;
+
+ for (int i=0; i<totalFreq; i++) {
+ sprintf(bufNum, "%d ", kernelAudio::getFreq(dev, i));
+ if (i%6 == 0) { // new line each X printed freqs AND on the first line (i%0 != 0)
+ bufTxt += "\n ";
+ lines++;
+ }
+ bufTxt += bufNum;
+ }
+
+ text->copy_label(bufTxt.c_str());
+
+ /* resize the window to fit the content. fl_height() returns the height
+ * of a line. fl_height() * total lines + margins + button size */
+
+ resize(x(), y(), w(), lines*fl_height() + 8 + 8 + 8 + 20);
+ close->position(close->x(), lines*fl_height() + 8 + 8);
+
+ close->callback(__cb_window_closer, (void*)this);
+ gu_setFavicon(this);
+ show();
+}
+
+
+gdDevInfo::~gdDevInfo() {}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_devInfo
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef GD_DEV_INFO_H
+#define GD_DEV_INFO_H
+
+#include <FL/Fl.H>
+#include <FL/Fl_Window.H>
+
+
+class gdDevInfo : public Fl_Window {
+private:
+ class gBox *text;
+ class gClick *close;
+
+public:
+ gdDevInfo(unsigned dev);
+ ~gdDevInfo();
+};
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_editor
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include "gd_editor.h"
+#include "gd_mainWindow.h"
+#include "ge_waveform.h"
+#include "gd_warnings.h"
+#include "gg_waveTools.h"
+#include "ge_mixed.h"
+#include "gg_keyboard.h"
+#include "ge_channel.h"
+#include "waveFx.h"
+#include "conf.h"
+#include "graphics.h"
+#include "gui_utils.h"
+#include "glue.h"
+#include "mixerHandler.h"
+#include "channel.h"
+#include "sampleChannel.h"
+#include "mixer.h"
+#include "wave.h"
+
+
+extern Mixer G_Mixer;
+extern gdMainWindow *mainWin;
+extern Conf G_Conf;
+
+
+gdEditor::gdEditor(SampleChannel *ch)
+ : gWindow(640, 480),
+ ch(ch)
+{
+ set_non_modal();
+
+ if (G_Conf.sampleEditorX)
+ resize(G_Conf.sampleEditorX, G_Conf.sampleEditorY, G_Conf.sampleEditorW, G_Conf.sampleEditorH);
+
+ /* top bar: grid and zoom tools */
+
+ Fl_Group *bar = new Fl_Group(8, 8, w()-16, 20);
+ bar->begin();
+ grid = new gChoice(bar->x(), bar->y(), 50, 20);
+ snap = new gCheck(grid->x()+grid->w()+4, bar->y()+4, 12, 12);
+ zoomOut = new gClick(bar->x()+bar->w()-20, bar->y(), 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm);
+ zoomIn = new gClick(zoomOut->x()-24, bar->y(), 20, 20, "", zoomInOff_xpm, zoomInOn_xpm);
+ bar->end();
+ bar->resizable(new gBox(grid->x()+grid->w()+4, bar->y(), 80, bar->h()));
+
+ /* waveform */
+
+ waveTools = new gWaveTools(8, 36, w()-16, h()-120, ch);
+ waveTools->end();
+
+ /* other tools */
+
+ Fl_Group *tools = new Fl_Group(8, waveTools->y()+waveTools->h()+8, w()-16, 130);
+ tools->begin();
+ volume = new gDial (tools->x()+42, tools->y(), 20, 20, "Volume");
+ volumeNum = new gInput(volume->x()+volume->w()+4, tools->y(), 46, 20, "dB");
+
+ boost = new gDial (volumeNum->x()+volumeNum->w()+80, tools->y(), 20, 20, "Boost");
+ boostNum = new gInput(boost->x()+boost->w()+4, tools->y(), 46, 20, "dB");
+
+ normalize = new gClick(boostNum->x()+boostNum->w()+54, tools->y(), 70, 20, "Normalize");
+ pan = new gDial (normalize->x()+normalize->w()+40, tools->y(), 20, 20, "Pan");
+ panNum = new gInput(pan->x()+pan->w()+4, tools->y(), 45, 20, "%");
+
+ pitch = new gDial (tools->x()+42, volume->y()+volume->h()+4, 20, 20, "Pitch");
+ pitchNum = new gInput(pitch->x()+pitch->w()+4, volume->y()+volume->h()+4, 46, 20);
+ pitchToBar = new gClick(pitchNum->x()+pitchNum->w()+4, volume->y()+volume->h()+4, 46, 20, "To bar");
+ pitchToSong = new gClick(pitchToBar->x()+pitchToBar->w()+4, volume->y()+volume->h()+4, 46, 20, "To song");
+ pitchHalf = new gClick(pitchToSong->x()+pitchToSong->w()+4, volume->y()+volume->h()+4, 21, 20, "÷");
+ pitchDouble = new gClick(pitchHalf->x()+pitchHalf->w()+4, volume->y()+volume->h()+4, 21, 20, "×");
+ pitchReset = new gClick(pitchDouble->x()+pitchDouble->w()+4, volume->y()+volume->h()+4, 46, 20, "Reset");
+ reload = new gClick(pitchReset->x()+pitchReset->w()+4, volume->y()+volume->h()+4, 70, 20, "Reload");
+
+ chanStart = new gInput(tools->x()+52, pitch->y()+pitch->h()+4, 60, 20, "Start");
+ chanEnd = new gInput(chanStart->x()+chanStart->w()+40, pitch->y()+pitch->h()+4, 60, 20, "End");
+ resetStartEnd = new gClick(chanEnd->x()+chanEnd->w()+4, pitch->y()+pitch->h()+4, 46, 20, "Reset");
+
+ tools->end();
+ tools->resizable(new gBox(panNum->x()+panNum->w()+4, tools->y(), 80, tools->h()));
+
+ /* grid tool setup */
+
+ grid->add("(off)");
+ grid->add("2");
+ grid->add("3");
+ grid->add("4");
+ grid->add("6");
+ grid->add("8");
+ grid->add("16");
+ grid->add("32");
+ grid->add("64");
+ grid->value(G_Conf.sampleEditorGridVal);
+ grid->callback(cb_changeGrid, (void*)this);
+
+ snap->value(G_Conf.sampleEditorGridOn);
+ snap->callback(cb_enableSnap, (void*)this);
+
+ /* TODO - redraw grid if != (off) */
+
+ char buf[16];
+ sprintf(buf, "%d", ch->begin / 2); // divided by 2 because stereo
+ chanStart->value(buf);
+ chanStart->type(FL_INT_INPUT);
+ chanStart->callback(cb_setChanPos, this);
+
+ /* inputs callback: fire when they lose focus or Enter is pressed. */
+
+ chanStart->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY);
+ chanEnd ->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY);
+
+ sprintf(buf, "%d", ch->end / 2); // divided by 2 because stereo
+ chanEnd->value(buf);
+ chanEnd->type(FL_INT_INPUT);
+ chanEnd->callback(cb_setChanPos, this);
+
+ resetStartEnd->callback(cb_resetStartEnd, this);
+
+ volume->callback(cb_setVolume, (void*)this);
+ volume->value(ch->guiChannel->vol->value());
+
+ float dB = 20*log10(ch->volume); // dB = 20*log_10(linear value)
+ if (dB > -INFINITY) sprintf(buf, "%.2f", dB);
+ else sprintf(buf, "-inf");
+ volumeNum->value(buf);
+ volumeNum->align(FL_ALIGN_RIGHT);
+ volumeNum->callback(cb_setVolumeNum, (void*)this);
+
+ boost->range(1.0f, 10.0f);
+ boost->callback(cb_setBoost, (void*)this);
+ if (ch->boost > 10.f)
+ boost->value(10.0f);
+ else
+ boost->value(ch->boost);
+ boost->when(FL_WHEN_CHANGED | FL_WHEN_RELEASE);
+
+ float boost = 20*log10(ch->boost); // dB = 20*log_10(linear value)
+ sprintf(buf, "%.2f", boost);
+ boostNum->value(buf);
+ boostNum->align(FL_ALIGN_RIGHT);
+ boostNum->callback(cb_setBoostNum, (void*)this);
+
+ normalize->callback(cb_normalize, (void*)this);
+
+ pan->range(0.0f, 2.0f);
+ pan->callback(cb_panning, (void*)this);
+
+ pitch->range(0.01f, 4.0f);
+ pitch->value(ch->pitch);
+ pitch->callback(cb_setPitch, (void*)this);
+ pitch->when(FL_WHEN_RELEASE);
+
+ sprintf(buf, "%.4f", ch->pitch); // 4 digits
+ pitchNum->value(buf);
+ pitchNum->align(FL_ALIGN_RIGHT);
+ pitchNum->callback(cb_setPitchNum, (void*)this);
+ pitchNum->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY);
+
+ pitchToBar->callback(cb_setPitchToBar, (void*)this);
+ pitchToSong->callback(cb_setPitchToSong, (void*)this);
+ pitchHalf->callback(cb_setPitchHalf, (void*)this);
+ pitchDouble->callback(cb_setPitchDouble, (void*)this);
+ pitchReset->callback(cb_resetPitch, (void*)this);
+
+ reload->callback(cb_reload, (void*)this);
+
+ zoomOut->callback(cb_zoomOut, (void*)this);
+ zoomIn->callback(cb_zoomIn, (void*)this);
+
+ /* logical samples (aka takes) cannot be reloaded. So far. */
+
+ if (ch->wave->isLogical)
+ reload->deactivate();
+
+ if (ch->panRight < 1.0f) {
+ char buf[8];
+ sprintf(buf, "%d L", abs((ch->panRight * 100.0f) - 100));
+ pan->value(ch->panRight);
+ panNum->value(buf);
+ }
+ else if (ch->panRight == 1.0f && ch->panLeft == 1.0f) {
+ pan->value(1.0f);
+ panNum->value("C");
+ }
+ else {
+ char buf[8];
+ sprintf(buf, "%d R", abs((ch->panLeft * 100.0f) - 100));
+ pan->value(2.0f - ch->panLeft);
+ panNum->value(buf);
+ }
+
+ panNum->align(FL_ALIGN_RIGHT);
+ panNum->readonly(1);
+ panNum->cursor_color(FL_WHITE);
+
+ gu_setFavicon(this);
+ size_range(640, 480);
+ resizable(waveTools);
+
+ label(ch->wave->name.c_str());
+
+ show();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+gdEditor::~gdEditor()
+{
+ G_Conf.sampleEditorX = x();
+ G_Conf.sampleEditorY = y();
+ G_Conf.sampleEditorW = w();
+ G_Conf.sampleEditorH = h();
+ G_Conf.sampleEditorGridVal = grid->value();
+ G_Conf.sampleEditorGridOn = snap->value();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdEditor::cb_setChanPos (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setChanPos(); }
+void gdEditor::cb_resetStartEnd (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_resetStartEnd(); }
+void gdEditor::cb_setVolume (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setVolume(); }
+void gdEditor::cb_setVolumeNum (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setVolumeNum(); }
+void gdEditor::cb_setBoost (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setBoost(); }
+void gdEditor::cb_setBoostNum (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setBoostNum(); }
+void gdEditor::cb_normalize (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_normalize(); }
+void gdEditor::cb_panning (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_panning(); }
+void gdEditor::cb_reload (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_reload(); }
+void gdEditor::cb_setPitch (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitch(); }
+void gdEditor::cb_setPitchToBar (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchToBar(); }
+void gdEditor::cb_setPitchToSong (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchToSong(); }
+void gdEditor::cb_setPitchHalf (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchHalf(); }
+void gdEditor::cb_setPitchDouble (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchDouble(); }
+void gdEditor::cb_resetPitch (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_resetPitch(); }
+void gdEditor::cb_setPitchNum (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_setPitchNum(); }
+void gdEditor::cb_zoomIn (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_zoomIn(); }
+void gdEditor::cb_zoomOut (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_zoomOut(); }
+void gdEditor::cb_changeGrid (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_changeGrid(); }
+void gdEditor::cb_enableSnap (Fl_Widget *w, void *p) { ((gdEditor*)p)->__cb_enableSnap(); }
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdEditor::__cb_enableSnap()
+{
+ waveTools->waveform->setSnap(!waveTools->waveform->getSnap());
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdEditor::__cb_setPitchToBar()
+{
+ glue_setPitch(this, ch, ch->end/(float)G_Mixer.framesPerBar, true);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdEditor::__cb_setPitchToSong()
+{
+ glue_setPitch(this, ch, ch->end/(float)G_Mixer.totalFrames, true);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdEditor::__cb_resetPitch()
+{
+ glue_setPitch(this, ch, 1.0f, true);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdEditor::__cb_setChanPos()
+{
+ glue_setBeginEndChannel(
+ this,
+ ch,
+ atoi(chanStart->value())*2, // glue halves printed values
+ atoi(chanEnd->value())*2,
+ true
+ );
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdEditor::__cb_resetStartEnd()
+{
+ glue_setBeginEndChannel(this, ch, 0, ch->wave->size, true);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdEditor::__cb_setVolume()
+{
+ glue_setVolEditor(this, ch, volume->value(), false);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdEditor::__cb_setVolumeNum()
+{
+ glue_setVolEditor(this, ch, atof(volumeNum->value()), true);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdEditor::__cb_setBoost()
+{
+ if (Fl::event() == FL_DRAG)
+ glue_setBoost(this, ch, boost->value(), false);
+ else if (Fl::event() == FL_RELEASE) {
+ glue_setBoost(this, ch, boost->value(), false);
+ waveTools->updateWaveform();
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdEditor::__cb_setBoostNum()
+{
+ glue_setBoost(this, ch, atof(boostNum->value()), true);
+ waveTools->updateWaveform();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdEditor::__cb_normalize()
+{
+ float val = wfx_normalizeSoft(ch->wave);
+ glue_setBoost(this, ch, val, false); // we pretend that a fake user turns the dial (numeric=false)
+ if (val < 0.0f)
+ boost->value(0.0f);
+ else
+ if (val > 20.0f) // a dial > than it's max value goes crazy
+ boost->value(10.0f);
+ else
+ boost->value(val);
+ waveTools->updateWaveform();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdEditor::__cb_panning()
+{
+ glue_setPanning(this, ch, pan->value());
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdEditor::__cb_reload()
+{
+ if (!gdConfirmWin("Warning", "Reload sample: are you sure?"))
+ return;
+
+ /* no need for glue_loadChan, there's no gui to refresh */
+
+ ch->load(ch->wave->pathfile.c_str());
+
+ glue_setBoost(this, ch, DEFAULT_BOOST, true);
+ glue_setPitch(this, ch, gDEFAULT_PITCH, true);
+ glue_setPanning(this, ch, 1.0f);
+ pan->value(1.0f); // glue_setPanning doesn't do it
+ pan->redraw(); // glue_setPanning doesn't do it
+
+ waveTools->waveform->stretchToWindow();
+ waveTools->updateWaveform();
+
+ glue_setBeginEndChannel(this, ch, 0, ch->wave->size, true);
+
+ redraw();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdEditor::__cb_setPitch()
+{
+ glue_setPitch(this, ch, pitch->value(), false);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdEditor::__cb_setPitchNum()
+{
+ glue_setPitch(this, ch, atof(pitchNum->value()), true);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdEditor::__cb_setPitchHalf()
+{
+ glue_setPitch(this, ch, pitch->value()/2, true);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdEditor::__cb_setPitchDouble()
+{
+ glue_setPitch(this, ch, pitch->value()*2, true);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdEditor::__cb_zoomIn()
+{
+ waveTools->waveform->setZoom(-1);
+ waveTools->redraw();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdEditor::__cb_zoomOut()
+{
+ waveTools->waveform->setZoom(0);
+ waveTools->redraw();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdEditor::__cb_changeGrid()
+{
+ waveTools->waveform->setGridLevel(atoi(grid->text()));
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_editor
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+#ifndef GD_EDITOR_H
+#define GD_EDITOR_H
+
+#include <FL/Fl.H>
+#include <FL/Fl_Double_Window.H>
+#include <math.h>
+#include "ge_window.h"
+
+
+class gdEditor : public gWindow {
+
+private:
+
+ static void cb_setChanPos (Fl_Widget *w, void *p);
+ static void cb_resetStartEnd (Fl_Widget *w, void *p);
+ static void cb_setVolume (Fl_Widget *w, void *p);
+ static void cb_setVolumeNum (Fl_Widget *w, void *p);
+ static void cb_setBoost (Fl_Widget *w, void *p);
+ static void cb_setBoostNum (Fl_Widget *w, void *p);
+ static void cb_normalize (Fl_Widget *w, void *p);
+ static void cb_panning (Fl_Widget *w, void *p);
+ static void cb_reload (Fl_Widget *w, void *p);
+ static void cb_setPitch (Fl_Widget *w, void *p);
+ static void cb_setPitchToBar (Fl_Widget *w, void *p);
+ static void cb_setPitchToSong(Fl_Widget *w, void *p);
+ static void cb_setPitchHalf (Fl_Widget *w, void *p);
+ static void cb_setPitchDouble(Fl_Widget *w, void *p);
+ static void cb_resetPitch (Fl_Widget *w, void *p);
+ static void cb_setPitchNum (Fl_Widget *w, void *p);
+ static void cb_zoomIn (Fl_Widget *w, void *p);
+ static void cb_zoomOut (Fl_Widget *w, void *p);
+ static void cb_changeGrid (Fl_Widget *w, void *p);
+ static void cb_enableSnap (Fl_Widget *w, void *p);
+ inline void __cb_setChanPos();
+ inline void __cb_resetStartEnd();
+ inline void __cb_setVolume();
+ inline void __cb_setVolumeNum();
+ inline void __cb_setBoost();
+ inline void __cb_setBoostNum();
+ inline void __cb_normalize();
+ inline void __cb_panning();
+ inline void __cb_reload();
+ inline void __cb_setPitch();
+ inline void __cb_setPitchToBar();
+ inline void __cb_setPitchToSong();
+ inline void __cb_setPitchHalf();
+ inline void __cb_setPitchDouble();
+ inline void __cb_resetPitch();
+ inline void __cb_setPitchNum();
+ inline void __cb_zoomIn();
+ inline void __cb_zoomOut();
+ inline void __cb_changeGrid();
+ inline void __cb_enableSnap();
+
+public:
+
+ gdEditor(class SampleChannel *ch);
+ ~gdEditor();
+
+ class gClick *zoomIn;
+ class gClick *zoomOut;
+ class gWaveTools *waveTools;
+ class gInput *chanStart;
+ class gInput *chanEnd;
+ class gClick *resetStartEnd;
+ class gDial *volume;
+ class gInput *volumeNum;
+ class gDial *boost;
+ class gInput *boostNum;
+ class gClick *normalize;
+ class gDial *pan;
+ class gInput *panNum;
+ class gClick *reload;
+ class gDial *pitch;
+ class gInput *pitchNum;
+ class gClick *pitchToBar;
+ class gClick *pitchToSong;
+ class gClick *pitchHalf;
+ class gClick *pitchDouble;
+ class gClick *pitchReset;
+ class gClick *close;
+ class gChoice *grid;
+ class gCheck *snap;
+
+ class SampleChannel *ch;
+};
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_keyGrabber
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "gd_keyGrabber.h"
+#include "gg_keyboard.h"
+#include "ge_mixed.h"
+#include "gd_config.h"
+#include "ge_channel.h"
+#include "gd_mainWindow.h"
+#include "gui_utils.h"
+#include "conf.h"
+#include "channel.h"
+#include "sampleChannel.h"
+#include "midiChannel.h"
+#include "log.h"
+
+
+extern Conf G_Conf;
+extern gdMainWindow *mainWin;
+
+
+gdKeyGrabber::gdKeyGrabber(Channel *ch)
+ : gWindow(300, 126, "Key configuration"), ch(ch)
+{
+ set_modal();
+ text = new gBox(8, 8, 284, 80, "");
+ clear = new gClick(w()-88, text->y()+text->h()+8, 80, 20, "Clear");
+ cancel = new gClick(clear->x()-88, clear->y(), 80, 20, "Close");
+ end();
+
+ clear->callback(cb_clear, (void*)this);
+ cancel->callback(cb_cancel, (void*)this);
+
+ updateText(ch->key);
+
+ gu_setFavicon(this);
+ show();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdKeyGrabber::cb_clear (Fl_Widget *w, void *p) { ((gdKeyGrabber*)p)->__cb_clear(); }
+void gdKeyGrabber::cb_cancel(Fl_Widget *w, void *p) { ((gdKeyGrabber*)p)->__cb_cancel(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdKeyGrabber::__cb_cancel()
+{
+ do_callback();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdKeyGrabber::__cb_clear()
+{
+ updateText(0);
+ setButtonLabel(0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdKeyGrabber::setButtonLabel(int key)
+{
+ char tmp[2]; sprintf(tmp, "%c", key);
+ ch->guiChannel->button->copy_label(tmp);
+ ch->key = key;
+}
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdKeyGrabber::updateText(int key)
+{
+ char tmp2[64];
+ if (key != 0)
+ sprintf(tmp2, "Press a key.\n\nCurrent binding: %c", key);
+ else
+ sprintf(tmp2, "Press a key.\n\nCurrent binding: [none]");
+ text->copy_label(tmp2);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gdKeyGrabber::handle(int e)
+{
+ int ret = Fl_Group::handle(e);
+ switch(e) {
+ case FL_KEYUP: {
+ int x = Fl::event_key();
+ if (strlen(Fl::event_text()) != 0
+ && x != FL_BackSpace
+ && x != FL_Enter
+ && x != FL_Delete
+ && x != FL_Tab
+ && x != FL_End
+ && x != ' ')
+ {
+ gLog("set key '%c' (%d) for channel %d\n", x, x, ch->index);
+ setButtonLabel(x);
+ updateText(x);
+ break;
+ }
+ else
+ gLog("invalid key\n");
+ }
+ }
+ return(ret);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_keyGrabber
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GD_KEYGRABBER_H
+#define GD_KEYGRABBER_H
+
+
+#include <FL/Fl.H>
+#include "ge_window.h"
+
+
+class gdKeyGrabber : public gWindow
+{
+private:
+
+ class Channel *ch;
+
+ class gBox *text;
+ class gClick *clear;
+ class gClick *cancel;
+
+ static void cb_clear (Fl_Widget *w, void *p);
+ static void cb_cancel(Fl_Widget *w, void *p);
+ inline void __cb_clear ();
+ inline void __cb_cancel();
+
+ void setButtonLabel(int key);
+
+ void updateText(int key);
+
+public:
+
+ gdKeyGrabber(class Channel *ch);
+ int handle(int e);
+};
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_mainWindow
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifdef __linux__
+ #include <sys/stat.h> // for mkdir
+#endif
+
+#include "gd_mainWindow.h"
+#include "gd_warnings.h"
+#include "gd_bpmInput.h"
+#include "gd_beatsInput.h"
+#include "gd_midiGrabber.h"
+#include "gg_keyboard.h"
+#include "gd_about.h"
+#include "gd_config.h"
+#include "gd_browser.h"
+#include "graphics.h"
+#include "glue.h"
+#include "mixer.h"
+#include "recorder.h"
+#include "mixerHandler.h"
+#include "pluginHost.h"
+#include "channel.h"
+#include "sampleChannel.h"
+#include "init.h"
+#include "patch.h"
+#include "conf.h"
+
+#ifdef WITH_VST
+#include "gd_pluginList.h"
+#endif
+
+
+extern Mixer G_Mixer;
+extern Patch G_Patch;
+extern Conf G_Conf;
+extern gdMainWindow *mainWin;
+extern bool G_quit;
+extern bool G_audio_status;
+
+#if defined(WITH_VST)
+extern PluginHost G_PluginHost;
+#endif
+
+
+gdMainWindow::gdMainWindow(int W, int H, const char *title, int argc, char **argv)
+ : gWindow(W, H, title)
+{
+ Fl::visible_focus(0);
+ Fl::background(25, 25, 25);
+ Fl::set_boxtype(G_BOX, gDrawBox, 1, 1, 2, 2); // custom box G_BOX
+
+ size_range(GUI_WIDTH, GUI_HEIGHT);
+
+ menu = new gMenu(8, -1);
+ inOut = new gInOut(408, 8);
+ controller = new gController(8, 39);
+ timing = new gTiming(632, 39);
+ beatMeter = new gBeatMeter(100, 83, 609, 20);
+ keyboard = new gKeyboard(8, 122, w()-16, 380);
+
+ /* zone 1 - menus, and I/O tools */
+
+ Fl_Group *zone1 = new Fl_Group(8, 8, W-16, 20);
+ zone1->add(menu);
+ zone1->resizable(new Fl_Box(300, 8, 80, 20));
+ zone1->add(inOut);
+
+ /* zone 2 - controller and timing tools */
+
+ Fl_Group *zone2 = new Fl_Group(8, controller->y(), W-16, controller->h());
+ zone2->add(controller);
+ zone2->resizable(new Fl_Box(controller->x()+controller->w()+4, zone2->y(), 80, 20));
+ zone2->add(timing);
+
+ /* zone 3 - beat meter */
+
+ Fl_Group *zone3 = new Fl_Group(8, beatMeter->y(), W-16, beatMeter->h());
+ zone3->add(beatMeter);
+
+ /* zone 4 - the keyboard (Fl_Group is unnecessary here, keyboard is
+ * a group by itself) */
+
+ resizable(keyboard);
+
+ add(zone1);
+ add(zone2);
+ add(zone3);
+ add(keyboard);
+ callback(cb_endprogram);
+ gu_setFavicon(this);
+ show(argc, argv);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdMainWindow::cb_endprogram(Fl_Widget *v, void *p) { mainWin->__cb_endprogram(); }
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdMainWindow::__cb_endprogram()
+{
+ if (!gdConfirmWin("Warning", "Quit Giada: are you sure?"))
+ return;
+ init_shutdown();
+ hide();
+ delete this;
+}
+
+
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+
+
+gInOut::gInOut(int x, int y)
+ : Fl_Group(x, y, 394, 20)
+{
+ begin();
+
+#if defined(WITH_VST)
+ masterFxIn = new gFxButton (x, y, 20, 20, fxOff_xpm, fxOn_xpm);
+ inVol = new gDial (masterFxIn->x()+masterFxIn->w()+4, y, 20, 20);
+ inMeter = new gSoundMeter(inVol->x()+inVol->w()+4, y+5, 140, 10);
+ inToOut = new gClick (inMeter->x()+inMeter->w()+4, y+5, 10, 10, "");
+ outMeter = new gSoundMeter(inToOut->x()+inToOut->w()+4, y+5, 140, 10);
+ outVol = new gDial (outMeter->x()+outMeter->w()+4, y, 20, 20);
+ masterFxOut = new gFxButton (outVol->x()+outVol->w()+4, y, 20, 20, fxOff_xpm, fxOn_xpm);
+#else
+ inVol = new gDial (x+62, y, 20, 20);
+ inMeter = new gSoundMeter(inVol->x()+inVol->w()+4, y+5, 140, 10);
+ outMeter = new gSoundMeter(inMeter->x()+inMeter->w()+4, y+5, 140, 10);
+ outVol = new gDial (outMeter->x()+outMeter->w()+4, y, 20, 20);
+#endif
+
+ end();
+
+ resizable(NULL); // don't resize any widget
+
+ outVol->callback(cb_outVol, (void*)this);
+ outVol->value(G_Mixer.outVol);
+ inVol->callback(cb_inVol, (void*)this);
+ inVol->value(G_Mixer.inVol);
+
+#ifdef WITH_VST
+ masterFxOut->callback(cb_masterFxOut, (void*)this);
+ masterFxIn->callback(cb_masterFxIn, (void*)this);
+ inToOut->callback(cb_inToOut, (void*)this);
+ inToOut->type(FL_TOGGLE_BUTTON);
+#endif
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gInOut::cb_outVol (Fl_Widget *v, void *p) { ((gInOut*)p)->__cb_outVol(); }
+void gInOut::cb_inVol (Fl_Widget *v, void *p) { ((gInOut*)p)->__cb_inVol(); }
+#ifdef WITH_VST
+void gInOut::cb_masterFxOut(Fl_Widget *v, void *p) { ((gInOut*)p)->__cb_masterFxOut(); }
+void gInOut::cb_masterFxIn (Fl_Widget *v, void *p) { ((gInOut*)p)->__cb_masterFxIn(); }
+void gInOut::cb_inToOut (Fl_Widget *v, void *p) { ((gInOut*)p)->__cb_inToOut(); }
+#endif
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gInOut::__cb_outVol()
+{
+ glue_setOutVol(outVol->value());
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gInOut::__cb_inVol()
+{
+ glue_setInVol(inVol->value());
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+#ifdef WITH_VST
+void gInOut::__cb_masterFxOut()
+{
+ gu_openSubWindow(mainWin, new gdPluginList(PluginHost::MASTER_OUT), WID_FX_LIST);
+}
+
+void gInOut::__cb_masterFxIn()
+{
+ gu_openSubWindow(mainWin, new gdPluginList(PluginHost::MASTER_IN), WID_FX_LIST);
+}
+
+void gInOut::__cb_inToOut()
+{
+ G_Mixer.inToOut = inToOut->value();
+}
+#endif
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gInOut::refresh()
+{
+ outMeter->mixerPeak = G_Mixer.peakOut;
+ inMeter->mixerPeak = G_Mixer.peakIn;
+ outMeter->redraw();
+ inMeter->redraw();
+}
+
+
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+
+
+gMenu::gMenu(int x, int y)
+ : Fl_Group(x, y, 300, 20)
+{
+ begin();
+
+ file = new gClick(x, y, 70, 21, "file");
+ edit = new gClick(file->x()+file->w()+4, y, 70, 21, "edit");
+ config = new gClick(edit->x()+edit->w()+4, y, 70, 21, "config");
+ about = new gClick(config->x()+config->w()+4, y, 70, 21, "about");
+
+ end();
+
+ resizable(NULL); // don't resize any widget
+
+ about->callback(cb_about, (void*)this);
+ file->callback(cb_file, (void*)this);
+ edit->callback(cb_edit, (void*)this);
+ config->callback(cb_config, (void*)this);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gMenu::cb_about (Fl_Widget *v, void *p) { ((gMenu*)p)->__cb_about(); }
+void gMenu::cb_config(Fl_Widget *v, void *p) { ((gMenu*)p)->__cb_config(); }
+void gMenu::cb_file (Fl_Widget *v, void *p) { ((gMenu*)p)->__cb_file(); }
+void gMenu::cb_edit (Fl_Widget *v, void *p) { ((gMenu*)p)->__cb_edit(); }
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gMenu::__cb_about()
+{
+ gu_openSubWindow(mainWin, new gdAbout(), WID_ABOUT);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gMenu::__cb_config()
+{
+ gu_openSubWindow(mainWin, new gdConfig(380, 370), WID_CONFIG);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gMenu::__cb_file()
+{
+ /* An Fl_Menu_Button is made of many Fl_Menu_Item */
+
+ Fl_Menu_Item menu[] = {
+ {"Open patch or project..."},
+ {"Save patch..."},
+ {"Save project..."},
+ {"Quit Giada"},
+ {0}
+ };
+
+ Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50);
+ b->box(G_BOX);
+ b->textsize(11);
+ b->textcolor(COLOR_TEXT_0);
+ b->color(COLOR_BG_0);
+
+ const Fl_Menu_Item *m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
+ if (!m) return;
+
+
+ if (strcmp(m->label(), "Open patch or project...") == 0) {
+ gWindow *childWin = new gdBrowser("Load Patch", G_Conf.patchPath, 0, BROWSER_LOAD_PATCH);
+ gu_openSubWindow(mainWin, childWin, WID_FILE_BROWSER);
+ return;
+ }
+ if (strcmp(m->label(), "Save patch...") == 0) {
+ if (G_Mixer.hasLogicalSamples() || G_Mixer.hasEditedSamples())
+ if (!gdConfirmWin("Warning", "You should save a project in order to store\nyour takes and/or processed samples."))
+ return;
+ gWindow *childWin = new gdBrowser("Save Patch", G_Conf.patchPath, 0, BROWSER_SAVE_PATCH);
+ gu_openSubWindow(mainWin, childWin, WID_FILE_BROWSER);
+ return;
+ }
+ if (strcmp(m->label(), "Save project...") == 0) {
+ gWindow *childWin = new gdBrowser("Save Project", G_Conf.patchPath, 0, BROWSER_SAVE_PROJECT);
+ gu_openSubWindow(mainWin, childWin, WID_FILE_BROWSER);
+ return;
+ }
+ if (strcmp(m->label(), "Quit Giada") == 0) {
+ mainWin->do_callback();
+ return;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gMenu::__cb_edit()
+{
+ Fl_Menu_Item menu[] = {
+ {"Clear all samples"},
+ {"Clear all actions"},
+ {"Remove empty columns"},
+ {"Reset to init state"},
+ {"Setup global MIDI input..."},
+ {0}
+ };
+
+ /* clear all actions disabled if no recs, clear all samples disabled
+ * if no samples. */
+
+ menu[1].deactivate();
+
+ for (unsigned i=0; i<G_Mixer.channels.size; i++)
+ if (G_Mixer.channels.at(i)->hasActions) {
+ menu[1].activate();
+ break;
+ }
+ for (unsigned i=0; i<G_Mixer.channels.size; i++)
+ if (G_Mixer.channels.at(i)->type == CHANNEL_SAMPLE)
+ if (((SampleChannel*)G_Mixer.channels.at(i))->wave != NULL) {
+ menu[0].activate();
+ break;
+ }
+
+ Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50);
+ b->box(G_BOX);
+ b->textsize(11);
+ b->textcolor(COLOR_TEXT_0);
+ b->color(COLOR_BG_0);
+
+ const Fl_Menu_Item *m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
+ if (!m) return;
+
+ if (strcmp(m->label(), "Clear all samples") == 0) {
+ if (!gdConfirmWin("Warning", "Clear all samples: are you sure?"))
+ return;
+ mainWin->delSubWindow(WID_SAMPLE_EDITOR);
+ glue_clearAllSamples();
+ return;
+ }
+ if (strcmp(m->label(), "Clear all actions") == 0) {
+ if (!gdConfirmWin("Warning", "Clear all actions: are you sure?"))
+ return;
+ mainWin->delSubWindow(WID_ACTION_EDITOR);
+ glue_clearAllRecs();
+ return;
+ }
+ if (strcmp(m->label(), "Reset to init state") == 0) {
+ if (!gdConfirmWin("Warning", "Reset to init state: are you sure?"))
+ return;
+ gu_closeAllSubwindows();
+ glue_resetToInitState();
+ return;
+ }
+ if (strcmp(m->label(), "Remove empty columns") == 0) {
+ mainWin->keyboard->organizeColumns();
+ return;
+ }
+ if (strcmp(m->label(), "Setup global MIDI input...") == 0) {
+ gu_openSubWindow(mainWin, new gdMidiGrabberMaster(), 0);
+ return;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+
+
+gController::gController(int x, int y)
+ : Fl_Group(x, y, 131, 25)
+{
+ begin();
+
+ rewind = new gClick(x, y, 25, 25, "", rewindOff_xpm, rewindOn_xpm);
+ play = new gClick(rewind->x()+rewind->w()+4, y, 25, 25, "", play_xpm, pause_xpm);
+ recAction = new gClick(play->x()+play->w()+4, y, 25, 25, "", recOff_xpm, recOn_xpm);
+ recInput = new gClick(recAction->x()+recAction->w()+4, y, 25, 25, "", inputRecOff_xpm, inputRecOn_xpm);
+ metronome = new gClick(recInput->x()+recInput->w()+4, y+10, 15, 15, "", metronomeOff_xpm, metronomeOn_xpm);
+
+ end();
+
+ resizable(NULL); // don't resize any widget
+
+ rewind->callback(cb_rewind, (void*)this);
+
+ play->callback(cb_play);
+ play->type(FL_TOGGLE_BUTTON);
+
+ recAction->callback(cb_recAction, (void*)this);
+ recAction->type(FL_TOGGLE_BUTTON);
+
+ recInput->callback(cb_recInput, (void*)this);
+ recInput->type(FL_TOGGLE_BUTTON);
+
+ metronome->callback(cb_metronome);
+ metronome->type(FL_TOGGLE_BUTTON);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gController::cb_rewind (Fl_Widget *v, void *p) { ((gController*)p)->__cb_rewind(); }
+void gController::cb_play (Fl_Widget *v, void *p) { ((gController*)p)->__cb_play(); }
+void gController::cb_recAction(Fl_Widget *v, void *p) { ((gController*)p)->__cb_recAction(); }
+void gController::cb_recInput (Fl_Widget *v, void *p) { ((gController*)p)->__cb_recInput(); }
+void gController::cb_metronome(Fl_Widget *v, void *p) { ((gController*)p)->__cb_metronome(); }
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gController::__cb_rewind()
+{
+ glue_rewindSeq();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gController::__cb_play()
+{
+ glue_startStopSeq();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gController::__cb_recAction()
+{
+ glue_startStopActionRec();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gController::__cb_recInput()
+{
+ glue_startStopInputRec();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gController::__cb_metronome()
+{
+ glue_startStopMetronome();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gController::updatePlay(int v)
+{
+ play->value(v);
+ play->redraw();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gController::updateMetronome(int v)
+{
+ metronome->value(v);
+ metronome->redraw();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gController::updateRecInput(int v)
+{
+ recInput->value(v);
+ recInput->redraw();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gController::updateRecAction(int v)
+{
+ recAction->value(v);
+ recAction->redraw();
+}
+
+
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+
+
+gTiming::gTiming(int x, int y)
+ : Fl_Group(x, y, 170, 15)
+{
+ begin();
+
+ quantizer = new gChoice(x, y, 40, 15, "", false);
+ bpm = new gClick (quantizer->x()+quantizer->w()+4, y, 40, 15);
+ meter = new gClick (bpm->x()+bpm->w()+8, y, 40, 15, "4/1");
+ multiplier = new gClick (meter->x()+meter->w()+4, y, 15, 15, "", beatsMultiplyOff_xpm, beatsMultiplyOn_xpm);
+ divider = new gClick (multiplier->x()+multiplier->w()+4, y, 15, 15, "÷", beatsDivideOff_xpm, beatsDivideOn_xpm);
+
+ end();
+
+ resizable(NULL); // don't resize any widget
+
+ char buf[6]; snprintf(buf, 6, "%f", G_Mixer.bpm);
+ bpm->copy_label(buf);
+
+ bpm->callback(cb_bpm, (void*)this);
+ meter->callback(cb_meter, (void*)this);
+ multiplier->callback(cb_multiplier, (void*)this);
+ divider->callback(cb_divider, (void*)this);
+
+ quantizer->add("off", 0, cb_quantizer, (void*)this);
+ quantizer->add("1b", 0, cb_quantizer, (void*)this);
+ quantizer->add("2b", 0, cb_quantizer, (void*)this);
+ quantizer->add("3b", 0, cb_quantizer, (void*)this);
+ quantizer->add("4b", 0, cb_quantizer, (void*)this);
+ quantizer->add("6b", 0, cb_quantizer, (void*)this);
+ quantizer->add("8b", 0, cb_quantizer, (void*)this);
+ quantizer->value(0); // "off" by default
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTiming::cb_bpm (Fl_Widget *v, void *p) { ((gTiming*)p)->__cb_bpm(); }
+void gTiming::cb_meter (Fl_Widget *v, void *p) { ((gTiming*)p)->__cb_meter(); }
+void gTiming::cb_quantizer (Fl_Widget *v, void *p) { ((gTiming*)p)->__cb_quantizer(); }
+void gTiming::cb_multiplier(Fl_Widget *v, void *p) { ((gTiming*)p)->__cb_multiplier(); }
+void gTiming::cb_divider (Fl_Widget *v, void *p) { ((gTiming*)p)->__cb_divider(); }
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTiming::__cb_bpm()
+{
+ gu_openSubWindow(mainWin, new gdBpmInput(bpm->label()), WID_BPM);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTiming::__cb_meter()
+{
+ gu_openSubWindow(mainWin, new gdBeatsInput(), WID_BEATS);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTiming::__cb_quantizer()
+{
+ glue_quantize(quantizer->value());
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTiming::__cb_multiplier()
+{
+ glue_beatsMultiply();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTiming::__cb_divider()
+{
+ glue_beatsDivide();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTiming::setBpm(const char *v)
+{
+ bpm->copy_label(v);
+}
+
+
+void gTiming::setBpm(float v)
+{
+ char buf[6];
+ sprintf(buf, "%.01f", v); // only 1 decimal place (e.g. 120.0)
+ bpm->copy_label(buf);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gTiming::setMeter(int beats, int bars)
+{
+ char buf[8];
+ sprintf(buf, "%d/%d", beats, bars);
+ meter->copy_label(buf);
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ * gd_mainWindow
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef GD_MAINWINDOW_H
+#define GD_MAINWINDOW_H
+
+
+#include <FL/Fl.H>
+#include <FL/x.H>
+#include "ge_mixed.h"
+#include "ge_window.h"
+
+
+/* ------------------------------------------------------------------ */
+
+
+class gdMainWindow : public gWindow
+{
+private:
+
+ static void cb_endprogram (Fl_Widget *v, void *p);
+ inline void __cb_endprogram();
+
+public:
+
+ class gKeyboard *keyboard;
+ class gBeatMeter *beatMeter;
+ class gMenu *menu;
+ class gInOut *inOut;
+ class gController *controller;
+ class gTiming *timing;
+
+ gdMainWindow(int w, int h, const char *title, int argc, char **argv);
+};
+
+
+/* ------------------------------------------------------------------ */
+
+
+class gInOut : public Fl_Group
+{
+private:
+
+ class gSoundMeter *outMeter;
+ class gSoundMeter *inMeter;
+ class gBeatMeter *beatMeter;
+ class gDial *outVol;
+ class gDial *inVol;
+#ifdef WITH_VST
+ class gFxButton *masterFxOut;
+ class gFxButton *masterFxIn;
+ class gClick *inToOut;
+#endif
+
+ static void cb_outVol (Fl_Widget *v, void *p);
+ static void cb_inVol (Fl_Widget *v, void *p);
+#ifdef WITH_VST
+ static void cb_masterFxOut(Fl_Widget *v, void *p);
+ static void cb_masterFxIn (Fl_Widget *v, void *p);
+ static void cb_inToOut (Fl_Widget *v, void *p);
+#endif
+
+ inline void __cb_outVol ();
+ inline void __cb_inVol ();
+#ifdef WITH_VST
+ inline void __cb_masterFxOut();
+ inline void __cb_masterFxIn ();
+ inline void __cb_inToOut ();
+#endif
+
+public:
+
+ gInOut(int x, int y);
+
+ void refresh();
+
+ inline void setOutVol(float v) { outVol->value(v); }
+ inline void setInVol (float v) { inVol->value(v); }
+#ifdef WITH_VST
+ inline void setMasterFxOutFull(bool v) { masterFxOut->full = v; masterFxOut->redraw(); }
+ inline void setMasterFxInFull(bool v) { masterFxIn->full = v; masterFxIn->redraw(); }
+#endif
+};
+
+
+/* ------------------------------------------------------------------ */
+
+
+class gMenu : public Fl_Group
+{
+private:
+
+ class gClick *file;
+ class gClick *edit;
+ class gClick *config;
+ class gClick *about;
+
+ static void cb_about (Fl_Widget *v, void *p);
+ static void cb_config(Fl_Widget *v, void *p);
+ static void cb_file (Fl_Widget *v, void *p);
+ static void cb_edit (Fl_Widget *v, void *p);
+
+ inline void __cb_about ();
+ inline void __cb_config();
+ inline void __cb_file ();
+ inline void __cb_edit ();
+
+public:
+
+ gMenu(int x, int y);
+};
+
+
+/* ------------------------------------------------------------------ */
+
+
+class gController : public Fl_Group
+{
+private:
+
+ class gClick *rewind;
+ class gClick *play;
+ class gClick *recAction;
+ class gClick *recInput;
+ class gClick *metronome;
+
+ static void cb_rewind (Fl_Widget *v, void *p);
+ static void cb_play (Fl_Widget *v, void *p);
+ static void cb_recAction(Fl_Widget *v, void *p);
+ static void cb_recInput (Fl_Widget *v, void *p);
+ static void cb_metronome(Fl_Widget *v, void *p);
+
+ inline void __cb_rewind ();
+ inline void __cb_play ();
+ inline void __cb_recAction();
+ inline void __cb_recInput ();
+ inline void __cb_metronome();
+
+public:
+
+ gController(int x, int y);
+
+ void updatePlay (int v);
+ void updateMetronome(int v);
+ void updateRecInput (int v);
+ void updateRecAction(int v);
+};
+
+
+/* ------------------------------------------------------------------ */
+
+
+class gTiming : public Fl_Group
+{
+private:
+
+ class gClick *bpm;
+ class gClick *meter;
+ class gChoice *quantizer;
+ class gClick *multiplier;
+ class gClick *divider;
+
+ static void cb_bpm (Fl_Widget *v, void *p);
+ static void cb_meter (Fl_Widget *v, void *p);
+ static void cb_quantizer (Fl_Widget *v, void *p);
+ static void cb_multiplier(Fl_Widget *v, void *p);
+ static void cb_divider (Fl_Widget *v, void *p);
+
+ inline void __cb_bpm();
+ inline void __cb_meter();
+ inline void __cb_quantizer();
+ inline void __cb_multiplier();
+ inline void __cb_divider();
+
+public:
+
+ gTiming(int x, int y);
+
+ void setBpm(const char *v);
+ void setBpm(float v);
+ void setMeter(int beats, int bars);
+};
+
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_midiGrabber
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include "gd_midiGrabber.h"
+#include "ge_mixed.h"
+#include "gui_utils.h"
+#include "kernelMidi.h"
+#include "conf.h"
+#include "sampleChannel.h"
+#include "log.h"
+
+
+extern Conf G_Conf;
+
+
+gdMidiGrabber::gdMidiGrabber(int w, int h, const char *title)
+ : gWindow(w, h, title)
+{
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+gdMidiGrabber::~gdMidiGrabber() {
+ kernelMidi::stopMidiLearn();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdMidiGrabber::stopMidiLearn(gLearner *learner) {
+ kernelMidi::stopMidiLearn();
+ learner->updateValue();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdMidiGrabber::__cb_learn(uint32_t *param, uint32_t msg, gLearner *l) {
+ *param = msg;
+ stopMidiLearn(l);
+ gLog("[gdMidiGrabber] MIDI learn done - message=0x%X\n", msg);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdMidiGrabber::cb_learn(uint32_t msg, void *d) {
+ cbData *data = (cbData*) d;
+ gdMidiGrabber *grabber = (gdMidiGrabber*) data->grabber;
+ gLearner *learner = data->learner;
+ uint32_t *param = learner->param;
+ grabber->__cb_learn(param, msg, learner);
+ free(data);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdMidiGrabber::cb_close(Fl_Widget *w, void *p) { ((gdMidiGrabber*)p)->__cb_close(); }
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdMidiGrabber::__cb_close() {
+ do_callback();
+}
+
+
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+
+
+gdMidiGrabberChannel::gdMidiGrabberChannel(Channel *ch)
+ : gdMidiGrabber(300, 206, "MIDI Input Setup"),
+ ch(ch)
+{
+ char title[64];
+ sprintf(title, "MIDI Input Setup (channel %d)", ch->index+1);
+ label(title);
+
+ set_modal();
+
+ enable = new gCheck(8, 8, 120, 20, "enable MIDI input");
+ new gLearner(8, 30, w()-16, "key press", cb_learn, &ch->midiInKeyPress);
+ new gLearner(8, 54, w()-16, "key release", cb_learn, &ch->midiInKeyRel);
+ new gLearner(8, 78, w()-16, "key kill", cb_learn, &ch->midiInKill);
+ new gLearner(8, 102, w()-16, "mute", cb_learn, &ch->midiInMute);
+ new gLearner(8, 126, w()-16, "solo", cb_learn, &ch->midiInSolo);
+ new gLearner(8, 150, w()-16, "volume", cb_learn, &ch->midiInVolume);
+ int yy = 178;
+
+ if (ch->type == CHANNEL_SAMPLE) {
+ size(300, 254);
+ new gLearner(8, 174, w()-16, "pitch", cb_learn, &((SampleChannel*)ch)->midiInPitch);
+ new gLearner(8, 198, w()-16, "read actions", cb_learn, &((SampleChannel*)ch)->midiInReadActions);
+ yy = 226;
+ }
+
+ ok = new gButton(w()-88, yy, 80, 20, "Ok");
+ ok->callback(cb_close, (void*)this);
+
+ enable->value(ch->midiIn);
+ enable->callback(cb_enable, (void*)this);
+
+ gu_setFavicon(this);
+ show();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdMidiGrabberChannel::cb_enable(Fl_Widget *w, void *p) { ((gdMidiGrabberChannel*)p)->__cb_enable(); }
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdMidiGrabberChannel::__cb_enable() {
+ ch->midiIn = enable->value();
+}
+
+
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+
+
+gdMidiGrabberMaster::gdMidiGrabberMaster()
+ : gdMidiGrabber(300, 256, "MIDI Input Setup (global)")
+{
+ set_modal();
+
+ new gLearner(8, 8, w()-16, "rewind", &cb_learn, &G_Conf.midiInRewind);
+ new gLearner(8, 32, w()-16, "play/stop", &cb_learn, &G_Conf.midiInStartStop);
+ new gLearner(8, 56, w()-16, "action recording", &cb_learn, &G_Conf.midiInActionRec);
+ new gLearner(8, 80, w()-16, "input recording", &cb_learn, &G_Conf.midiInInputRec);
+ new gLearner(8, 104, w()-16, "metronome", &cb_learn, &G_Conf.midiInMetronome);
+ new gLearner(8, 128, w()-16, "input volume", &cb_learn, &G_Conf.midiInVolumeIn);
+ new gLearner(8, 152, w()-16, "output volume", &cb_learn, &G_Conf.midiInVolumeOut);
+ new gLearner(8, 176, w()-16, "sequencer ×2", &cb_learn, &G_Conf.midiInBeatDouble);
+ new gLearner(8, 200, w()-16, "sequencer ÷2", &cb_learn, &G_Conf.midiInBeatHalf);
+ ok = new gButton(w()-88, 228, 80, 20, "Ok");
+
+ ok->callback(cb_close, (void*)this);
+
+ gu_setFavicon(this);
+ show();
+}
+
+
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+
+
+gLearner::gLearner(int X, int Y, int W, const char *l, kernelMidi::cb_midiLearn *cb, uint32_t *param)
+ : Fl_Group(X, Y, W, 20),
+ callback(cb),
+ param (param)
+{
+ begin();
+ text = new gBox(x(), y(), 156, 20, l);
+ value = new gClick(text->x()+text->w()+4, y(), 80, 20, "(not set)");
+ button = new gButton(value->x()+value->w()+4, y(), 40, 20, "learn");
+ end();
+
+ text->box(G_BOX);
+ text->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
+
+ value->box(G_BOX);
+ value->callback(cb_value, (void*)this);
+ value->when(FL_WHEN_RELEASE);
+ updateValue();
+
+ button->type(FL_TOGGLE_BUTTON);
+ button->callback(cb_button, (void*)this);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gLearner::updateValue() {
+ char buf[16];
+ if (*param != 0x0)
+ snprintf(buf, 9, "0x%X", *param);
+ else
+ snprintf(buf, 16, "(not set)");
+ value->copy_label(buf);
+ button->value(0);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gLearner::cb_button(Fl_Widget *v, void *p) { ((gLearner*)p)->__cb_button(); }
+void gLearner::cb_value(Fl_Widget *v, void *p) { ((gLearner*)p)->__cb_value(); }
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gLearner::__cb_value() {
+ if (Fl::event_button() == FL_RIGHT_MOUSE) {
+ *param = 0x0;
+ updateValue();
+ }
+ /// TODO - elif (LEFT_MOUSE) : insert values by hand
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gLearner::__cb_button() {
+ if (button->value() == 1) {
+ cbData *data = (cbData*) malloc(sizeof(cbData));
+ data->grabber = (gdMidiGrabber*) parent(); // parent = gdMidiGrabberChannel
+ data->learner = this;
+ kernelMidi::startMidiLearn(callback, (void*)data);
+ }
+ else
+ kernelMidi::stopMidiLearn();
+}
+
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_midiGrabber
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef GD_MIDIGRABBER_H
+#define GD_MIDIGRABBER_H
+
+
+#include "ge_window.h"
+#include "kernelMidi.h"
+#include "utils.h"
+#include "ge_mixed.h"
+
+
+class gLearner : public Fl_Group {
+
+private:
+
+ /* callback
+ * cb to pass to kernelMidi. Requires two parameters:
+ * uint32_t msg - MIDI message
+ * void *data - extra data */
+
+ kernelMidi::cb_midiLearn *callback;
+
+ class gBox *text;
+ class gClick *value;
+ class gButton *button;
+
+ static void cb_button(Fl_Widget *v, void *p);
+ static void cb_value (Fl_Widget *v, void *p);
+ inline void __cb_button();
+ inline void __cb_value();
+
+public:
+
+ /* param
+ * pointer to ch->midiIn[value] */
+
+ uint32_t *param;
+
+ gLearner(int x, int y, int w, const char *l, kernelMidi::cb_midiLearn *cb, uint32_t *param);
+
+ void updateValue();
+};
+
+
+/* ------------------------------------------------------------------ */
+
+
+class gdMidiGrabber : public gWindow {
+
+protected:
+
+ gClick *ok;
+
+ void stopMidiLearn(gLearner *l);
+
+ /* cb_learn
+ * callback attached to kernelMidi to learn various actions. */
+
+ static void cb_learn (uint32_t msg, void *data);
+ inline void __cb_learn(uint32_t *param, uint32_t msg, gLearner *l);
+
+ static void cb_close (Fl_Widget *w, void *p);
+ inline void __cb_close();
+
+public:
+
+ gdMidiGrabber(int w, int h, const char *title);
+ ~gdMidiGrabber();
+};
+
+
+/* ------------------------------------------------------------------ */
+
+
+class gdMidiGrabberChannel : public gdMidiGrabber {
+
+private:
+
+ class Channel *ch;
+
+ gCheck *enable;
+
+
+ //gVector <gLearner *> items; for future use, with vst parameters
+
+ static void cb_enable (Fl_Widget *w, void *p);
+ inline void __cb_enable();
+
+public:
+
+ gdMidiGrabberChannel(class Channel *ch);
+};
+
+
+/* ------------------------------------------------------------------ */
+
+
+class gdMidiGrabberMaster : public gdMidiGrabber {
+
+public:
+
+ gdMidiGrabberMaster();
+};
+
+
+/* ------------------------------------------------------------------ */
+
+
+/* cbData
+ * struct we pass to kernelMidi as extra parameter. Local scope made
+ * with unnamed namespace. Infos:
+ * http://stackoverflow.com/questions/4422507/superiority-of-unnamed-namespace-over-static */
+
+namespace {
+ struct cbData {
+ gdMidiGrabber *grabber;
+ gLearner *learner;
+ };
+}
+
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_midiOutputSetup
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include "gd_midiOutputSetup.h"
+#include "ge_mixed.h"
+#include "gg_keyboard.h"
+#include "ge_channel.h"
+#include "channel.h"
+#include "conf.h"
+#include "midiChannel.h"
+#include "gui_utils.h"
+
+
+extern Conf G_Conf;
+
+
+gdMidiOutputSetup::gdMidiOutputSetup(MidiChannel *ch)
+ : gWindow(300, 64, "Midi Output Setup"), ch(ch)
+{
+ begin();
+ enableOut = new gCheck(x()+8, y()+8, 150, 20, "Enable MIDI output");
+ chanListOut = new gChoice(w()-108, y()+8, 100, 20);
+
+ save = new gButton(w()-88, chanListOut->y()+chanListOut->h()+8, 80, 20, "Save");
+ cancel = new gButton(w()-88-save->w()-8, save->y(), 80, 20, "Cancel");
+ end();
+
+ fillChanMenu(chanListOut);
+
+ if (ch->midiOut)
+ enableOut->value(1);
+ else
+ chanListOut->deactivate();
+
+ chanListOut->value(ch->midiOutChan);
+
+ enableOut->callback(cb_enableChanList, (void*)this);
+ save->callback(cb_save, (void*)this);
+ cancel->callback(cb_cancel, (void*)this);
+
+ set_modal();
+ gu_setFavicon(this);
+ show();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdMidiOutputSetup::cb_save (Fl_Widget *w, void *p) { ((gdMidiOutputSetup*)p)->__cb_save(); }
+void gdMidiOutputSetup::cb_cancel (Fl_Widget *w, void *p) { ((gdMidiOutputSetup*)p)->__cb_cancel(); }
+void gdMidiOutputSetup::cb_enableChanList(Fl_Widget *w, void *p) { ((gdMidiOutputSetup*)p)->__cb_enableChanList(); }
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdMidiOutputSetup::__cb_enableChanList() {
+ enableOut->value() ? chanListOut->activate() : chanListOut->deactivate();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdMidiOutputSetup::__cb_save() {
+ ch->midiOut = enableOut->value();
+ ch->midiOutChan = chanListOut->value();
+ ch->guiChannel->update();
+ do_callback();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdMidiOutputSetup::__cb_cancel() { do_callback(); }
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gdMidiOutputSetup::fillChanMenu(gChoice *m) {
+ m->add("Channel 1");
+ m->add("Channel 2");
+ m->add("Channel 3");
+ m->add("Channel 4");
+ m->add("Channel 5");
+ m->add("Channel 6");
+ m->add("Channel 7");
+ m->add("Channel 8");
+ m->add("Channel 9");
+ m->add("Channel 10");
+ m->add("Channel 11");
+ m->add("Channel 12");
+ m->add("Channel 13");
+ m->add("Channel 14");
+ m->add("Channel 15");
+ m->add("Channel 16");
+ m->value(0);
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_midiOutputSetup
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef GD_MIDI_OUTPUT_SETUP_H
+#define GD_MIDI_OUTPUT_SETUP_H
+
+
+#include <FL/Fl.H>
+#include "ge_window.h"
+
+
+class gdMidiOutputSetup : public gWindow {
+
+private:
+
+ static void cb_save (Fl_Widget *w, void *p);
+ static void cb_cancel (Fl_Widget *w, void *p);
+ static void cb_enableChanList(Fl_Widget *w, void *p);
+ inline void __cb_save ();
+ inline void __cb_cancel ();
+ inline void __cb_enableChanList();
+
+ void fillChanMenu(class gChoice *m);
+
+ class gCheck *enableOut;
+ class gChoice *chanListOut;
+ class gButton *save;
+ class gButton *cancel;
+
+ class MidiChannel *ch;
+
+public:
+
+ gdMidiOutputSetup(class MidiChannel *ch);
+};
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_pluginList
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+#include "gd_pluginList.h"
+#include "gd_pluginWindow.h"
+#include "gd_pluginWindowGUI.h"
+#include "gd_browser.h"
+#include "gd_mainWindow.h"
+#include "ge_mixed.h"
+#include "gui_utils.h"
+#include "utils.h"
+#include "conf.h"
+#include "graphics.h"
+#include "pluginHost.h"
+#include "mixer.h"
+#include "channel.h"
+#include "ge_channel.h"
+
+
+extern Conf G_Conf;
+extern PluginHost G_PluginHost;
+extern gdMainWindow *mainWin;
+
+
+gdPluginList::gdPluginList(int stackType, Channel *ch)
+ : gWindow(468, 204), ch(ch), stackType(stackType)
+{
+
+ if (G_Conf.pluginListX)
+ resize(G_Conf.pluginListX, G_Conf.pluginListY, w(), h());
+
+ list = new Fl_Scroll(8, 8, 476, 188);
+ list->type(Fl_Scroll::VERTICAL);
+ list->scrollbar.color(COLOR_BG_0);
+ list->scrollbar.selection_color(COLOR_BG_1);
+ list->scrollbar.labelcolor(COLOR_BD_1);
+ list->scrollbar.slider(G_BOX);
+
+ list->begin();
+ refreshList();
+ list->end();
+
+ end();
+ set_non_modal();
+
+ /* TODO - awful stuff... we should subclass into gdPluginListChannel and
+ gdPluginListMaster */
+
+ if (stackType == PluginHost::MASTER_OUT)
+ label("Master Out Plugins");
+ else
+ if (stackType == PluginHost::MASTER_IN)
+ label("Master In Plugins");
+ else {
+ char tmp[32];
+ sprintf(tmp, "Channel %d Plugins", ch->index+1);
+ copy_label(tmp);
+ }
+
+ gu_setFavicon(this);
+ show();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gdPluginList::~gdPluginList() {
+ G_Conf.pluginListX = x();
+ G_Conf.pluginListY = y();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdPluginList::cb_addPlugin(Fl_Widget *v, void *p) { ((gdPluginList*)p)->__cb_addPlugin(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdPluginList::cb_refreshList(Fl_Widget *v, void *p) {
+
+ /* note: this callback is fired by gdBrowser. Close its window first,
+ * by calling the parent (pluginList) and telling it to delete its
+ * subwindow (i.e. gdBrowser). */
+
+ gWindow *child = (gWindow*) v;
+ if (child->getParent() != NULL)
+ (child->getParent())->delSubWindow(child);
+
+ /* finally refresh plugin list: void *p is a pointer to gdPluginList.
+ * This callback works even when you click 'x' to close the window...
+ * well, who cares */
+
+ ((gdPluginList*)p)->refreshList();
+ ((gdPluginList*)p)->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdPluginList::__cb_addPlugin() {
+
+ /* the usual callback that gWindow adds to each subwindow in this case
+ * is not enough, because when we close the browser the plugin list
+ * must be redrawn. We have a special callback, cb_refreshList, which
+ * we add to gdBrowser. It does exactly what we need. */
+
+ gdBrowser *b = new gdBrowser("Browse Plugin", G_Conf.pluginPath, ch, BROWSER_LOAD_PLUGIN, stackType);
+ addSubWindow(b);
+ b->callback(cb_refreshList, (void*)this); // 'this' refers to gdPluginList
+
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdPluginList::refreshList() {
+
+ /* delete the previous list */
+
+ list->clear();
+ list->scroll_to(0, 0);
+
+ /* add new buttons, as many as the plugin in pluginHost::stack + 1,
+ * the 'add new' button. Warning: if ch == NULL we are working with
+ * master in/master out stacks. */
+
+ int numPlugins = G_PluginHost.countPlugins(stackType, ch);
+ int i = 0;
+
+ while (i<numPlugins) {
+ Plugin *pPlugin = G_PluginHost.getPluginByIndex(i, stackType, ch);
+ gdPlugin *gdp = new gdPlugin(this, pPlugin, list->x(), list->y()-list->yposition()+(i*24), 800);
+ list->add(gdp);
+ i++;
+ }
+
+ int addPlugY = numPlugins == 0 ? 90 : list->y()-list->yposition()+(i*24);
+ addPlugin = new gClick(8, addPlugY, 452, 20, "-- add new plugin --");
+ addPlugin->callback(cb_addPlugin, (void*)this);
+ list->add(addPlugin);
+
+ /* if num(plugins) > 7 make room for the side scrollbar.
+ * Scrollbar.width = 20 + 4(margin) */
+
+ if (i>7)
+ size(492, h());
+ else
+ size(468, h());
+
+ redraw();
+
+ /* set 'full' flag to FX button */
+
+ /* TODO - awful stuff... we should subclass into gdPluginListChannel and
+ gdPluginListMaster */
+
+ if (stackType == PluginHost::MASTER_OUT) {
+ mainWin->inOut->setMasterFxOutFull(G_PluginHost.countPlugins(stackType, ch) > 0);
+ }
+ else
+ if (stackType == PluginHost::MASTER_IN) {
+ mainWin->inOut->setMasterFxInFull(G_PluginHost.countPlugins(stackType, ch) > 0);
+ }
+ else {
+ ch->guiChannel->fx->full = G_PluginHost.countPlugins(stackType, ch) > 0;
+ ch->guiChannel->fx->redraw();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+gdPlugin::gdPlugin(gdPluginList *gdp, Plugin *p, int X, int Y, int W)
+ : Fl_Group(X, Y, W, 20), pParent(gdp), pPlugin (p)
+{
+ begin();
+ button = new gButton(8, y(), 220, 20);
+ program = new gChoice(button->x()+button->w()+4, y(), 132, 20);
+ bypass = new gButton(program->x()+program->w()+4, y(), 20, 20);
+ shiftUp = new gButton(bypass->x()+bypass->w()+4, y(), 20, 20, "", fxShiftUpOff_xpm, fxShiftUpOn_xpm);
+ shiftDown = new gButton(shiftUp->x()+shiftUp->w()+4, y(), 20, 20, "", fxShiftDownOff_xpm, fxShiftDownOn_xpm);
+ remove = new gButton(shiftDown->x()+shiftDown->w()+4, y(), 20, 20, "", fxRemoveOff_xpm, fxRemoveOn_xpm);
+ end();
+
+ if (pPlugin->status != 1) { // bad state
+ char name[256];
+ sprintf(name, "* %s *", gBasename(pPlugin->pathfile).c_str());
+ button->copy_label(name);
+ }
+ else {
+ char name[256];
+ pPlugin->getProduct(name);
+ if (strcmp(name, " ")==0)
+ pPlugin->getName(name);
+
+ button->copy_label(name);
+ button->callback(cb_openPluginWindow, (void*)this);
+
+ program->callback(cb_setProgram, (void*)this);
+
+ /* loading vst programs */
+ /* FIXME - max programs = 128 (unknown source) */
+
+ for (int i=0; i<64; i++) {
+ char out[kVstMaxProgNameLen];
+ pPlugin->getProgramName(i, out);
+ for (int j=0; j<kVstMaxProgNameLen; j++) // escape FLTK special chars
+ if (out[j] == '/' || out[j] == '\\' || out[j] == '&' || out[j] == '_')
+ out[j] = '-';
+ if (strlen(out) > 0)
+ program->add(out);
+ }
+ if (program->size() == 0) {
+ program->add("-- no programs --\0");
+ program->deactivate();
+ }
+ if (pPlugin->getProgram() == -1)
+ program->value(0);
+ else
+ program->value(pPlugin->getProgram());
+
+ bypass->callback(cb_setBypass, (void*)this);
+ bypass->type(FL_TOGGLE_BUTTON);
+ bypass->value(pPlugin->bypass ? 0 : 1);
+ }
+
+ shiftUp->callback(cb_shiftUp, (void*)this);
+ shiftDown->callback(cb_shiftDown, (void*)this);
+ remove->callback(cb_removePlugin, (void*)this);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdPlugin::cb_removePlugin (Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_removePlugin(); }
+void gdPlugin::cb_openPluginWindow(Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_openPluginWindow(); }
+void gdPlugin::cb_setBypass (Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_setBypass(); }
+void gdPlugin::cb_shiftUp (Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_shiftUp(); }
+void gdPlugin::cb_shiftDown (Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_shiftDown(); }
+void gdPlugin::cb_setProgram (Fl_Widget *v, void *p) { ((gdPlugin*)p)->__cb_setProgram(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdPlugin::__cb_shiftUp() {
+
+ /*nothing to do if there's only one plugin */
+
+ if (G_PluginHost.countPlugins(pParent->stackType, pParent->ch) == 1)
+ return;
+
+ int pluginIndex = G_PluginHost.getPluginIndex(pPlugin->getId(), pParent->stackType, pParent->ch);
+
+ if (pluginIndex == 0) // first of the stack, do nothing
+ return;
+
+ G_PluginHost.swapPlugin(pluginIndex, pluginIndex-1, pParent->stackType, pParent->ch);
+ pParent->refreshList();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdPlugin::__cb_shiftDown() {
+
+ /*nothing to do if there's only one plugin */
+
+ if (G_PluginHost.countPlugins(pParent->stackType, pParent->ch) == 1)
+ return;
+
+ unsigned pluginIndex = G_PluginHost.getPluginIndex(pPlugin->getId(), pParent->stackType, pParent->ch);
+ unsigned stackSize = (G_PluginHost.getStack(pParent->stackType, pParent->ch))->size;
+
+ if (pluginIndex == stackSize-1) // last one in the stack, do nothing
+ return;
+
+ G_PluginHost.swapPlugin(pluginIndex, pluginIndex+1, pParent->stackType, pParent->ch);
+ pParent->refreshList();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdPlugin::__cb_removePlugin() {
+
+ /* os x hack: show window before deleting it */
+
+#ifdef __APPLE__
+ gdPluginWindowGUImac* w = (gdPluginWindowGUImac*) pParent->getChild(pPlugin->getId()+1);
+ if (w)
+ w->show();
+#endif
+
+ /* any subwindow linked to the plugin must be destroyed */
+
+ pParent->delSubWindow(pPlugin->getId()+1);
+ G_PluginHost.freePlugin(pPlugin->getId(), pParent->stackType, pParent->ch);
+ pParent->refreshList();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdPlugin::__cb_openPluginWindow() {
+
+ /* the new pluginWindow has id = id_plugin + 1, because id=0 is reserved
+ * for the window 'add plugin'. */
+
+ /* TODO - at the moment you can open a window for each plugin in the stack.
+ * This is not consistent with the rest of the gui. You can avoid this by
+ * calling
+ *
+ * gu_openSubWindow(this, new gdPluginWindow(pPlugin), WID_FX);
+ *
+ * instead of the following code.
+ *
+ * EDIT 2 - having only 1 plugin window would be very uncomfortable */
+
+ if (!pParent->hasWindow(pPlugin->getId()+1)) {
+ gWindow *w;
+ if (pPlugin->hasGui())
+#ifdef __APPLE__
+ w = new gdPluginWindowGUImac(pPlugin);
+#else
+ w = new gdPluginWindowGUI(pPlugin);
+#endif
+ else
+ w = new gdPluginWindow(pPlugin);
+ w->setId(pPlugin->getId()+1);
+ pParent->addSubWindow(w);
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdPlugin::__cb_setBypass() {
+ pPlugin->bypass = !pPlugin->bypass;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdPlugin::__cb_setProgram() {
+ pPlugin->setProgram(program->value());
+}
+
+
+#endif // #ifdef WITH_VST
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_pluginList
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+#ifndef __GD_PLUGINLIST_H__
+#define __GD_PLUGINLIST_H__
+
+#include <FL/Fl.H>
+#include <FL/Fl_Scroll.H>
+#include "ge_window.h"
+
+
+class gdPluginList : public gWindow {
+
+private:
+
+ class gClick *addPlugin;
+ Fl_Scroll *list;
+
+ //gVector<class gdPluginWindow *> subWindows;
+
+ static void cb_addPlugin (Fl_Widget *v, void *p);
+ inline void __cb_addPlugin ();
+
+public:
+
+ class Channel *ch; // ch == NULL ? masterOut
+ int stackType;
+
+ gdPluginList(int stackType, class Channel *ch=NULL);
+ ~gdPluginList();
+
+ /* special callback, passed to browser. When closed (i.e. plugin
+ * has been selected) the same browser will refresh this window. */
+
+ static void cb_refreshList(Fl_Widget*, void*);
+
+ void refreshList();
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+class gdPlugin : public Fl_Group {
+
+private:
+
+ class gdPluginList *pParent;
+ class Plugin *pPlugin;
+
+ static void cb_removePlugin (Fl_Widget *v, void *p);
+ static void cb_openPluginWindow (Fl_Widget *v, void *p);
+ static void cb_setBypass (Fl_Widget *v, void *p);
+ static void cb_shiftUp (Fl_Widget *v, void *p);
+ static void cb_shiftDown (Fl_Widget *v, void *p);
+ static void cb_setProgram (Fl_Widget *v, void *p);
+ inline void __cb_removePlugin ();
+ inline void __cb_openPluginWindow ();
+ inline void __cb_setBypass ();
+ inline void __cb_shiftUp ();
+ inline void __cb_shiftDown ();
+ inline void __cb_setProgram ();
+
+public:
+
+ class gButton *button;
+ class gChoice *program;
+ class gButton *bypass;
+ class gButton *shiftUp;
+ class gButton *shiftDown;
+ class gButton *remove;
+
+ gdPlugin(gdPluginList *gdp, class Plugin *p, int x, int y, int w);
+
+};
+
+#endif
+
+#endif // #ifdef WITH_VST
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_pluginWindow
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifdef WITH_VST
+
+#include <FL/Fl_Scroll.H>
+#include "gd_pluginWindow.h"
+#include "pluginHost.h"
+#include "ge_mixed.h"
+#include "gui_utils.h"
+
+
+extern PluginHost G_PluginHost;
+
+
+Parameter::Parameter(int id, Plugin *p, int X, int Y, int W)
+ : Fl_Group(X,Y,W-24,20), id(id), pPlugin(p)
+{
+ begin();
+
+ label = new gBox(x(), y(), 60, 20);
+ char name[kVstMaxParamStrLen];
+ pPlugin->getParamName(id, name);
+ label->copy_label(name);
+ label->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
+
+ slider = new gSlider(label->x()+label->w()+8, y(), W-200, 20);
+ slider->value(pPlugin->getParam(id));
+ slider->callback(cb_setValue, (void *)this);
+
+ value = new gBox(slider->x()+slider->w()+8, y(), 100, 20);
+ char disp[kVstMaxParamStrLen];
+ char labl[kVstMaxParamStrLen];
+ char str [256];
+ pPlugin->getParamDisplay(id, disp);
+ pPlugin->getParamLabel(id, labl);
+ sprintf(str, "%s %s", disp, labl);
+ value->copy_label(str);
+ value->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
+ value->box(G_BOX);
+
+ resizable(slider);
+
+ end();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Parameter::cb_setValue(Fl_Widget *v, void *p) { ((Parameter*)p)->__cb_setValue(); }
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Parameter::__cb_setValue() {
+
+ pPlugin->setParam(id, slider->value());
+
+ char disp[256];
+ char labl[256];
+ char str [256];
+
+ pPlugin->getParamDisplay(id, disp);
+ pPlugin->getParamLabel(id, labl);
+ sprintf(str, "%s %s", disp, labl);
+
+ value->copy_label(str);
+ value->redraw();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+gdPluginWindow::gdPluginWindow(Plugin *pPlugin)
+ : gWindow(400, 156), pPlugin(pPlugin) // 350
+{
+ set_non_modal();
+
+ gLiquidScroll *list = new gLiquidScroll(8, 8, w()-16, h()-16);
+ list->type(Fl_Scroll::VERTICAL_ALWAYS);
+ list->begin();
+
+ int numParams = pPlugin->getNumParams();
+ for (int i=0; i<numParams; i++)
+ new Parameter(i, pPlugin, list->x(), list->y()+(i*24), list->w());
+ list->end();
+
+ end();
+
+ char name[256];
+ pPlugin->getProduct(name);
+ if (strcmp(name, " ")==0)
+ pPlugin->getName(name);
+ label(name);
+
+ size_range(400, (24*1)+12);
+ resizable(list);
+
+ gu_setFavicon(this);
+ show();
+
+}
+
+
+gdPluginWindow::~gdPluginWindow() {}
+
+
+#endif // #ifdef WITH_VST
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_pluginWindow
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+#ifdef WITH_VST
+
+#ifndef __GD_PLUGINWINDOW_H__
+#define __GD_PLUGINWINDOW_H__
+
+
+#include <FL/Fl.H>
+#include <FL/Fl_Window.H>
+#include "ge_window.h"
+
+
+class gdPluginWindow : public gWindow {
+
+private:
+ class Plugin *pPlugin;
+
+public:
+ int id;
+
+ gdPluginWindow(Plugin *pPlugin);
+ ~gdPluginWindow();
+};
+
+
+/* ------------------------------------------------------------------ */
+
+
+class Parameter : public Fl_Group {
+
+private:
+ int id;
+ class Plugin *pPlugin;
+
+ static void cb_setValue(Fl_Widget *v, void *p);
+ inline void __cb_setValue();
+
+public:
+ class gBox *label;
+ class gSlider *slider;
+ class gBox *value;
+
+ Parameter(int id, class Plugin *p, int x, int y, int w);
+};
+
+
+#endif
+
+#endif // #ifdef WITH_VST
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_pluginWindowGUI
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifdef WITH_VST
+
+
+#include "gd_pluginWindowGUI.h"
+#include "pluginHost.h"
+#include "ge_mixed.h"
+#include "gui_utils.h"
+#include "log.h"
+
+
+extern PluginHost G_PluginHost;
+
+
+gdPluginWindowGUI::gdPluginWindowGUI(Plugin *pPlugin)
+ : gWindow(450, 300), pPlugin(pPlugin)
+{
+
+ /* some effects like to have us get their rect before opening them */
+
+ ERect *rect;
+ pPlugin->getRect(&rect);
+
+ gu_setFavicon(this);
+ set_non_modal();
+ resize(x(), y(), pPlugin->getGuiWidth(), pPlugin->getGuiWidth());
+ show();
+
+ gLog("[gdPluginWindowGUI] open window, w=%d h=%d\n",
+ pPlugin->getGuiWidth(), pPlugin->getGuiWidth());
+
+ /* Fl::check(): Waits until "something happens" and then returns. It's
+ * mandatory on linux, otherwise X can't find 'this' window. */
+
+ Fl::check();
+ pPlugin->openGui((void*)fl_xid(this));
+
+ char name[256];
+ pPlugin->getProduct(name);
+ copy_label(name);
+
+ /* add a pointer to this window to plugin */
+
+ pPlugin->window = this;
+
+ pPlugin->idle();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+gdPluginWindowGUI::~gdPluginWindowGUI() {
+ pPlugin->closeGui();
+}
+
+
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+
+
+#if defined(__APPLE__)
+
+
+pascal OSStatus gdPluginWindowGUImac::windowHandler(EventHandlerCallRef ehc, EventRef e, void *data) {
+ return ((gdPluginWindowGUImac*)data)->__wh(ehc, e);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+pascal OSStatus gdPluginWindowGUImac::__wh(EventHandlerCallRef inHandlerCallRef, EventRef inEvent) {
+ OSStatus result = eventNotHandledErr; // let the Carbon Event Manager close the window
+ UInt32 eventClass = GetEventClass(inEvent);
+ UInt32 eventKind = GetEventKind(inEvent);
+
+ switch (eventClass) {
+ case kEventClassWindow: {
+ switch (eventKind) {
+ case kEventWindowClose: {
+ gLog("[pluginWindowMac] <<< CALLBACK >>> kEventWindowClose for gWindow=%p, window=%p\n", (void*)this, (void*)carbonWindow);
+ show();
+ break;
+ }
+ case kEventWindowClosed: {
+ gLog("[pluginWindowMac] <<< CALLBACK >>> kEventWindowClosed for gWindow=%p, window=%p\n", (void*)this, (void*)carbonWindow);
+ open = false;
+ result = noErr;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ return result;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+gdPluginWindowGUImac::gdPluginWindowGUImac(Plugin *pPlugin)
+ : gWindow(450, 300), pPlugin(pPlugin), carbonWindow(NULL)
+{
+
+ /* some effects like to have us get their rect before opening them */
+
+ ERect *rect;
+ pPlugin->getRect(&rect);
+
+ /* window initialization */
+
+ Rect wRect;
+
+ wRect.top = rect->top;
+ wRect.left = rect->left;
+ wRect.bottom = rect->bottom;
+ wRect.right = rect->right;
+
+ int winclass = kDocumentWindowClass;
+ int winattr = kWindowStandardHandlerAttribute |
+ kWindowCloseBoxAttribute |
+ kWindowCompositingAttribute |
+ kWindowAsyncDragAttribute;
+
+ // winattr &= GetAvailableWindowAttributes(winclass); // make sure that the window will open
+
+ OSStatus status = CreateNewWindow(winclass, winattr, &wRect, &carbonWindow);
+ if (status != noErr) {
+ gLog("[pluginWindowMac] Unable to create window! Status=%d\n", (int) status);
+ return;
+ }
+ else
+ gLog("[pluginWindowMac] created window=%p\n", (void*)carbonWindow);
+
+ /* install event handler, called when window is closed */
+
+ static EventTypeSpec eventTypes[] = {
+ { kEventClassWindow, kEventWindowClose },
+ { kEventClassWindow, kEventWindowClosed }
+ };
+ InstallWindowEventHandler(carbonWindow, windowHandler, GetEventTypeCount(eventTypes), eventTypes, this, NULL);
+
+ /* open window, center it, show it and start the handler */
+
+ pPlugin->openGui((void*)carbonWindow);
+ RepositionWindow(carbonWindow, NULL, kWindowCenterOnMainScreen);
+ ShowWindow(carbonWindow);
+ open = true;
+}
+
+
+
+/* ------------------------------------------------------------------ */
+
+
+gdPluginWindowGUImac::~gdPluginWindowGUImac() {
+ gLog("[pluginWindowMac] [[[ destructor ]]] gWindow=%p deleted, window=%p deleted\n", (void*)this, (void*)carbonWindow);
+ pPlugin->closeGui();
+ if (open)
+ DisposeWindow(carbonWindow);
+}
+
+#endif
+
+#endif // #ifdef WITH_VST
--- /dev/null
+
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_pluginWindowGUI
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifdef WITH_VST
+
+
+#ifndef __GD_PLUGINWINDOW_GUI_H__
+#define __GD_PLUGINWINDOW_GUI_H__
+
+
+#include <FL/Fl.H>
+#include <FL/Fl_Window.H>
+#include "ge_window.h"
+#if defined(__APPLE__)
+ #include <Carbon/Carbon.h>
+#endif
+
+
+class gdPluginWindowGUI : public gWindow {
+private:
+
+ class Plugin *pPlugin;
+
+public:
+
+ gdPluginWindowGUI(Plugin *pPlugin);
+ ~gdPluginWindowGUI();
+};
+
+
+/* ------------------------------------------------------------------ */
+
+#if defined(__APPLE__)
+
+class gdPluginWindowGUImac : public gWindow {
+
+private:
+
+ static pascal OSStatus windowHandler(EventHandlerCallRef ehc, EventRef e, void *data);
+ inline pascal OSStatus __wh(EventHandlerCallRef ehc, EventRef e);
+
+ class Plugin *pPlugin;
+ WindowRef carbonWindow;
+ bool open;
+
+public:
+
+ gdPluginWindowGUImac(Plugin *pPlugin);
+ ~gdPluginWindowGUImac();
+};
+
+#endif
+
+
+#endif // include guard
+
+#endif // #ifdef WITH_VST
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_warnings
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include "gd_warnings.h"
+
+
+void gdAlert(const char *c) {
+ Fl_Window *modal = new Fl_Window(
+ (Fl::w() / 2) - 150,
+ (Fl::h() / 2) - 47,
+ 300, 90, "Alert");
+ modal->set_modal();
+ modal->begin();
+ gBox *box = new gBox(10, 10, 280, 40, c);
+ gClick *b = new gClick(210, 60, 80, 20, "Close");
+ modal->end();
+ box->labelsize(11);
+ b->callback(__cb_window_closer, (void *)modal);
+ b->shortcut(FL_Enter);
+ gu_setFavicon(modal);
+ modal->show();
+}
+
+
+int gdConfirmWin(const char *title, const char *msg) {
+ Fl_Window *win = new Fl_Window(
+ (Fl::w() / 2) - 150,
+ (Fl::h() / 2) - 47,
+ 300, 90, title);
+ win->set_modal();
+ win->begin();
+ new gBox(10, 10, 280, 40, msg);
+ gClick *ok = new gClick(212, 62, 80, 20, "Ok");
+ gClick *ko = new gClick(124, 62, 80, 20, "Cancel");
+ win->end();
+ ok->shortcut(FL_Enter);
+ gu_setFavicon(win);
+ win->show();
+
+ /* no callbacks here. readqueue() check the event stack. */
+
+ int r = 0;
+ while (true) {
+ Fl_Widget *o = Fl::readqueue();
+ if (!o) Fl::wait();
+ else if (o == ok) {r = 1; break;}
+ else if (o == ko) {r = 0; break;}
+ }
+ //delete win;
+ win->hide();
+ return r;
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_warnings
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+#ifndef GD_WARNINGS_H
+#define GD_WARNINGS_H
+
+#include <FL/Fl.H>
+#include <FL/Fl_Window.H>
+#include <FL/Fl_Box.H>
+#include "ge_mixed.h"
+#include "gui_utils.h"
+
+
+void gdAlert(const char *c);
+
+int gdConfirmWin(const char *title, const char *msg);
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_actionChannel
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include <FL/fl_draw.H>
+#include "glue.h"
+#include "ge_actionChannel.h"
+#include "gd_mainWindow.h"
+#include "gd_actionEditor.h"
+#include "gg_keyboard.h"
+#include "conf.h"
+#include "channel.h"
+#include "sampleChannel.h"
+#include "log.h"
+
+
+extern gdMainWindow *mainWin;
+extern Mixer G_Mixer;
+extern Conf G_Conf;
+
+
+/* ------------------------------------------------------------------ */
+
+
+gActionChannel::gActionChannel(int x, int y, gdActionEditor *pParent, SampleChannel *ch)
+ : gActionWidget(x, y, 200, 40, pParent), ch(ch), selected(NULL)
+{
+ size(pParent->totalWidth, h());
+
+ /* add actions when the window opens. Their position is zoom-based;
+ * each frame is / 2 because we don't care about stereo infos. */
+
+ for (unsigned i=0; i<recorder::frames.size; i++) {
+ for (unsigned j=0; j<recorder::global.at(i).size; j++) {
+
+ recorder::action *ra = recorder::global.at(i).at(j);
+
+ if (ra->chan == pParent->chan->index) {
+
+ /* don't show actions > than the grey area */
+
+ if (recorder::frames.at(i) > G_Mixer.totalFrames)
+ continue;
+
+ /* skip the killchan actions in a singlepress channel. They cannot be recorded
+ * in such mode, but they can exist if you change from another mode to singlepress */
+
+ if (ra->type == ACTION_KILLCHAN && ch->mode == SINGLE_PRESS)
+ continue;
+
+ /* also filter out ACTION_KEYREL: it's up to gAction to find the other piece
+ * (namely frame_b) */
+
+ if (ra->type & (ACTION_KEYPRESS | ACTION_KILLCHAN)) {
+ int ax = x+(ra->frame/pParent->zoom);
+ gAction *a = new gAction(
+ ax, // x
+ y+4, // y
+ h()-8, // h
+ ra->frame, // frame_a
+ i, // n. of recordings
+ pParent, // pointer to the pParent window
+ ch, // pointer to SampleChannel
+ false, // record = false: don't record it, we are just displaying it!
+ ra->type); // type of action
+ add(a);
+ }
+ }
+ }
+ }
+ end(); // mandatory when you add widgets to a fl_group, otherwise mega malfunctions
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+gAction *gActionChannel::getSelectedAction() {
+ for (int i=0; i<children(); i++) {
+ int action_x = ((gAction*)child(i))->x();
+ int action_w = ((gAction*)child(i))->w();
+ if (Fl::event_x() >= action_x && Fl::event_x() <= action_x + action_w)
+ return (gAction*)child(i);
+ }
+ return NULL;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gActionChannel::updateActions() {
+
+ /* when zooming, don't delete and re-add actions, just MOVE them. This
+ * function shifts the action by a zoom factor. Those singlepress are
+ * stretched, as well */
+
+ gAction *a;
+ for (int i=0; i<children(); i++) {
+
+ a = (gAction*)child(i);
+ int newX = x() + (a->frame_a / pParent->zoom);
+
+ if (ch->mode == SINGLE_PRESS) {
+ int newW = ((a->frame_b - a->frame_a) / pParent->zoom);
+ if (newW < gAction::MIN_WIDTH)
+ newW = gAction::MIN_WIDTH;
+ a->resize(newX, a->y(), newW, a->h());
+ }
+ else
+ a->resize(newX, a->y(), gAction::MIN_WIDTH, a->h());
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gActionChannel::draw() {
+
+ /* draw basic boundaries (+ beat bars) and hide the unused area. Then
+ * draw the children (the actions) */
+
+ baseDraw();
+
+ /* print label */
+
+ fl_color(COLOR_BG_1);
+ fl_font(FL_HELVETICA, 12);
+ if (active())
+ fl_draw("start/stop", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER)); /// FIXME h() is too much!
+ else
+ fl_draw("start/stop (disabled)", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER)); /// FIXME h() is too much!
+
+ draw_children();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gActionChannel::handle(int e) {
+
+ int ret = Fl_Group::handle(e);
+
+ /* do nothing if the widget is deactivated. It could happen for loopmode
+ * channels */
+
+ if (!active())
+ return 1;
+
+ switch (e) {
+
+ case FL_DRAG: {
+ if (selected != NULL) { // if you don't drag an empty area
+
+ /* if onLeftEdge o onRightEdge are true it means that you're resizing
+ * an action. Otherwise move the widget. */
+
+ if (selected->onLeftEdge || selected->onRightEdge) {
+
+ /* some checks: a) cannot resize an action < N pixels, b) no beyond zero,
+ * c) no beyond bar maxwidth. Checks for overlap are done in FL_RELEASE */
+
+ if (selected->onRightEdge) {
+
+ int aw = Fl::event_x()-selected->x();
+ int ah = selected->h();
+
+ if (Fl::event_x() < selected->x()+gAction::MIN_WIDTH)
+ aw = gAction::MIN_WIDTH;
+ else
+ if (Fl::event_x() > pParent->coverX)
+ aw = pParent->coverX-selected->x();
+
+ selected->size(aw, ah);
+ }
+ else {
+
+ int ax = Fl::event_x();
+ int ay = selected->y();
+ int aw = selected->x()-Fl::event_x()+selected->w();
+ int ah = selected->h();
+
+ if (Fl::event_x() < x()) {
+ ax = x();
+ aw = selected->w()+selected->x()-x();
+ }
+ else
+ if (Fl::event_x() > selected->x()+selected->w()-gAction::MIN_WIDTH) {
+ ax = selected->x()+selected->w()-gAction::MIN_WIDTH;
+ aw = gAction::MIN_WIDTH;
+ }
+ selected->resize(ax, ay, aw, ah);
+ }
+ }
+
+ /* move the widget around */
+
+ else {
+ int real_x = Fl::event_x() - actionPickPoint;
+ if (real_x < x()) // don't go beyond the left border
+ selected->position(x(), selected->y());
+ else
+ if (real_x+selected->w() > pParent->coverX+x()) // don't go beyond the right border
+ selected->position(pParent->coverX+x()-selected->w(), selected->y());
+ else {
+ if (pParent->gridTool->isOn()) {
+ int snpx = pParent->gridTool->getSnapPoint(real_x-x()) + x() -1;
+ selected->position(snpx, selected->y());
+ }
+ else
+ selected->position(real_x, selected->y());
+ }
+ }
+ redraw();
+ }
+ ret = 1;
+ break;
+ }
+
+ case FL_PUSH: {
+
+ if (Fl::event_button1()) {
+
+ /* avoid at all costs two overlapping actions. We use 'selected' because
+ * the selected action can be reused in FL_DRAG, in case you want to move
+ * it. */
+
+ selected = getSelectedAction();
+
+ if (selected == NULL) {
+
+ /* avoid click on grey area */
+
+ if (Fl::event_x() >= pParent->coverX) {
+ ret = 1;
+ break;
+ }
+
+ /* snap function, if enabled */
+
+ int ax = Fl::event_x();
+ int fx = (ax - x()) * pParent->zoom;
+ if (pParent->gridTool->isOn()) {
+ ax = pParent->gridTool->getSnapPoint(ax-x()) + x() -1;
+ fx = pParent->gridTool->getSnapFrame(ax-x());
+
+ /* with snap=on an action can fall onto another */
+
+ if (actionCollides(fx)) {
+ ret = 1;
+ break;
+ }
+ }
+
+ gAction *a = new gAction(
+ ax, // x
+ y()+4, // y
+ h()-8, // h
+ fx, // frame_a
+ recorder::frames.size-1, // n. of actions recorded
+ pParent, // pParent window pointer
+ ch, // pointer to SampleChannel
+ true, // record = true: record it!
+ pParent->getActionType()); // type of action
+ add(a);
+ mainWin->keyboard->setChannelWithActions((gSampleChannel*)ch->guiChannel); // mainWindow update
+ redraw();
+ ret = 1;
+ }
+ else {
+ actionOriginalX = selected->x();
+ actionOriginalW = selected->w();
+ actionPickPoint = Fl::event_x() - selected->x();
+ ret = 1; // for dragging
+ }
+ }
+ else
+ if (Fl::event_button3()) {
+ gAction *a = getSelectedAction();
+ if (a != NULL) {
+ a->delAction();
+ remove(a);
+ delete a;
+ mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel);
+ redraw();
+ ret = 1;
+ }
+ }
+ break;
+ }
+ case FL_RELEASE: {
+
+ if (selected == NULL) {
+ ret = 1;
+ break;
+ }
+
+ /* noChanges = true when you click on an action without doing anything
+ * (dragging or moving it). */
+
+ bool noChanges = false;
+ if (actionOriginalX == selected->x())
+ noChanges = true;
+ if (ch->mode == SINGLE_PRESS &&
+ actionOriginalX+actionOriginalW != selected->x()+selected->w())
+ noChanges = false;
+
+ if (noChanges) {
+ ret = 1;
+ selected = NULL;
+ break;
+ }
+
+ /* step 1: check if the action doesn't overlap with another one.
+ * In case of overlap the moved action returns to the original X
+ * value ("actionOriginalX"). */
+
+ bool overlap = false;
+ for (int i=0; i<children() && !overlap; i++) {
+
+ /* never check against itself. */
+
+ if ((gAction*)child(i) == selected)
+ continue;
+
+ int action_x = ((gAction*)child(i))->x();
+ int action_w = ((gAction*)child(i))->w();
+ if (ch->mode == SINGLE_PRESS) {
+
+ /* when 2 segments overlap?
+ * start = the highest value between the two starting points
+ * end = the lowest value between the two ending points
+ * if start < end then there's an overlap of end-start pixels. */
+
+ int start = action_x >= selected->x() ? action_x : selected->x();
+ int end = action_x+action_w < selected->x()+selected->w() ? action_x+action_w : selected->x()+selected->w();
+ if (start < end) {
+ selected->resize(actionOriginalX, selected->y(), actionOriginalW, selected->h());
+ redraw();
+ overlap = true; // one overlap: stop checking
+ }
+ }
+ else {
+ if (selected->x() == action_x) {
+ selected->position(actionOriginalX, selected->y());
+ redraw();
+ overlap = true; // one overlap: stop checking
+ }
+ }
+ }
+
+ /* step 2: no overlap? then update the coordinates of the action, ie
+ * delete the previous rec and create a new one.
+ * Anyway the selected action becomes NULL, because when you release
+ * the mouse button the dragging process ends. */
+
+ if (!overlap) {
+ if (pParent->gridTool->isOn()) {
+ int f = pParent->gridTool->getSnapFrame(selected->absx());
+ selected->moveAction(f);
+ }
+ else
+ selected->moveAction();
+ }
+ selected = NULL;
+ ret = 1;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool gActionChannel::actionCollides(int frame) {
+
+ /* if SINGLE_PRESS we check that the tail (frame_b) of the action doesn't
+ * overlap the head (frame) of the new one. First the general case, yet. */
+
+ bool collision = false;
+
+ for (int i=0; i<children() && !collision; i++)
+ if ( ((gAction*)child(i))->frame_a == frame)
+ collision = true;
+
+ if (ch->mode == SINGLE_PRESS) {
+ for (int i=0; i<children() && !collision; i++) {
+ gAction *c = ((gAction*)child(i));
+ if (frame <= c->frame_b && frame >= c->frame_a)
+ collision = true;
+ }
+ }
+
+ return collision;
+}
+
+
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+
+
+const int gAction::MIN_WIDTH = 8;
+
+
+/* ------------------------------------------------------------------ */
+
+
+/** TODO - index is useless?
+ * TODO - pass a record::action pointer and let gAction compute values */
+
+gAction::gAction(int X, int Y, int H, int frame_a, unsigned index, gdActionEditor *parent, SampleChannel *ch, bool record, char type)
+: Fl_Box (X, Y, MIN_WIDTH, H),
+ selected (false),
+ index (index),
+ parent (parent),
+ ch (ch),
+ type (type),
+ frame_a (frame_a),
+ onRightEdge(false),
+ onLeftEdge (false)
+{
+ /* bool 'record' defines how to understand the action.
+ * record = false: don't record it, just show it. It happens when you
+ * open the editor with some actions to be shown.
+ *
+ * record = true: record it AND show it. It happens when you click on
+ * an empty area in order to add a new actions. First you record it
+ * (addAction()) then you show it (FLTK::Draw()) */
+
+ if (record)
+ addAction();
+
+ /* in order to show a singlepress action we must compute the frame_b. We
+ * do that after the possible recording, otherwise we don't know which
+ * key_release is associated. */
+
+ if (ch->mode == SINGLE_PRESS && type == ACTION_KEYPRESS) {
+ recorder::action *a2 = NULL;
+ recorder::getNextAction(ch->index, ACTION_KEYREL, frame_a, &a2);
+ if (a2) {
+ frame_b = a2->frame;
+ w((frame_b - frame_a)/parent->zoom);
+ }
+ else
+ gLog("[gActionChannel] frame_b not found! [%d:???]\n", frame_a);
+
+ /* a singlepress action narrower than 8 pixel is useless. So check it.
+ * Warning: if an action is 8 px narrow, it has no body space to drag
+ * it. It's up to the user to zoom in and drag it. */
+
+ if (w() < MIN_WIDTH)
+ size(MIN_WIDTH, h());
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gAction::draw() {
+
+ int color;
+ if (selected) /// && gActionChannel !disabled
+ color = COLOR_BD_1;
+ else
+ color = COLOR_BG_2;
+
+ if (ch->mode == SINGLE_PRESS) {
+ fl_rectf(x(), y(), w(), h(), (Fl_Color) color);
+ }
+ else {
+ if (type == ACTION_KILLCHAN)
+ fl_rect(x(), y(), MIN_WIDTH, h(), (Fl_Color) color);
+ else {
+ fl_rectf(x(), y(), MIN_WIDTH, h(), (Fl_Color) color);
+ if (type == ACTION_KEYPRESS)
+ fl_rectf(x()+3, y()+h()-11, 2, 8, COLOR_BD_0);
+ else
+ if (type == ACTION_KEYREL)
+ fl_rectf(x()+3, y()+3, 2, 8, COLOR_BD_0);
+ }
+ }
+
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gAction::handle(int e) {
+
+ /* ret = 0 sends the event to the parent window. */
+
+ int ret = 0;
+
+ switch (e) {
+
+ case FL_ENTER: {
+ selected = true;
+ ret = 1;
+ redraw();
+ break;
+ }
+ case FL_LEAVE: {
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+ selected = false;
+ ret = 1;
+ redraw();
+ break;
+ }
+ case FL_MOVE: {
+
+ /* handling of the two margins, left & right. 4 pixels are good enough */
+
+ if (ch->mode == SINGLE_PRESS) {
+ onLeftEdge = false;
+ onRightEdge = false;
+ if (Fl::event_x() >= x() && Fl::event_x() < x()+4) {
+ onLeftEdge = true;
+ fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+ }
+ else
+ if (Fl::event_x() >= x()+w()-4 && Fl::event_x() <= x()+w()) {
+ onRightEdge = true;
+ fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+ }
+ else
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+ }
+ }
+ }
+
+ return ret;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gAction::addAction() {
+
+ /* always check frame parity */
+
+ if (frame_a % 2 != 0)
+ frame_a++;
+
+ /* anatomy of an action
+ * ____[#######]_____ (a) is the left margin, ACTION_KEYPRESS. (b) is
+ * a b the right margin, the ACTION_KEYREL. This is the
+ * theory behind the singleshot.press actions; for any other kind the
+ * (b) is just a graphical and meaningless point. */
+
+ if (ch->mode == SINGLE_PRESS) {
+ recorder::rec(parent->chan->index, ACTION_KEYPRESS, frame_a);
+ recorder::rec(parent->chan->index, ACTION_KEYREL, frame_a+4096);
+ //gLog("action added, [%d, %d]\n", frame_a, frame_a+4096);
+ }
+ else {
+ recorder::rec(parent->chan->index, parent->getActionType(), frame_a);
+ //gLog("action added, [%d]\n", frame_a);
+ }
+
+ recorder::sortActions();
+
+ index++; // important!
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gAction::delAction() {
+
+ /* if SINGLE_PRESS you must delete both the keypress and the keyrelease
+ * actions. */
+
+ if (ch->mode == SINGLE_PRESS) {
+ recorder::deleteAction(parent->chan->index, frame_a, ACTION_KEYPRESS, false);
+ recorder::deleteAction(parent->chan->index, frame_b, ACTION_KEYREL, false);
+ }
+ else
+ recorder::deleteAction(parent->chan->index, frame_a, type, false);
+
+ /* restore the initial cursor shape, in case you delete an action and
+ * the double arrow (for resizing) is displayed */
+
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gAction::moveAction(int frame_a) {
+
+ /* easy one: delete previous action and record the new ones. As usual,
+ * SINGLE_PRESS requires two jobs. If frame_a is valid, use that frame
+ * value. */
+
+ delAction();
+
+ if (frame_a != -1)
+ this->frame_a = frame_a;
+ else
+ this->frame_a = xToFrame_a();
+
+
+ /* always check frame parity */
+
+ if (this->frame_a % 2 != 0)
+ this->frame_a++;
+
+ recorder::rec(parent->chan->index, type, this->frame_a);
+
+ if (ch->mode == SINGLE_PRESS) {
+ frame_b = xToFrame_b();
+ recorder::rec(parent->chan->index, ACTION_KEYREL, frame_b);
+ }
+
+ recorder::sortActions();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gAction::absx() {
+ return x() - parent->ac->x();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gAction::xToFrame_a() {
+ return (absx()) * parent->zoom;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gAction::xToFrame_b() {
+ return (absx() + w()) * parent->zoom;
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_actionChannel
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+#ifndef GE_ACTIONCHANNEL_H
+#define GE_ACTIONCHANNEL_H
+
+#include <FL/Fl.H>
+#include <FL/Fl_Box.H>
+#include "ge_actionWidget.h"
+#include "gui_utils.h"
+#include "mixer.h"
+#include "recorder.h"
+
+
+class gAction : public Fl_Box {
+
+private:
+
+ bool selected;
+ unsigned index;
+ class gdActionEditor *parent; // pointer to parent (gActionEditor)
+ class SampleChannel *ch;
+ char type; // type of action
+
+public:
+ gAction(int x, int y, int h, int frame_a, unsigned index,
+ gdActionEditor *parent, class SampleChannel *ch, bool record,
+ char type);
+ void draw();
+ int handle(int e);
+ void addAction();
+ void delAction();
+
+ /* moveAction
+ * shift the action on the x-axis and update Recorder. If frame_a != -1
+ * use the new frame in input (used while snapping) */
+
+ void moveAction(int frame_a=-1);
+
+ /* absx
+ * x() is relative to scrolling position. absx() returns the absolute
+ * x value of the action, from the leftmost edge. */
+
+ int absx();
+
+ /* xToFrame_a,b
+ * return the real frames of x() position */
+
+ int xToFrame_a();
+ int xToFrame_b();
+
+ int frame_a; // initial frame (KEYPRESS for singlemode.press)
+ int frame_b; // terminal frame (KEYREL for singlemode.press, null for others)
+
+ bool onRightEdge;
+ bool onLeftEdge;
+
+ static const int MIN_WIDTH;
+};
+
+
+/* ------------------------------------------------------------------ */
+
+
+class gActionChannel : public gActionWidget {
+
+private:
+
+ class SampleChannel *ch;
+
+ /* getSelectedAction
+ * get the action under the mouse. NULL if nothing found. */
+
+ gAction *getSelectedAction();
+
+ /* selected
+ * pointer to the selected action. Useful when dragging around. */
+
+ gAction *selected;
+
+ /* actionOriginalX, actionOriginalW
+ * x and w of the action, when moved. Useful for checking if the action
+ * overlaps another one: in that case the moved action returns to
+ * actionOriginalX (and to actionOriginalW if resized). */
+
+ int actionOriginalX;
+ int actionOriginalW;
+
+ /* actionPickPoint
+ * the precise x point in which the action has been picked with the mouse,
+ * before a dragging action. */
+
+ int actionPickPoint;
+
+
+ /* actionCollides
+ * true if an action collides with another. Used while adding new points
+ * with snap active.*/
+
+ bool actionCollides(int frame);
+
+public:
+ gActionChannel(int x, int y, gdActionEditor *pParent, class SampleChannel *ch);
+ void draw();
+ int handle(int e);
+ void updateActions();
+};
+
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_actionWidget
+ *
+ * pParent class of any widget inside the action editor.
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include <FL/fl_draw.H>
+#include "ge_actionWidget.h"
+#include "gd_actionEditor.h"
+#include "mixer.h"
+#include "ge_mixed.h"
+
+
+extern Mixer G_Mixer;
+
+
+gActionWidget::gActionWidget(int x, int y, int w, int h, gdActionEditor *pParent)
+ : Fl_Group(x, y, w, h), pParent(pParent) {}
+
+
+/* ------------------------------------------------------------------ */
+
+
+gActionWidget::~gActionWidget() {}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gActionWidget::baseDraw(bool clear) {
+
+ /* clear the screen */
+
+ if (clear)
+ fl_rectf(x(), y(), w(), h(), COLOR_BG_MAIN);
+
+ /* draw the container */
+
+ fl_color(COLOR_BD_0);
+ fl_rect(x(), y(), w(), h());
+
+ /* grid drawing, if > 1 */
+
+ if (pParent->gridTool->getValue() > 1) {
+
+ fl_color(fl_rgb_color(54, 54, 54));
+ fl_line_style(FL_DASH, 0, NULL);
+
+ for (int i=0; i<(int) pParent->gridTool->points.size; i++) {
+ int px = pParent->gridTool->points.at(i)+x()-1;
+ fl_line(px, y()+1, px, y()+h()-2);
+ }
+ fl_line_style(0);
+ }
+
+ /* bars and beats drawing */
+
+ fl_color(COLOR_BD_0);
+ for (int i=0; i<(int) pParent->gridTool->beats.size; i++) {
+ int px = pParent->gridTool->beats.at(i)+x()-1;
+ fl_line(px, y()+1, px, y()+h()-2);
+ }
+
+ fl_color(COLOR_BG_2);
+ for (int i=0; i<(int) pParent->gridTool->bars.size; i++) {
+ int px = pParent->gridTool->bars.at(i)+x()-1;
+ fl_line(px, y()+1, px, y()+h()-2);
+ }
+
+ /* cover unused area. Avoid drawing cover if width == 0 (i.e. beats
+ * are 32) */
+
+ int coverWidth = pParent->totalWidth-pParent->coverX;
+ if (coverWidth != 0)
+ fl_rectf(pParent->coverX+x(), y()+1, coverWidth, h()-2, COLOR_BG_1);
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_actionWidget
+ *
+ * parent class of any widget inside the action editor.
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef __GE_ACTIONWIDGET_H__
+#define __GE_ACTIONWIDGET_H__
+
+#include <FL/Fl.H>
+#include <FL/Fl_Group.H>
+#include "const.h"
+
+
+class gActionWidget : public Fl_Group {
+
+protected:
+ class gdActionEditor *pParent;
+ void baseDraw(bool clear=true);
+
+public:
+ virtual void updateActions() = 0;
+
+ gActionWidget(int x, int y, int w, int h, gdActionEditor *pParent);
+ ~gActionWidget();
+};
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gd_browser
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include <limits.h>
+#include "ge_browser.h"
+#include "const.h"
+#include "utils.h"
+#include "log.h"
+
+
+gBrowser::gBrowser(int x, int y, int w, int h, const char *L)
+ : Fl_Hold_Browser(x, y, w, h, L)
+{
+ box(G_BOX);
+ textsize(11);
+ textcolor(COLOR_TEXT_0);
+ selection_color(COLOR_BG_1);
+ color(COLOR_BG_0);
+
+ this->scrollbar.color(COLOR_BG_0);
+ this->scrollbar.selection_color(COLOR_BG_1);
+ this->scrollbar.labelcolor(COLOR_BD_1);
+ this->scrollbar.slider(G_BOX);
+
+ this->hscrollbar.color(COLOR_BG_0);
+ this->hscrollbar.selection_color(COLOR_BG_1);
+ this->hscrollbar.labelcolor(COLOR_BD_1);
+ this->hscrollbar.slider(G_BOX);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+gBrowser::~gBrowser() {}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gBrowser::init(const char *init_path) {
+
+ gLog("[gBrowser] init path = '%s'\n", init_path);
+
+ if (init_path == NULL || !gIsDir(init_path)) {
+#if defined(__linux__) || defined(__APPLE__)
+ path_obj->value("/home");
+#elif defined(_WIN32)
+
+ /* SHGetFolderPath is deprecated. We should use SHGetKnownFolderPath
+ * but that would break compatibility with XP. On Vista, GetFolderPath
+ * is a wrapper of GetKnownFolderPath, so no problem. */
+
+ char winRoot[1024];
+ SHGetFolderPath(NULL, CSIDL_COMMON_DESKTOPDIRECTORY, NULL, 0, winRoot); // si parte dal Desktop
+ path_obj->value(winRoot);
+#endif
+ gLog("[gBrowser] init_path null or invalid, using default\n");
+ }
+ else
+ path_obj->value(init_path);
+
+ refresh();
+ sort();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gBrowser::refresh() {
+ DIR *dp;
+ struct dirent *ep;
+ dp = opendir(path_obj->value());
+ if (dp != NULL) {
+ while ((ep = readdir(dp))) {
+
+ /* skip:
+ * - "." e ".."
+ * - hidden files */
+
+ if (strcmp(ep->d_name, ".") != 0 && strcmp(ep->d_name, "..") != 0) {
+ if (ep->d_name[0] != '.') {
+
+ /* is it a folder? add square brackets. Is it a file? Append
+ * a '/' (on Windows seems useless, though) */
+
+ std::string file = path_obj->value();
+ file.insert(file.size(), gGetSlash());
+ file += ep->d_name;
+
+ if (gIsDir(file.c_str())) {
+ char name[PATH_MAX];
+ sprintf(name, "@b[%s]", ep->d_name);
+ add(name);
+ }
+ else
+ if (gIsProject(file.c_str())) {
+ char name[PATH_MAX];
+ sprintf(name, "@i@b%s", ep->d_name);
+ add(name);
+ }
+ else
+ add(ep->d_name);
+ }
+ }
+ }
+ closedir(dp);
+ }
+ else
+ gLog("[gBrowser] Couldn't open the directory '%s'\n", path_obj->value());
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gBrowser::sort() {
+ for (int t=1; t<=size(); t++)
+ for (int r=t+1; r<=size(); r++)
+ if (strcmp(text(t), text(r)) > 0)
+ swap(t,r);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gBrowser::up_dir() {
+
+ /* updir = remove last folder from the path. Start from strlen(-1) to
+ * skip the trailing slash */
+
+ int i = strlen(path_obj->value())-1;
+
+ /* on Windows an updir from the path "X:\" (3 chars long) must redirect
+ * to the list of available devices. */
+
+#if defined(_WIN32)
+ if (i <= 3 || !strcmp(path_obj->value(), "All drives")) {
+ path_obj->value("All drives");
+ showDrives();
+ return;
+ }
+ else {
+ while (i >= 0) {
+ if (path_obj->value()[i] == '\\')
+ break;
+ i--;
+ }
+
+ /* delete the last part of the string, from i to len-i, ie everything
+ * after the "/" */
+
+ std::string tmp = path_obj->value();
+ tmp.erase(i, tmp.size()-i);
+
+ /* if tmp.size == 2 we have something like 'C:'. Add a trailing
+ * slash */
+
+ if (tmp.size() == 2)
+ tmp += "\\";
+
+ path_obj->value(tmp.c_str());
+ refresh();
+ }
+#elif defined(__linux__) || defined (__APPLE__)
+ while (i >= 0) {
+ if (path_obj->value()[i] == '/')
+ break;
+ i--;
+ }
+
+ /* i == 0 means '/', the root dir. It's meaningless to go updir */
+
+ if (i==0)
+ path_obj->value("/");
+ else {
+
+ /* delete the last part of the string, from i to len-i, ie everything
+ * after the "/" */
+
+ std::string tmp = path_obj->value();
+ tmp.erase(i, tmp.size()-i);
+ path_obj->value(tmp.c_str());
+ }
+ refresh();
+#endif
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gBrowser::down_dir(const char *path) {
+ path_obj->value(path);
+ refresh();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+const char *gBrowser::get_selected_item() {
+
+ /* click on an empty line */
+
+ if (text(value()) == NULL)
+ return NULL;
+
+ selected_item = text(value());
+
+ /* @ = formatting marks.
+ * @b = bold, i.e. a directory. Erease '@b[' and ']' */
+
+ if (selected_item[0] == '@') {
+ if (selected_item[1] == 'b') {
+ selected_item.erase(0, 3);
+ selected_item.erase(selected_item.size()-1, 1);
+ }
+ else
+ if (selected_item[1] == 'i')
+ selected_item.erase(0, 4);
+ }
+
+#if defined(__linux__) || defined(__APPLE__)
+
+ /* add path to file name, to get an absolute path. Avoid double
+ * slashes like '//' */
+
+ if (strcmp("/", path_obj->value()))
+ selected_item.insert(0, "/");
+
+ selected_item.insert(0, path_obj->value());
+ return selected_item.c_str();
+#elif defined(_WIN32)
+
+ /* if path is 'All drives' we are in the devices list and the user
+ * has clicked on a device such as 'X:\' */
+
+ if (strcmp(path_obj->value(), "All drives") == 0)
+ return selected_item.c_str();
+ else {
+
+ /* add '\' if the path is like 'X:\' */
+
+ if (strlen(path_obj->value()) > 3) /// shouln't it be == 3?
+ selected_item.insert(0, "\\");
+
+ selected_item.insert(0, path_obj->value());
+ return selected_item.c_str();
+ }
+#endif
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+#ifdef _WIN32
+void gBrowser::showDrives() {
+
+ /* GetLogicalDriveStrings fills drives like that:
+ *
+ * a:\[null]b:\[null]c:\[null]...[null][null]
+ *
+ * where [null] stands for \0. */
+
+ char drives[64];
+ char *i = drives; // pointer to 0th element in drives
+ GetLogicalDriveStrings(64, drives);
+
+ /* code stolen from the web, still unknown. (Jan 09, 2012). */
+
+ while (*i) {
+ add(i);
+ i = &i[strlen(i) + 1];
+ }
+}
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_browser
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+#ifndef GE_BROWSER_H
+#define GE_BROWSER_H
+
+#include <FL/Fl.H>
+#include <FL/Fl_Hold_Browser.H>
+#include <string>
+#include "ge_mixed.h"
+
+class gBrowser : public Fl_Hold_Browser {
+public:
+ gBrowser(int x, int y, int w, int h, const char *L=0);
+ ~gBrowser();
+ void init(const char *init_path=NULL);
+ void refresh();
+ void sort();
+ void up_dir();
+ void down_dir(const char *path);
+ const char *get_selected_item();
+
+ /* path_obj
+ * the actual path*/
+
+ class gInput *path_obj;
+
+ /* selected_item
+ * choosen item */
+
+ std::string selected_item;
+
+#ifdef _WIN32
+private:
+
+ /* showDrives [WIN32 only]
+ * lists all the available drivers */
+
+ void showDrives();
+#endif
+};
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_channel
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "ge_channel.h"
+#include "ge_sampleChannel.h"
+#include "gd_mainWindow.h"
+#include "gd_keyGrabber.h"
+#include "gd_midiGrabber.h"
+#include "gd_editor.h"
+#include "gd_actionEditor.h"
+#include "gd_warnings.h"
+#include "gd_browser.h"
+#include "gd_midiOutputSetup.h"
+#include "gg_keyboard.h"
+#include "pluginHost.h"
+#include "mixer.h"
+#include "conf.h"
+#include "patch.h"
+#include "graphics.h"
+#include "channel.h"
+#include "wave.h"
+#include "sampleChannel.h"
+#include "midiChannel.h"
+#include "glue.h"
+#include "gui_utils.h"
+
+#ifdef WITH_VST
+#include "gd_pluginList.h"
+#endif
+
+
+extern Mixer G_Mixer;
+extern Conf G_Conf;
+extern Patch G_Patch;
+extern gdMainWindow *mainWin;
+
+
+gChannel::gChannel(int X, int Y, int W, int H, int type)
+ : Fl_Group(X, Y, W, H, NULL), type(type) {}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gChannel::getColumnIndex()
+{
+ return ((gColumn*)parent())->getIndex();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gChannel::blink()
+{
+ if (gu_getBlinker() > 6) {
+ mainButton->bgColor0 = COLOR_BG_2;
+ mainButton->bdColor = COLOR_BD_1;
+ mainButton->txtColor = COLOR_TEXT_1;
+ }
+ else {
+ mainButton->bgColor0 = COLOR_BG_0;
+ mainButton->bdColor = COLOR_BD_0;
+ mainButton->txtColor = COLOR_TEXT_0;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gChannel::setColorsByStatus(int playStatus, int recStatus)
+{
+ switch (playStatus) {
+ case STATUS_OFF:
+ mainButton->bgColor0 = COLOR_BG_0;
+ mainButton->bdColor = COLOR_BD_0;
+ mainButton->txtColor = COLOR_TEXT_0;
+ break;
+ case STATUS_PLAY:
+ mainButton->bgColor0 = COLOR_BG_2;
+ mainButton->bdColor = COLOR_BD_1;
+ mainButton->txtColor = COLOR_TEXT_1;
+ break;
+ case STATUS_WAIT:
+ blink();
+ break;
+ case STATUS_ENDING:
+ mainButton->bgColor0 = COLOR_BD_0;
+ break;
+ }
+
+ switch (recStatus) {
+ case REC_WAITING:
+ blink();
+ break;
+ case REC_ENDING:
+ mainButton->bgColor0 = COLOR_BD_0;
+ break;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gChannel::handleKey(int e, int key)
+{
+ int ret;
+ if (e == FL_KEYDOWN && button->value()) // key already pressed! skip it
+ ret = 1;
+ else
+ if (Fl::event_key() == key && !button->value()) {
+ button->take_focus(); // move focus to this button
+ button->value((e == FL_KEYDOWN || e == FL_SHORTCUT) ? 1 : 0); // change the button's state
+ button->do_callback(); // invoke the button's callback
+ ret = 1;
+ }
+ else
+ ret = 0;
+
+ if (Fl::event_key() == key)
+ button->value((e == FL_KEYDOWN || e == FL_SHORTCUT) ? 1 : 0); // change the button's state
+
+ return ret;
+}
+
+
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+gStatus::gStatus(int x, int y, int w, int h, SampleChannel *ch, const char *L)
+: Fl_Box(x, y, w, h, L), ch(ch) {}
+
+void gStatus::draw()
+{
+ fl_rect(x(), y(), w(), h(), COLOR_BD_0); // reset border
+ fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_0); // reset background
+
+ if (ch != NULL) {
+ if (ch->status & (STATUS_WAIT | STATUS_ENDING | REC_ENDING | REC_WAITING) ||
+ ch->recStatus & (REC_WAITING | REC_ENDING))
+ {
+ fl_rect(x(), y(), w(), h(), COLOR_BD_1);
+ }
+ else
+ if (ch->status == STATUS_PLAY)
+ fl_rect(x(), y(), w(), h(), COLOR_BD_1);
+ else
+ fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_0); // status empty
+
+
+ if (G_Mixer.chanInput == ch)
+ fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_3); // take in progress
+ else
+ if (recorder::active && recorder::canRec(ch))
+ fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_4); // action record
+
+ /* equation for the progress bar:
+ * ((chanTracker - chanStart) * w()) / (chanEnd - chanStart). */
+
+ int pos = ch->getPosition();
+ if (pos == -1)
+ pos = 0;
+ else
+ pos = (pos * (w()-1)) / (ch->end - ch->begin);
+ fl_rectf(x()+1, y()+1, pos, h()-2, COLOR_BG_2);
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+gModeBox::gModeBox(int x, int y, int w, int h, SampleChannel *ch, const char *L)
+ : Fl_Menu_Button(x, y, w, h, L), ch(ch)
+{
+ box(G_BOX);
+ textsize(11);
+ textcolor(COLOR_TEXT_0);
+ color(COLOR_BG_0);
+
+ add("Loop . basic", 0, cb_change_chanmode, (void *)LOOP_BASIC);
+ add("Loop . once", 0, cb_change_chanmode, (void *)LOOP_ONCE);
+ add("Loop . once . bar", 0, cb_change_chanmode, (void *)LOOP_ONCE_BAR);
+ add("Loop . repeat", 0, cb_change_chanmode, (void *)LOOP_REPEAT);
+ add("Oneshot . basic", 0, cb_change_chanmode, (void *)SINGLE_BASIC);
+ add("Oneshot . press", 0, cb_change_chanmode, (void *)SINGLE_PRESS);
+ add("Oneshot . retrig", 0, cb_change_chanmode, (void *)SINGLE_RETRIG);
+ add("Oneshot . endless", 0, cb_change_chanmode, (void *)SINGLE_ENDLESS);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gModeBox::draw() {
+ fl_rect(x(), y(), w(), h(), COLOR_BD_0); // border
+ switch (ch->mode) {
+ case LOOP_BASIC:
+ fl_draw_pixmap(loopBasic_xpm, x()+1, y()+1);
+ break;
+ case LOOP_ONCE:
+ fl_draw_pixmap(loopOnce_xpm, x()+1, y()+1);
+ break;
+ case LOOP_ONCE_BAR:
+ fl_draw_pixmap(loopOnceBar_xpm, x()+1, y()+1);
+ break;
+ case LOOP_REPEAT:
+ fl_draw_pixmap(loopRepeat_xpm, x()+1, y()+1);
+ break;
+ case SINGLE_BASIC:
+ fl_draw_pixmap(oneshotBasic_xpm, x()+1, y()+1);
+ break;
+ case SINGLE_PRESS:
+ fl_draw_pixmap(oneshotPress_xpm, x()+1, y()+1);
+ break;
+ case SINGLE_RETRIG:
+ fl_draw_pixmap(oneshotRetrig_xpm, x()+1, y()+1);
+ break;
+ case SINGLE_ENDLESS:
+ fl_draw_pixmap(oneshotEndless_xpm, x()+1, y()+1);
+ break;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gModeBox::cb_change_chanmode(Fl_Widget *v, void *p) { ((gModeBox*)v)->__cb_change_chanmode((intptr_t)p); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gModeBox::__cb_change_chanmode(int mode)
+{
+ ch->mode = mode;
+
+ /* what to do when the channel is playing and you change the mode?
+ * Nothing, since v0.5.3. Just refresh the action editor window, in
+ * case it's open */
+
+ gu_refreshActionEditor();
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+gMainButton::gMainButton(int x, int y, int w, int h, const char *l)
+ : gClick(x, y, w, h, l) {}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_channel
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_CHANNEL_H
+#define GE_CHANNEL_H
+
+
+#include <FL/Fl_Scroll.H>
+#include <FL/Fl_Box.H>
+#include <FL/Fl_Menu_Button.H>
+#include "ge_mixed.h"
+
+
+class gChannel : public Fl_Group
+{
+protected:
+
+ /* define some breakpoints for dynamic resize */
+
+#ifdef WITH_VST
+ static const int BREAK_READ_ACTIONS = 212;
+ static const int BREAK_MODE_BOX = 188;
+ static const int BREAK_FX = 164;
+ static const int BREAK_DELTA = 120;
+#else
+ static const int BREAK_READ_ACTIONS = 188;
+ static const int BREAK_MODE_BOX = 164;
+ static const int BREAK_FX = 140;
+ static const int BREAK_DELTA = 96;
+#endif
+ static const int BREAK_UNIT = 24;
+
+ /* blink
+ * blink button when channel is in wait/ending status. */
+
+ void blink();
+
+ /* setColorByStatus
+ * update colors depending on channel status. */
+
+ void setColorsByStatus(int playStatus, int recStatus);
+
+ /* handleKey
+ * method wrapped by virtual handle(int e). */
+
+ int handleKey(int e, int key);
+
+public:
+
+ gChannel(int x, int y, int w, int h, int type);
+
+ /* reset
+ * reset channel to initial status. */
+
+ virtual void reset() = 0;
+
+ /* update
+ * update the label of sample button and everything else such as 'R'
+ * button, key box and so on, according to global values. */
+
+ virtual void update() = 0;
+
+ /* refresh
+ * update graphics. */
+
+ virtual void refresh() = 0;
+
+ /* keypress
+ * what to do when the corresponding key is pressed. */
+
+ virtual int keyPress(int event) = 0;
+
+ /* getColumnIndex
+ * return the numeric index of the column in which this channel is
+ * located. */
+
+ int getColumnIndex();
+
+ class gButton *button;
+ class gStatus *status;
+ class gMainButton *mainButton;
+ class gDial *vol;
+ class gClick *mute;
+ class gClick *solo;
+#ifdef WITH_VST
+ class gFxButton *fx;
+#endif
+
+ int type;
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+class gStatus : public Fl_Box
+{
+public:
+ gStatus(int X, int Y, int W, int H, class SampleChannel *ch, const char *L=0);
+ void draw();
+ class SampleChannel *ch;
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+class gModeBox : public Fl_Menu_Button
+{
+private:
+ static void cb_change_chanmode(Fl_Widget *v, void *p);
+ inline void __cb_change_chanmode(int mode);
+
+ class SampleChannel *ch;
+
+public:
+ gModeBox(int x, int y, int w, int h, class SampleChannel *ch, const char *l=0);
+ void draw();
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+/* gMainButton
+ * base main button for MIDI and Sample Channels. */
+
+class gMainButton : public gClick
+{
+public:
+ gMainButton(int x, int y, int w, int h, const char *l=0);
+ virtual int handle(int e) = 0;
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_column
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "ge_column.h"
+#include "gd_mainWindow.h"
+#include "gd_warnings.h"
+#include "gg_keyboard.h"
+#include "ge_channel.h"
+#include "ge_sampleChannel.h"
+#include "ge_midiChannel.h"
+#include "mixer.h"
+#include "conf.h"
+#include "log.h"
+#include "patch.h"
+#include "glue.h"
+#include "channel.h"
+#include "sampleChannel.h"
+#include "midiChannel.h"
+
+#ifdef WITH_VST
+ #include "gd_pluginList.h"
+#endif
+
+
+extern Mixer G_Mixer;
+extern Conf G_Conf;
+extern Patch G_Patch;
+extern gdMainWindow *mainWin;
+
+
+gColumn::gColumn(int X, int Y, int W, int H, int index, gKeyboard *parent)
+ : Fl_Group(X, Y, W, H), parent(parent), index(index)
+{
+ /* gColumn does a bit of a mess: we pass a pointer to its parent (gKeyboard) and
+ the gColumn itself deals with the creation of another widget, outside gColumn
+ and inside gKeyboard, which handles the vertical resize bar (gResizerBar).
+ The resizer cannot stay inside gColumn: it needs a broader view on the other
+ side widgets. The view can be obtained from gKeyboard only (the upper level).
+ Unfortunately, parent() can be NULL: at this point (i.e the constructor)
+ gColumn is still detached from any parent. We use a custom gKeyboard *parent
+ instead. */
+
+ begin();
+ addChannelBtn = new gClick(x(), y(), w(), 20, "Add new channel");
+ end();
+
+ resizer = new gResizerBar(x()+w(), y(), 16, h(), false);
+ resizer->setMinSize(140);
+ parent->add(resizer);
+
+ addChannelBtn->callback(cb_addChannel, (void*)this);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gColumn::~gColumn()
+{
+ /* FIXME - this could actually cause a memory leak. resizer is
+ just removed, not deleted. But we cannot delete it right now. */
+
+ parent->remove(resizer);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gColumn::handle(int e)
+{
+ switch (e) {
+ case FL_DND_ENTER: // return(1) for these events to 'accept' dnd
+ case FL_DND_DRAG:
+ case FL_DND_RELEASE: {
+ return 1;
+ }
+ case FL_PASTE: { // handle actual drop (paste) operation
+ gVector<std::string> paths;
+ gSplit(Fl::event_text(), "\n", &paths);
+ bool fails = false;
+ int result = 0;
+ for (unsigned i=0; i<paths.size; i++) {
+ gLog("[gColumn::handle] loading %s...\n", paths.at(i).c_str());
+ SampleChannel *c = (SampleChannel*) glue_addChannel(index, CHANNEL_SAMPLE);
+ result = glue_loadChannel(c, gStripFileUrl(paths.at(i).c_str()).c_str());
+ if (result != SAMPLE_LOADED_OK) {
+ deleteChannel(c->guiChannel);
+ fails = true;
+ }
+ }
+ if (fails) {
+ if (paths.size > 1)
+ gdAlert("Some files were not loaded successfully.");
+ else
+ parent->printChannelMessage(result);
+ }
+ return 1;
+ }
+ }
+
+ /* we return fl_Group::handle only if none of the cases above are fired. That
+ is because we don't want to propagate a dnd drop to all the sub widgets. */
+
+ return Fl_Group::handle(e);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gColumn::resize(int X, int Y, int W, int H)
+{
+ /* resize all children */
+
+ int ch = children();
+ for (int i=0; i<ch; i++) {
+ Fl_Widget *c = child(i);
+ c->resize(X, Y + (i * (c->h() + 4)), W, c->h());
+ }
+
+ /* resize group itself */
+
+ x(X); y(Y); w(W); h(H);
+
+ /* resize resizerBar */
+
+ resizer->size(16, H);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gColumn::refreshChannels()
+{
+ for (int i=1; i<children(); i++)
+ ((gChannel*) child(i))->refresh();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gColumn::draw()
+{
+ fl_color(fl_rgb_color(27, 27, 27));
+ fl_rectf(x(), y(), w(), h());
+
+ /* call draw and then redraw in order to avoid channel corruption when
+ scrolling horizontally */
+
+ for (int i=0; i<children(); i++) {
+ child(i)->draw();
+ child(i)->redraw();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gColumn::cb_addChannel(Fl_Widget *v, void *p) { ((gColumn*)p)->__cb_addChannel(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gChannel *gColumn::addChannel(class Channel *ch)
+{
+ gChannel *gch = NULL;
+
+ if (ch->type == CHANNEL_SAMPLE)
+ gch = (gSampleChannel*) new gSampleChannel(
+ x(),
+ y() + children() * 24,
+ 600, // (1) see notes below
+ 20,
+ (SampleChannel*) ch);
+ else
+ gch = (gMidiChannel*) new gMidiChannel(
+ x(),
+ y() + children() * 24,
+ w(),
+ 20,
+ (MidiChannel*) ch);
+
+ /* (1) we create a new sample channel with a fake width, instead of w() (i.e.
+ the column width), in case the column is too narrow to display all widgets.
+ This workaround prevents the widgets to disappear if they have an initial
+ negative width. MidiChannel does not need such hack because it already fits
+ nicely in a collapsed column. */
+
+ add(gch);
+ resize(x(), y(), w(), (children() * 24) + 66); // evil space for drag n drop
+ gch->redraw(); // avoid corruption
+ parent->redraw(); // redraw Keyboard
+ return gch;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gColumn::deleteChannel(gChannel *gch)
+{
+ gch->hide();
+ remove(gch);
+ delete gch;
+
+ /* reposition all other channels and resize this group */
+ /** TODO
+ * reposition is useless when called by gColumn::clear(). Add a new
+ * parameter to skip the operation */
+
+ for (int i=0; i<children(); i++) {
+ gch = (gChannel*) child(i);
+ gch->position(gch->x(), y()+(i*24));
+ }
+ size(w(), children() * 24 + 66); // evil space for drag n drop
+ redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gColumn::__cb_addChannel()
+{
+ gLog("[gColumn::__cb_addChannel] index = %d\n", index);
+ int type = openTypeMenu();
+ if (type)
+ glue_addChannel(index, type);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gColumn::openTypeMenu()
+{
+ Fl_Menu_Item rclick_menu[] = {
+ {"Sample channel"},
+ {"MIDI channel"},
+ {0}
+ };
+
+ Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50);
+ b->box(G_BOX);
+ b->textsize(11);
+ b->textcolor(COLOR_TEXT_0);
+ b->color(COLOR_BG_0);
+
+ const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
+ if (!m) return 0;
+
+ if (strcmp(m->label(), "Sample channel") == 0)
+ return CHANNEL_SAMPLE;
+ if (strcmp(m->label(), "MIDI channel") == 0)
+ return CHANNEL_MIDI;
+ return 0;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gColumn::clear(bool full)
+{
+ if (full)
+ Fl_Group::clear();
+ else {
+ while (children() >= 2) { // skip "add new channel" btn
+ int i = children()-1;
+ deleteChannel((gChannel*)child(i));
+ }
+ }
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_column
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_COLUMN_H
+#define GE_COLUMN_H
+
+
+#include <FL/Fl.H>
+#include <FL/Fl_Group.H>
+
+
+class gColumn : public Fl_Group
+{
+private:
+
+ static void cb_addChannel (Fl_Widget *v, void *p);
+ inline void __cb_addChannel();
+
+ int openTypeMenu();
+
+ class gClick *addChannelBtn;
+ class gResizerBar *resizer;
+ class gKeyboard *parent;
+
+ int index;
+
+public:
+
+ gColumn(int x, int y, int w, int h, int index, class gKeyboard *parent);
+ ~gColumn();
+
+ /* addChannel
+ * add a new channel in this column and set the internal pointer
+ * to channel to 'ch'. */
+
+ class gChannel *addChannel(class Channel *ch);
+
+ /* handle */
+
+ int handle(int e);
+
+ /* resize
+ * custom resize behavior. */
+
+ void resize(int x, int y, int w, int h);
+
+ /* deleteChannel
+ * remove the channel 'gch' from this column. */
+
+ void deleteChannel(gChannel *gch);
+
+ /* refreshChannels
+ * update channels' graphical statues. Called on each GUI cycle. */
+
+ void refreshChannels();
+
+ /* clear
+ * remove all channels from the column. If full==true, delete also the
+ * "add new channel" button. This method ovverrides the inherited one
+ * from Fl_Group. */
+
+ void clear(bool full=false);
+
+ void draw();
+
+ inline int getIndex() { return index; }
+ inline void setIndex(int i) { index = i; }
+ inline bool isEmpty() { return children() == 1; }
+};
+
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_envelopeWidget
+ *
+ * Parent class of any envelope controller, from volume to VST parameter
+ * automations.
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include <FL/fl_draw.H>
+#include "ge_envelopeChannel.h"
+#include "gd_actionEditor.h"
+#include "gd_mainWindow.h"
+#include "gg_keyboard.h"
+#include "channel.h"
+#include "recorder.h"
+#include "mixer.h"
+
+
+extern Mixer G_Mixer;
+extern gdMainWindow *mainWin;
+
+
+gEnvelopeChannel::gEnvelopeChannel(int x, int y, gdActionEditor *pParent, int type, int range, const char *l)
+ : gActionWidget(x, y, 200, 80, pParent), l(l), type(type), range(range),
+ selectedPoint(-1), draggedPoint(-1)
+{
+ size(pParent->totalWidth, h());
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+gEnvelopeChannel::~gEnvelopeChannel() {
+ clearPoints();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gEnvelopeChannel::addPoint(int frame, int iValue, float fValue, int px, int py) {
+ point p;
+ p.frame = frame;
+ p.iValue = iValue;
+ p.fValue = fValue;
+ p.x = px;
+ p.y = py;
+ points.add(p);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gEnvelopeChannel::updateActions() {
+ for (unsigned i=0; i<points.size; i++)
+ points.at(i).x = points.at(i).frame / pParent->zoom;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gEnvelopeChannel::draw() {
+
+ baseDraw();
+
+ /* print label */
+
+ fl_color(COLOR_BG_1);
+ fl_font(FL_HELVETICA, 12);
+ fl_draw(l, x()+4, y(), 80, h(), (Fl_Align) (FL_ALIGN_LEFT));
+
+ int pxOld = x()-3;
+ int pyOld = y()+1;
+ int pxNew = 0;
+ int pyNew = 0;
+
+ fl_color(COLOR_BG_2);
+
+ for (unsigned i=0; i<points.size; i++) {
+
+ pxNew = points.at(i).x+x()-3;
+ pyNew = points.at(i).y+y();
+
+ if (selectedPoint == (int) i) {
+ fl_color(COLOR_BD_1);
+ fl_rectf(pxNew, pyNew, 7, 7);
+ fl_color(COLOR_BG_2);
+ }
+ else
+ fl_rectf(pxNew, pyNew, 7, 7);
+
+ if (i > 0)
+ fl_line(pxOld+3, pyOld+3, pxNew+3, pyNew+3);
+
+ pxOld = pxNew;
+ pyOld = pyNew;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gEnvelopeChannel::handle(int e) {
+
+ /* Adding an action: no further checks required, just record it on frame
+ * mx*pParent->zoom. Deleting action is trickier: find the active
+ * point and derive from it the corresponding frame. */
+
+ int ret = 0;
+ int mx = Fl::event_x()-x(); // mouse x
+ int my = Fl::event_y()-y(); // mouse y
+
+ switch (e) {
+
+ case FL_ENTER: {
+ ret = 1;
+ break;
+ }
+
+ case FL_MOVE: {
+ selectedPoint = getSelectedPoint();
+ redraw();
+ ret = 1;
+ break;
+ }
+
+ case FL_LEAVE: {
+ draggedPoint = -1;
+ selectedPoint = -1;
+ redraw();
+ ret = 1;
+ break;
+ }
+
+ case FL_PUSH: {
+
+ /* left click on point: drag
+ * right click on point: delete
+ * left click on void: add */
+
+ if (Fl::event_button1()) {
+
+ if (selectedPoint != -1) {
+ draggedPoint = selectedPoint;
+ }
+ else {
+
+ /* top & border fix */
+
+ if (my > h()-8) my = h()-8;
+ if (mx > pParent->coverX-x()) mx = pParent->coverX-x();
+
+ if (range == RANGE_FLOAT) {
+
+ /* if this is the first point ever, add other two points at the beginning
+ * and the end of the range */
+
+ if (points.size == 0) {
+ addPoint(0, 0, 1.0f, 0, 1);
+ recorder::rec(pParent->chan->index, type, 0, 0, 1.0f);
+ addPoint(G_Mixer.totalFrames, 0, 1.0f, pParent->coverX, 1);
+ recorder::rec(pParent->chan->index, type, G_Mixer.totalFrames, 0, 1.0f);
+ }
+
+ /* line between 2 points y = (x-a) / (b-a); a = h() - 8; b = 1 */
+
+ int frame = mx * pParent->zoom;
+ float value = (my - h() + 8) / (float) (1 - h() + 8);
+ addPoint(frame, 0, value, mx, my);
+ recorder::rec(pParent->chan->index, type, frame, 0, value);
+ recorder::sortActions();
+ sortPoints();
+ }
+ else {
+ /// TODO
+ }
+ mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel); // update mainWindow
+ redraw();
+ }
+ }
+ else {
+
+ /* right click on point 0 or point size-1 deletes the entire envelope */
+
+ if (selectedPoint != -1) {
+ if (selectedPoint == 0 || (unsigned) selectedPoint == points.size-1) {
+ recorder::clearAction(pParent->chan->index, type);
+ points.clear();
+ }
+ else {
+ recorder::deleteAction(pParent->chan->index, points.at(selectedPoint).frame, type, false);
+ recorder::sortActions();
+ points.del(selectedPoint);
+ }
+ mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel); // update mainWindow
+ redraw();
+ }
+ }
+
+ ret = 1;
+ break;
+ }
+
+ case FL_RELEASE: {
+ if (draggedPoint != -1) {
+
+ if (points.at(draggedPoint).x == previousXPoint) {
+ //gLog("nothing to do\n");
+ }
+ else {
+ int newFrame = points.at(draggedPoint).x * pParent->zoom;
+
+ /* x edge correction */
+
+ if (newFrame < 0)
+ newFrame = 0;
+ else if (newFrame > G_Mixer.totalFrames)
+ newFrame = G_Mixer.totalFrames;
+
+ /* vertical line check */
+
+ int vp = verticalPoint(points.at(draggedPoint));
+ if (vp == 1) newFrame -= 256;
+ else if (vp == -1) newFrame += 256;
+
+ /* delete previous point and record a new one */
+
+ recorder::deleteAction(pParent->chan->index, points.at(draggedPoint).frame, type, false);
+
+ if (range == RANGE_FLOAT) {
+ float value = (points.at(draggedPoint).y - h() + 8) / (float) (1 - h() + 8);
+ recorder::rec(pParent->chan->index, type, newFrame, 0, value);
+ }
+ else {
+ /// TODO
+ }
+
+ recorder::sortActions();
+ points.at(draggedPoint).frame = newFrame;
+ draggedPoint = -1;
+ selectedPoint = -1;
+ }
+ }
+ ret = 1;
+ break;
+ }
+
+ case FL_DRAG: {
+
+ if (draggedPoint != -1) {
+
+ /* y constraint */
+
+ if (my > h()-8)
+ points.at(draggedPoint).y = h()-8;
+ else
+ if (my < 1)
+ points.at(draggedPoint).y = 1;
+ else
+ points.at(draggedPoint).y = my;
+
+ /* x constraint
+ * constrain the point between two ends (leftBorder-point, point-point,
+ * point-rightBorder). First & last points cannot be shifted on x */
+
+ if (draggedPoint == 0)
+ points.at(draggedPoint).x = x()-8;
+ else
+ if ((unsigned) draggedPoint == points.size-1)
+ points.at(draggedPoint).x = pParent->coverX;
+ else {
+ int prevPoint = points.at(draggedPoint-1).x;
+ int nextPoint = points.at(draggedPoint+1).x;
+ if (mx <= prevPoint)
+ points.at(draggedPoint).x = prevPoint;
+ else
+ if (mx >= nextPoint)
+ points.at(draggedPoint).x = nextPoint;
+ //else
+ // points.at(draggedPoint).x = mx;
+ else {
+ if (pParent->gridTool->isOn())
+ points.at(draggedPoint).x = pParent->gridTool->getSnapPoint(mx)-1;
+ else
+ points.at(draggedPoint).x = mx;
+ }
+ }
+ redraw();
+ }
+
+ ret = 1;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gEnvelopeChannel::verticalPoint(const point &p) {
+ for (unsigned i=0; i<points.size; i++) {
+ if (&p == &points.at(i)) {
+ if (i == 0 || i == points.size-1) // first or last point
+ return 0;
+ else {
+ if (points.at(i-1).x == p.x) // vertical with point[i-1]
+ return -1;
+ else
+ if (points.at(i+1).x == p.x) // vertical with point[i+1]
+ return 1;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gEnvelopeChannel::sortPoints() {
+ for (unsigned i=0; i<points.size; i++)
+ for (unsigned j=0; j<points.size; j++)
+ if (points.at(j).x > points.at(i).x)
+ points.swap(j, i);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gEnvelopeChannel::getSelectedPoint() {
+
+ /* point is a 7x7 dot */
+
+ for (unsigned i=0; i<points.size; i++) {
+ if (Fl::event_x() >= points.at(i).x+x()-4 &&
+ Fl::event_x() <= points.at(i).x+x()+4 &&
+ Fl::event_y() >= points.at(i).y+y() &&
+ Fl::event_y() <= points.at(i).y+y()+7)
+ return i;
+ }
+ return -1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gEnvelopeChannel::fill() {
+ points.clear();
+ for (unsigned i=0; i<recorder::global.size; i++)
+ for (unsigned j=0; j<recorder::global.at(i).size; j++) {
+ recorder::action *a = recorder::global.at(i).at(j);
+ if (a->type == type && a->chan == pParent->chan->index) {
+ if (range == RANGE_FLOAT)
+ addPoint(
+ a->frame, // frame
+ 0, // int value (unused)
+ a->fValue, // float value
+ a->frame / pParent->zoom, // x
+ ((1-h()+8)*a->fValue)+h()-8); // y = (b-a)x + a (line between two points)
+ // else: TODO
+ }
+ }
+
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_envelopeWidget
+ *
+ * parent class of any envelope controller, from volume to VST parameter
+ * automations.
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+#ifndef __GE_ENVELOPECHANNEL_H__
+#define __GE_ENVELOPECHANNEL_H__
+
+#include <FL/Fl.H>
+#include <FL/Fl_Group.H>
+#include "ge_actionWidget.h"
+#include "utils.h"
+
+
+class gEnvelopeChannel : public gActionWidget {
+
+ const char *l; // internal label
+ int type; // type of action
+ int range;
+
+ /* point
+ * a single dot in the graph. x = relative frame, y = relative value */
+
+ struct point {
+ int frame;
+ int iValue;
+ float fValue;
+ int x;
+ int y;
+ };
+
+ /* points
+ * array of points, filled by fillPoints() */
+
+ gVector<point> points;
+
+ /* selectedPoint
+ * which point we are selecting? */
+
+ int selectedPoint;
+
+ /* draggedPoint
+ * which point we are dragging? */
+
+ int draggedPoint;
+
+ /* previousXPoint
+ * x coordinate of point at time t-1. Used to check effective shifts */
+
+ int previousXPoint;
+
+ void draw();
+
+ int handle(int e);
+
+ int getSelectedPoint();
+
+ void sortPoints();
+
+ /* verticalPoint
+ * check if two points form a vertical line. In that case the frame value
+ * would be the same and recorder would go crazy, so shift by a small value
+ * of frames to create a minimal fadein/fadeout level. Return 0: no
+ * vertical points; return 1: vertical with the next one, return -1: vertical
+ * with the previous one. */
+
+ int verticalPoint(const point &p);
+
+public:
+ gEnvelopeChannel(int x, int y, gdActionEditor *pParent, int type, int range, const char *l);
+ ~gEnvelopeChannel();
+
+ /* addPoint
+ * add a point made of frame+value to internal points[]. */
+
+ void addPoint(int frame, int iValue=0, float fValue=0.0f, int x=-1, int y=-1);
+
+ void updateActions();
+
+ /* fill
+ * parse recorder's stack and fill the widget with points. It's up to
+ * the caller to call this method as initialization. */
+
+ void fill();
+
+ inline void clearPoints() { points.clear(); }
+};
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_midiChannel
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "ge_midiChannel.h"
+#include "ge_channel.h"
+#include "ge_sampleChannel.h"
+#include "gd_mainWindow.h"
+#include "gd_keyGrabber.h"
+#include "gd_midiGrabber.h"
+#include "gd_editor.h"
+#include "gd_actionEditor.h"
+#include "gd_warnings.h"
+#include "gd_browser.h"
+#include "gd_keyGrabber.h"
+#include "gd_midiOutputSetup.h"
+#include "gg_keyboard.h"
+#include "pluginHost.h"
+#include "mixer.h"
+#include "conf.h"
+#include "patch.h"
+#include "graphics.h"
+#include "channel.h"
+#include "wave.h"
+#include "sampleChannel.h"
+#include "midiChannel.h"
+#include "glue.h"
+#include "gui_utils.h"
+
+#ifdef WITH_VST
+#include "gd_pluginList.h"
+#endif
+
+
+extern Mixer G_Mixer;
+extern Conf G_Conf;
+extern Patch G_Patch;
+extern gdMainWindow *mainWin;
+
+
+gMidiChannel::gMidiChannel(int X, int Y, int W, int H, class MidiChannel *ch)
+ : gChannel(X, Y, W, H, CHANNEL_MIDI), ch(ch)
+{
+ begin();
+
+#if defined(WITH_VST)
+ int delta = 120; // (5 widgets * 20) + (5 paddings * 4)
+#else
+ int delta = 96; // (4 widgets * 20) + (4 paddings * 4)
+#endif
+
+ button = new gButton(x(), y(), 20, 20);
+ mainButton = new gMidiMainButton(button->x()+button->w()+4, y(), w() - delta, 20, "-- MIDI --");
+ mute = new gClick(mainButton->x()+mainButton->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm);
+ solo = new gClick(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm);
+#if defined(WITH_VST)
+ fx = new gFxButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm);
+ vol = new gDial(fx->x()+fx->w()+4, y(), 20, 20);
+#else
+ vol = new gDial(solo->x()+solo->w()+4, y(), 20, 20);
+#endif
+
+ end();
+
+ resizable(mainButton);
+
+ update();
+
+ button->callback(cb_button, (void*)this);
+ button->when(FL_WHEN_CHANGED); // do callback on keypress && on keyrelease
+
+#ifdef WITH_VST
+ fx->callback(cb_openFxWindow, (void*)this);
+#endif
+
+ mute->type(FL_TOGGLE_BUTTON);
+ mute->callback(cb_mute, (void*)this);
+
+ solo->type(FL_TOGGLE_BUTTON);
+ solo->callback(cb_solo, (void*)this);
+
+ mainButton->callback(cb_openMenu, (void*)this);
+ vol->callback(cb_changeVol, (void*)this);
+
+ ch->guiChannel = this;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gMidiChannel::cb_button (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_button(); }
+void gMidiChannel::cb_mute (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_mute(); }
+void gMidiChannel::cb_solo (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_solo(); }
+void gMidiChannel::cb_openMenu (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_openMenu(); }
+void gMidiChannel::cb_changeVol (Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_changeVol(); }
+#ifdef WITH_VST
+void gMidiChannel::cb_openFxWindow(Fl_Widget *v, void *p) { ((gMidiChannel*)p)->__cb_openFxWindow(); }
+#endif
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gMidiChannel::__cb_mute()
+{
+ glue_setMute(ch);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gMidiChannel::__cb_solo()
+{
+ solo->value() ? glue_setSoloOn(ch) : glue_setSoloOff(ch);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gMidiChannel::__cb_changeVol()
+{
+ glue_setChanVol(ch, vol->value());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+void gMidiChannel::__cb_openFxWindow()
+{
+ gu_openSubWindow(mainWin, new gdPluginList(PluginHost::CHANNEL, ch), WID_FX_LIST);
+}
+#endif
+
+/* -------------------------------------------------------------------------- */
+
+
+void gMidiChannel::__cb_button()
+{
+ if (button->value())
+ glue_keyPress(ch, Fl::event_ctrl(), Fl::event_shift());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gMidiChannel::__cb_openMenu()
+{
+ Fl_Menu_Item rclick_menu[] = {
+ {"Edit actions..."}, // 0
+ {"Clear actions", 0, 0, 0, FL_SUBMENU}, // 1
+ {"All"}, // 2
+ {0}, // 3
+ {"Setup keyboard input..."}, // 5
+ {"Setup MIDI input..."}, // 6
+ {"Setup MIDI output..."}, // 7
+ {"Delete channel"}, // 8
+ {0}
+ };
+
+ /* no 'clear actions' if there are no actions */
+
+ if (!ch->hasActions)
+ rclick_menu[1].deactivate();
+
+ Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50);
+ b->box(G_BOX);
+ b->textsize(11);
+ b->textcolor(COLOR_TEXT_0);
+ b->color(COLOR_BG_0);
+
+ const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
+ if (!m) return;
+
+ if (strcmp(m->label(), "Delete channel") == 0) {
+ if (!gdConfirmWin("Warning", "Delete channel: are you sure?"))
+ return;
+ glue_deleteChannel(ch);
+ return;
+ }
+
+ if (strcmp(m->label(), "Setup keyboard input...") == 0) {
+ gu_openSubWindow(mainWin, new gdKeyGrabber(ch), 0);
+ //new gdKeyGrabber(ch);
+ return;
+ }
+
+ if (strcmp(m->label(), "All") == 0) {
+ if (!gdConfirmWin("Warning", "Clear all actions: are you sure?"))
+ return;
+ recorder::clearChan(ch->index);
+ gu_refreshActionEditor(); // refresh a.editor window, it could be open
+ return;
+ }
+
+ if (strcmp(m->label(), "Edit actions...") == 0) {
+ gu_openSubWindow(mainWin, new gdActionEditor(ch), WID_ACTION_EDITOR);
+ return;
+ }
+
+ if (strcmp(m->label(), "Setup MIDI output...") == 0) {
+ gu_openSubWindow(mainWin, new gdMidiOutputSetup(ch), 0);
+ return;
+ }
+
+ if (strcmp(m->label(), "Setup MIDI input...") == 0) {
+ gu_openSubWindow(mainWin, new gdMidiGrabberChannel(ch), 0);
+ return;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gMidiChannel::refresh()
+{
+ setColorsByStatus(ch->status, ch->recStatus);
+ mainButton->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gMidiChannel::reset()
+{
+ mainButton->bgColor0 = COLOR_BG_0;
+ mainButton->bdColor = COLOR_BD_0;
+ mainButton->txtColor = COLOR_TEXT_0;
+ mainButton->label("-- MIDI --");
+ mainButton->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gMidiChannel::update()
+{
+ if (ch->midiOut) {
+ char tmp[32];
+ sprintf(tmp, "-- MIDI (channel %d) --", ch->midiOutChan+1);
+ mainButton->copy_label(tmp);
+ }
+ else
+ mainButton->label("-- MIDI --");
+
+ vol->value(ch->volume);
+ mute->value(ch->mute);
+ solo->value(ch->solo);
+
+#ifdef WITH_VST
+ fx->full = ch->plugins.size > 0;
+#endif
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gMidiChannel::resize(int X, int Y, int W, int H)
+{
+ gChannel::resize(X, Y, W, H);
+
+ /* this stuff makes sense only with FX button available. Do nothing
+ * otherwise */
+
+#ifdef WITH_VST
+ if (w() < BREAK_FX) {
+ fx->hide();
+
+ mainButton->size(w() - (BREAK_DELTA - BREAK_UNIT), mainButton->h());
+ }
+ else {
+ fx->show();
+ mainButton->size(w() - BREAK_DELTA, mainButton->h());
+ }
+ mute->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20);
+ solo->resize(mute->x()+mute->w()+4, y(), 20, 20);
+
+ gChannel::init_sizes();
+#endif
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gMidiChannel::keyPress(int e)
+{
+ return handleKey(e, ch->key);
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+gMidiMainButton::gMidiMainButton(int x, int y, int w, int h, const char *l)
+ : gMainButton(x, y, w, h, l) {}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gMidiMainButton::handle(int e)
+{
+ // MIDI drag-n-drop does nothing so far.
+ return gClick::handle(e);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_midiChannel
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_MIDI_CHANNEL_H
+#define GE_MIDI_CHANNEL_H
+
+
+#include <FL/Fl_Scroll.H>
+#include <FL/Fl_Box.H>
+#include <FL/Fl_Menu_Button.H>
+#include "ge_channel.h"
+#include "ge_mixed.h"
+
+
+class gMidiChannel : public gChannel
+{
+private:
+
+ static void cb_button (Fl_Widget *v, void *p);
+ static void cb_mute (Fl_Widget *v, void *p);
+ static void cb_solo (Fl_Widget *v, void *p);
+ static void cb_openMenu (Fl_Widget *v, void *p);
+ static void cb_changeVol (Fl_Widget *v, void *p);
+#ifdef WITH_VST
+ static void cb_openFxWindow (Fl_Widget *v, void *p);
+#endif
+
+ inline void __cb_mute ();
+ inline void __cb_solo ();
+ inline void __cb_changeVol ();
+ inline void __cb_button ();
+ inline void __cb_openMenu ();
+ inline void __cb_readActions ();
+#ifdef WITH_VST
+ inline void __cb_openFxWindow();
+#endif
+
+public:
+
+ gMidiChannel(int x, int y, int w, int h, class MidiChannel *ch);
+
+ void reset ();
+ void update ();
+ void refresh ();
+ int keyPress(int event);
+ void resize (int x, int y, int w, int h);
+
+ class MidiChannel *ch;
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+class gMidiMainButton : public gMainButton
+{
+public:
+ gMidiMainButton(int x, int y, int w, int h, const char *l=0);
+ int handle(int e);
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_mixed
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <math.h>
+#include "ge_mixed.h"
+#include "gd_mainWindow.h"
+#include "const.h"
+#include "mixer.h"
+#include "graphics.h"
+#include "recorder.h"
+#include "gui_utils.h"
+#include "channel.h"
+#include "sampleChannel.h"
+
+
+extern Mixer G_Mixer;
+extern unsigned G_beats;
+extern bool G_audio_status;
+extern gdMainWindow *mainWin;
+
+
+void __cb_window_closer(Fl_Widget *v, void *p)
+{
+ delete (Fl_Window*)p;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gButton::gButton(int X, int Y, int W, int H, const char *L, const char **imgOff, const char **imgOn)
+ : gClick(X, Y, W, H, L, imgOff, imgOn) {}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gClick::gClick(int x, int y, int w, int h, const char *L, const char **imgOff, const char **imgOn)
+: gBaseButton(x, y, w, h, L),
+ imgOff(imgOff),
+ imgOn(imgOn),
+ bgColor0(COLOR_BG_0),
+ bgColor1(COLOR_BG_1),
+ bdColor(COLOR_BD_0),
+ txtColor(COLOR_TEXT_0) {}
+
+void gClick::draw()
+{
+ if (!active()) txtColor = bdColor;
+ else txtColor = COLOR_TEXT_0;
+
+ fl_rect(x(), y(), w(), h(), bdColor); // borders
+ if (value()) { // -- clicked
+ if (imgOn != NULL)
+ fl_draw_pixmap(imgOn, x()+1, y()+1);
+ else
+ fl_rectf(x(), y(), w(), h(), bgColor1); // covers the border
+ }
+ else { // -- not clicked
+ fl_rectf(x()+1, y()+1, w()-2, h()-2, bgColor0); // bg inside the border
+ if (imgOff != NULL)
+ fl_draw_pixmap(imgOff, x()+1, y()+1);
+ }
+ if (!active())
+ fl_color(FL_INACTIVE_COLOR);
+
+ fl_color(txtColor);
+ fl_font(FL_HELVETICA, 11);
+ fl_draw(label(), x(), y(), w(), h(), FL_ALIGN_CENTER);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gClickRepeat::gClickRepeat(int x, int y, int w, int h, const char *L, const char **imgOff, const char **imgOn)
+: Fl_Repeat_Button(x, y, w, h, L), imgOff(imgOff), imgOn(imgOn) {}
+
+void gClickRepeat::draw()
+{
+ if (value()) { // -- clicked
+ fl_rectf(x(), y(), w(), h(), COLOR_BG_1); // bg
+ if (imgOn != NULL)
+ fl_draw_pixmap(imgOn, x()+1, y()+1);
+ }
+ else { // -- not clicked
+ fl_rectf(x(), y(), w(), h(), COLOR_BG_0); // bg
+ fl_rect(x(), y(), w(), h(), COLOR_BD_0); // border
+ if (imgOff != NULL)
+ fl_draw_pixmap(imgOff, x()+1, y()+1);
+ }
+ if (!active())
+ fl_color(FL_INACTIVE_COLOR);
+
+ fl_color(COLOR_TEXT_0);
+ fl_font(FL_HELVETICA, 11);
+ fl_draw(label(), x(), y(), w(), h(), FL_ALIGN_CENTER);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gInput::gInput(int x, int y, int w, int h, const char *L)
+: Fl_Input(x, y, w, h, L)
+{
+ //Fl::set_boxtype(G_BOX, gDrawBox, 1, 1, 2, 2);
+ box(G_BOX);
+ labelsize(11);
+ labelcolor(COLOR_TEXT_0);
+ color(COLOR_BG_DARK);
+ textcolor(COLOR_TEXT_0);
+ cursor_color(COLOR_TEXT_0);
+ selection_color(COLOR_BD_0);
+ textsize(11);
+
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gDial::gDial(int x, int y, int w, int h, const char *L)
+: Fl_Dial(x, y, w, h, L)
+{
+ labelsize(11);
+ labelcolor(COLOR_TEXT_0);
+ align(FL_ALIGN_LEFT);
+ type(FL_FILL_DIAL);
+ angles(0, 360);
+ color(COLOR_BG_0); // background
+ selection_color(COLOR_BG_1); // selection
+}
+
+void gDial::draw()
+{
+ double angle = (angle2()-angle1())*(value()-minimum())/(maximum()-minimum()) + angle1();
+
+ fl_color(COLOR_BG_0);
+ fl_pie(x(), y(), w(), h(), 270-angle1(), angle > angle1() ? 360+270-angle : 270-360-angle);
+
+ fl_color(COLOR_BD_0);
+ fl_arc(x(), y(), w(), h(), 0, 360);
+ fl_pie(x(), y(), w(), h(), 270-angle, 270-angle1());
+}
+
+/* -------------------------------------------------------------------------- */
+
+
+gBox::gBox(int x, int y, int w, int h, const char *L, Fl_Align al)
+: Fl_Box(x, y, w, h, L)
+{
+ labelsize(11);
+ box(FL_NO_BOX);
+ labelcolor(COLOR_TEXT_0);
+ if (al != 0)
+ align(al | FL_ALIGN_INSIDE);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gCheck::gCheck(int x, int y, int w, int h, const char *L)
+: Fl_Check_Button(x, y, w, h, L) {}
+
+void gCheck::draw()
+{
+ int color = !active() ? FL_INACTIVE_COLOR : COLOR_BD_0;
+
+ if (value()) {
+ fl_rect(x(), y(), 12, 12, (Fl_Color) color);
+ fl_rectf(x(), y(), 12, 12, (Fl_Color) color);
+ }
+ else {
+ fl_rectf(x(), y(), 12, 12, FL_BACKGROUND_COLOR);
+ fl_rect(x(), y(), 12, 12, (Fl_Color) color);
+ }
+
+ fl_rectf(x()+20, y(), w(), h(), FL_BACKGROUND_COLOR); // clearer
+ fl_font(FL_HELVETICA, 11);
+ fl_color(COLOR_TEXT_0);
+ fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gRadio::gRadio(int x, int y, int w, int h, const char *L)
+: Fl_Radio_Button(x, y, w, h, L) {}
+
+void gRadio::draw()
+{
+ int color = !active() ? FL_INACTIVE_COLOR : COLOR_BD_0;
+
+ if (value()) {
+ fl_rect(x(), y(), 12, 12, (Fl_Color) color);
+ fl_rectf(x(), y(), 12, 12, (Fl_Color) color);
+ }
+ else {
+ fl_rectf(x(), y(), 12, 12, FL_BACKGROUND_COLOR);
+ fl_rect(x(), y(), 12, 12, (Fl_Color) color);
+ }
+
+ fl_rectf(x()+20, y(), w(), h(), FL_BACKGROUND_COLOR); // clearer
+ fl_font(FL_HELVETICA, 11);
+ fl_color(COLOR_TEXT_0);
+ fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gProgress::gProgress(int x, int y, int w, int h, const char *L)
+: Fl_Progress(x, y, w, h, L) {
+ color(COLOR_BG_0, COLOR_BD_0);
+ box(G_BOX);
+
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gSoundMeter::gSoundMeter(int x, int y, int w, int h, const char *L)
+ : Fl_Box(x, y, w, h, L),
+ clip(false),
+ mixerPeak(0.0f),
+ peak(0.0f),
+ peak_old(0.0f),
+ db_level(0.0f),
+ db_level_old(0.0f) {}
+
+void gSoundMeter::draw()
+{
+ fl_rect(x(), y(), w(), h(), COLOR_BD_0);
+
+ /* peak = the highest value inside the frame */
+
+ peak = 0.0f;
+ float tmp_peak = 0.0f;
+
+ tmp_peak = fabs(mixerPeak);
+ if (tmp_peak > peak)
+ peak = tmp_peak;
+
+ clip = peak >= 1.0f ? true : false; // 1.0f is considered clip
+
+
+ /* dBFS (full scale) calculation, plus decay of -2dB per frame */
+
+ db_level = 20 * log10(peak);
+ if (db_level < db_level_old)
+ if (db_level_old > -DB_MIN_SCALE)
+ db_level = db_level_old - 2.0f;
+
+ db_level_old = db_level;
+
+ /* graphical part */
+
+ float px_level = 0.0f;
+ if (db_level < 0.0f)
+ px_level = ((w()/DB_MIN_SCALE) * db_level) + w();
+ else
+ px_level = w();
+
+ fl_rectf(x()+1, y()+1, w()-2, h()-2, COLOR_BG_0);
+ fl_rectf(x()+1, y()+1, (int) px_level, h()-2, clip || !G_audio_status ? COLOR_ALERT : COLOR_BD_0);
+}
+
+/* -------------------------------------------------------------------------- */
+
+gBeatMeter::gBeatMeter(int x, int y, int w, int h, const char *L)
+ : Fl_Box(x, y, w, h, L) {}
+
+void gBeatMeter::draw()
+{
+ int cursorW = w() / MAX_BEATS;
+ int greyX = G_Mixer.beats * cursorW;
+
+ fl_rect(x(), y(), w(), h(), COLOR_BD_0); // border
+ fl_rectf(x()+1, y()+1, w()-2, h()-2, FL_BACKGROUND_COLOR); // bg
+ fl_rectf(x()+(G_Mixer.actualBeat*cursorW)+3, y()+3, cursorW-5, h()-6, COLOR_BG_2); // cursor
+
+ /* beat cells */
+
+ fl_color(COLOR_BD_0);
+ for (int i=1; i<=G_Mixer.beats; i++)
+ fl_line(x()+cursorW*i, y()+1, x()+cursorW*i, y()+h()-2);
+
+ /* bar line */
+
+ fl_color(COLOR_BG_2);
+ int delta = G_Mixer.beats / G_Mixer.bars;
+ for (int i=1; i<G_Mixer.bars; i++)
+ fl_line(x()+cursorW*(i*delta), y()+1, x()+cursorW*(i*delta), y()+h()-2);
+
+ /* unused grey area */
+
+ fl_rectf(x()+greyX+1, y()+1, w()-greyX-1, h()-2, COLOR_BG_1);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gChoice::gChoice(int x, int y, int w, int h, const char *l, bool ang)
+ : Fl_Choice(x, y, w, h, l), angle(ang)
+{
+ labelsize(11);
+ labelcolor(COLOR_TEXT_0);
+ box(FL_BORDER_BOX);
+ textsize(11);
+ textcolor(COLOR_TEXT_0);
+ color(COLOR_BG_0);
+}
+
+
+void gChoice::draw()
+{
+ fl_rectf(x(), y(), w(), h(), COLOR_BG_0); // bg
+ fl_rect(x(), y(), w(), h(), (Fl_Color) COLOR_BD_0); // border
+ if (angle)
+ fl_polygon(x()+w()-8, y()+h()-1, x()+w()-1, y()+h()-8, x()+w()-1, y()+h()-1);
+
+ /* pick up the text() from the selected item (value()) and print it in
+ * the box and avoid overflows */
+
+ fl_color(!active() ? COLOR_BD_0 : COLOR_TEXT_0);
+ if (value() != -1) {
+ if (fl_width(text(value())) < w()-8) {
+ fl_draw(text(value()), x(), y(), w(), h(), FL_ALIGN_CENTER);
+ }
+ else {
+ std::string tmp = text(value());
+ int size = tmp.size();
+ while (fl_width(tmp.c_str()) >= w()-16) {
+ tmp.resize(size);
+ size--;
+ }
+ tmp += "...";
+ fl_draw(tmp.c_str(), x(), y(), w(), h(), FL_ALIGN_CENTER);
+ }
+
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gDrawBox(int x, int y, int w, int h, Fl_Color c)
+{
+ fl_color(c);
+ fl_rectf(x, y, w, h);
+ fl_color(COLOR_BD_0);
+ fl_rect(x, y, w, h);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gLiquidScroll::gLiquidScroll(int x, int y, int w, int h, const char *l)
+ : Fl_Scroll(x, y, w, h, l)
+{
+ type(Fl_Scroll::VERTICAL);
+ scrollbar.color(COLOR_BG_0);
+ scrollbar.selection_color(COLOR_BG_1);
+ scrollbar.labelcolor(COLOR_BD_1);
+ scrollbar.slider(G_BOX);
+}
+
+
+void gLiquidScroll::resize(int X, int Y, int W, int H)
+{
+ int nc = children()-2; // skip hscrollbar and vscrollbar
+ for ( int t=0; t<nc; t++) { // tell children to resize to our new width
+ Fl_Widget *c = child(t);
+ c->resize(c->x(), c->y(), W-24, c->h()); // W-24: leave room for scrollbar
+ }
+ init_sizes(); // tell scroll children changed in size
+ Fl_Scroll::resize(X,Y,W,H);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gSlider::gSlider(int x, int y, int w, int h, const char *l)
+ : Fl_Slider(x, y, w, h, l)
+{
+ type(FL_HOR_FILL_SLIDER);
+
+ labelsize(11);
+ align(FL_ALIGN_LEFT);
+ labelcolor(COLOR_TEXT_0);
+
+ box(G_BOX);
+ color(COLOR_BG_0);
+ selection_color(COLOR_BD_0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gResizerBar::gResizerBar(int X,int Y,int W,int H, bool vertical)
+ : Fl_Box(X,Y,W,H), vertical(vertical)
+{
+ last_y = 0;
+ min_h = 30;
+ if (vertical) {
+ orig_h = H;
+ labelsize(H);
+ }
+ else {
+ orig_h = W;
+ labelsize(W);
+ }
+ align(FL_ALIGN_CENTER|FL_ALIGN_INSIDE);
+ labelfont(FL_COURIER);
+ visible_focus(0);
+}
+
+/*
+gResizerBar::~gResizerBar()
+{
+ gLog("------ resizerbar %p destroyed\n", (void*)this);
+}
+*/
+
+void gResizerBar::HandleDrag(int diff)
+{
+ Fl_Scroll *grp = (Fl_Scroll*)parent();
+ int top;
+ int bot;
+ if (vertical) {
+ top = y();
+ bot = y()+h();
+ }
+ else {
+ top = x();
+ bot = x()+w();
+ }
+
+ // First pass: find widget directly above us with common edge
+ // Possibly clamp 'diff' if widget would get too small..
+
+ for (int t=0; t<grp->children(); t++) {
+ Fl_Widget *wd = grp->child(t);
+ if (vertical) {
+ if ((wd->y()+wd->h()) == top) { // found widget directly above?
+ if ((wd->h()+diff) < min_h)
+ diff = wd->h() - min_h; // clamp
+ wd->resize(wd->x(), wd->y(), wd->w(), wd->h()+diff); // change height
+ break; // done with first pass
+ }
+ }
+ else {
+ if ((wd->x()+wd->w()) == top) { // found widget directly above?
+ if ((wd->w()+diff) < min_h)
+ diff = wd->w() - min_h; // clamp
+ wd->resize(wd->x(), wd->y(), wd->w()+diff, wd->h()); // change height
+ break; // done with first pass
+ }
+ }
+ }
+
+ // Second pass: find widgets below us, move based on clamped diff
+
+ for (int t=0; t<grp->children(); t++) {
+ Fl_Widget *wd = grp->child(t);
+ if (vertical) {
+ if (wd->y() >= bot) // found widget below us?
+ wd->resize(wd->x(), wd->y()+diff, wd->w(), wd->h()); // change position
+ }
+ else {
+ if (wd->x() >= bot)
+ wd->resize(wd->x()+diff, wd->y(), wd->w(), wd->h());
+ }
+ }
+
+ // Change our position last
+
+ if (vertical)
+ resize(x(), y()+diff, w(), h());
+ else
+ resize(x()+diff, y(), w(), h());
+
+ grp->init_sizes();
+ grp->redraw();
+}
+
+
+int gResizerBar::handle(int e)
+{
+ int ret = 0;
+ int this_y;
+ if (vertical)
+ this_y = Fl::event_y_root();
+ else
+ this_y = Fl::event_x_root();
+ switch (e) {
+ case FL_FOCUS:
+ ret = 1;
+ break;
+ case FL_ENTER:
+ ret = 1;
+ fl_cursor(vertical ? FL_CURSOR_NS : FL_CURSOR_WE);
+ break;
+ case FL_LEAVE:
+ ret = 1;
+ fl_cursor(FL_CURSOR_DEFAULT);
+ break;
+ case FL_PUSH:
+ ret = 1;
+ last_y = this_y;
+ break;
+ case FL_DRAG:
+ HandleDrag(this_y-last_y);
+ last_y = this_y;
+ ret = 1;
+ break;
+ default: break;
+ }
+ return(Fl_Box::handle(e) | ret);
+}
+
+
+void gResizerBar::resize(int X,int Y,int W,int H)
+{
+ if (vertical)
+ Fl_Box::resize(X,Y,W,orig_h); // height of resizer stays constant size
+ else
+ Fl_Box::resize(X,Y,orig_h,H);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gScroll::gScroll(int x, int y, int w, int h, int t)
+ : Fl_Scroll(x, y, w, h)
+{
+ type(t);
+
+ scrollbar.color(COLOR_BG_0);
+ scrollbar.selection_color(COLOR_BG_1);
+ scrollbar.labelcolor(COLOR_BD_1);
+ scrollbar.slider(G_BOX);
+
+ hscrollbar.color(COLOR_BG_0);
+ hscrollbar.selection_color(COLOR_BG_1);
+ hscrollbar.labelcolor(COLOR_BD_1);
+ hscrollbar.slider(G_BOX);
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+gBaseButton::gBaseButton(int x, int y, int w, int h, const char *l)
+ : Fl_Button(x, y, w, h, l)
+{
+ initLabel = l ? l : "";
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gBaseButton::trimLabel()
+{
+ if (initLabel.empty())
+ return;
+
+ std::string out;
+ if (w() > 20) {
+ out = initLabel;
+ int len = initLabel.size();
+ while (fl_width(out.c_str(), out.size()) > w()) {
+ out = initLabel.substr(0, len) + "...";
+ len--;
+ }
+ }
+ else
+ out = "";
+ copy_label(out.c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gBaseButton::label(const char *l)
+{
+ Fl_Button::label(l);
+ initLabel = l;
+ trimLabel();
+}
+
+const char *gBaseButton::label()
+{
+ return Fl_Button::label();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gBaseButton::resize(int X, int Y, int W, int H)
+{
+ trimLabel();
+ Fl_Button::resize(X, Y, W, H);
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+gFxButton::gFxButton(int x, int y, int w, int h, const char **imgOff, const char **imgOn)
+ : gClick(x, y, w, h, NULL, imgOff, imgOn), full(false) {}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gFxButton::draw()
+{
+ gClick::draw();
+ if (full)
+ fl_draw_pixmap(imgOn, x()+1, y()+1, COLOR_BD_0);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_mixed
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_MIXED_H
+#define GE_MIXED_H
+
+#include <stdio.h>
+#include <dirent.h>
+#include <stdint.h> // for intptr_t
+#include <string>
+#include <FL/Fl.H>
+#include <FL/Fl_Menu_Window.H>
+#include <FL/Fl_Button.H>
+#include <FL/Fl_Repeat_Button.H>
+#include <FL/Fl_Check_Button.H>
+#include <FL/Fl_Box.H>
+#include <FL/fl_draw.H>
+#include <FL/Fl_Dial.H>
+#include <FL/Fl_Pixmap.H>
+#include <FL/Fl_Menu_Button.H>
+#include <FL/Fl_Hold_Browser.H>
+#include <FL/Fl_Radio_Button.H>
+#include <FL/Fl_Progress.H>
+#include <FL/Fl_Input.H>
+#include <FL/Fl_Int_Input.H>
+#include <FL/Fl_Choice.H>
+#include <FL/Fl_Scroll.H>
+
+#ifdef _WIN32
+ #include <shlobj.h> // for SHGetFolderPath
+#endif
+
+
+/* cb_window_closer
+ * callback for when closing windows. Deletes the widget (delete). */
+
+void __cb_window_closer(Fl_Widget *v, void *p);
+
+
+/* -------------------------------------------------------------------------- */
+
+
+class gBaseButton : public Fl_Button
+{
+private:
+ std::string initLabel;
+
+ void trimLabel();
+
+public:
+ gBaseButton(int x, int y, int w, int h, const char *l=0);
+ void resize(int x, int y, int w, int h);
+ void label(const char *l);
+ const char *label();
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+/* gClick
+ * a normal button. */
+
+class gClick : public gBaseButton
+{
+public:
+ gClick(int x, int y, int w, int h, const char *L=0, const char **imgOff=NULL, const char **imgOn=NULL);
+ void draw();
+ const char **imgOff;
+ const char **imgOn;
+ Fl_Color bgColor0; // background not clicked
+ Fl_Color bgColor1; // background clicked
+ Fl_Color bdColor; // border
+ Fl_Color txtColor; // testo
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+class gClickRepeat : public Fl_Repeat_Button
+{
+public:
+ gClickRepeat(int x, int y, int w, int h, const char *L=0, const char **imgOff=NULL, const char **imgOn=NULL);
+ void draw();
+ const char **imgOff;
+ const char **imgOn;
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+/* gButton
+ * exactly as gClick but with a unique id inside of it. Used for the buttons in
+ * channels and for FXs. */
+
+class gButton : public gClick
+{
+public:
+ gButton(int X,int Y,int W,int H,const char *L=0, const char **imgOff=NULL, const char **imgOn=NULL);
+ int key;
+ int id;
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+class gInput : public Fl_Input
+{
+public:
+ gInput(int x, int y, int w, int h, const char *L=0);
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+class gDial : public Fl_Dial
+{
+public:
+ gDial(int x, int y, int w, int h, const char *L=0);
+ void draw();
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+class gBox : public Fl_Box
+{
+public:
+ gBox(int x, int y, int w, int h, const char *L=0, Fl_Align al=FL_ALIGN_CENTER);
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+class gCheck : public Fl_Check_Button
+{
+public:
+ gCheck(int x, int y, int w, int h, const char *L=0);
+ void draw();
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+class gRadio : public Fl_Radio_Button
+{
+public:
+ gRadio(int x, int y, int w, int h, const char *L=0);
+ void draw();
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+class gProgress : public Fl_Progress
+{
+public:
+ gProgress(int x, int y, int w, int h, const char *L=0);
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+class gSoundMeter : public Fl_Box
+{
+public:
+ gSoundMeter(int X,int Y,int W,int H,const char *L=0);
+ void draw();
+ bool clip;
+ float mixerPeak; // peak from mixer
+private:
+ float peak;
+ float peak_old;
+ float db_level;
+ float db_level_old;
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+class gBeatMeter : public Fl_Box
+{
+public:
+ gBeatMeter(int X,int Y,int W,int H,const char *L=0);
+ void draw();
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+class gChoice : public Fl_Choice
+{
+public:
+
+ gChoice(int X,int Y,int W,int H,const char *L=0, bool angle=true);
+ void draw();
+
+ inline void show(const char *c) {value(find_index(c)); }
+
+ bool angle;
+ int id;
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+/* gDrawBox
+ * custom boxes in FLTK. */
+
+#define G_BOX FL_FREE_BOXTYPE
+void gDrawBox(int x, int y, int w, int h, Fl_Color c);
+
+
+/* -------------------------------------------------------------------------- */
+
+
+/* gLiquidScroll
+ * custom scroll that tells children to follow scroll's width when
+ * resized. Thanks to Greg Ercolano from FLTK dev team.
+ * http://seriss.com/people/erco/fltk/ */
+
+class gLiquidScroll : public Fl_Scroll
+{
+public:
+ gLiquidScroll(int x, int y, int w, int h, const char *l=0);
+ void resize(int x, int y, int w, int h);
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+/* gScroll
+ * custom scroll with nice scrollbars and something else. */
+
+class gScroll : public Fl_Scroll
+{
+public:
+ gScroll(int x, int y, int w, int h, int type=Fl_Scroll::BOTH);
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+/* gResizerBar
+ * 'resizer bar' between widgets Fl_Scroll. Thanks to Greg Ercolano from
+ * FLTK dev team. http://seriss.com/people/erco/fltk/
+ *
+ * Shows a resize cursor when hovered over.
+ * Assumes:
+ * - Parent is an Fl_Scroll
+ * - All children of Fl_Scroll are vertically arranged
+ * - The widget above us has a bottom edge touching our top edge
+ * ie. (w->y()+w->h() == this->y())
+ *
+ * When this widget is dragged:
+ * - The widget above us (with a common edge) will be /resized/
+ * vertically
+ * - All children below us will be /moved/ vertically */
+
+/* TODO - use more general variable names
+ * (last_y -> last_?, min_h -> min_?, ...) */
+
+class gResizerBar : public Fl_Box
+{
+private:
+ bool vertical;
+ int orig_h;
+ int last_y;
+ int min_h; // min height for widget above us
+
+ void HandleDrag(int diff);
+
+public:
+
+ /* 'vertical' defines the bar movement. Vertical=true: the bar moves
+ * vertically (up and down). */
+
+ gResizerBar(int x, int y, int w, int h, bool vertical=true);
+ //~gResizerBar();
+
+ inline void setMinSize(int val) { min_h = val; }
+ inline int getMinSize() { return min_h; }
+
+ int handle(int e);
+ void resize(int x, int y, int w, int h);
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+class gSlider : public Fl_Slider
+{
+public:
+ gSlider(int x, int y, int w, int h, const char *l=0);
+ int id;
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+/* gFxButton
+ * a simple gClick with 'full' parameter (i.e. has plugins). If 'full' is true,
+ * draw something somewhere. */
+
+class gFxButton : public gClick
+{
+public:
+ gFxButton(int x, int y, int w, int h, const char **imgOff=NULL, const char **imgOn=NULL);
+ void draw();
+ bool full;
+};
+
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_muteChannel
+ * a widget that represents mute actions inside the action editor.
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include "ge_muteChannel.h"
+#include "gd_actionEditor.h"
+#include "ge_actionWidget.h"
+#include "gd_mainWindow.h"
+#include "gg_keyboard.h"
+#include "recorder.h"
+#include "mixer.h"
+#include "glue.h"
+#include "channel.h"
+#include "log.h"
+
+
+extern gdMainWindow *mainWin;
+extern Mixer G_Mixer;
+
+
+gMuteChannel::gMuteChannel(int x, int y, gdActionEditor *pParent)
+ : gActionWidget(x, y, 200, 80, pParent), draggedPoint(-1), selectedPoint(-1)
+{
+ size(pParent->totalWidth, h());
+ extractPoints();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gMuteChannel::draw() {
+
+ baseDraw();
+
+ /* print label */
+
+ fl_color(COLOR_BG_1);
+ fl_font(FL_HELVETICA, 12);
+ fl_draw("mute", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
+
+ /* draw "on" and "off" labels. Must stay in background */
+
+ fl_color(COLOR_BG_1);
+ fl_font(FL_HELVETICA, 9);
+ fl_draw("on", x()+4, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
+ fl_draw("off", x()+4, y()+h()-14, w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_TOP));
+
+ /* draw on-off points. On = higher rect, off = lower rect. It always
+ * starts with a note_off */
+
+ fl_color(COLOR_BG_2);
+
+ int pxOld = x()+1;
+ int pxNew = 0;
+ int py = y()+h()-5;
+ int pyDot = py-6;
+
+ for (unsigned i=0; i<points.size; i++) {
+
+ /* next px */
+
+ pxNew = points.at(i).x+x();
+
+ /* draw line from pxOld to pxNew.
+ * i % 2 == 0: first point, mute_on
+ * i % 2 != 0: second point, mute_off */
+
+ fl_line(pxOld, py, pxNew, py);
+ pxOld = pxNew;
+
+ py = i % 2 == 0 ? y()+4 : y()+h()-5;
+
+ /* draw dots (handles) */
+
+ fl_line(pxNew, y()+h()-5, pxNew, y()+4);
+
+ if (selectedPoint == (int) i) {
+ fl_color(COLOR_BD_1);
+ fl_rectf(pxNew-3, pyDot, 7, 7);
+ fl_color(COLOR_BG_2);
+ }
+ else
+ fl_rectf(pxNew-3, pyDot, 7, 7);
+ }
+
+ /* last section */
+
+ py = y()+h()-5;
+ fl_line(pxNew+3, py, pParent->coverX+x()-1, py);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gMuteChannel::extractPoints() {
+
+ points.clear();
+
+ /* actions are already sorted by recorder::sortActions() */
+
+ for (unsigned i=0; i<recorder::frames.size; i++) {
+ for (unsigned j=0; j<recorder::global.at(i).size; j++) {
+ if (recorder::global.at(i).at(j)->chan == pParent->chan->index) {
+ if (recorder::global.at(i).at(j)->type & (ACTION_MUTEON | ACTION_MUTEOFF)) {
+ point p;
+ p.frame = recorder::frames.at(i);
+ p.type = recorder::global.at(i).at(j)->type;
+ p.x = p.frame / pParent->zoom;
+ points.add(p);
+ //gLog("[gMuteChannel::extractPoints] point found, type=%d, frame=%d\n", p.type, p.frame);
+ }
+ }
+ }
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gMuteChannel::updateActions() {
+ for (unsigned i=0; i<points.size; i++)
+ points.at(i).x = points.at(i).frame / pParent->zoom;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gMuteChannel::handle(int e) {
+
+ int ret = 0;
+ int mouseX = Fl::event_x()-x();
+
+ switch (e) {
+
+ case FL_ENTER: {
+ ret = 1;
+ break;
+ }
+
+ case FL_MOVE: {
+ selectedPoint = getSelectedPoint();
+ redraw();
+ ret = 1;
+ break;
+ }
+
+ case FL_LEAVE: {
+ draggedPoint = -1;
+ selectedPoint = -1;
+ redraw();
+ ret = 1;
+ break;
+ }
+
+ case FL_PUSH: {
+
+ /* left click on point: drag
+ * right click on point: delete
+ * left click on void: add */
+
+ if (Fl::event_button1()) {
+
+ if (selectedPoint != -1) {
+ draggedPoint = selectedPoint;
+ previousXPoint = points.at(selectedPoint).x;
+ }
+ else {
+
+ /* click on the grey area leads to nowhere */
+
+ if (mouseX > pParent->coverX) {
+ ret = 1;
+ break;
+ }
+
+ /* click in the middle of a long mute_on (between two points): new actions
+ * must be added in reverse: first mute_off then mute_on. Let's find the
+ * next point from here. */
+
+ unsigned nextPoint = points.size;
+ for (unsigned i=0; i<points.size; i++) {
+ if (mouseX < points.at(i).x) {
+ nextPoint = i;
+ break;
+ }
+ }
+
+ /* next point odd = mute_on [click here] mute_off
+ * next point even = mute_off [click here] mute_on */
+
+ int frame_a = mouseX * pParent->zoom;
+ int frame_b = frame_a+2048;
+
+ if (pParent->gridTool->isOn()) {
+ frame_a = pParent->gridTool->getSnapFrame(mouseX);
+ frame_b = pParent->gridTool->getSnapFrame(mouseX + pParent->gridTool->getCellSize());
+
+ /* with snap=on a point can fall onto another */
+
+ if (pointCollides(frame_a) || pointCollides(frame_b)) {
+ ret = 1;
+ break;
+ }
+ }
+
+ /* ensure frame parity */
+
+ if (frame_a % 2 != 0) frame_a++;
+ if (frame_b % 2 != 0) frame_b++;
+
+ /* avoid overflow: frame_b must be within the sequencer range. In that
+ * case shift the ON-OFF block */
+
+ if (frame_b >= G_Mixer.totalFrames) {
+ frame_b = G_Mixer.totalFrames;
+ frame_a = frame_b-2048;
+ }
+
+ if (nextPoint % 2 != 0) {
+ recorder::rec(pParent->chan->index, ACTION_MUTEOFF, frame_a);
+ recorder::rec(pParent->chan->index, ACTION_MUTEON, frame_b);
+ }
+ else {
+ recorder::rec(pParent->chan->index, ACTION_MUTEON, frame_a);
+ recorder::rec(pParent->chan->index, ACTION_MUTEOFF, frame_b);
+ }
+ recorder::sortActions();
+
+ mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel); // update mainWindow
+ extractPoints();
+ redraw();
+ }
+ }
+ else {
+
+ /* delete points pair */
+
+ if (selectedPoint != -1) {
+
+ unsigned a;
+ unsigned b;
+
+ if (points.at(selectedPoint).type == ACTION_MUTEOFF) {
+ a = selectedPoint-1;
+ b = selectedPoint;
+ }
+ else {
+ a = selectedPoint;
+ b = selectedPoint+1;
+ }
+
+ //gLog("selected: a=%d, b=%d >>> frame_a=%d, frame_b=%d\n",
+ // a, b, points.at(a).frame, points.at(b).frame);
+
+ recorder::deleteAction(pParent->chan->index, points.at(a).frame, points.at(a).type, false); // false = don't check vals
+ recorder::deleteAction(pParent->chan->index, points.at(b).frame, points.at(b).type, false); // false = don't check vals
+ recorder::sortActions();
+
+ mainWin->keyboard->setChannelWithActions((gSampleChannel*)pParent->chan->guiChannel); // update mainWindow
+ extractPoints();
+ redraw();
+ }
+ }
+ ret = 1;
+ break;
+ }
+
+ case FL_RELEASE: {
+
+ if (draggedPoint != -1) {
+
+ if (points.at(draggedPoint).x == previousXPoint) {
+ //gLog("nothing to do\n");
+ }
+ else {
+
+ int newFrame = points.at(draggedPoint).x * pParent->zoom;
+
+ recorder::deleteAction(
+ pParent->chan->index,
+ points.at(draggedPoint).frame,
+ points.at(draggedPoint).type,
+ false); // don't check values
+
+ recorder::rec(
+ pParent->chan->index,
+ points.at(draggedPoint).type,
+ newFrame);
+
+ recorder::sortActions();
+
+ points.at(draggedPoint).frame = newFrame;
+ }
+ }
+ draggedPoint = -1;
+ selectedPoint = -1;
+
+ ret = 1;
+ break;
+ }
+
+ case FL_DRAG: {
+
+ if (draggedPoint != -1) {
+
+ /* constrain the point between two ends (leftBorder-point,
+ * point-point, point-rightBorder) */
+
+ int prevPoint;
+ int nextPoint;
+
+ if (draggedPoint == 0) {
+ prevPoint = 0;
+ nextPoint = points.at(draggedPoint+1).x - 1;
+ if (pParent->gridTool->isOn())
+ nextPoint -= pParent->gridTool->getCellSize();
+ }
+ else
+ if ((unsigned) draggedPoint == points.size-1) {
+ prevPoint = points.at(draggedPoint-1).x + 1;
+ nextPoint = pParent->coverX-x();
+ if (pParent->gridTool->isOn())
+ prevPoint += pParent->gridTool->getCellSize();
+ }
+ else {
+ prevPoint = points.at(draggedPoint-1).x + 1;
+ nextPoint = points.at(draggedPoint+1).x - 1;
+ if (pParent->gridTool->isOn()) {
+ prevPoint += pParent->gridTool->getCellSize();
+ nextPoint -= pParent->gridTool->getCellSize();
+ }
+ }
+
+ if (mouseX <= prevPoint)
+ points.at(draggedPoint).x = prevPoint;
+ else
+ if (mouseX >= nextPoint)
+ points.at(draggedPoint).x = nextPoint;
+ else
+ if (pParent->gridTool->isOn())
+ points.at(draggedPoint).x = pParent->gridTool->getSnapPoint(mouseX)-1;
+ else
+ points.at(draggedPoint).x = mouseX;
+
+ redraw();
+ }
+ ret = 1;
+ break;
+ }
+ }
+
+
+ return ret;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool gMuteChannel::pointCollides(int frame) {
+ for (unsigned i=0; i<points.size; i++)
+ if (frame == points.at(i).frame)
+ return true;
+ return false;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gMuteChannel::getSelectedPoint() {
+
+ /* point is a 7x7 dot */
+
+ for (unsigned i=0; i<points.size; i++) {
+ if (Fl::event_x() >= points.at(i).x+x()-3 &&
+ Fl::event_x() <= points.at(i).x+x()+3)
+ return i;
+ }
+ return -1;
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_muteChannel
+ * a widget representing mute actions inside the action editor.
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef GE_MUTECHANNEL_H
+#define GE_MUTECHANNEL_H
+
+#include <FL/Fl.H>
+#include <FL/Fl_Widget.H>
+#include <FL/fl_draw.H>
+#include "ge_actionWidget.h"
+#include "utils.h"
+
+
+class gMuteChannel : public gActionWidget {
+
+private:
+
+ /* point
+ * a single dot in the graph. */
+
+ struct point {
+ int frame;
+ char type;
+ int x;
+ };
+
+ /* points
+ * array of on/off points, in frames */
+
+ gVector<point> points;
+
+ /* draggedPoint
+ * which point we are dragging? */
+
+ int draggedPoint;
+
+ /* selectedPoint
+ * which point we are selecting? */
+
+ int selectedPoint;
+
+ /* previousXPoint
+ * x coordinate of point at time t-1. Used to check effective shifts */
+
+ int previousXPoint;
+
+ /* extractPoints
+ * va a leggere l'array di azioni di Recorder ed estrae tutti i punti
+ * interessanti mute_on o mute_off. Li mette poi nel gVector points. */
+ void extractPoints();
+
+ /* getSelectedPoint
+ * ritorna l'indice di points[] in base al punto selezionato (quello
+ * con il mouse hover). Ritorna -1 se non trova niente. */
+ int getSelectedPoint();
+
+ /* pointCollides
+ * true if a point collides with another. Used while adding new points
+ * with snap active.*/
+
+ bool pointCollides(int frame);
+
+public:
+
+ gMuteChannel(int x, int y, class gdActionEditor *pParent);
+ void draw();
+ int handle(int e);
+
+ /* updateActions
+ * calculates new points affected by the zoom. Call this one after
+ * each zoom update. */
+
+ void updateActions();
+};
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_pianoRoll
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include <FL/fl_draw.H>
+#include "ge_pianoRoll.h"
+#include "gd_mainWindow.h"
+#include "gd_actionEditor.h"
+#include "channel.h"
+#include "midiChannel.h"
+#include "const.h"
+#include "kernelMidi.h"
+#include "log.h"
+#include "conf.h"
+
+
+extern gdMainWindow *mainWin;
+extern Mixer G_Mixer;
+extern Conf G_Conf;
+
+
+gPianoRollContainer::gPianoRollContainer(int x, int y, class gdActionEditor *pParent)
+ : Fl_Scroll(x, y, 200, 422), pParent(pParent)
+{
+ size(pParent->totalWidth, G_Conf.pianoRollH);
+ pianoRoll = new gPianoRoll(x, y, pParent->totalWidth, pParent);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+gPianoRollContainer::~gPianoRollContainer() {
+ clear();
+ G_Conf.pianoRollH = h();
+ G_Conf.pianoRollY = pianoRoll->y();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gPianoRollContainer::updateActions() {
+ pianoRoll->updateActions();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gPianoRollContainer::draw() {
+
+ pianoRoll->size(this->w(), pianoRoll->h()); /// <--- not optimal
+
+ /* clear background */
+
+ fl_rectf(x(), y(), w(), h(), COLOR_BG_MAIN);
+
+ /* clip pianoRoll to pianoRollContainer size */
+
+ fl_push_clip(x(), y(), w(), h());
+ draw_child(*pianoRoll);
+ fl_pop_clip();
+
+ fl_color(COLOR_BD_0);
+ fl_line_style(0);
+ fl_rect(x(), y(), pParent->totalWidth, h());
+}
+
+
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+
+
+gPianoRoll::gPianoRoll(int X, int Y, int W, class gdActionEditor *pParent)
+ : gActionWidget(X, Y, W, 40, pParent)
+{
+ resizable(NULL); // don't resize children (i.e. pianoItem)
+ size(W, (MAX_NOTES+1) * CELL_H); // 128 MIDI channels * 15 px height
+
+ if (G_Conf.pianoRollY == -1)
+ position(x(), y()-(h()/2)); // center
+ else
+ position(x(), G_Conf.pianoRollY);
+
+ drawSurface1();
+ drawSurface2();
+
+ /* add actions when the window is opened. Position is zoom-based. MIDI
+ * actions come always in pair: start + end. */
+
+ recorder::sortActions();
+
+ recorder::action *a2 = NULL;
+ recorder::action *prev = NULL;
+
+ for (unsigned i=0; i<recorder::frames.size; i++) {
+ for (unsigned j=0; j<recorder::global.at(i).size; j++) {
+
+ /* don't show actions > than the grey area */
+ /** FIXME - can we move this to the outer cycle? */
+
+ if (recorder::frames.at(i) > G_Mixer.totalFrames)
+ continue;
+
+ recorder::action *a1 = recorder::global.at(i).at(j);
+
+ if (a1->chan != pParent->chan->index)
+ continue;
+
+ if (a1->type == ACTION_MIDI) {
+
+ /* if this action is == to previous one: skip it, we have already
+ * checked it */
+
+ if (a1 == prev) {
+ //gLog("[gPianoRoll] ACTION_MIDI found, but skipping - was previous\n");
+ continue;
+ }
+
+ /* extract MIDI infos from a1: if is note off skip it, we are looking
+ * for note on only */
+
+ int a1_type = kernelMidi::getB1(a1->iValue);
+ int a1_note = kernelMidi::getB2(a1->iValue);
+ int a1_velo = kernelMidi::getB3(a1->iValue);
+
+ if (a1_type == 0x80) {
+ //gLog("[gPianoRoll] ACTION_MIDI found, but skipping - was note off\n");
+ continue;
+ }
+
+ /* search for the next action. Must have: same channel, ACTION_MIDI, greater
+ * than a1->frame and with MIDI properties of note_off (0x80), same note
+ * of a1, same velocity of a1 */
+
+ recorder::getNextAction(
+ a1->chan,
+ ACTION_MIDI,
+ a1->frame,
+ &a2,
+ kernelMidi::getIValue(0x80, a1_note, a1_velo));
+
+ /* next action note off found: add a new gPianoItem to piano roll */
+
+ if (a2) {
+ //gLog("[gPianoRoll] ACTION_MIDI pair found, frame_a=%d frame_b=%d, note_a=%d, note_b=%d, type_a=%d, type_b=%d\n",
+ // a1->frame, a2->frame, kernelMidi::getNoteValue(a1->iValue), kernelMidi::getNoteValue(a2->iValue),
+ // kernelMidi::getNoteOnOff(a1->iValue), kernelMidi::getNoteOnOff(a2->iValue));
+ new gPianoItem(0, 0, x(), y()+3, a1, a2, pParent);
+ prev = a2;
+ a2 = NULL;
+ }
+ else
+ gLog("[gPianoRoll] recorder didn't find action!\n");
+
+ }
+ }
+ }
+
+ end();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gPianoRoll::drawSurface1() {
+
+ surface1 = fl_create_offscreen(40, h());
+ fl_begin_offscreen(surface1);
+
+ /* warning: only w() and h() come from this widget, x and y coordinates
+ * are absolute, since we are writing in a memory chunk */
+
+ fl_rectf(0, 0, 40, h(), COLOR_BG_MAIN);
+
+ fl_line_style(FL_DASH, 0, NULL);
+ fl_font(FL_HELVETICA, 11);
+
+ int octave = 9;
+
+ for (int i=1; i<=MAX_NOTES+1; i++) {
+
+ /* print key note label. C C# D D# E F F# G G# A A# B */
+
+ char note[6];
+ int step = i % 12;
+
+ switch (step) {
+ case 1:
+ fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30);
+ sprintf(note, "%dG", octave);
+ break;
+ case 2:
+ sprintf(note, "%dF#", octave);
+ break;
+ case 3:
+ sprintf(note, "%dF", octave);
+ break;
+ case 4:
+ fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30);
+ sprintf(note, "%dE", octave);
+ break;
+ case 5:
+ sprintf(note, "%dD#", octave);
+ break;
+ case 6:
+ fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30);
+ sprintf(note, "%dD", octave);
+ break;
+ case 7:
+ sprintf(note, "%dC#", octave);
+ break;
+ case 8:
+ sprintf(note, "%dC", octave);
+ break;
+ case 9:
+ fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30);
+ sprintf(note, "%dB", octave);
+ break;
+ case 10:
+ sprintf(note, "%dA#", octave);
+ break;
+ case 11:
+ fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30);
+ sprintf(note, "%dA", octave);
+ break;
+ case 0:
+ sprintf(note, "%dG#", octave);
+ octave--;
+ break;
+ }
+
+ fl_color(fl_rgb_color(54, 54, 54));
+ fl_draw(note, 4, ((i-1)*CELL_H)+1, 30, CELL_H, (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
+
+ /* print horizontal line */
+
+ if (i < 128)
+ fl_line(0, i*CELL_H, 40, +i*CELL_H);
+ }
+
+ fl_line_style(0);
+ fl_end_offscreen();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gPianoRoll::drawSurface2() {
+ surface2 = fl_create_offscreen(40, h());
+ fl_begin_offscreen(surface2);
+ fl_rectf(0, 0, 40, h(), COLOR_BG_MAIN);
+ fl_color(fl_rgb_color(54, 54, 54));
+ fl_line_style(FL_DASH, 0, NULL);
+ for (int i=1; i<=MAX_NOTES+1; i++) {
+ int step = i % 12;
+ switch (step) {
+ case 1:
+ case 4:
+ case 6:
+ case 9:
+ case 11:
+ fl_rectf(0, i*CELL_H, 40, CELL_H, 30, 30, 30);
+ break;
+ }
+ if (i < 128) {
+ fl_color(fl_rgb_color(54, 54, 54));
+ fl_line(0, i*CELL_H, 40, +i*CELL_H);
+ }
+ }
+ fl_line_style(0);
+ fl_end_offscreen();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gPianoRoll::draw() {
+
+ fl_copy_offscreen(x(), y(), 40, h(), surface1, 0, 0);
+
+#if defined(__APPLE__)
+ for (int i=36; i<pParent->totalWidth; i+=36) /// TODO: i < pParent->coverX is faster
+ fl_copy_offscreen(x()+i, y(), 40, h(), surface2, 1, 0);
+#else
+ for (int i=40; i<pParent->totalWidth; i+=40) /// TODO: i < pParent->coverX is faster
+ fl_copy_offscreen(x()+i, y(), 40, h(), surface2, 0, 0);
+#endif
+
+ baseDraw(false);
+ draw_children();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gPianoRoll::handle(int e) {
+
+ int ret = Fl_Group::handle(e);
+
+ switch (e) {
+ case FL_PUSH: {
+
+ /* avoid click on grey area */
+
+ if (Fl::event_x() >= pParent->coverX) {
+ ret = 1;
+ break;
+ }
+
+
+ push_y = Fl::event_y() - y();
+
+ if (Fl::event_button1()) {
+
+ /* ax is driven by grid, ay by the height in px of each note */
+
+ int ax = Fl::event_x();
+ int ay = Fl::event_y();
+
+ /* vertical snap */
+
+ int edge = (ay-y()-3) % 15;
+ if (edge != 0) ay -= edge;
+
+ /* if no overlap, add new piano item. Also check that it doesn't
+ * overflow on the grey area, by shifting it to the left if
+ * necessary. */
+
+ if (!onItem(ax, ay-y()-3)) {
+ int greyover = ax+20 - pParent->coverX-x();
+ if (greyover > 0)
+ ax -= greyover;
+ add(new gPianoItem(ax, ay, ax-x(), ay-y()-3, NULL, NULL, pParent));
+ redraw();
+ }
+ }
+ ret = 1;
+ break;
+ }
+ case FL_DRAG: {
+
+ if (Fl::event_button3()) {
+
+ gPianoRollContainer *prc = (gPianoRollContainer*) parent();
+ position(x(), Fl::event_y() - push_y);
+
+ if (y() > prc->y())
+ position(x(), prc->y());
+ else
+ if (y() < prc->y()+prc->h()-h())
+ position(x(), prc->y()+prc->h()-h());
+
+ prc->redraw();
+ }
+ ret = 1;
+ break;
+ }
+ case FL_MOUSEWHEEL: { // nothing to do, just avoid small internal scroll
+ ret = 1;
+ break;
+ }
+ }
+ return ret;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gPianoRoll::updateActions() {
+
+ /* when zooming, don't delete and re-add actions, just MOVE them. This
+ * function shifts the action by a zoom factor. Those singlepress are
+ * stretched, as well */
+
+ gPianoItem *i;
+ for (int k=0; k<children(); k++) {
+ i = (gPianoItem*) child(k);
+
+ //gLog("found point %p, frame_a=%d frame_b=%d, x()=%d\n", (void*) i, i->getFrame_a(), i->getFrame_b(), i->x());
+
+ int newX = x() + (i->getFrame_a() / pParent->zoom);
+ int newW = ((i->getFrame_b() - i->getFrame_a()) / pParent->zoom);
+ if (newW < 8)
+ newW = 8;
+ i->resize(newX, i->y(), newW, i->h());
+ i->redraw();
+
+ //gLog("update point %p, frame_a=%d frame_b=%d, x()=%d\n", (void*) i, i->getFrame_a(), i->getFrame_b(), i->x());
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool gPianoRoll::onItem(int rel_x, int rel_y) {
+
+ if (!pParent->chan->hasActions)
+ return false;
+
+ int note = MAX_NOTES - (rel_y / CELL_H);
+
+ int n = children();
+ for (int i=0; i<n; i++) { // no scrollbars to skip
+
+ gPianoItem *p = (gPianoItem*) child(i);
+ if (p->getNote() != note)
+ continue;
+
+ /* when 2 segments overlap?
+ * start = the highest value between the two starting points
+ * end = the lowest value between the two ending points
+ * if start < end then there's an overlap of end-start pixels. We
+ * also add 1 px to the edges in order to gain some space:
+ * [ ][ ] ---> no
+ * [ ] [ ] ---> yes! */
+
+ int start = p->x() > rel_x ? p->x() : rel_x-1;
+ int end = p->x()+p->w() < rel_x + 20 ? p->x()+p->w() : rel_x + 21;
+ if (start < end)
+ return true;
+ }
+ return false;
+}
+
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
+
+
+gPianoItem::gPianoItem(int X, int Y, int rel_x, int rel_y, recorder::action *a, recorder::action *b, gdActionEditor *pParent)
+ : Fl_Box (X, Y, 20, gPianoRoll::CELL_H-5),
+ a (a),
+ b (b),
+ pParent (pParent),
+ selected(false),
+ event_a (0x00),
+ event_b (0x00),
+ changed (false)
+{
+
+ /* a is a pointer: action exists, needs to be displayed */
+
+ if (a) {
+ note = kernelMidi::getB2(a->iValue);
+ frame_a = a->frame;
+ frame_b = b->frame;
+ event_a = a->iValue;
+ event_b = b->iValue;
+ int newX = rel_x + (frame_a / pParent->zoom);
+ int newY = rel_y + getY(note);
+ int newW = (frame_b - frame_a) / pParent->zoom;
+ resize(newX, newY, newW, h());
+ }
+
+ /* a is null: action needs to be recorded from scratch */
+
+ else {
+ note = getNote(rel_y);
+ frame_a = rel_x * pParent->zoom;
+ frame_b = (rel_x + 20) * pParent->zoom;
+ record();
+ size((frame_b - frame_a) / pParent->zoom, h());
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool gPianoItem::overlap() {
+
+ /* when 2 segments overlap?
+ * start = the highest value between the two starting points
+ * end = the lowest value between the two ending points
+ * if start < end then there's an overlap of end-start pixels. */
+
+ gPianoRoll *pPiano = (gPianoRoll*) parent();
+
+ for (int i=0; i<pPiano->children(); i++) {
+
+ gPianoItem *pItem = (gPianoItem*) pPiano->child(i);
+
+ /* don't check against itself and with different y positions */
+
+ if (pItem == this || pItem->y() != y())
+ continue;
+
+ int start = pItem->x() >= x() ? pItem->x() : x();
+ int end = pItem->x()+pItem->w() < x()+w() ? pItem->x()+pItem->w() : x()+w();
+ if (start < end)
+ return true;
+ }
+
+ return false;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gPianoItem::draw() {
+ int _w = w() > 4 ? w() : 4;
+ //gLog("[gPianoItem] draw me (%p) at x=%d\n", (void*)this, x());
+ fl_rectf(x(), y(), _w, h(), (Fl_Color) selected ? COLOR_BD_1 : COLOR_BG_2);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gPianoItem::record() {
+
+ /* avoid frame overflow */
+
+ int overflow = frame_b - G_Mixer.totalFrames;
+ if (overflow > 0) {
+ frame_b -= overflow;
+ frame_a -= overflow;
+ }
+
+ /* note off */
+ /** FIXME - use constants */
+ event_a |= (0x90 << 24); // note on
+ event_a |= (note << 16); // note value
+ event_a |= (0x3F << 8); // velocity
+ event_a |= (0x00);
+
+ event_b |= (0x80 << 24); // note off
+ event_b |= (note << 16); // note value
+ event_b |= (0x3F << 8); // velocity
+ event_b |= (0x00);
+
+ recorder::rec(pParent->chan->index, ACTION_MIDI, frame_a, event_a);
+ recorder::rec(pParent->chan->index, ACTION_MIDI, frame_b, event_b);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gPianoItem::remove() {
+ recorder::deleteAction(pParent->chan->index, frame_a, ACTION_MIDI, true, event_a, 0.0);
+ recorder::deleteAction(pParent->chan->index, frame_b, ACTION_MIDI, true, event_b, 0.0);
+
+ /* send a note-off in case we are deleting it in a middle of a key_on
+ * key_off sequence. */
+
+ ((MidiChannel*) pParent->chan)->sendMidi(event_b);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gPianoItem::handle(int e) {
+
+ int ret = 0;
+
+ switch (e) {
+
+ case FL_ENTER: {
+ selected = true;
+ ret = 1;
+ redraw();
+ break;
+ }
+
+ case FL_LEAVE: {
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+ selected = false;
+ ret = 1;
+ redraw();
+ break;
+ }
+
+ case FL_MOVE: {
+ onLeftEdge = false;
+ onRightEdge = false;
+
+ if (Fl::event_x() >= x() && Fl::event_x() < x()+4) {
+ onLeftEdge = true;
+ fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+ }
+ else
+ if (Fl::event_x() >= x()+w()-4 && Fl::event_x() <= x()+w()) {
+ onRightEdge = true;
+ fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+ }
+ else
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+
+ ret = 1;
+ break;
+ }
+
+ case FL_PUSH: {
+
+ push_x = Fl::event_x() - x();
+ old_x = x();
+ old_w = w();
+
+ if (Fl::event_button3()) {
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+ remove();
+ hide(); // for Windows
+ Fl::delete_widget(this);
+ ((gPianoRoll*)parent())->redraw();
+ }
+ ret = 1;
+ break;
+ }
+
+ case FL_DRAG: {
+
+ changed = true;
+
+ gPianoRoll *pr = (gPianoRoll*) parent();
+ int coverX = pParent->coverX + pr->x(); // relative coverX
+ int nx, ny, nw;
+
+ if (onLeftEdge) {
+ nx = Fl::event_x();
+ ny = y();
+ nw = x()-Fl::event_x()+w();
+ if (nx < pr->x()) {
+ nx = pr->x();
+ nw = w()+x()-pr->x();
+ }
+ else
+ if (nx > x()+w()-8) {
+ nx = x()+w()-8;
+ nw = 8;
+ }
+ resize(nx, ny, nw, h());
+ }
+ else
+ if (onRightEdge) {
+ nw = Fl::event_x()-x();
+ if (Fl::event_x() < x()+8)
+ nw = 8;
+ else
+ if (Fl::event_x() > coverX)
+ nw = coverX-x();
+ size(nw, h());
+ }
+ else {
+ nx = Fl::event_x() - push_x;
+ if (nx < pr->x()+1)
+ nx = pr->x()+1;
+ else
+ if (nx+w() > coverX)
+ nx = coverX-w();
+
+ /* snapping */
+
+ if (pParent->gridTool->isOn())
+ nx = pParent->gridTool->getSnapPoint(nx-pr->x()) + pr->x() - 1;
+
+ position(nx, y());
+ }
+
+ /* update screen */
+
+ redraw();
+ ((gPianoRoll*)parent())->redraw();
+ ret = 1;
+ break;
+ }
+
+ case FL_RELEASE: {
+
+ /* delete & record the action, only if it doesn't overlap with
+ * another one */
+
+ if (overlap()) {
+ resize(old_x, y(), old_w, h());
+ redraw();
+ }
+ else
+ if (changed) {
+ remove();
+ note = getNote(getRelY());
+ frame_a = getRelX() * pParent->zoom;
+ frame_b = (getRelX()+w()) * pParent->zoom;
+ record();
+ changed = false;
+ }
+
+ ((gPianoRoll*)parent())->redraw();
+
+ ret = 1;
+ break;
+ }
+ }
+ return ret;
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_pianoRoll
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef GE_PIANOROLL_H
+#define GE_PIANOROLL_H
+
+#include <FL/Fl.H>
+#include <FL/Fl_Scroll.H>
+#include <FL/Fl_Box.H>
+#include "ge_actionWidget.h"
+#include "recorder.h"
+
+
+class gPianoRollContainer : public Fl_Scroll {
+
+private:
+ class gdActionEditor *pParent;
+ class gPianoRoll *pianoRoll;
+
+public:
+ gPianoRollContainer(int x, int y, class gdActionEditor *parent);
+ ~gPianoRollContainer();
+ void draw();
+ void updateActions();
+};
+
+
+/* ------------------------------------------------------------------ */
+
+
+class gPianoRoll : public gActionWidget {
+
+private:
+
+ /* onItem
+ * is curson on a gPianoItem? */
+
+ bool onItem(int rel_x, int rel_y);
+
+ /* drawSurface*
+ * generate a complex drawing in memory first and copy it to the
+ * screen at a later point in time. Fl_Offscreen surface holds the
+ * necessary data. */
+
+ /* drawSurface1
+ * draw first tile of note values. */
+
+ void drawSurface1();
+
+ /* drawSurface2
+ * draw the rest of the piano roll. */
+
+ void drawSurface2();
+
+ int push_y;
+ Fl_Offscreen surface1; // notes, no repeat
+ Fl_Offscreen surface2; // lines, x-repeat
+
+
+public:
+ gPianoRoll(int x, int y, int w, class gdActionEditor *pParent);
+
+ void draw();
+ int handle(int e);
+ void updateActions();
+
+ enum { MAX_NOTES = 127, CELL_H = 15 };
+};
+
+
+/* ------------------------------------------------------------------ */
+
+
+class gPianoItem : public Fl_Box {
+
+private:
+
+ /* getRelX/Y
+ * return x/y point of this item, relative to piano roll (and not to
+ * entire screen) */
+
+ inline int getRelY() { return y() - parent()->y() - 3; };
+ inline int getRelX() { return x() - parent()->x(); };
+
+ /* getNote
+ * from a relative_y return the real MIDI note, range 0-127. 15 is
+ * the hardcoded value for note height in pixels */
+
+ inline int getNote(int rel_y) {
+ return gPianoRoll::MAX_NOTES - (rel_y / gPianoRoll::CELL_H);
+ };
+
+ /* getY
+ * from a note, return the y position on piano roll */
+
+ inline int getY(int note) {
+ return (gPianoRoll::MAX_NOTES * gPianoRoll::CELL_H) - (note * gPianoRoll::CELL_H);
+ };
+
+ /* overlap
+ * check if this item don't overlap with another one. */
+
+ bool overlap();
+
+ recorder::action *a;
+ recorder::action *b;
+ class gdActionEditor *pParent;
+
+ bool selected;
+ int push_x;
+
+ /* MIDI note, start frame, end frame - Used only if it's a newly added
+ * action */ /** FIXME - is it true? */
+
+ int note;
+ int frame_a;
+ int frame_b;
+
+ /* event - bitmasked MIDI events, generated by record() or by ctor if
+ * not newly added action */
+
+ int event_a;
+ int event_b;
+
+ /* changed - if Item has been moved or resized: re-recording needed */
+
+ bool changed;
+
+ /* onLeft,RightEdge - if cursor is on a widget's edge */
+
+ bool onLeftEdge;
+ bool onRightEdge;
+
+ /* old_x, old_w - store previous width and position while dragging
+ * and moving, in order to restore it if overlap */
+
+ int old_x, old_w;
+
+public:
+
+ /* pianoItem ctor
+ * if action *a == NULL, record a new action */
+
+ gPianoItem(int x, int y, int rel_x, int rel_y, recorder::action *a, recorder::action *b, class gdActionEditor *pParent);
+
+ void draw();
+ int handle(int e);
+ void record();
+ void remove();
+
+ inline int getFrame_a() { return frame_a; }
+ inline int getFrame_b() { return frame_b; }
+ inline int getNote() { return note; }
+
+};
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_sampleChannel
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "ge_sampleChannel.h"
+#include "gd_mainWindow.h"
+#include "gd_keyGrabber.h"
+#include "gd_midiGrabber.h"
+#include "gd_editor.h"
+#include "gd_actionEditor.h"
+#include "gd_warnings.h"
+#include "gd_browser.h"
+#include "gd_midiOutputSetup.h"
+#include "gg_keyboard.h"
+#include "pluginHost.h"
+#include "mixer.h"
+#include "conf.h"
+#include "patch.h"
+#include "graphics.h"
+#include "channel.h"
+#include "wave.h"
+#include "sampleChannel.h"
+#include "midiChannel.h"
+#include "glue.h"
+#include "gui_utils.h"
+
+#ifdef WITH_VST
+#include "gd_pluginList.h"
+#endif
+
+
+extern Mixer G_Mixer;
+extern Conf G_Conf;
+extern Patch G_Patch;
+extern gdMainWindow *mainWin;
+
+
+gSampleChannel::gSampleChannel(int X, int Y, int W, int H, class SampleChannel *ch)
+ : gChannel(X, Y, W, H, CHANNEL_SAMPLE), ch(ch)
+{
+ begin();
+
+#if defined(WITH_VST)
+ int delta = 168; // (7 widgets * 20) + (7 paddings * 4)
+#else
+ int delta = 144; // (6 widgets * 20) + (6 paddings * 4)
+#endif
+
+ button = new gButton(x(), y(), 20, 20);
+ status = new gStatus(button->x()+button->w()+4, y(), 20, 20, ch);
+ mainButton = new gSampleMainButton(status->x()+status->w()+4, y(), w() - delta, 20, "-- no sample --");
+ modeBox = new gModeBox(mainButton->x()+mainButton->w()+4, y(), 20, 20, ch);
+ mute = new gClick(modeBox->x()+modeBox->w()+4, y(), 20, 20, "", muteOff_xpm, muteOn_xpm);
+ solo = new gClick(mute->x()+mute->w()+4, y(), 20, 20, "", soloOff_xpm, soloOn_xpm);
+ readActions = NULL; // no 'R' button
+
+#if defined(WITH_VST)
+ fx = new gFxButton(solo->x()+solo->w()+4, y(), 20, 20, fxOff_xpm, fxOn_xpm);
+ vol = new gDial(fx->x()+fx->w()+4, y(), 20, 20);
+#else
+ vol = new gDial(solo->x()+solo->w()+4, y(), 20, 20);
+#endif
+
+ end();
+
+ resizable(mainButton);
+
+ update();
+
+ button->callback(cb_button, (void*)this);
+ button->when(FL_WHEN_CHANGED); // do callback on keypress && on keyrelease
+
+#ifdef WITH_VST
+ fx->callback(cb_openFxWindow, (void*)this);
+#endif
+
+ mute->type(FL_TOGGLE_BUTTON);
+ mute->callback(cb_mute, (void*)this);
+
+ solo->type(FL_TOGGLE_BUTTON);
+ solo->callback(cb_solo, (void*)this);
+
+ mainButton->callback(cb_openMenu, (void*)this);
+ vol->callback(cb_changeVol, (void*)this);
+
+ ch->guiChannel = this;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gSampleChannel::cb_button (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_button(); }
+void gSampleChannel::cb_mute (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_mute(); }
+void gSampleChannel::cb_solo (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_solo(); }
+void gSampleChannel::cb_openMenu (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_openMenu(); }
+void gSampleChannel::cb_changeVol (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_changeVol(); }
+void gSampleChannel::cb_readActions (Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_readActions(); }
+#ifdef WITH_VST
+void gSampleChannel::cb_openFxWindow(Fl_Widget *v, void *p) { ((gSampleChannel*)p)->__cb_openFxWindow(); }
+#endif
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gSampleChannel::__cb_mute()
+{
+ glue_setMute(ch);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gSampleChannel::__cb_solo()
+{
+ solo->value() ? glue_setSoloOn(ch) : glue_setSoloOff(ch);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gSampleChannel::__cb_changeVol()
+{
+ glue_setChanVol(ch, vol->value());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+void gSampleChannel::__cb_openFxWindow()
+{
+ gu_openSubWindow(mainWin, new gdPluginList(PluginHost::CHANNEL, ch), WID_FX_LIST);
+}
+#endif
+
+
+/* -------------------------------------------------------------------------- */
+
+
+
+void gSampleChannel::__cb_button()
+{
+ if (button->value()) // pushed
+ glue_keyPress(ch, Fl::event_ctrl(), Fl::event_shift());
+ else // released
+ glue_keyRelease(ch, Fl::event_ctrl(), Fl::event_shift());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gSampleChannel::__cb_openMenu()
+{
+ /* if you're recording (actions or input) no menu is allowed; you can't
+ * do anything, especially deallocate the channel */
+
+ if (G_Mixer.chanInput == ch || recorder::active)
+ return;
+
+ /* the following is a trash workaround for a FLTK menu. We need a gMenu
+ * widget asap */
+
+ Fl_Menu_Item rclick_menu[] = {
+ {"Load new sample..."}, // 0
+ {"Export sample to file..."}, // 1
+ {"Setup keyboard input..."}, // 2
+ {"Setup MIDI input..."}, // 3
+ {"Edit sample..."}, // 4
+ {"Edit actions..."}, // 5
+ {"Clear actions", 0, 0, 0, FL_SUBMENU}, // 6
+ {"All"}, // 7
+ {"Mute"}, // 8
+ {"Volume"}, // 9
+ {"Start/Stop"}, // 10
+ {0}, // 11
+ {"Free channel"}, // 12
+ {"Delete channel"}, // 13
+ {0}
+ };
+
+ if (ch->status & (STATUS_EMPTY | STATUS_MISSING)) {
+ rclick_menu[1].deactivate();
+ rclick_menu[4].deactivate();
+ rclick_menu[12].deactivate();
+ }
+
+ /* no 'clear actions' if there are no actions */
+
+ if (!ch->hasActions)
+ rclick_menu[6].deactivate();
+
+ /* no 'clear start/stop actions' for those channels in loop mode:
+ * they cannot have start/stop actions. */
+
+ if (ch->mode & LOOP_ANY)
+ rclick_menu[10].deactivate();
+
+ Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50);
+ b->box(G_BOX);
+ b->textsize(11);
+ b->textcolor(COLOR_TEXT_0);
+ b->color(COLOR_BG_0);
+
+ const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
+ if (!m) return;
+
+ if (strcmp(m->label(), "Load new sample...") == 0) {
+ openBrowser(BROWSER_LOAD_SAMPLE);
+ return;
+ }
+
+ if (strcmp(m->label(), "Setup keyboard input...") == 0) {
+ new gdKeyGrabber(ch); /// FIXME - use gu_openSubWindow
+ return;
+ }
+
+ if (strcmp(m->label(), "Setup MIDI input...") == 0) {
+ gu_openSubWindow(mainWin, new gdMidiGrabberChannel(ch), 0);
+ return;
+ }
+
+ if (strcmp(m->label(), "Edit sample...") == 0) {
+ gu_openSubWindow(mainWin, new gdEditor(ch), WID_SAMPLE_EDITOR); /// FIXME title it's up to gdEditor
+ return;
+ }
+
+ if (strcmp(m->label(), "Export sample to file...") == 0) {
+ openBrowser(BROWSER_SAVE_SAMPLE);
+ return;
+ }
+
+ if (strcmp(m->label(), "Delete channel") == 0) {
+ if (!gdConfirmWin("Warning", "Delete channel: are you sure?"))
+ return;
+ glue_deleteChannel(ch);
+ return;
+ }
+
+ if (strcmp(m->label(), "Free channel") == 0) {
+ if (ch->status == STATUS_PLAY) {
+ if (!gdConfirmWin("Warning", "This action will stop the channel: are you sure?"))
+ return;
+ }
+ else if (!gdConfirmWin("Warning", "Free channel: are you sure?"))
+ return;
+
+ glue_freeChannel(ch);
+
+ /* delete any related subwindow */
+
+ /** FIXME - use gu_closeAllSubwindows() */
+
+ mainWin->delSubWindow(WID_FILE_BROWSER);
+ mainWin->delSubWindow(WID_ACTION_EDITOR);
+ mainWin->delSubWindow(WID_SAMPLE_EDITOR);
+ mainWin->delSubWindow(WID_FX_LIST);
+
+ return;
+ }
+
+ if (strcmp(m->label(), "Mute") == 0) {
+ if (!gdConfirmWin("Warning", "Clear all mute actions: are you sure?"))
+ return;
+ recorder::clearAction(ch->index, ACTION_MUTEON | ACTION_MUTEOFF);
+ if (!ch->hasActions)
+ delActionButton();
+
+ /* TODO - set mute=false */
+
+ gu_refreshActionEditor(); // refresh a.editor window, it could be open
+ return;
+ }
+
+ if (strcmp(m->label(), "Start/Stop") == 0) {
+ if (!gdConfirmWin("Warning", "Clear all start/stop actions: are you sure?"))
+ return;
+ recorder::clearAction(ch->index, ACTION_KEYPRESS | ACTION_KEYREL | ACTION_KILLCHAN);
+ if (!ch->hasActions)
+ delActionButton();
+ gu_refreshActionEditor(); // refresh a.editor window, it could be open
+ return;
+ }
+
+ if (strcmp(m->label(), "Volume") == 0) {
+ if (!gdConfirmWin("Warning", "Clear all volume actions: are you sure?"))
+ return;
+ recorder::clearAction(ch->index, ACTION_VOLUME);
+ if (!ch->hasActions)
+ delActionButton();
+ gu_refreshActionEditor(); // refresh a.editor window, it could be open
+ return;
+ }
+
+ if (strcmp(m->label(), "All") == 0) {
+ if (!gdConfirmWin("Warning", "Clear all actions: are you sure?"))
+ return;
+ recorder::clearChan(ch->index);
+ delActionButton();
+ gu_refreshActionEditor(); // refresh a.editor window, it could be open
+ return;
+ }
+
+ if (strcmp(m->label(), "Edit actions...") == 0) {
+ gu_openSubWindow(mainWin, new gdActionEditor(ch), WID_ACTION_EDITOR);
+ return;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gSampleChannel::__cb_readActions()
+{
+ ch->readActions ? glue_stopReadingRecs(ch) : glue_startReadingRecs(ch);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gSampleChannel::openBrowser(int type)
+{
+ const char *title = "";
+ switch (type) {
+ case BROWSER_LOAD_SAMPLE:
+ title = "Browse Sample";
+ break;
+ case BROWSER_SAVE_SAMPLE:
+ title = "Save Sample";
+ break;
+ case -1:
+ title = "Edit Sample";
+ break;
+ }
+ gWindow *childWin = new gdBrowser(title, G_Conf.samplePath, ch, type);
+ gu_openSubWindow(mainWin, childWin, WID_FILE_BROWSER);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gSampleChannel::refresh()
+{
+ if (!mainButton->visible()) // mainButton invisible? status too (see below)
+ return;
+
+ setColorsByStatus(ch->status, ch->recStatus);
+
+ if (ch->wave != NULL) {
+
+ if (G_Mixer.chanInput == ch)
+ mainButton->bgColor0 = COLOR_BG_3;
+
+ if (recorder::active) {
+ if (recorder::canRec(ch)) {
+ mainButton->bgColor0 = COLOR_BG_4;
+ mainButton->txtColor = COLOR_TEXT_0;
+ }
+ }
+ status->redraw(); // status invisible? sampleButton too (see below)
+ }
+ mainButton->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gSampleChannel::reset()
+{
+ mainButton->bgColor0 = COLOR_BG_0;
+ mainButton->bdColor = COLOR_BD_0;
+ mainButton->txtColor = COLOR_TEXT_0;
+ mainButton->label("-- no sample --");
+ delActionButton(true); // force==true, don't check, just remove it
+ mainButton->redraw();
+ status->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gSampleChannel::update()
+{
+ /* update sample button's label */
+
+ switch (ch->status) {
+ case STATUS_EMPTY:
+ mainButton->label("-- no sample --");
+ break;
+ case STATUS_MISSING:
+ case STATUS_WRONG:
+ mainButton->label("* file not found! *");
+ break;
+ default:
+ mainButton->label(ch->wave->name.c_str());
+ break;
+ }
+
+ /* update channels. If you load a patch with recorded actions, the 'R'
+ * button must be shown. Moreover if the actions are active, the 'R'
+ * button must be activated accordingly. */
+
+ if (ch->hasActions)
+ addActionButton();
+ else
+ delActionButton();
+
+ /* update key box */
+
+ char k[4];
+ sprintf(k, "%c", ch->key);
+ button->copy_label(k);
+ button->redraw();
+
+ /* updates modebox */
+
+ modeBox->value(ch->mode);
+ modeBox->redraw();
+
+ /* update volumes+mute+solo */
+
+ vol->value(ch->volume);
+ mute->value(ch->mute);
+ solo->value(ch->solo);
+
+#ifdef WITH_VST
+ fx->full = ch->plugins.size > 0;
+#endif
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gSampleChannel::keyPress(int e)
+{
+ return handleKey(e, ch->key);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gSampleChannel::addActionButton()
+{
+ /* quit if 'R' exists yet. */
+
+ if (readActions != NULL)
+ return;
+
+ mainButton->size(mainButton->w()-24, mainButton->h());
+
+ redraw();
+
+ readActions = new gClick(mainButton->x() + mainButton->w() + 4,
+ mainButton->y(), 20, 20, "", readActionOff_xpm,
+ readActionOn_xpm);
+ readActions->type(FL_TOGGLE_BUTTON);
+ readActions->value(ch->readActions);
+ readActions->callback(cb_readActions, (void*)this);
+ add(readActions);
+
+ /* hard redraw: there's no other way to avoid glitches when moving
+ * the 'R' button */
+
+ mainWin->keyboard->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gSampleChannel::delActionButton(bool force)
+{
+ if (readActions == NULL)
+ return;
+
+ /* TODO - readActions check is useless here */
+
+ if (!force && (readActions == NULL || ch->hasActions))
+ return;
+
+ remove(readActions); // delete from Keyboard group (FLTK)
+ delete readActions; // delete (C++)
+ readActions = NULL;
+
+ mainButton->size(mainButton->w()+24, mainButton->h());
+ mainButton->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gSampleChannel::resize(int X, int Y, int W, int H)
+{
+ gChannel::resize(X, Y, W, H);
+
+ if (w() < BREAK_FX) {
+#ifdef WITH_VST
+ fx->hide();
+#endif
+ mainButton->size(w() - BREAK_DELTA, mainButton->h());
+ mute->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20);
+ solo->resize(mute->x()+mute->w()+4, y(), 20, 20);
+ }
+ else
+ if (w() < BREAK_MODE_BOX) {
+#ifdef WITH_VST
+ fx->show();
+#endif
+ mainButton->size(w() - (BREAK_DELTA + BREAK_UNIT), mainButton->h());
+ mute->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20);
+ solo->resize(mute->x()+mute->w()+4, y(), 20, 20);
+ modeBox->hide();
+ }
+ else
+ if (w() < BREAK_READ_ACTIONS) {
+ modeBox->show();
+ mainButton->size(w() - (BREAK_DELTA + (BREAK_UNIT * 2)), mainButton->h());
+ modeBox->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20);
+ if (readActions) {
+ readActions->hide();
+ }
+ }
+ else {
+ if (readActions) {
+ mainButton->size(w() - (BREAK_DELTA + (BREAK_UNIT * 3)), mainButton->h());
+ readActions->resize(mainButton->x()+mainButton->w()+4, y(), 20, 20);
+ readActions->show();
+ }
+ }
+
+ gChannel::init_sizes();
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+gSampleMainButton::gSampleMainButton(int x, int y, int w, int h, const char *l)
+ : gMainButton(x, y, w, h, l) {}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gSampleMainButton::handle(int e)
+{
+ int ret = gClick::handle(e);
+ switch (e) {
+ case FL_DND_ENTER:
+ case FL_DND_DRAG:
+ case FL_DND_RELEASE: {
+ ret = 1;
+ break;
+ }
+ case FL_PASTE: {
+ gSampleChannel *gch = (gSampleChannel*) parent(); // parent is gSampleChannel
+ SampleChannel *ch = gch->ch;
+ int result = glue_loadChannel(ch, gTrim(gStripFileUrl(Fl::event_text())).c_str());
+ if (result != SAMPLE_LOADED_OK)
+ mainWin->keyboard->printChannelMessage(result);
+ ret = 1;
+ break;
+ }
+ }
+ return ret;
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_sampleChannel
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_SAMPLE_CHANNEL_H
+#define GE_SAMPLE_CHANNEL_H
+
+
+#include <FL/Fl_Scroll.H>
+#include <FL/Fl_Box.H>
+#include <FL/Fl_Menu_Button.H>
+#include "ge_channel.h"
+#include "ge_mixed.h"
+
+
+class gSampleChannel : public gChannel
+{
+private:
+
+ static void cb_button (Fl_Widget *v, void *p);
+ static void cb_mute (Fl_Widget *v, void *p);
+ static void cb_solo (Fl_Widget *v, void *p);
+ static void cb_openMenu (Fl_Widget *v, void *p);
+ static void cb_changeVol (Fl_Widget *v, void *p);
+ static void cb_readActions (Fl_Widget *v, void *p);
+#ifdef WITH_VST
+ static void cb_openFxWindow (Fl_Widget *v, void *p);
+#endif
+
+ inline void __cb_mute ();
+ inline void __cb_solo ();
+ inline void __cb_changeVol ();
+ inline void __cb_button ();
+ inline void __cb_openMenu ();
+ inline void __cb_readActions ();
+#ifdef WITH_VST
+ inline void __cb_openFxWindow();
+#endif
+
+ void openBrowser(int type);
+
+public:
+
+ gSampleChannel(int x, int y, int w, int h, class SampleChannel *ch);
+
+ void reset ();
+ void update ();
+ void refresh ();
+ int keyPress(int event);
+ void resize (int x, int y, int w, int h);
+
+ /* add/delActionButton
+ * add or remove 'R' button when actions are available. 'Status' is
+ * the initial status of the button: on or off.
+ * If force==true remove the button with no further checks. */
+
+ void addActionButton();
+ void delActionButton(bool force=false);
+
+ class gModeBox *modeBox;
+ class gClick *readActions;
+
+ class SampleChannel *ch;
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+class gSampleMainButton : public gMainButton
+{
+public:
+ gSampleMainButton(int x, int y, int w, int h, const char *l=0);
+ int handle(int e);
+};
+
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_waveform
+ * an element which represents a waveform.
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include <FL/Fl_Menu_Item.H>
+#include <FL/Fl_Menu_Button.H>
+#include <samplerate.h>
+#include "ge_waveform.h"
+#include "gd_editor.h"
+#include "wave.h"
+#include "conf.h"
+#include "glue.h"
+#include "mixer.h"
+#include "waveFx.h"
+#include "ge_mixed.h"
+#include "gg_waveTools.h"
+#include "channel.h"
+#include "sampleChannel.h"
+
+
+extern Mixer G_Mixer;
+extern Conf G_Conf;
+
+
+gWaveform::gWaveform(int x, int y, int w, int h, class SampleChannel *ch, const char *l)
+: Fl_Widget(x, y, w, h, l),
+ chan(ch),
+ menuOpen(false),
+ chanStart(0),
+ chanStartLit(false),
+ chanEnd(0),
+ chanEndLit(false),
+ ratio(0.0f),
+ selectionA(0),
+ selectionB(0),
+ selectionA_abs(0),
+ selectionB_abs(0)
+{
+ data.sup = NULL;
+ data.inf = NULL;
+ data.size = 0;
+
+ grid.snap = G_Conf.sampleEditorGridOn;
+ grid.level = G_Conf.sampleEditorGridVal;
+
+ stretchToWindow();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+gWaveform::~gWaveform()
+{
+ freeData();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gWaveform::freeData()
+{
+ if (data.sup != NULL) {
+ free(data.sup);
+ free(data.inf);
+ data.sup = NULL;
+ data.inf = NULL;
+ data.size = 0;
+ }
+ grid.points.clear();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gWaveform::alloc(int datasize)
+{
+ ratio = chan->wave->size / (float) datasize;
+
+ if (ratio < 2)
+ return 0;
+
+ freeData();
+
+ data.size = datasize;
+ data.sup = (int*) malloc(data.size * sizeof(int));
+ data.inf = (int*) malloc(data.size * sizeof(int));
+
+ int offset = h() / 2;
+ int zero = y() + offset; // center, zero amplitude (-inf dB)
+
+ /* grid frequency: store a grid point every 'gridFreq' pixel. Must be
+ * even, as always */
+
+ int gridFreq = 0;
+ if (grid.level != 0) {
+ gridFreq = chan->wave->size / grid.level;
+ if (gridFreq % 2 != 0)
+ gridFreq--;
+ }
+
+ for (int i=0; i<data.size; i++) {
+
+ int pp; // point prev
+ int pn; // point next
+
+ /* resampling the waveform, hardcore way. Many thanks to
+ * http://fourier.eng.hmc.edu/e161/lectures/resize/node3.html
+ * Note: we use
+ * p = j * (m-1 / n)
+ * instead of
+ * p = j * (m-1 / n-1)
+ * in order to obtain 'datasize' cells to parse (and not datasize-1) */
+
+ pp = i * ((chan->wave->size - 1) / (float) datasize);
+ pn = (i+1) * ((chan->wave->size - 1) / (float) datasize);
+
+ if (pp % 2 != 0) pp -= 1;
+ if (pn % 2 != 0) pn -= 1;
+
+ float peaksup = 0.0f;
+ float peakinf = 0.0f;
+
+ /* scan the original data in chunks */
+
+ int k = pp;
+ while (k < pn) {
+
+ if (chan->wave->data[k] > peaksup)
+ peaksup = chan->wave->data[k]; // FIXME - Left data only
+ else
+ if (chan->wave->data[k] <= peakinf)
+ peakinf = chan->wave->data[k]; // FIXME - Left data only
+
+ /* print grid */
+
+ if (gridFreq != 0)
+ if (k % gridFreq == 0 && k != 0)
+ grid.points.add(i);
+
+ k += 2;
+ }
+
+ data.sup[i] = zero - (peaksup * chan->boost * offset);
+ data.inf[i] = zero - (peakinf * chan->boost * offset);
+
+ // avoid window overflow
+
+ if (data.sup[i] < y()) data.sup[i] = y();
+ if (data.inf[i] > y()+h()-1) data.inf[i] = y()+h()-1;
+ }
+
+ recalcPoints();
+ return 1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gWaveform::recalcPoints()
+{
+ selectionA = relativePoint(selectionA_abs);
+ selectionB = relativePoint(selectionB_abs);
+ chanStart = relativePoint(chan->begin / 2);
+
+ /* fix the rounding error when chanEnd is set on the very end of the
+ * sample */
+
+ if (chan->end == chan->wave->size)
+ chanEnd = data.size - 2; // 2 px border
+ else
+ chanEnd = relativePoint(chan->end / 2);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gWaveform::draw()
+{
+ /* blank canvas */
+
+ fl_rectf(x(), y(), w(), h(), COLOR_BG_0);
+
+ /* draw selection (if any) */
+
+ if (selectionA != selectionB) {
+
+ int a_x = selectionA + x() - BORDER; // - start;
+ int b_x = selectionB + x() - BORDER; // - start;
+
+ if (a_x < 0)
+ a_x = 0;
+ if (b_x >= w()-1)
+ b_x = w()-1;
+
+ if (selectionA < selectionB)
+ fl_rectf(a_x+BORDER, y(), b_x-a_x, h(), COLOR_BD_0);
+ else
+ fl_rectf(b_x+BORDER, y(), a_x-b_x, h(), COLOR_BD_0);
+ }
+
+ /* draw waveform from x1 (offset driven by the scrollbar) to x2
+ * (width of parent window). We don't draw the entire waveform,
+ * only the visibile part. */
+
+ int offset = h() / 2;
+ int zero = y() + offset; // sample zero (-inf dB)
+
+ int wx1 = abs(x() - ((gWaveTools*)parent())->x());
+ int wx2 = wx1 + ((gWaveTools*)parent())->w();
+ if (x()+w() < ((gWaveTools*)parent())->w())
+ wx2 = x() + w() - BORDER;
+
+ fl_color(0, 0, 0);
+ for (int i=wx1; i<wx2; i++) {
+ fl_line(i+x(), zero, i+x(), data.sup[i]);
+ fl_line(i+x(), zero, i+x(), data.inf[i]);
+
+ /* print grid */
+
+ for (unsigned k=0; k<grid.points.size; k++) {
+ if (grid.points.at(k) == i) {
+ //gLog("draw grid line at %d\n", i);
+ fl_color(fl_rgb_color(54, 54, 54));
+ fl_line_style(FL_DASH, 0, NULL);
+ fl_line(i+x(), y(), i+x(), y()+h());
+ fl_color(0, 0, 0);
+ fl_line_style(FL_SOLID, 0, NULL);
+ break;
+ }
+ }
+ }
+
+ /* border box */
+
+ fl_rect(x(), y(), w(), h(), COLOR_BD_0);
+
+ /* print chanStart */
+
+ int lineX = x()+chanStart+1;
+
+ if (chanStartLit) fl_color(COLOR_BD_1);
+ else fl_color(COLOR_BD_0);
+
+ /* vertical line */
+
+ fl_line(lineX, y()+1, lineX, y()+h()-2);
+
+ /* print flag and avoid overflow */
+
+ if (lineX+FLAG_WIDTH > w()+x()-2)
+ fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, w()-lineX+x()-1, FLAG_HEIGHT);
+ else {
+ fl_rectf(lineX, y()+h()-FLAG_HEIGHT-1, FLAG_WIDTH, FLAG_HEIGHT);
+ fl_color(255, 255, 255);
+ fl_draw("s", lineX+4, y()+h()-3);
+ }
+
+ /* print chanEnd */
+
+ lineX = x()+chanEnd;
+ if (chanEndLit) fl_color(COLOR_BD_1);
+ else fl_color(COLOR_BD_0);
+
+ fl_line(lineX, y()+1, lineX, y()+h()-2);
+
+ if (lineX-FLAG_WIDTH < x())
+ fl_rectf(x()+1, y()+1, lineX-x(), FLAG_HEIGHT);
+ else {
+ fl_rectf(lineX-FLAG_WIDTH, y()+1, FLAG_WIDTH, FLAG_HEIGHT);
+ fl_color(255, 255, 255);
+ fl_draw("e", lineX-10, y()+10);
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gWaveform::handle(int e)
+{
+ int ret = 0;
+
+ switch (e) {
+
+ case FL_PUSH: {
+
+ mouseX = Fl::event_x();
+ pushed = true;
+
+ if (!mouseOnEnd() && !mouseOnStart()) {
+
+ /* right button? show the menu. Don't set selectionA,B,etc */
+
+ if (Fl::event_button3()) {
+ openEditMenu();
+ }
+ else
+ if (mouseOnSelectionA() || mouseOnSelectionB()) {
+ resized = true;
+ }
+ else {
+ dragged = true;
+ selectionA = Fl::event_x() - x();
+
+ if (selectionA >= data.size) selectionA = data.size;
+
+ selectionB = selectionA;
+ selectionA_abs = absolutePoint(selectionA);
+ selectionB_abs = selectionA_abs;
+ }
+ }
+
+ ret = 1;
+ break;
+ }
+
+ case FL_RELEASE: {
+
+ /* don't recompute points if something is selected */
+
+ if (selectionA != selectionB) {
+ pushed = false;
+ dragged = false;
+ ret = 1;
+ break;
+ }
+
+ int realChanStart = chan->begin;
+ int realChanEnd = chan->end;
+
+ if (chanStartLit)
+ realChanStart = absolutePoint(chanStart)*2;
+ else
+ if (chanEndLit)
+ realChanEnd = absolutePoint(chanEnd)*2;
+
+ glue_setBeginEndChannel((gdEditor *) window(), chan, realChanStart, realChanEnd, false);
+
+ pushed = false;
+ dragged = false;
+
+ redraw();
+ ret = 1;
+ break;
+ }
+
+ case FL_ENTER: { // enables FL_DRAG
+ ret = 1;
+ break;
+ }
+
+ case FL_LEAVE: {
+ if (chanStartLit || chanEndLit) {
+ chanStartLit = false;
+ chanEndLit = false;
+ redraw();
+ }
+ ret = 1;
+ break;
+ }
+
+ case FL_MOVE: {
+ mouseX = Fl::event_x();
+ mouseY = Fl::event_y();
+
+ if (mouseOnStart()) {
+ chanStartLit = true;
+ redraw();
+ }
+ else
+ if (chanStartLit) {
+ chanStartLit = false;
+ redraw();
+ }
+
+ if (mouseOnEnd()) {
+ chanEndLit = true;
+ redraw();
+ }
+ else
+ if (chanEndLit) {
+ chanEndLit = false;
+ redraw();
+ }
+
+ if (mouseOnSelectionA())
+ fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+ else
+ if (mouseOnSelectionB())
+ fl_cursor(FL_CURSOR_WE, FL_WHITE, FL_BLACK);
+ else
+ fl_cursor(FL_CURSOR_DEFAULT, FL_WHITE, FL_BLACK);
+
+ ret = 1;
+ break;
+ }
+
+ case FL_DRAG: {
+
+ /* here the mouse is on the chanStart tool */
+
+ if (chanStartLit && pushed) {
+
+ chanStart = Fl::event_x() - x();
+
+ if (grid.snap)
+ chanStart = applySnap(chanStart);
+
+ if (chanStart < 0)
+ chanStart = 0;
+ else
+ if (chanStart >= chanEnd)
+ chanStart = chanEnd-2;
+
+ redraw();
+ }
+ else
+ if (chanEndLit && pushed) {
+
+ chanEnd = Fl::event_x() - x();
+
+ if (grid.snap)
+ chanEnd = applySnap(chanEnd);
+
+ if (chanEnd >= data.size - 2)
+ chanEnd = data.size - 2;
+ else
+ if (chanEnd <= chanStart)
+ chanEnd = chanStart + 2;
+
+ redraw();
+ }
+
+ /* here the mouse is on the waveform, i.e. a selection */
+
+ else
+ if (dragged) {
+
+ selectionB = Fl::event_x() - x();
+
+ if (selectionB >= data.size)
+ selectionB = data.size;
+
+ if (selectionB <= 0)
+ selectionB = 0;
+
+ if (grid.snap)
+ selectionB = applySnap(selectionB);
+
+ selectionB_abs = absolutePoint(selectionB);
+ redraw();
+ }
+
+ /* here the mouse is on a selection boundary i.e. resize */
+
+ else
+ if (resized) {
+ int pos = Fl::event_x() - x();
+ if (mouseOnSelectionA()) {
+ selectionA = grid.snap ? applySnap(pos) : pos;
+ selectionA_abs = absolutePoint(selectionA);
+ }
+ else
+ if (mouseOnSelectionB()) {
+ selectionB = grid.snap ? applySnap(pos) : pos;
+ selectionB_abs = absolutePoint(selectionB);
+ }
+ redraw();
+ }
+ mouseX = Fl::event_x();
+ ret = 1;
+ break;
+ }
+ }
+ return ret;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+/* pixel snap disances (10px) must be equal to those defined in
+ * gWaveform::mouseOnSelectionA() and gWaverfrom::mouseOnSelectionB() */
+/* TODO - use constant for 10px */
+
+int gWaveform::applySnap(int pos)
+{
+ for (unsigned i=0; i<grid.points.size; i++) {
+ if (pos >= grid.points.at(i) - 10 &&
+ pos <= grid.points.at(i) + 10)
+ {
+ return grid.points.at(i);
+ }
+ }
+ return pos;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool gWaveform::mouseOnStart()
+{
+ return mouseX-10 > chanStart + x() - BORDER &&
+ mouseX-10 <= chanStart + x() - BORDER + FLAG_WIDTH &&
+ mouseY > h() + y() - FLAG_HEIGHT;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool gWaveform::mouseOnEnd()
+{
+ return mouseX-10 >= chanEnd + x() - BORDER - FLAG_WIDTH &&
+ mouseX-10 <= chanEnd + x() - BORDER &&
+ mouseY <= y() + FLAG_HEIGHT + 1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+/* pixel boundaries (10px) must be equal to the snap factor distance
+ * defined in gWaveform::applySnap() */
+
+bool gWaveform::mouseOnSelectionA()
+{
+ if (selectionA == selectionB)
+ return false;
+ return mouseX >= selectionA-10+x() && mouseX <= selectionA+10+x();
+}
+
+
+bool gWaveform::mouseOnSelectionB()
+{
+ if (selectionA == selectionB)
+ return false;
+ return mouseX >= selectionB-10+x() && mouseX <= selectionB+10+x();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gWaveform::absolutePoint(int p)
+{
+ if (p <= 0)
+ return 0;
+
+ if (p > data.size)
+ return chan->wave->size / 2;
+
+ return (p * ratio) / 2;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gWaveform::relativePoint(int p)
+{
+ return (ceilf(p / ratio)) * 2;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gWaveform::openEditMenu()
+{
+ if (selectionA == selectionB)
+ return;
+
+ menuOpen = true;
+
+ Fl_Menu_Item menu[] = {
+ {"Cut"},
+ {"Trim"},
+ {"Silence"},
+ {"Fade in"},
+ {"Fade out"},
+ {"Smooth edges"},
+ {"Set start/end here"},
+ {0}
+ };
+
+ if (chan->status == STATUS_PLAY) {
+ menu[0].deactivate();
+ menu[1].deactivate();
+ }
+
+ Fl_Menu_Button *b = new Fl_Menu_Button(0, 0, 100, 50);
+ b->box(G_BOX);
+ b->textsize(11);
+ b->textcolor(COLOR_TEXT_0);
+ b->color(COLOR_BG_0);
+
+ const Fl_Menu_Item *m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, b);
+ if (!m) {
+ menuOpen = false;
+ return;
+ }
+
+ /* straightSel() to ensure that point A is always lower than B */
+
+ straightSel();
+
+ if (strcmp(m->label(), "Silence") == 0) {
+ wfx_silence(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB));
+
+ selectionA = 0;
+ selectionB = 0;
+
+ stretchToWindow();
+ redraw();
+ menuOpen = false;
+ return;
+ }
+
+ if (strcmp(m->label(), "Set start/end here") == 0) {
+
+ glue_setBeginEndChannel(
+ (gdEditor *) window(), // parent
+ chan,
+ absolutePoint(selectionA) * 2, // stereo!
+ absolutePoint(selectionB) * 2, // stereo!
+ false, // no recalc (we do it here)
+ false // don't check
+ );
+
+ selectionA = 0;
+ selectionB = 0;
+ selectionA_abs = 0;
+ selectionB_abs = 0;
+
+ recalcPoints();
+ redraw();
+ menuOpen = false;
+ return;
+ }
+
+ if (strcmp(m->label(), "Cut") == 0) {
+ wfx_cut(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB));
+
+ /* for convenience reset start/end points */
+
+ glue_setBeginEndChannel(
+ (gdEditor *) window(),
+ chan,
+ 0,
+ chan->wave->size,
+ false);
+
+ selectionA = 0;
+ selectionB = 0;
+ selectionA_abs = 0;
+ selectionB_abs = 0;
+
+ setZoom(0);
+
+ menuOpen = false;
+ return;
+ }
+
+ if (strcmp(m->label(), "Trim") == 0) {
+ wfx_trim(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB));
+
+ glue_setBeginEndChannel(
+ (gdEditor *) window(),
+ chan,
+ 0,
+ chan->wave->size,
+ false);
+
+ selectionA = 0;
+ selectionB = 0;
+ selectionA_abs = 0;
+ selectionB_abs = 0;
+
+ stretchToWindow();
+ menuOpen = false;
+ redraw();
+ return;
+ }
+
+ if (!strcmp(m->label(), "Fade in") || !strcmp(m->label(), "Fade out")) {
+
+ int type = !strcmp(m->label(), "Fade in") ? 0 : 1;
+ wfx_fade(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB), type);
+
+ selectionA = 0;
+ selectionB = 0;
+
+ stretchToWindow();
+ redraw();
+ menuOpen = false;
+ return;
+ }
+
+ if (!strcmp(m->label(), "Smooth edges")) {
+
+ wfx_smooth(chan->wave, absolutePoint(selectionA), absolutePoint(selectionB));
+
+ selectionA = 0;
+ selectionB = 0;
+
+ stretchToWindow();
+ redraw();
+ menuOpen = false;
+ return;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gWaveform::straightSel()
+{
+ if (selectionA > selectionB) {
+ unsigned tmp = selectionB;
+ selectionB = selectionA;
+ selectionA = tmp;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gWaveform::setZoom(int type)
+{
+ int newSize;
+ if (type == -1) newSize = data.size*2; // zoom in
+ else newSize = data.size/2; // zoom out
+
+ if (alloc(newSize)) {
+ size(data.size, h());
+
+ /* zoom to pointer */
+
+ int shift;
+ if (x() > 0)
+ shift = Fl::event_x() - x();
+ else
+ if (type == -1)
+ shift = Fl::event_x() + abs(x());
+ else
+ shift = (Fl::event_x() + abs(x())) / -2;
+
+ if (x() - shift > BORDER)
+ shift = 0;
+
+ position(x() - shift, y());
+
+
+ /* avoid overflow when zooming out with scrollbar like that:
+ * |----------[scrollbar]|
+ *
+ * offset vs smaller:
+ * |[wave------------| offset > 0 smaller = false
+ * |[wave----] | offset < 0, smaller = true
+ * |-------------] | offset < 0, smaller = false */
+
+ int parentW = ((gWaveTools*)parent())->w();
+ int thisW = x() + w() - BORDER; // visible width, not full width
+
+ if (thisW < parentW)
+ position(x() + parentW - thisW, y());
+ if (smaller())
+ stretchToWindow();
+
+ redraw();
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gWaveform::stretchToWindow()
+{
+ int s = ((gWaveTools*)parent())->w();
+ alloc(s);
+ position(BORDER, y());
+ size(s, h());
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool gWaveform::smaller()
+{
+ return w() < ((gWaveTools*)parent())->w();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gWaveform::setGridLevel(int l)
+{
+ grid.points.clear();
+ grid.level = l;
+ alloc(data.size);
+ redraw();
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_waveform
+ * an element which represents a waveform.
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef GE_WAVEFORM_H
+#define GE_WAVEFORM_H
+
+#include <FL/Fl.H>
+#include <FL/Fl_Widget.H>
+#include <FL/fl_draw.H>
+#include <math.h>
+#include "utils.h"
+
+#define FLAG_WIDTH 14
+#define FLAG_HEIGHT 12
+#define BORDER 8 // window border <-> widget border
+
+
+class gWaveform : public Fl_Widget {
+
+private:
+
+ /* data
+ * real graphic stuff from the underlying waveform */
+
+ struct data {
+ int *sup;
+ int *inf;
+ int size;
+ } data;
+
+ /* grid */
+
+ struct grid {
+ bool snap;
+ int level;
+ gVector<int> points;
+ } grid;
+
+ /* chan
+ * chan in use. */
+
+ class SampleChannel *chan;
+
+ /* menuOpen
+ * is the menu open? */
+
+ bool menuOpen;
+
+ /* mouseOnStart/end
+ * is mouse on start or end flag? */
+
+ bool mouseOnStart();
+ bool mouseOnEnd();
+
+ /* mouseOnSelectionA/B
+ * as above, for the selection */
+
+ bool mouseOnSelectionA();
+ bool mouseOnSelectionB();
+
+ /* absolutePoint
+ * from a relative 'p' point (zoom affected) returns the same point
+ * zoom 1:1 based */
+
+ int absolutePoint(int p);
+
+ /* relativePoint
+ * from an absolute 'p' point (1:1 zoom), returns the same point zoom
+ * affected */
+
+ int relativePoint(int p);
+
+ /* straightSel
+ * helper function which flattens the selection if it was made from
+ * right to left (inverse selection) */
+
+ void straightSel();
+
+ /* freeData
+ * destroy any graphical buffer */
+
+ void freeData();
+
+ /* smaller
+ * is the waveform smaller than the parent window? */
+
+ bool smaller();
+
+ /* applySnap
+ * snap a point at 'pos' pixel */
+
+ int applySnap(int pos);
+
+public:
+
+ gWaveform(int x, int y, int w, int h, class SampleChannel *ch, const char *l=0);
+ ~gWaveform();
+ void draw();
+ int handle(int e);
+
+ /* alloc
+ * allocate memory for the picture */
+
+ int alloc(int datasize=0);
+
+ /* recalcPoints
+ * re-calc chanStart, chanEnd, ... */
+
+ void recalcPoints();
+
+ /* openEditMenu
+ * show edit menu on right-click */
+
+ void openEditMenu();
+
+ /* displayRatio
+ * how much of the waveform is being displayed on screen */
+
+ inline float displayRatio() { return 1.0f / (data.size / (float) w()); };
+
+ /* zoom
+ * type == 1 : zoom out, type == -1: zoom in */
+
+ void setZoom(int type);
+
+ /* strecthToWindow
+ * shrink or enlarge the waveform to match parent's width (gWaveTools) */
+
+ void stretchToWindow();
+
+ /* setGridLevel
+ * set a new frequency level for the grid. 0 means disabled. */
+
+ void setGridLevel(int l);
+
+ inline void setSnap(bool v) { grid.snap = v; }
+ inline bool getSnap() { return grid.snap; }
+
+ inline int getSize() { return data.size; }
+
+ int chanStart;
+ bool chanStartLit;
+ int chanEnd;
+ bool chanEndLit;
+ bool pushed;
+ bool dragged;
+ bool resized;
+
+ float ratio;
+
+ /* TODO - useless! use Fl::mouse_x() and Fl::mouse_y() instead */
+ int mouseX; // mouse pos for drag.n.drop
+ int mouseY;
+
+ /* selectionA/B = portion of the selected wave
+ * " " "" " _abs = selectionA/B not affected by zoom */
+ /** TODO - change selectionA to selectionA_rel
+ TODO - change selectionB to selectionB_rel */
+ int selectionA;
+ int selectionB;
+ int selectionA_abs;
+ int selectionB_abs;
+};
+
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_window
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include "ge_window.h"
+#include "log.h"
+
+
+gWindow::gWindow(int x, int y, int w, int h, const char *title, int id)
+ : Fl_Double_Window(x, y, w, h, title), id(id), parent(NULL) { }
+
+
+/* ------------------------------------------------------------------ */
+
+
+gWindow::gWindow(int w, int h, const char *title, int id)
+ : Fl_Double_Window(w, h, title), id(id), parent(NULL) { }
+
+
+/* ------------------------------------------------------------------ */
+
+
+gWindow::~gWindow() {
+
+ /* delete all subwindows in order to empty the stack */
+
+ for (unsigned i=0; i<subWindows.size; i++)
+ delete subWindows.at(i);
+ subWindows.clear();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+/* this is the default callback of each window, fired when the user closes
+ * the window with the 'x'. Watch out: is the parent that calls delSubWIndow */
+
+void gWindow::cb_closeChild(Fl_Widget *v, void *p) {
+ gWindow *child = (gWindow*) v;
+ if (child->getParent() != NULL)
+ (child->getParent())->delSubWindow(child);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gWindow::addSubWindow(gWindow *w) {
+
+ /** TODO - useless: delete ---------------------------------------- */
+ for (unsigned i=0; i<subWindows.size; i++)
+ if (w->getId() == subWindows.at(i)->getId()) {
+ //gLog("[gWindow] window %p (id=%d) exists, not added (and deleted)\n", (void*)w, w->getId());
+ delete w;
+ return;
+ }
+ /** --------------------------------------------------------------- */
+
+ w->setParent(this);
+ w->callback(cb_closeChild); // you can pass params: w->callback(cb_closeChild, (void*)params)
+ subWindows.add(w);
+ //debug();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gWindow::delSubWindow(gWindow *w) {
+ for (unsigned i=0; i<subWindows.size; i++)
+ if (w->getId() == subWindows.at(i)->getId()) {
+ delete subWindows.at(i);
+ subWindows.del(i);
+ //debug();
+ return;
+ }
+ //debug();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gWindow::delSubWindow(int id) {
+ for (unsigned i=0; i<subWindows.size; i++)
+ if (subWindows.at(i)->getId() == id) {
+ delete subWindows.at(i);
+ subWindows.del(i);
+ //debug();
+ return;
+ }
+ //debug();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gWindow::getId() {
+ return id;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gWindow::setId(int id) {
+ this->id = id;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gWindow::debug() {
+ gLog("---- window stack (id=%d): ----\n", getId());
+ for (unsigned i=0; i<subWindows.size; i++)
+ gLog("[gWindow] %p (id=%d)\n", (void*)subWindows.at(i), subWindows.at(i)->getId());
+ gLog("----\n");
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+gWindow *gWindow::getParent() {
+ return parent;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gWindow::setParent(gWindow *w) {
+ parent = w;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool gWindow::hasWindow(int id) {
+ for (unsigned i=0; i<subWindows.size; i++)
+ if (id == subWindows.at(i)->getId())
+ return true;
+ return false;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+gWindow *gWindow::getChild(int id) {
+ for (unsigned i=0; i<subWindows.size; i++)
+ if (id == subWindows.at(i)->getId())
+ return subWindows.at(i);
+ return NULL;
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ge_window
+ * A custom window.
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef __GE_WINDOW_H__
+#define __GE_WINDOW_H__
+
+
+#include <FL/Fl_Double_Window.H>
+#include "utils.h"
+
+
+class gWindow : public Fl_Double_Window {
+
+protected:
+ gVector <gWindow *> subWindows;
+ int id;
+ gWindow *parent;
+
+public:
+ gWindow(int x, int y, int w, int h, const char *title=0, int id=0);
+ gWindow(int w, int h, const char *title=0, int id=0);
+ ~gWindow();
+
+ static void cb_closeChild(Fl_Widget *v, void *p);
+
+ void addSubWindow(gWindow *w);
+ void delSubWindow(gWindow *w);
+ void delSubWindow(int id);
+
+ int getId();
+ void setId(int id);
+ void debug();
+
+ void setParent(gWindow *);
+ gWindow *getParent();
+ gWindow *getChild(int id);
+
+ /* hasWindow
+ * true if the window with id 'id' exists in the stack. */
+
+ bool hasWindow(int id);
+
+};
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gg_keyboard
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "gg_keyboard.h"
+#include "gd_browser.h"
+#include "gd_mainWindow.h"
+#include "gd_editor.h"
+#include "gd_warnings.h"
+#include "ge_channel.h"
+#include "ge_sampleChannel.h"
+#include "mixer.h"
+#include "conf.h"
+#include "const.h"
+#include "glue.h"
+#include "patch.h"
+#include "channel.h"
+#include "sampleChannel.h"
+#include "log.h"
+
+
+extern Mixer G_Mixer;
+extern Conf G_Conf;
+extern Patch G_Patch;
+extern gdMainWindow *mainWin;
+
+
+int gKeyboard::indexColumn = 0;
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gKeyboard::gKeyboard(int X, int Y, int W, int H)
+: Fl_Scroll (X, Y, W, H),
+ bckspcPressed(false),
+ endPressed (false),
+ spacePressed (false),
+ addColumnBtn (NULL)
+{
+ color(COLOR_BG_MAIN);
+ type(Fl_Scroll::BOTH_ALWAYS);
+ scrollbar.color(COLOR_BG_0);
+ scrollbar.selection_color(COLOR_BG_1);
+ scrollbar.labelcolor(COLOR_BD_1);
+ scrollbar.slider(G_BOX);
+ hscrollbar.color(COLOR_BG_0);
+ hscrollbar.selection_color(COLOR_BG_1);
+ hscrollbar.labelcolor(COLOR_BD_1);
+ hscrollbar.slider(G_BOX);
+
+ addColumnBtn = new gClick(8, y(), 200, 20, "Add new column");
+ addColumnBtn->callback(cb_addColumn, (void*) this);
+ add(addColumnBtn);
+
+ init();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gKeyboard::init()
+{
+ /* add 6 empty columns as init layout */
+
+ __cb_addColumn();
+ __cb_addColumn();
+ __cb_addColumn();
+ __cb_addColumn();
+ __cb_addColumn();
+ __cb_addColumn();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gKeyboard::freeChannel(gChannel *gch)
+{
+ gch->reset();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gKeyboard::deleteChannel(gChannel *gch)
+{
+ for (unsigned i=0; i<columns.size; i++) {
+ int k = columns.at(i)->find(gch);
+ if (k != columns.at(i)->children()) {
+ columns.at(i)->deleteChannel(gch);
+ return;
+ }
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gKeyboard::updateChannel(gChannel *gch)
+{
+ gch->update();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gKeyboard::organizeColumns()
+{
+ /* if only one column exists don't cleanup: the initial column must
+ * stay here. */
+
+ if (columns.size == 1)
+ return;
+
+ /* otherwise delete all empty columns */
+ /** FIXME - this for loop might not work correctly! */
+
+ for (unsigned i=columns.size-1; i>=1; i--) {
+ if (columns.at(i)->isEmpty()) {
+ //Fl::delete_widget(columns.at(i));
+ delete columns.at(i);
+ columns.del(i);
+ }
+ }
+
+ /* compact column, avoid empty spaces */
+
+ for (unsigned i=1; i<columns.size; i++)
+ columns.at(i)->position(columns.at(i-1)->x() + columns.at(i-1)->w() + 16, y());
+
+ addColumnBtn->position(columns.last()->x() + columns.last()->w() + 16, y());
+
+ redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gKeyboard::cb_addColumn(Fl_Widget *v, void *p) { ((gKeyboard*)p)->__cb_addColumn(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gChannel *gKeyboard::addChannel(int colIndex, Channel *ch, bool build)
+{
+ gColumn *col = getColumn(colIndex);
+
+ /* no column with index 'colIndex' found? Just create it and set its index
+ to 'colIndex'. */
+
+ if (!col) {
+ __cb_addColumn();
+ col = columns.last();
+ col->setIndex(colIndex);
+ gLog("[gKeyboard::addChannel] created new column with index=%d\n", colIndex);
+ }
+
+ gLog("[gKeyboard::addChannel] add to column with index = %d\n", col->getIndex());
+ return col->addChannel(ch);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gKeyboard::refreshColumns()
+{
+ for (unsigned i=0; i<columns.size; i++)
+ columns.at(i)->refreshChannels();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gColumn *gKeyboard::getColumn(int index)
+{
+ for (unsigned i=0; i<columns.size; i++)
+ if (columns.at(i)->getIndex() == index)
+ return columns.at(i);
+ return NULL;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gKeyboard::handle(int e)
+{
+ int ret = Fl_Group::handle(e); // assume the buttons won't handle the Keyboard events
+ switch (e) {
+ case FL_FOCUS:
+ case FL_UNFOCUS: {
+ ret = 1; // enables receiving Keyboard events
+ break;
+ }
+ case FL_SHORTCUT: // in case widget that isn't ours has focus
+ case FL_KEYDOWN: // Keyboard key pushed
+ case FL_KEYUP: { // Keyboard key released
+
+ /* rewind session. Avoid retrigs */
+
+ if (e == FL_KEYDOWN) {
+ if (Fl::event_key() == FL_BackSpace && !bckspcPressed) {
+ bckspcPressed = true;
+ glue_rewindSeq();
+ ret = 1;
+ break;
+ }
+ else if (Fl::event_key() == FL_End && !endPressed) {
+ endPressed = true;
+ glue_startStopInputRec(false); // update gui
+ ret = 1;
+ break;
+ }
+ else if (Fl::event_key() == FL_Enter && !enterPressed) {
+ enterPressed = true;
+ glue_startStopActionRec();
+ ret = 1;
+ break;
+ }
+ else if (Fl::event_key() == ' ' && !spacePressed) {
+ spacePressed = true;
+ G_Mixer.running ? glue_stopSeq() : glue_startSeq(); // TODO - glue_startStopSeq, no core logic here
+ ret = 1;
+ break;
+ }
+ }
+ else if (e == FL_KEYUP) {
+ if (Fl::event_key() == FL_BackSpace)
+ bckspcPressed = false;
+ else if (Fl::event_key() == FL_End)
+ endPressed = false;
+ else if (Fl::event_key() == ' ')
+ spacePressed = false;
+ else if (Fl::event_key() == FL_Enter)
+ enterPressed = false;
+ }
+
+ /* Walk button arrays, trying to match button's label with the Keyboard event.
+ * If found, set that button's value() based on up/down event,
+ * and invoke that button's callback() */
+
+ for (unsigned i=0; i<columns.size; i++)
+ for (int k=1; k<columns.at(i)->children(); k++)
+ ret &= ((gChannel*)columns.at(i)->child(k))->keyPress(e);
+ break;
+ }
+ }
+ return ret;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gKeyboard::clear()
+{
+ for (unsigned i=0; i<columns.size; i++)
+ delete columns.at(i);
+ columns.clear();
+ indexColumn = 0; // new columns will start from index=0
+ addColumnBtn->position(8, y());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gKeyboard::setChannelWithActions(gSampleChannel *gch)
+{
+ if (gch->ch->hasActions)
+ gch->addActionButton();
+ else
+ gch->delActionButton();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gKeyboard::printChannelMessage(int res)
+{
+ if (res == SAMPLE_NOT_VALID)
+ gdAlert("This is not a valid WAVE file.");
+ else if (res == SAMPLE_MULTICHANNEL)
+ gdAlert("Multichannel samples not supported.");
+ else if (res == SAMPLE_WRONG_BIT)
+ gdAlert("This sample has an\nunsupported bit-depth (> 32 bit).");
+ else if (res == SAMPLE_WRONG_ENDIAN)
+ gdAlert("This sample has a wrong\nbyte order (not little-endian).");
+ else if (res == SAMPLE_WRONG_FORMAT)
+ gdAlert("This sample is encoded in\nan unsupported audio format.");
+ else if (res == SAMPLE_READ_ERROR)
+ gdAlert("Unable to read this sample.");
+ else if (res == SAMPLE_PATH_TOO_LONG)
+ gdAlert("File path too long.");
+ else
+ gdAlert("Unknown error.");
+}
+
+/* -------------------------------------------------------------------------- */
+
+
+void gKeyboard::__cb_addColumn()
+{
+ int colx;
+ int colxw;
+ int colw = 380;
+ if (columns.size == 0) {
+ colx = x() - xposition(); // mind the offset with xposition()
+ colxw = colx + colw;
+ }
+ else {
+ gColumn *prev = columns.last();
+ colx = prev->x()+prev->w() + 16;
+ colxw = colx + colw;
+ }
+
+ /* add gColumn to gKeyboard and to columns vector */
+
+ gColumn *gc = new gColumn(colx, y(), colw-20, 2000, indexColumn, this);
+ add(gc);
+ columns.add(gc);
+ indexColumn++;
+
+ /* move addColumn button */
+
+ addColumnBtn->position(colxw-4, y());
+ redraw();
+
+ gLog("[gKeyboard::__cb_addColumn] new column added (index = %d), total count=%d, addColumn(x)=%d\n",
+ gc->getIndex(), columns.size, addColumnBtn->x());
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gg_keyboard
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GG_KEYBOARD_H
+#define GG_KEYBOARD_H
+
+
+#include <FL/Fl.H>
+#include <FL/Fl_Scroll.H>
+#include <FL/Fl_Group.H>
+#include <FL/Fl_Box.H>
+#include <FL/Fl_Menu_Button.H>
+#include "ge_column.h"
+#include "utils.h"
+
+
+class gKeyboard : public Fl_Scroll
+{
+private:
+
+ static void cb_addColumn (Fl_Widget *v, void *p);
+ inline void __cb_addColumn();
+
+ bool bckspcPressed;
+ bool endPressed;
+ bool spacePressed;
+ bool enterPressed;
+
+ /* indexColumn
+ * the last index used for column. */
+
+ static int indexColumn;
+
+ class gClick *addColumnBtn;
+
+ /* columns
+ * a vector of columns which in turn contain channels. */
+
+ gVector<gColumn*> columns;
+
+public:
+
+ gKeyboard(int X, int Y, int W, int H);
+
+ int handle(int e);
+
+ /* init
+ * build the initial setup of empty channels. */
+
+ void init();
+
+ /* addChannel
+ * add a new channel to gChannels. Used by callbacks and during
+ * patch loading. Requires Channel (and not gChannel). If build is
+ * set to true, also generate the corresponding column.*/
+
+ gChannel *addChannel(int column, class Channel *ch, bool build=false);
+
+ /* deleteChannel
+ * delete a channel from gChannels<> where gChannel->ch == ch and remove
+ * it from the stack. */
+
+ void deleteChannel(gChannel *gch);
+
+ /* freeChannel
+ * free a channel from gChannels<> where gChannel->ch == ch. No channels
+ * are deleted */
+
+ void freeChannel(gChannel *gch);
+
+ /* updateChannel
+ * wrapper function to call gch->update(). */
+
+ void updateChannel(gChannel *gch);
+
+ /* organizeColumns
+ * reorganize columns layout by removing empty gaps. */
+
+ void organizeColumns();
+
+ /* refreshColumns
+ * refresh each column's channel, called on each GUI cycle. */
+
+ void refreshColumns();
+
+ /* getColumn
+ * return the column with index 'index', or NULL if not found. */
+
+ gColumn *getColumn(int index);
+
+ /* clear
+ * delete all channels and groups. */
+
+ void clear();
+
+ /* setChannelWithActions
+ * add 'R' button if channel has actions, and set recorder to active. */
+
+ void setChannelWithActions(class gSampleChannel *gch);
+
+ /* printChannelMessage
+ * given any output by glue_loadChannel, print the message on screen
+ * on a gdAlert subwindow. */
+
+ void printChannelMessage(int res);
+
+ /* getTotalColumns */
+
+ inline unsigned getTotalColumns() { return columns.size; }
+
+ /* getColumnWidth
+ * return the width in pixel of i-th column. Warning: 'i' is the i-th column
+ * in the column array, NOT the index. */
+
+ inline int getColumnWidth(int i) { return getColumn(i)->w(); }
+};
+
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gg_waveTools
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include "gg_waveTools.h"
+#include "graphics.h"
+#include "ge_mixed.h"
+#include "ge_waveform.h"
+#include "mixer.h"
+
+
+gWaveTools::gWaveTools(int x, int y, int w, int h, SampleChannel *ch, const char *l)
+ : Fl_Scroll(x, y, w, h, l)
+{
+ type(Fl_Scroll::HORIZONTAL_ALWAYS);
+ hscrollbar.color(COLOR_BG_0);
+ hscrollbar.selection_color(COLOR_BG_1);
+ hscrollbar.labelcolor(COLOR_BD_1);
+ hscrollbar.slider(G_BOX);
+
+ waveform = new gWaveform(x, y, w, h-24, ch);
+
+
+ //resizable(waveform);
+}
+
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gWaveTools::updateWaveform()
+{
+ waveform->alloc(w());
+ waveform->redraw();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gWaveTools::resize(int x, int y, int w, int h)
+{
+ if (this->w() == w || (this->w() != w && this->h() != h)) { // vertical or both resize
+ Fl_Widget::resize(x, y, w, h);
+ waveform->resize(x, y, waveform->w(), h-24);
+ updateWaveform();
+ }
+ else { // horizontal resize
+ Fl_Widget::resize(x, y, w, h);
+ }
+
+ if (this->w() > waveform->w())
+ waveform->stretchToWindow();
+
+ int offset = waveform->x() + waveform->w() - this->w() - this->x();
+ if (offset < 0)
+ waveform->position(waveform->x()-offset, this->y());
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int gWaveTools::handle(int e)
+{
+ int ret = Fl_Group::handle(e);
+ switch (e) {
+ case FL_MOUSEWHEEL: {
+ waveform->setZoom(Fl::event_dy());
+ redraw();
+ ret = 1;
+ break;
+ }
+ }
+ return ret;
+}
+
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gg_waveTools
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef GG_WAVETOOLS_H
+#define GG_WAVETOOLS_H
+
+#include <FL/Fl.H>
+#include <FL/Fl_Group.H>
+#include <FL/Fl_Scroll.H>
+
+
+class gWaveTools : public Fl_Scroll {
+public:
+ class gWaveform *waveform;
+
+ gWaveTools(int X,int Y,int W, int H, class SampleChannel *ch, const char *L=0);
+ void resize(int x, int y, int w, int h);
+ int handle(int e);
+
+ void updateWaveform();
+};
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * glue
+ * Intermediate layer GUI <-> CORE.
+ *
+ * How to know if you need another glue_ function? Ask yourself if the
+ * new action will ever be called via MIDI or keyboard/mouse. If yes,
+ * put it here.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "glue.h"
+#include "ge_waveform.h"
+#include "gd_mainWindow.h"
+#include "gd_editor.h"
+#include "gd_warnings.h"
+#include "gg_waveTools.h"
+#include "ge_mixed.h"
+#include "gg_keyboard.h"
+#include "ge_channel.h"
+#include "ge_sampleChannel.h"
+#include "gui_utils.h"
+#include "mixerHandler.h"
+#include "mixer.h"
+#include "recorder.h"
+#include "wave.h"
+#include "pluginHost.h"
+#include "channel.h"
+#include "sampleChannel.h"
+#include "midiChannel.h"
+#include "utils.h"
+#include "kernelMidi.h"
+#include "log.h"
+#include "patch.h"
+#include "conf.h"
+
+
+extern gdMainWindow *mainWin;
+extern Mixer G_Mixer;
+extern Patch G_Patch;
+extern Conf G_Conf;
+extern bool G_audio_status;
+#ifdef WITH_VST
+extern PluginHost G_PluginHost;
+#endif
+
+
+static bool __soloSession__ = false;
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int glue_loadChannel(SampleChannel *ch, const char *fname)
+{
+ /* save the patch and take the last browser's dir in order to re-use it
+ * the next time */
+
+ G_Conf.setPath(G_Conf.samplePath, gDirname(fname).c_str());
+
+ int result = ch->load(fname);
+
+ if (result == SAMPLE_LOADED_OK)
+ mainWin->keyboard->updateChannel(ch->guiChannel);
+
+ return result;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Channel *glue_addChannel(int column, int type)
+{
+ Channel *ch = G_Mixer.addChannel(type);
+ gChannel *gch = mainWin->keyboard->addChannel(column, ch);
+ ch->guiChannel = gch;
+ glue_setChanVol(ch, 1.0, false); // false = not from gui click
+ return ch;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int glue_loadPatch(const char *fname, const char *fpath, gProgress *status, bool isProject)
+{
+ /* update browser's status bar with % 0.1 */
+
+ status->show();
+ status->value(0.1f);
+ //Fl::check();
+ Fl::wait(0);
+
+ /* is it a valid patch? */
+
+ int res = G_Patch.open(fname);
+ if (res != PATCH_OPEN_OK)
+ return res;
+
+ /* close all other windows. This prevents segfault if plugin windows
+ * GUI are on. */
+
+ if (res)
+ gu_closeAllSubwindows();
+
+ /* reset the system. False(1): don't update the gui right now. False(2): do
+ * not create empty columns. */
+
+ glue_resetToInitState(false, false);
+
+ status->value(0.2f); // progress status: % 0.2
+ //Fl::check();
+ Fl::wait(0);
+
+ /* mixerHandler will update the samples inside Mixer */
+
+ mh_loadPatch(isProject, fname);
+
+ /* take the patch name and update the main window's title */
+
+ G_Patch.getName();
+ gu_update_win_label(G_Patch.name);
+
+ status->value(0.4f); // progress status: 0.4
+ //Fl::check();
+ Fl::wait(0);
+
+ G_Patch.readRecs();
+ status->value(0.6f); // progress status: 0.6
+ //Fl::check();
+ Fl::wait(0);
+
+#ifdef WITH_VST
+ int resPlugins = G_Patch.readPlugins();
+ status->value(0.8f); // progress status: 0.8
+ //Fl::check();
+ Fl::wait(0);
+#endif
+
+ /* this one is vital: let recorder recompute the actions' positions if
+ * the current samplerate != patch samplerate */
+
+ recorder::updateSamplerate(G_Conf.samplerate, G_Patch.samplerate);
+
+ /* update gui */
+
+ gu_updateControls();
+
+ status->value(1.0f); // progress status: 1.0 (done)
+ //Fl::check();
+ Fl::wait(0);
+
+ /* save patchPath by taking the last dir of the broswer, in order to
+ * reuse it the next time */
+
+ G_Conf.setPath(G_Conf.patchPath, fpath);
+
+ gLog("[glue] patch %s loaded\n", fname);
+
+#ifdef WITH_VST
+ if (resPlugins != 1)
+ gdAlert("Some VST plugins were not loaded successfully.");
+#endif
+
+ return res;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int glue_savePatch(const char *fullpath, const char *name, bool isProject)
+{
+ if (G_Patch.write(fullpath, name, isProject) == 1) {
+ strcpy(G_Patch.name, name);
+ G_Patch.name[strlen(name)] = '\0';
+ gu_update_win_label(name);
+ gLog("[glue] patch saved as %s\n", fullpath);
+ return 1;
+ }
+ else
+ return 0;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_deleteChannel(Channel *ch)
+{
+ int index = ch->index;
+ recorder::clearChan(index);
+ Fl::lock();
+ mainWin->keyboard->deleteChannel(ch->guiChannel);
+ Fl::unlock();
+ G_Mixer.deleteChannel(ch);
+ gu_closeAllSubwindows();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_freeChannel(Channel *ch)
+{
+ mainWin->keyboard->freeChannel(ch->guiChannel);
+ recorder::clearChan(ch->index);
+ ch->empty();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setBpm(const char *v1, const char *v2)
+{
+ char buf[6];
+ float value = atof(v1) + (atof(v2)/10);
+ if (value < 20.0f) {
+ value = 20.0f;
+ sprintf(buf, "20.0");
+ }
+ else
+ sprintf(buf, "%s.%s", v1, !strcmp(v2, "") ? "0" : v2);
+
+ /* a value such as atof("120.1") will never be 120.1 but 120.0999999,
+ * because of the rounding error. So we pass the real "wrong" value to
+ * G_Mixer and we show the nice looking (but fake) one to the GUI. */
+
+ float old_bpm = G_Mixer.bpm;
+ G_Mixer.bpm = value;
+ G_Mixer.updateFrameBars();
+
+ /* inform recorder and actionEditor of the change */
+
+ recorder::updateBpm(old_bpm, value, G_Mixer.quanto);
+ gu_refreshActionEditor();
+
+ mainWin->timing->setBpm(buf);
+ gLog("[glue] Bpm changed to %s (real=%f)\n", buf, G_Mixer.bpm);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setBeats(int beats, int bars, bool expand)
+{
+ /* temp vars to store old data (they are necessary) */
+
+ int oldvalue = G_Mixer.beats;
+ unsigned oldfpb = G_Mixer.totalFrames;
+
+ if (beats > MAX_BEATS)
+ G_Mixer.beats = MAX_BEATS;
+ else if (beats < 1)
+ G_Mixer.beats = 1;
+ else
+ G_Mixer.beats = beats;
+
+ /* update bars - bars cannot be greate than beats and must be a sub
+ * multiple of beats. If not, approximation to the nearest (and greater)
+ * value available. */
+
+ if (bars > G_Mixer.beats)
+ G_Mixer.bars = G_Mixer.beats;
+ else if (bars <= 0)
+ G_Mixer.bars = 1;
+ else if (beats % bars != 0) {
+ G_Mixer.bars = bars + (beats % bars);
+ if (beats % G_Mixer.bars != 0) // it could be an odd value, let's check it (and avoid it)
+ G_Mixer.bars = G_Mixer.bars - (beats % G_Mixer.bars);
+ }
+ else
+ G_Mixer.bars = bars;
+
+ G_Mixer.updateFrameBars();
+
+ /* update recorded actions */
+
+ if (expand) {
+ if (G_Mixer.beats > oldvalue)
+ recorder::expand(oldfpb, G_Mixer.totalFrames);
+ //else if (G_Mixer.beats < oldvalue)
+ // recorder::shrink(G_Mixer.totalFrames);
+ }
+
+ mainWin->timing->setMeter(G_Mixer.beats, G_Mixer.bars);
+ gu_refreshActionEditor(); // in case the action editor is open
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_startStopSeq(bool gui)
+{
+ G_Mixer.running ? glue_stopSeq(gui) : glue_startSeq(gui);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_startSeq(bool gui)
+{
+ G_Mixer.running = true;
+
+ if (gui) {
+#ifdef __linux__
+ kernelAudio::jackStart();
+#endif
+ }
+
+ if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) {
+ kernelMidi::send(MIDI_START, -1, -1);
+ kernelMidi::send(MIDI_POSITION_PTR, 0, 0);
+ }
+
+ if (gui) Fl::lock();
+ mainWin->controller->updatePlay(1);
+ if (gui) Fl::unlock();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_stopSeq(bool gui) {
+
+ mh_stopSequencer();
+
+ if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M)
+ kernelMidi::send(MIDI_STOP, -1, -1);
+
+#ifdef __linux__
+ if (gui)
+ kernelAudio::jackStop();
+#endif
+
+ /* what to do if we stop the sequencer and some action recs are active?
+ * Deactivate the button and delete any 'rec on' status */
+
+ if (recorder::active) {
+ recorder::active = false;
+ if (gui) Fl::lock();
+ mainWin->controller->updateRecAction(0);
+ if (gui) Fl::unlock();
+ }
+
+ /* if input recs are active (who knows why) we must deactivate them.
+ * One might stop the sequencer while an input rec is running. */
+
+ if (G_Mixer.chanInput != NULL) {
+ mh_stopInputRec();
+ if (gui) Fl::lock();
+ mainWin->controller->updateRecInput(0);
+ if (gui) Fl::unlock();
+ }
+
+ if (gui) Fl::lock();
+ mainWin->controller->updatePlay(0);
+ if (gui) Fl::unlock();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_rewindSeq() {
+ mh_rewindSequencer();
+ if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M)
+ kernelMidi::send(MIDI_POSITION_PTR, 0, 0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_startStopActionRec() {
+ recorder::active ? glue_stopActionRec() : glue_startActionRec();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_startActionRec() {
+ if (G_audio_status == false)
+ return;
+ if (!G_Mixer.running)
+ glue_startSeq(); // start the sequencer for convenience
+ recorder::active = true;
+
+ Fl::lock();
+ mainWin->controller->updateRecAction(1);
+ Fl::unlock();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_stopActionRec() {
+
+ /* stop the recorder and sort new actions */
+
+ recorder::active = false;
+ recorder::sortActions();
+
+ for (unsigned i=0; i<G_Mixer.channels.size; i++)
+ if (G_Mixer.channels.at(i)->type == CHANNEL_SAMPLE) {
+ SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i);
+ if (ch->hasActions)
+ ch->readActions = true;
+ else
+ ch->readActions = false;
+ mainWin->keyboard->setChannelWithActions((gSampleChannel*)ch->guiChannel);
+ }
+
+ Fl::lock();
+ mainWin->controller->updateRecAction(0);
+ Fl::unlock();
+
+ /* in case acton editor is on, refresh it */
+
+ gu_refreshActionEditor();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_startStopReadingRecs(SampleChannel *ch, bool gui) {
+ if (ch->readActions)
+ glue_stopReadingRecs(ch, gui);
+ else
+ glue_startReadingRecs(ch, gui);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_startReadingRecs(SampleChannel *ch, bool gui) {
+ if (G_Conf.treatRecsAsLoops)
+ ch->recStatus = REC_WAITING;
+ else
+ ch->setReadActions(true);
+ if (!gui) {
+ gSampleChannel *gch = (gSampleChannel*)ch->guiChannel;
+ if (gch->readActions) { // if button exists
+ Fl::lock();
+ gch->readActions->value(1);
+ Fl::unlock();
+ }
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_stopReadingRecs(SampleChannel *ch, bool gui) {
+
+ /* if "treatRecsAsLoop" wait until the sequencer reaches beat 0, so put
+ * the channel in REC_ENDING status */
+
+ if (G_Conf.treatRecsAsLoops)
+ ch->recStatus = REC_ENDING;
+ else
+ ch->setReadActions(false);
+ if (!gui) {
+ gSampleChannel *gch = (gSampleChannel*)ch->guiChannel;
+ if (gch->readActions) { // if button exists
+ Fl::lock();
+ gch->readActions->value(0);
+ Fl::unlock();
+ }
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_quantize(int val) {
+ G_Mixer.quantize = val;
+ G_Mixer.updateQuanto();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setChanVol(Channel *ch, float v, bool gui) {
+
+ ch->volume = v;
+
+ /* also update wave editor if it's shown */
+
+ gdEditor *editor = (gdEditor*) gu_getSubwindow(mainWin, WID_SAMPLE_EDITOR);
+ if (editor) {
+ glue_setVolEditor(editor, (SampleChannel*) ch, v, false);
+ Fl::lock();
+ editor->volume->value(v);
+ Fl::unlock();
+ }
+
+ if (!gui) {
+ Fl::lock();
+ ch->guiChannel->vol->value(v);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setOutVol(float v, bool gui) {
+ G_Mixer.outVol = v;
+ if (!gui) {
+ Fl::lock();
+ mainWin->inOut->setOutVol(v);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setInVol(float v, bool gui)
+{
+ G_Mixer.inVol = v;
+ if (!gui) {
+ Fl::lock();
+ mainWin->inOut->setInVol(v);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_clearAllSamples()
+{
+ G_Mixer.running = false;
+ for (unsigned i=0; i<G_Mixer.channels.size; i++) {
+ G_Mixer.channels.at(i)->empty();
+ G_Mixer.channels.at(i)->guiChannel->reset();
+ }
+ recorder::init();
+ return;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_clearAllRecs()
+{
+ recorder::init();
+ gu_updateControls();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_resetToInitState(bool resetGui, bool createColumns)
+{
+ G_Mixer.ready = false;
+
+ mh_clear();
+ mainWin->keyboard->clear();
+ if (createColumns)
+ mainWin->keyboard->init();
+ recorder::init();
+ G_Patch.setDefault();
+ G_Mixer.init();
+#ifdef WITH_VST
+ G_PluginHost.freeAllStacks();
+#endif
+
+ if (resetGui)
+ gu_updateControls();
+
+ G_Mixer.ready = true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_startStopMetronome(bool gui)
+{
+ G_Mixer.metronome = !G_Mixer.metronome;
+ if (!gui) {
+ Fl::lock();
+ mainWin->controller->updateMetronome(G_Mixer.metronome);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setBeginEndChannel(gdEditor *win, SampleChannel *ch, int b, int e, bool recalc, bool check)
+{
+ if (check) {
+ if (e > ch->wave->size)
+ e = ch->wave->size;
+ if (b < 0)
+ b = 0;
+ if (b > ch->wave->size)
+ b = ch->wave->size-2;
+ if (b >= ch->end)
+ b = ch->begin;
+ if (e <= ch->begin)
+ e = ch->end;
+ }
+
+ /* continue only if new values != old values */
+
+ if (b == ch->begin && e == ch->end)
+ return;
+
+ /* print mono values */
+
+ char tmp[16];
+ sprintf(tmp, "%d", b/2);
+ win->chanStart->value(tmp);
+
+ tmp[0] = '\0';
+ sprintf(tmp, "%d", e/2);
+ win->chanEnd->value(tmp);
+
+ ch->setBegin(b);
+ ch->setEnd(e);
+
+ /* recalc is not needed when the user drags the bars directly over the waveform */
+
+ if (recalc) {
+ win->waveTools->waveform->recalcPoints(); // importante, altrimenti non si vedono
+ win->waveTools->waveform->redraw();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setBoost(gdEditor *win, SampleChannel *ch, float val, bool numeric)
+{
+ if (numeric) {
+ if (val > 20.0f)
+ val = 20.0f;
+ else if (val < 0.0f)
+ val = 0.0f;
+
+ float linear = pow(10, (val / 20)); // linear = 10^(dB/20)
+
+ ch->boost = linear;
+
+ char buf[10];
+ sprintf(buf, "%.2f", val);
+ win->boostNum->value(buf);
+ win->boostNum->redraw();
+
+ win->boost->value(linear);
+ win->boost->redraw(); /// inutile
+ }
+ else {
+ ch->boost = val;
+ char buf[10];
+ sprintf(buf, "%.2f", 20*log10(val));
+ win->boostNum->value(buf);
+ win->boostNum->redraw();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setVolEditor(class gdEditor *win, SampleChannel *ch, float val, bool numeric)
+{
+ if (numeric) {
+ if (val > 0.0f)
+ val = 0.0f;
+ else if (val < -60.0f)
+ val = -INFINITY;
+
+ float linear = pow(10, (val / 20)); // linear = 10^(dB/20)
+
+ ch->volume = linear;
+
+ win->volume->value(linear);
+ win->volume->redraw();
+
+ char buf[10];
+ if (val > -INFINITY)
+ sprintf(buf, "%.2f", val);
+ else
+ sprintf(buf, "-inf");
+ win->volumeNum->value(buf);
+ win->volumeNum->redraw();
+
+ ch->guiChannel->vol->value(linear);
+ ch->guiChannel->vol->redraw();
+ }
+ else {
+ ch->volume = val;
+
+ float dbVal = 20 * log10(val);
+ char buf[10];
+ if (dbVal > -INFINITY)
+ sprintf(buf, "%.2f", dbVal);
+ else
+ sprintf(buf, "-inf");
+
+ win->volumeNum->value(buf);
+ win->volumeNum->redraw();
+
+ ch->guiChannel->vol->value(val);
+ ch->guiChannel->vol->redraw();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setMute(Channel *ch, bool gui)
+{
+ if (recorder::active && recorder::canRec(ch)) {
+ if (!ch->mute)
+ recorder::startOverdub(ch->index, ACTION_MUTES, G_Mixer.actualFrame);
+ else
+ recorder::stopOverdub(G_Mixer.actualFrame);
+ }
+
+ ch->mute ? ch->unsetMute(false) : ch->setMute(false);
+
+ if (!gui) {
+ Fl::lock();
+ ch->guiChannel->mute->value(ch->mute);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setSoloOn(Channel *ch, bool gui)
+{
+ /* if there's no solo session, store mute configuration of all chans
+ * and start the session */
+
+ if (!__soloSession__) {
+ for (unsigned i=0; i<G_Mixer.channels.size; i++) {
+ Channel *och = G_Mixer.channels.at(i);
+ och->mute_s = och->mute;
+ }
+ __soloSession__ = true;
+ }
+
+ ch->solo = !ch->solo;
+
+ /* mute all other channels and unmute this (if muted) */
+
+ for (unsigned i=0; i<G_Mixer.channels.size; i++) {
+ Channel *och = G_Mixer.channels.at(i);
+ if (!och->solo && !och->mute) {
+ och->setMute(false);
+ Fl::lock();
+ och->guiChannel->mute->value(true);
+ Fl::unlock();
+ }
+ }
+
+ if (ch->mute) {
+ ch->unsetMute(false);
+ Fl::lock();
+ ch->guiChannel->mute->value(false);
+ Fl::unlock();
+ }
+
+ if (!gui) {
+ Fl::lock();
+ ch->guiChannel->solo->value(1);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setSoloOff(Channel *ch, bool gui)
+{
+ /* if this is uniqueSolo, stop solo session and restore mute status,
+ * else mute this */
+
+ if (mh_uniqueSolo(ch)) {
+ __soloSession__ = false;
+ for (unsigned i=0; i<G_Mixer.channels.size; i++) {
+ Channel *och = G_Mixer.channels.at(i);
+ if (och->mute_s) {
+ och->setMute(false);
+ Fl::lock();
+ och->guiChannel->mute->value(true);
+ Fl::unlock();
+ }
+ else {
+ och->unsetMute(false);
+ Fl::lock();
+ och->guiChannel->mute->value(false);
+ Fl::unlock();
+ }
+ och->mute_s = false;
+ }
+ }
+ else {
+ ch->setMute(false);
+ Fl::lock();
+ ch->guiChannel->mute->value(true);
+ Fl::unlock();
+ }
+
+ ch->solo = !ch->solo;
+
+ if (!gui) {
+ Fl::lock();
+ ch->guiChannel->solo->value(0);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setPanning(class gdEditor *win, SampleChannel *ch, float val)
+{
+ if (val < 1.0f) {
+ ch->panLeft = 1.0f;
+ ch->panRight= 0.0f + val;
+
+ char buf[8];
+ sprintf(buf, "%d L", abs((ch->panRight * 100.0f) - 100));
+ win->panNum->value(buf);
+ }
+ else if (val == 1.0f) {
+ ch->panLeft = 1.0f;
+ ch->panRight= 1.0f;
+ win->panNum->value("C");
+ }
+ else {
+ ch->panLeft = 2.0f - val;
+ ch->panRight= 1.0f;
+
+ char buf[8];
+ sprintf(buf, "%d R", abs((ch->panLeft * 100.0f) - 100));
+ win->panNum->value(buf);
+ }
+ win->panNum->redraw();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_startStopInputRec(bool gui, bool alert)
+{
+ if (G_Mixer.chanInput == NULL) {
+ if (!glue_startInputRec(gui)) {
+ if (alert) gdAlert("No channels available for recording.");
+ else gLog("[glue] no channels available for recording\n");
+ }
+ }
+ else
+ glue_stopInputRec(gui);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int glue_startInputRec(bool gui)
+{
+ if (G_audio_status == false)
+ return -1;
+
+ SampleChannel *ch = mh_startInputRec();
+ if (ch == NULL) { // no chans available
+ Fl::lock();
+ mainWin->controller->updateRecInput(0);
+ Fl::unlock();
+ return 0;
+ }
+
+ if (!G_Mixer.running) {
+ glue_startSeq();
+ Fl::lock();
+ mainWin->controller->updatePlay(1);
+ Fl::unlock();
+ }
+
+ glue_setChanVol(ch, 1.0f, false); // false = not from gui click
+
+ ch->guiChannel->mainButton->label(ch->wave->name.c_str());
+
+ if (!gui) {
+ Fl::lock();
+ mainWin->controller->updateRecInput(1);
+ Fl::unlock();
+ }
+
+ return 1;
+
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int glue_stopInputRec(bool gui)
+{
+ SampleChannel *ch = mh_stopInputRec();
+
+ if (ch->mode & (LOOP_BASIC | LOOP_ONCE | LOOP_REPEAT))
+ ch->start(0, true); // on frame 0: user-generated event
+
+ if (!gui) {
+ Fl::lock();
+ mainWin->controller->updateRecInput(0);
+ Fl::unlock();
+ }
+
+ return 1;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int glue_saveProject(const char *folderPath, const char *projName)
+{
+ if (gIsProject(folderPath)) {
+ gLog("[glue] the project folder already exists\n");
+ // don't exit
+ }
+ else if (!gMkdir(folderPath)) {
+ gLog("[glue] unable to make project directory!\n");
+ return 0;
+ }
+
+ /* copy all samples inside the folder. Takes and logical ones are saved
+ * via glue_saveSample() */
+
+ for (unsigned i=0; i<G_Mixer.channels.size; i++) {
+
+ if (G_Mixer.channels.at(i)->type == CHANNEL_SAMPLE) {
+
+ SampleChannel *ch = (SampleChannel*) G_Mixer.channels.at(i);
+
+ if (ch->wave == NULL)
+ continue;
+
+ /* update the new samplePath: everything now comes from the
+ * project folder (folderPath) */
+
+ char samplePath[PATH_MAX];
+ sprintf(samplePath, "%s%s%s.%s", folderPath, gGetSlash().c_str(), ch->wave->basename().c_str(), ch->wave->extension().c_str());
+
+ /* remove any existing file */
+
+ if (gFileExists(samplePath))
+ remove(samplePath);
+ if (ch->save(samplePath))
+ ch->wave->pathfile = samplePath;
+ }
+ }
+
+ char gptcPath[PATH_MAX];
+ sprintf(gptcPath, "%s%s%s.gptc", folderPath, gGetSlash().c_str(), gStripExt(projName).c_str());
+ glue_savePatch(gptcPath, projName, true); // true == it's a project
+
+ return 1;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_keyPress(Channel *ch, bool ctrl, bool shift)
+{
+ if (ch->type == CHANNEL_SAMPLE)
+ glue_keyPress((SampleChannel*)ch, ctrl, shift);
+ else
+ glue_keyPress((MidiChannel*)ch, ctrl, shift);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_keyRelease(Channel *ch, bool ctrl, bool shift)
+{
+ if (ch->type == CHANNEL_SAMPLE)
+ glue_keyRelease((SampleChannel*)ch, ctrl, shift);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_keyPress(MidiChannel *ch, bool ctrl, bool shift)
+{
+ if (ctrl)
+ glue_setMute(ch);
+ else
+ if (shift)
+ ch->kill(0); // on frame 0: user-generated event
+ else
+ ch->start(0, true); // on frame 0: user-generated event
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_keyPress(SampleChannel *ch, bool ctrl, bool shift)
+{
+ /* case CTRL */
+
+ if (ctrl)
+ glue_setMute(ch);
+
+ /* case SHIFT
+ *
+ * action recording on:
+ * if seq is playing, rec a killchan
+ * action recording off:
+ * if chan has recorded events:
+ * | if seq is playing OR channel 'c' is stopped, de/activate recs
+ * | else kill chan
+ * else kill chan */
+
+ else
+ if (shift) {
+ if (recorder::active) {
+ if (G_Mixer.running) {
+ ch->kill(0); // on frame 0: user-generated event
+ if (recorder::canRec(ch) && !(ch->mode & LOOP_ANY)) // don't record killChan actions for LOOP channels
+ recorder::rec(ch->index, ACTION_KILLCHAN, G_Mixer.actualFrame);
+ }
+ }
+ else {
+ if (ch->hasActions) {
+ if (G_Mixer.running || ch->status == STATUS_OFF)
+ ch->readActions ? glue_stopReadingRecs(ch) : glue_startReadingRecs(ch);
+ else
+ ch->kill(0); // on frame 0: user-generated event
+ }
+ else
+ ch->kill(0); // on frame 0: user-generated event
+ }
+ }
+
+ /* case no modifier */
+
+ else {
+
+ /* record now if the quantizer is off, otherwise let mixer to handle it
+ * when a quantoWait has passed. Moreover, KEYPRESS and KEYREL are
+ * meaningless for loop modes */
+
+ if (G_Mixer.quantize == 0 &&
+ recorder::canRec(ch) &&
+ !(ch->mode & LOOP_ANY))
+ {
+ if (ch->mode == SINGLE_PRESS)
+ recorder::startOverdub(ch->index, ACTION_KEYS, G_Mixer.actualFrame);
+ else
+ recorder::rec(ch->index, ACTION_KEYPRESS, G_Mixer.actualFrame);
+ }
+
+ ch->start(0, true); // on frame 0: user-generated event
+ }
+
+ /* the GUI update is done by gui_refresh() */
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_keyRelease(SampleChannel *ch, bool ctrl, bool shift)
+{
+ if (!ctrl && !shift) {
+ ch->stop();
+
+ /* record a key release only if channel is single_press. For any
+ * other mode the KEY REL is meaningless. */
+
+ if (ch->mode == SINGLE_PRESS && recorder::canRec(ch))
+ recorder::stopOverdub(G_Mixer.actualFrame);
+ }
+
+ /* the GUI update is done by gui_refresh() */
+
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void glue_setPitch(class gdEditor *win, SampleChannel *ch, float val, bool numeric)
+{
+ if (numeric) {
+ if (val <= 0.0f)
+ val = 0.1000f;
+ if (val > 4.0f)
+ val = 4.0000f;
+ if (win)
+ win->pitch->value(val);
+ }
+
+ ch->setPitch(val);
+
+ if (win) {
+ char buf[16];
+ sprintf(buf, "%.4f", val);
+ Fl::lock();
+ win->pitchNum->value(buf);
+ win->pitchNum->redraw();
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+/* never expand or shrink recordings (last param of setBeats = false):
+ * this is live manipulation */
+
+void glue_beatsMultiply()
+{
+ glue_setBeats(G_Mixer.beats*2, G_Mixer.bars, false);
+}
+
+void glue_beatsDivide()
+{
+ glue_setBeats(G_Mixer.beats/2, G_Mixer.bars, false);
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * glue
+ * Intermediate layer GUI <-> CORE.
+ *
+ * How to know if you need another glue_ function? Ask yourself if the
+ * new action will ever be called via MIDI or keyboard/mouse. If yes,
+ * put it here.
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef GLUE_H
+#define GLUE_H
+
+/* addChannel
+ * add an empty new channel to the stack. Returns the new channel. */
+
+class Channel *glue_addChannel(int column, int type);
+
+/* loadChannel
+ * fill an existing channel with a wave. */
+
+int glue_loadChannel(class SampleChannel *ch, const char *fname);
+
+void glue_deleteChannel(class Channel *ch);
+
+void glue_freeChannel(class Channel *ch);
+
+/** FIXME - nobody will call these via MIDI/keyb/mouse! */
+int glue_loadPatch(const char *fname, const char *fpath, class gProgress *status, bool isProject);
+int glue_savePatch(const char *fullpath, const char *name, bool isProject);
+
+/* keyPress / keyRelease
+ * handle the key pressure, either via mouse/keyboard or MIDI. If gui
+ * is true it means that the event comes from the main window (mouse,
+ * keyb or MIDI), otherwise the event comes from the action recorder. */
+
+void glue_keyPress (class Channel *ch, bool ctrl=0, bool shift=0);
+void glue_keyPress (class SampleChannel *ch, bool ctrl=0, bool shift=0);
+void glue_keyPress (class MidiChannel *ch, bool ctrl=0, bool shift=0);
+void glue_keyRelease(class Channel *ch, bool ctrl=0, bool shift=0);
+void glue_keyRelease(class SampleChannel *ch, bool ctrl=0, bool shift=0);
+
+void glue_setBpm(const char *v1, const char *v2);
+void glue_setBeats(int beats, int bars, bool expand);
+
+/* start, stop, rewind sequencer
+ * if gui == true the signal comes from an internal interaction on the
+ * GUI, otherwise it's a MIDI/Jack/external signal. */
+
+void glue_startStopSeq(bool gui=true);
+void glue_startSeq (bool gui=true);
+void glue_stopSeq (bool gui=true);
+void glue_rewindSeq ();
+
+/* start/stopActionRec
+ * handle the action recording. */
+
+void glue_startStopActionRec();
+void glue_startActionRec();
+void glue_stopActionRec();
+
+/* start/stopInputRec
+ * handle the input recording (take). If gui == true the signal comes
+ * from an internal interaction on the GUI, otherwise it's a
+ * MIDI/Jack/external signal. Alert displays or not the popup message
+ * if there are no available channels. */
+
+void glue_startStopInputRec(bool gui=true, bool alert=true);
+int glue_startInputRec (bool gui=true);
+int glue_stopInputRec (bool gui=true);
+
+/* start/stopReadingRecs
+ * handle the 'R' button. If gui == true the signal comes from an
+ * internal interaction on the GUI, otherwise it's a MIDI/Jack/external
+ * signal. */
+
+void glue_startStopReadingRecs(class SampleChannel *ch, bool gui=true);
+void glue_startReadingRecs (class SampleChannel *ch, bool gui=true);
+void glue_stopReadingRecs (class SampleChannel *ch, bool gui=true);
+
+void glue_quantize(int val);
+
+void glue_setChanVol(class Channel *ch, float v, bool gui=true);
+void glue_setOutVol (float v, bool gui=true);
+void glue_setInVol (float v, bool gui=true);
+
+void glue_setPanning(class gdEditor *win, class SampleChannel *ch, float val);
+
+void glue_clearAllSamples();
+void glue_clearAllRecs();
+
+/* resetToInitState
+ * reset Giada to init state. If resetGui also refresh all widgets. If
+ * createColumns also build initial empty columns. */
+
+void glue_resetToInitState(bool resetGui=true, bool createColumns=true);
+
+void glue_startStopMetronome(bool gui=true);
+
+/* setBeginEndChannel
+ * sets start/end points in the sample editor.
+ * Recalc=false: don't recalc internal position
+ * check=true: check the points' consistency */
+
+/** FIXME - nobody will call this via MIDI/keyb/mouse! */
+void glue_setBeginEndChannel(class gdEditor *win, class SampleChannel *ch, int b, int e,
+ bool recalc=false, bool check=true);
+
+/** FIXME - nobody will call this via MIDI/keyb/mouse! */
+void glue_setBoost(class gdEditor *win, class SampleChannel *ch, float val, bool numeric);
+
+void glue_setPitch(class gdEditor *win, class SampleChannel *ch, float val, bool numeric);
+
+/* setVolEditor
+ * handles the volume inside the SAMPLE EDITOR (not the main gui). The
+ * numeric flag tells if we want to handle the dial or the numeric input
+ * field. */
+
+ /** FIXME - nobody will call this via MIDI/keyb/mouse! */
+void glue_setVolEditor(class gdEditor *win, class SampleChannel *ch, float val, bool numeric);
+
+/* mute
+ * set mute on or off. If gui == true the signal comes from an internal
+ * interaction on the GUI, otherwise it's a MIDI/Jack/external signal. */
+
+void glue_setMute(class Channel *ch, bool gui=true);
+
+/* solo on/off
+ * set solo on and off. If gui == true the signal comes from an internal
+ * interaction on the GUI, otherwise it's a MIDI/Jack/external signal. */
+
+void glue_setSoloOn (class Channel *ch, bool gui=true);
+void glue_setSoloOff(class Channel *ch, bool gui=true);
+
+/** FIXME - nobody will call this via MIDI/keyb/mouse! */
+int glue_saveProject(const char *folderPath, const char *projName);
+
+
+/* beatsDivide/Multiply
+ * shrinks or enlarges the number of beats by 2. */
+
+void glue_beatsMultiply();
+void glue_beatsDivide();
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * graphics
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+#include "graphics.h"
+
+const char *giada_logo_xpm[] = {
+"300 82 8 1",
+" c #181917",
+". c #333432",
+"+ c #484A47",
+"@ c #5F615E",
+"# c #767875",
+"$ c #8D8F8C",
+"% c #A5A7A4",
+"& c #C5C7C4",
+" .#%%&$ ",
+" ..#%&&&&&# ",
+" +@#$%&&&&&&&&&@ ",
+" .. +&&&&&&&&&&&&&&. ",
+" +$%%#+ +%&&&&&&&&&&&&&. ",
+" #&&&&&%+ .+@@$&&&&&&&&&%. ",
+" #&&&&&&&$ +&&&&&&&&# ",
+" .&&&&&&&&%. #&&&&&&&@ ",
+" @&&&&&&&&$ +&&&&&&&+ ",
+" $&&&&&&&&# .&&&&&&&. ",
+" #&&&&&&&&. +&&&&&&%. ",
+" .&&&&&&&@ @&&&&&&$. ",
+" @&&&&&@ $&&&&&&# ",
+" .##@. %&&&&&&@ ",
+" &&&&&&&+ ",
+" .&&&&&&%. ",
+" @&&&&&&$. ",
+" .+@@###@+. ...... ... ++@@###@@. ....... .+@@###@@+. #&&&&&&# .+@@###@@+ ....... ",
+" .@$%%&&&&&&&%$+ +%%%%%%. .+@#$%%$. .@$%&&&&&&&&%$@. $%%%%%%+ .@#%%&&&&&&&&%$@ %&&&&&&@ .@$%%&&&&&&&%$#. @%%%%%%@ ",
+" #%&&&&&&&&&&&&&&$. #&&&&&& +@#$$%%%&&&&&%. .$%&&&&&&&&&&&&&&%@ .&&&&&&&+ #%&&&&&&&&&&&&&&&$+ &&&&&&&@ #%&&&&&&&&&&&&&&%#. %&&&&&&@ ",
+" +%&&&&&&&&&&&&&&&&&%@ .&&&&&&% .%&&&&&&&&&&&&$ #%&&&&&&&&&&&&&&&&&&$ +&&&&&&%. @%&&&&&&&&&&&&&&&&&&&# .&&&&&&%+ @%&&&&&&&&&&&&&&&&&&%. &&&&&&&+ ",
+" #&&&&&&&&&#+....@$&&&&@@&&&&&&$ .&&&&&&&&&&&&&# +%&&&&&&&&%#+....+#%&&&$#&&&&&&$. .$&&&&&&&&&$+.....@%&&&&$@&&&&&&%. .$&&&&&&&&&#@.....#%&&&%+&&&&&&%. ",
+" $&&&&&&&&@ .%&&&&&&&&&&@ .@$&&&&&&&&&&@ +%&&&&&&&%@ .#&&&&&&&&&&$ .%&&&&&&&&@ .$&&&&&&&&&&$ .%&&&&&&&&#. @&&&&&&&&&&%. ",
+" $&&&&&&&%. @&&&&&&&&&+ .%&&&&&&&%+ @&&&&&&&&# +%&&&&&&&&# +%&&&&&&&$. @&&&&&&&&&# .%&&&&&&&$. .%&&&&&&&&$ ",
+" %&&&&&&&# @&&&&&&&&. +%&&&&&&%. @&&&&&&&&# .%&&&&&&&@ +%&&&&&&&# @&&&&&&&&@ +%&&&&&&&# %&&&&&&&@ ",
+" $&&&&&&&$ #&&&&&&%. %&&&&&&% +&&&&&&&&@ +&&&&&&%+ .%&&&&&&&# @&&&&&&&+ .%&&&&&&&# &&&&&&&+ ",
+" @&&&&&&&$. #&&&&&&$ %&&&&&&$ .%&&&&&&&@ @&&&&&&%. .$&&&&&&&$ +&&&&&&%+ $&&&&&&&$ .&&&&&&&+ ",
+" +&&&&&&&%+ %&&&&&&# %&&&&&&# $&&&&&&&$ $&&&&&&% @&&&&&&&% #&&&&&&%. #&&&&&&&%. @&&&&&&%. ",
+" %&&&&&&&# &&&&&&&+ .%&&&&&&@ @&&&&&&&$ %&&&&&&$ +&&&&&&&&+ $&&&&&&$ +&&&&&&&%. $&&&&&&$. ",
+" @&&&&&&&%. .&&&&&&&. +%&&&&&%. .&&&&&&&&. .%&&&&&&# $&&&&&&&# %&&&&&&# .$&&&&&&&@ %&&&&&&# ",
+" .%&&&&&&&@ @&&&&&&&. @&&&&&&% @&&&&&&&# @&&&&&&&+ +&&&&&&&&. +&&&&&&&@ +&&&&&&&% .&&&&&&&@ ",
+" @&&&&&&&% $&&&&&&%. #&&&&&&% &&&&&&&&. #&&&&&&%. %&&&&&&&# @&&&&&&%+ .$&&&&&&&@ +&&&&&&&+ ",
+" .$&&&&&&&# %&&&&&&# $&&&&&&# #&&&&&&&# $&&&&&&% +&&&&&&&&. $&&&&&&%. +&&&&&&&% #&&&&&&%+ ",
+" +%&&&&&&&+ .&&&&&&&@ .%&&&&&&@ %&&&&&&&+ .%&&&&&&$ $&&&&&&&$ %&&&&&&% #&&&&&&&# %&&&&&&%. ",
+" @&&&&&&&% +&&&&&&&+ +%&&&&&&+ .&&&&&&&%. +&&&&&&&# &&&&&&&&+ +%&&&&&&$ .%&&&&&&&. &&&&&&&$ ",
+" $&&&&&&&@ #&&&&&&&. @&&&&&&&. #&&&&&&&# #&&&&&&&@ +&&&&&&&%. @&&&&&&&# .&&&&&&&% +&&&&&&&# ",
+" .%&&&&&&&. %&&&&&&%. #&&&&&&% %&&&&&&&+ $&&&&&&&+ #&&&&&&&$. $&&&&&&&+ @&&&&&&&@ #&&&&&&&@ ",
+" +&&&&&&&& &&&&&&&$. #&&&&&&$ &&&&&&&%. .%&&&&&&& %&&&&&&&# .%&&&&&&%. $&&&&&&&+ $&&&&&&%+ ",
+" +&&&&&&&$ +&&&&&&&# .$&&&&&&# .&&&&&&&$. +&&&&&&&% %&&&&&&&+ +%&&&&&&% %&&&&&&&. .%&&&&&&%. ",
+" @&&&&&&&@ $&&&&&&&@ .%&&&&&&+ +&&&&&&&# #&&&&&&&$ &&&&&&&&+ #&&&&&&&% &&&&&&&% @&&&&&&&$ ",
+" @&&&&&&&+ &&&&&&&&+ +%&&&&&&. @&&&&&&&@ .%&&&&&&&@ .&&&&&&&%. .%&&&&&&&# &&&&&&&# $&&&&&&&$ ",
+" #&&&&&&&. @&&&&&&&%. @&&&&&&& @&&&&&&&@ @&&&&&&&&+ .&&&&&&&%. @&&&&&&&&@ .&&&&&&&# +%&&&&&&&# ",
+" #&&&&&&&. %&&&&&&&$. #&&&&&&% #&&&&&&&+ .$&&&&&&&&. .&&&&&&&$. .$&&&&&&&&. .&&&&&&&@ $&&&&&&&&@ ",
+" #&&&&&&& #&&&&&&&&$ $&&&&&&# @&&&&&&&+ @&&&&&&&&& .&&&&&&&$. @&&&&&&&&& .&&&&&&&+ +%&&&&&&&%+ ",
+" @&&&&&&& .%&&&&&&&&# .%&&&&&&@ @&&&&&&%+ .%&&&&&&&&% &&&&&&&$. .%&&&&&&&&% &&&&&&&+ $&&&&&&&&%. ",
+" @&&&&&&&. $&&&&&&&&&@ +%&&&&&&. +&&&&&&&@ @&&&&&&&&&$ &&&&&&&%. #&&&&&&&&&$ &&&&&&&@ +&&&&&&&&&% ",
+" +&&&&&&&+ @&&&&&&&&&%+ +&&&&&&& &&&&&&&@ +&&&&&&&&&&# $&&&&&&%+ @&&&&&&&&&&# $&&&&&&# .%&&&&&&&&&% ",
+" .%&&&&&&@ +%&&$&&&&&&%. +&&&&&&& %&&&&&&# .&&&&&&&&&&&@ @&&&&&&&+ +&&&$%&&&&&&@ @&&&&&&$ .$&&&&&&&&&&$ ",
+" #&&&&&&$ .$&&##&&&&&&$ @&&&&&&& @&&&&&&%. .%&&%@%&&&&&&@ .&&&&&&&# .&&&$+%&&&&&&. .&&&&&&&. .#&&%@%&&&&&&$ ",
+" +&&&&&&&. .%&&% $&&&&&&# +&&&&&&&. +.&&&&&&&@ .%&&%+.%&&&&&&$ @+$&&&&&&&+ +&&&% +&&&&&&&+ .+ $&&&&&&$ .$&&&@ %&&&&&&% .# ",
+" $&&&&&&$ +%&&&+ %&&&&&&@ +&&&&&&&@ #&$#&&&&&&%+ @&&&&@ .$&&&&&&&+ #&&@&&&&&&&$. .#&&&%. +%&&&&&&# .$&$ +&&&&&&&+ +%&&&# $&&&&&&&# +&&$ ",
+" +%&&&&&&#. +#&&&%@ .%&&&&&&+ .%&&&&&&&+ .$&&%.%&&&&&&%+ +#&&&&@ #&&&&&&&%@..+@$&&&+@&&&&&&&%+ .@%&&&%+ .%&&&&&&&+ +%&&$. #&&&&&&%@ +#&&&&# @&&&&&&&&@...+#&&&# ",
+" @&&&&&&&$@+..++#%&&&%+ +&&&&&&%. $&&&&&&&%#@@#%&&%. .%&&&&&&%@+...+@$&&&&%+ +%&&&&&&&&%$%&&&&+ $&&&&&&&&$#@+@@#%&&&&$. #&&&&&&&&#@+@$&&&$. .$&&&&&&&#+...++#%&&&&@ .%&&&&&&&&%$%&&&&# ",
+" @&&&&&&&&%%%%&&&&&$. @&&&&&&% +%&&&&&&&&&&&&&$. +%&&&&&&&&%$%%&&&&&$. #&&&&&&&&&&&&&%+ .$&&&&&&&&&&&&&&&&&&# .%&&&&&&&&&&&&&&# .$&&&&&&&&%$%%&&&&&%+ @&&&&&&&&&&&&&%@ ",
+" @%&&&&&&&&&&&&&%@. #&&&&&&$ #&&&&&&&&&&&%@. .$&&&&&&&&&&&&&&%@. .%&&&&&&&&&&&#. @&&&&&&&&&&&&&&&$+ +&&&&&&&&&&&&%+ .#&&&&&&&&&&&&&&&#. $&&&&&&&&&&&$+ ",
+" .#%&&&&&&&&&%+. %&&&&&&# +%&&&&&&&%#. +$&&&&&&&&&&%@. .$&&&&&&&&#+ .#&&&&&&&&&&%#. .$&&&&&&&&%@. +$&&&&&&&&&&%@. .@&&&&&&&&$+. ",
+" .+#$%%$#+.. .%&&&&&&+ .@#$%$#+. .@#$%%$#@+ +@$%$#+. .+#$%%$#@+. +@$%$$@. .+#$%%$#@+. .@$%$#@. ",
+" +&&&&&&%. ",
+" #&&&&&&% ",
+" .%&&&&&&@ ",
+" @&&&&&&%. ",
+" $&&&&&&$ ",
+" @&&&&&&&+ ",
+" @#$#+ .$&&&&&&$ ",
+" $&&&&&# #&&&&&&% ",
+"#&&&&&&&@ @&&&&&&&+ ",
+"%&&&&&&&%. @&&&&&&%+ ",
+"%&&&&&&&&# #&&&&&&%. ",
+"@&&&&&&&&&@ .$&&&&&&#. ",
+" $&&&&&&&&&$+ +$&&&&&&%+ ",
+" +&&&&&&&&&&%#@@#$%&&&&&&&#. +. .+ +@. ++ .+ +++++ +. .+ ++ +++++. .++++. +@. .@+ .++++. .+++++++ +. +@. .+@. .++++. ++. ++. .+. .+@. .+ +. .+ .+. .+ .+++++++ ",
+" .$&&&&&&&&&&&&&&&&&&&%@ $%. %@ +&&%&%. .$$ +& .&&&&&&# .%@ +&. %&+ $&&&&&%. @&&&&&%. +&&%&%. .%&%&&+ #&&&&&&@ +&&&&&&%. &# +&&%&%. @&&%&%. +&&&&&&@ $&% +%&+ .$&@ @&&%&$. $% .%$ $% +&%. +%. +&&&&&&$. ",
+" +$%%&&&&&&&&&&%$@. +&# #% +&# %% $$ +& .&+ @&@ .%@ +&. +$&$ $$ .%% @% .%&. .&$ .%$. %%. #&+ #& .$&+ +&. &# +&# $%. @&# .%%. +&. $&+ $%&+ #%&+ .&%$ @&# +%$ $% .%# $% +&%$ +%. +&. ",
+" .++@###@@+. #&. .&+ $%. .&@ $$ +& .&+ %$ .%@ +&. ##$% $$ @&. @% %$ $%. @&. @&+ %$. #& .%@ +&. &# %% .&# %% .&@ +&. &# $#%$ $#&+ @%@%. %% @%+ $% .%# $% +&+%+ +%. +&. ",
+" .%$ $$ .&# %% $$ +& .&+ %$ .%@ +&. .%@+&+ $$ @&. @% #& &# +. %% #&. #& .%@ +&. &# .&@ $$ +&+ .$$ +&. %# $#@& +$@&+ $#.%@ +&@ .+. $% .%# $% +& $$ +%. +&. ",
+" @&#&. .&@ $& $$ +& .&#+++#&+ .%%####$&. +%. %$ $%.++@&$ @% +&..&@ &$ @&+ #&++++$%. +&$###@ &# +&+ #%.@&. #% +&+ ..#&+ $#.&@ ##+&+ .&..$$ @&. $&$###$&# $% +& +%@ +%. +&####+ ",
+" %&@ +&+ #& $$ +& .&%$$%%@ .%%####$&. #$ #& $&$$%&#. @% +&++&@ &# +&+ #&$$%&%+ +&$$$$# &# @&. #%.@&. #%.+%%%%%%@ $# $% .$+.&+ @% @%. @&. $&$###$&# $% +& #%.+%. +&$$$$@ ",
+" #&. .&@ $% $$ +& .&#+.$%. .%@ +&. .$%##$&+ $%.+@&@ @% +&..&@ &$ @&. #&+++$$ +&+ &# +&. #% @&. $$ +&#@@++ $# +&.+$.+&+ $%@#$&+ @&. $% .%# $% +& .%@+%. +&. ",
+" @& .%# &$ $$ +% .&+ .%@ .%@ +&. +%$###&$ $$ $% @% $& &$ .$+ $% #%. #& +&+ +&. &# .&@ .%$ +&@ .%# +&. $# .%### +&+ .&####&# .&@ .$+ $% .%# $% +& @%@%. +&. ",
+" @& #%. @&. #%. $% .&+ $% .%@ +&. @$. #& $$ +&+ @% +&@ #&. #&. +&+ .%@ #& .%# +&. &# $% +&+ %% @&+ +&. $# #&%+ +&+ @% #%. %% #%. $% .%# $% +& .$%%. +&. ",
+" @& .%%+.@&# .%%++#&@ .&+ +&+ .%@ +&. $# .&+ $$ %$ @&+++#&%. $%+.#&# #&@.+%%. #& @%. +&@+++++. &$++++. .%%+.@%# .%%..@&# +&. $# +&$ +&+ %# +&+ .%%+.#&# $% .%# $% +& +&&. +&@++++. %&",
+" @& .$&&%@ +%&&&@ .&+ %$ .%@ +&..%+ %$ $$ @&. @&&&&%@ .$%&&# @%&&$. #& .%@ +&&&&&&%+ &&&&&&$. .$&&%# .$&&%@ +&. $# .%# +&+.&. .%# .$&&&@ $% .%# $% +& $&. +&&&&&&%. &&"};
+
+
+const char *loopRepeat_xpm[] = {
+"18 18 8 1",
+" c #181917",
+". c #242523",
+"+ c #323331",
+"@ c #4D4F4C",
+"# c #646663",
+"$ c #787A77",
+"% c #919390",
+"& c #BFC1BD",
+"..................",
+"..................",
+"..................",
+"...&%#......#%&...",
+"...&&&%+..+%&&&...",
+"...$%&&%..%&&%$...",
+".....$&&##&&$.....",
+"......%&%%&%......",
+"......$&&&&$......",
+"......$&&&&$......",
+"......%&%%&%......",
+".....$&&##&&$.....",
+"...$%&&%..%&&%$...",
+"...&&&%+..+%&&&...",
+"...&%#......#%&...",
+"..................",
+"..................",
+".................."};
+
+
+const char *loopBasic_xpm[] = {
+"18 18 8 1",
+" c #181917",
+". c #242523",
+"+ c #313230",
+"@ c #4D4F4C",
+"# c #666765",
+"$ c #787A77",
+"% c #919390",
+"& c #BEC0BD",
+"..................",
+"..................",
+"..................",
+"......#%&&%#......",
+"....+%&&&&&&%+....",
+"....%&&&%%&&&%....",
+"...#&&%+..+%&&#...",
+"...%&&+....+&&%...",
+"...&&%......%&&...",
+"...&&%......%&&...",
+"...%&&+....+&&%...",
+"...#&&%+..+%&&#...",
+"....%&&&%%&&&%....",
+"....+%&&&&&&%+....",
+"......#%&&%#......",
+"..................",
+"..................",
+".................."};
+
+
+const char *loopOnce_xpm[] = {
+"18 18 8 1",
+" c #181917",
+". c #242523",
+"+ c #323331",
+"@ c #4D4F4C",
+"# c #646663",
+"$ c #787A77",
+"% c #919390",
+"& c #BFC1BD",
+"..................",
+"..................",
+"..................",
+"......$%&&%#......",
+"....+&&&&&&&&+....",
+"...+&&&&$$&&&&+...",
+"...$&&$....$&&$...",
+"...%&&......%&%...",
+"..................",
+"..................",
+"...%&&+.....&&&...",
+"...#&&$....$&&#...",
+"....%&&&%$&&&%....",
+"....+%&&&&&&%+....",
+"......#%&&%#......",
+"..................",
+"..................",
+".................."};
+
+
+const char *loopOnceBar_xpm[] = {
+"18 18 8 1",
+" c #242523",
+". c #393A38",
+"+ c #545553",
+"@ c #747673",
+"# c #A3A5A2",
+"$ c #ADAFAC",
+"% c #B5B7B4",
+"& c #C7C9C6",
+" ",
+" ",
+" ",
+" @$&%#@ ",
+" .$&&&&&&$. ",
+" %&&#@@#&&$ ",
+" @&&@ @&&@ ",
+" %&# +%$+ #&$ ",
+" %&&% ",
+" %&&% ",
+" $&# +%%+ #&$ ",
+" @&&@ @&&@ ",
+" $&&#@@#&&$ ",
+" .$&&&&&&$. ",
+" @#&&#@ ",
+" ",
+" ",
+" "};
+
+
+const char *oneshotBasic_xpm[] = {
+"18 18 8 1",
+" c #181917",
+". c #242523",
+"+ c #313230",
+"@ c #4D4F4C",
+"# c #666765",
+"$ c #787A77",
+"% c #919390",
+"& c #BEC0BD",
+"..................",
+"..................",
+"..................",
+"..................",
+"..................",
+"..................",
+"..................",
+"..................",
+"..................",
+"..................",
+"..................",
+"..................",
+"...$$$$$$$$$$$$...",
+"...&&&&&&&&&&&&...",
+"...&&&&&&&&&&&&...",
+"..................",
+"..................",
+".................."};
+
+
+const char *oneshotRetrig_xpm[] = {
+"18 18 8 1",
+" c #181917",
+". c #242523",
+"+ c #313230",
+"@ c #4D4F4C",
+"# c #666765",
+"$ c #787A77",
+"% c #919390",
+"& c #BEC0BD",
+"..................",
+"..................",
+"..................",
+"..................",
+"..................",
+"...$$$$$$$#@......",
+"...&&&&&&&&&&@....",
+"...&&&&&&&&&&&+...",
+"..........+$&&%...",
+"............%&&...",
+"............%&&...",
+"...........+&&%...",
+"...$$$$$$$%&&&#...",
+"...&&&&&&&&&&%....",
+"...&&&&&&&&%#.....",
+"..................",
+"..................",
+".................."};
+
+
+const char *oneshotPress_xpm[] = {
+"18 18 8 1",
+" c #181917",
+". c #242523",
+"+ c #313230",
+"@ c #4D4F4C",
+"# c #666765",
+"$ c #787A77",
+"% c #919390",
+"& c #BEC0BD",
+"..................",
+"..................",
+"..................",
+"..................",
+"..................",
+"..................",
+"...+%&%+..........",
+"...%&&&%..........",
+"...&&&&&..........",
+"...$&&&$..........",
+"...+$&$+..........",
+"..................",
+"...$$$$$$$$$$$$...",
+"...&&&&&&&&&&&&...",
+"...&&&&&&&&&&&&...",
+"..................",
+"..................",
+".................."};
+
+
+const char *oneshotEndless_xpm[] = {
+"18 18 6 1",
+" c #242523",
+". c #464745",
+"+ c #6D6F6C",
+"@ c #888A87",
+"# c #ADAFAC",
+"$ c #C6C8C5",
+" ",
+" ",
+" ",
+" ",
+" ",
+" .++. ",
+" @$$$$#. ",
+" @$$$$$$$. ",
+" .$$#. +$$@ ",
+" +$$. @$# ",
+" +$$ @$$ ",
+" .$$+ #$# ",
+" @@@$$$@@#$$+ ",
+" $$$$$$$$$$@ ",
+" $$$$$$$$#+ ",
+" ",
+" ",
+" "};
+
+
+const char *updirOff_xpm[] = {
+"18 18 8 1",
+" c #242523",
+". c #332F2E",
+"+ c #54494A",
+"@ c #6B5A5C",
+"# c #866C6B",
+"$ c #967B7A",
+"% c #987D7C",
+"& c #B18E8F",
+" ",
+" ",
+" ",
+" ",
+" @@ ",
+" #&&# ",
+" .#&&&&#. ",
+" .$&&&&&&$. ",
+" +@%&&&&%@+ ",
+" #&&&&# ",
+" #&&&&# ",
+" #&&&&# ",
+" #&&&&# ",
+" #&&&&# ",
+" .... ",
+" ",
+" ",
+" "};
+
+
+const char *updirOn_xpm[] = {
+"18 18 8 1",
+" c #4D4F4C",
+". c #555150",
+"+ c #706465",
+"@ c #7D6B6E",
+"# c #877373",
+"$ c #957978",
+"% c #9F8382",
+"& c #B18E8F",
+" ",
+" ",
+" ",
+" ",
+" ## ",
+" #&&# ",
+" .$&&&&$. ",
+" .%&&&&&&%. ",
+" +@%&&&&%@+ ",
+" $&&&&$ ",
+" $&&&&$ ",
+" $&&&&$ ",
+" $&&&&$ ",
+" $&&&&$ ",
+" .... ",
+" ",
+" ",
+" "};
+
+
+const char *pause_xpm[] = {
+"23 23 8 1",
+" c #4D4F4C",
+". c #514E53",
+"+ c #5C4F61",
+"@ c #6F507E",
+"# c #855098",
+"$ c #9551AE",
+"% c #A652C5",
+"& c #AE52D1",
+" ",
+" ",
+" ",
+" ",
+" ",
+" #+ ",
+" &%#. ",
+" &&&%@ ",
+" &&&&&$+ ",
+" &&&&&&&#. ",
+" &&&&&&&&%@ ",
+" &&&&&&&&&&# ",
+" &&&&&&&&%@. ",
+" &&&&&&&#. ",
+" &&&&&$+ ",
+" &&&%@ ",
+" &&#. ",
+" $+ ",
+" ",
+" ",
+" ",
+" ",
+" "};
+
+
+const char *play_xpm[] = {
+"23 23 8 1",
+" c #242523",
+". c #393534",
+"+ c #574B4C",
+"@ c #6E5B5A",
+"# c #7C6663",
+"$ c #8C7170",
+"% c #A48384",
+"& c #B18E8F",
+" ",
+" ",
+" ",
+" ",
+" ",
+" $. ",
+" &&@ ",
+" &&&%+ ",
+" &&&&&$. ",
+" &&&&&&&@ ",
+" &&&&&&&&%+ ",
+" &&&&&&&&&&# ",
+" &&&&&&&&%+ ",
+" &&&&&&&#. ",
+" &&&&&$. ",
+" &&&%+ ",
+" &&@ ",
+" $. ",
+" ",
+" ",
+" ",
+" ",
+" "};
+
+
+const char *rewindOff_xpm[] = {
+"23 23 8 1",
+" c #242523",
+". c #393534",
+"+ c #574B4C",
+"@ c #6E5B5A",
+"# c #7C6663",
+"$ c #8C7170",
+"% c #A48384",
+"& c #B18E8F",
+" ",
+" ",
+" ",
+" ",
+" ",
+" .$ ",
+" @&& ",
+" +%&&& ",
+" .$&&&&& ",
+" @&&&&&&& ",
+" +%&&&&&&&& ",
+" #&&&&&&&&&& ",
+" +%&&&&&&&& ",
+" .#&&&&&&& ",
+" .$&&&&& ",
+" +%&&& ",
+" @&& ",
+" .$ ",
+" ",
+" ",
+" ",
+" ",
+" "};
+
+
+const char *rewindOn_xpm[] = {
+"23 23 8 1",
+" c #4D4F4C",
+". c #514E53",
+"+ c #5C4F61",
+"@ c #6F507E",
+"# c #855098",
+"$ c #9551AE",
+"% c #A652C5",
+"& c #AE52D1",
+" ",
+" ",
+" ",
+" ",
+" ",
+" +# ",
+" .#%& ",
+" @%&&& ",
+" +$&&&&& ",
+" .#&&&&&&& ",
+" @%&&&&&&&& ",
+" #&&&&&&&&&& ",
+" .@%&&&&&&&& ",
+" .#&&&&&&& ",
+" +$&&&&& ",
+" @%&&& ",
+" .#&& ",
+" +$ ",
+" ",
+" ",
+" ",
+" ",
+" "};
+
+
+// 18x18
+/*
+const unsigned char giada_icon[] = {
+ 0x00, 0x00, 0x00, 0xfc, 0xff, 0x00, 0xfe, 0xff, 0x01, 0xfe, 0xff, 0x01,
+ 0x3e, 0xf0, 0x01, 0x1e, 0xe0, 0x01, 0x0e, 0xc3, 0x01, 0x8e, 0xff, 0x01,
+ 0x8e, 0xc1, 0x01, 0x8e, 0xc1, 0x01, 0x8e, 0xc7, 0x01, 0x0e, 0xc7, 0x01,
+ 0x1e, 0xc0, 0x01, 0x3e, 0xf0, 0x01, 0xfe, 0xff, 0x01, 0xfe, 0xff, 0x01,
+ 0xfc, 0xff, 0x00, 0x00, 0x00, 0x00 };
+*/
+
+const char *giada_icon[] = {
+"65 65 11 1",
+" c None",
+". c #000000",
+"+ c #000100",
+"@ c #FFFFFF",
+"# c #FDFFFC",
+"$ c #CBCDC9",
+"% c #292B28",
+"& c #626461",
+"* c #484A47",
+"= c #888A87",
+"- c #A7A9A6",
+"....+++++++++++++++++++++++++++++++++++++++++++++++++++++++++....",
+".@@@#####################$%+++++++++++++%&&*%+++++++&##%+++++....",
+".@@#####################&++++++++++++=$#######-*+++++&$+++++++...",
+".@@####################&++++++++++%-############$*+++++++++++++..",
+"+#####################*++++++++++&################-++++++++++++%+",
+"+####################*++++++++++=##################$+++++++++++*+",
+"+###################*++++++++++$####################$++++++++++=+",
+"+##################=++++++++++-######################-+++++++++-+",
+"+#################$++++++++++=#######################=+++++++++$+",
+"+#################%+++++++++*########################*+++++++++#+",
+"+################*++++++++++#########################%++++++++*#+",
+"+###############-++++++++++=########################$+++++++++&#+",
+"+###############%+++++++++%#########################-+++++++++-#+",
+"+##############-++++++++++-#########################&+++++++++$#+",
+"+##############%+++++++++%##########################*+++++++++##+",
+"+#############-++++++++++-##########################+++++++++%##+",
+"+#############%++++++++++##########################$+++++++++*##+",
+"+############$++++++++++&##########################=+++++++++=##+",
+"+############=++++++++++$##########################&+++++++++-##+",
+"+############*+++++++++%###########################%+++++++++$##+",
+"+############++++++++++=###########################++++++++++###+",
+"+###########$++++++++++$##########################-+++++++++*###+",
+"+###########=++++++++++###########################=+++++++++&###+",
+"+###########*+++++++++%###########################*+++++++++-###+",
+"+###########%+++++++++&###########################++++++++++$###+",
+"+###########%+++++++++-##########################=++++++++++####+",
+"+###########++++++++++$##########################%+++++++++%####+",
+"+###########++++++++++##########################$++++++++++&####+",
+"+##########$++++++++++##########################&++++++++++=####+",
+"+##########$+++++++++%#########################$+++++++++++-####+",
+"+##########$+++++++++%#########################&+++++++++++$####+",
+"+###########+++++++++*########################$++++++++++++#####+",
+"+###########+++++++++*########################*+++++++++++*#####+",
+"+###########%++++++++%#######################=++++++++++++&#####+",
+"+###########&+++++++++######################$+++++++++++++-#####+",
+"+###########=+++++++++$#####################%+++%+++++++++$#####+",
+"+###########$+++++++++$####################&+++%=+++++++++######+",
+"+############%++++++++&###################=++++$&++++++++%######+",
+"+############-+++++++++$#################&++++=#*++++++++&######+",
+"+#############+++++++++&################*++++*##+++++++++=######+",
+"+#############=+++++++++-#############$%++++%###+++++++++-######+",
+"+##############*+++++++++=##########$*+++++%###$+++++++++#######+",
+"+###############++++++++++&$######$&++++++&####=+++++++++#######+",
+"+###############$++++++++++++%&*%++++++++=#####&++++++++*#######+",
+"+################$%+++++++++++++++++++++-######%++++++++=#######+",
+"+##################&++++++++++++++++++=########+++++++++-#######+",
+"+###################$%+++++++++++++%=#########$+++++++++########+",
+"+#####################$=%++++++%*=-###########=++++++++%########+",
+"+##########################$$$################*++++++++&########+",
+"+#############################################+++++++++=########+",
+"+############################################$+++++++++$########+",
+"+############################################&++++++++%#########+",
+"+############################################+++++++++=#########+",
+"+###########################################=+++++++++##########+",
+"+###########################################%++++++++*##########+",
+"+##########################################=+++++++++-##########+",
+"+#########-==$############################$+++++++++&###########+",
+"+#######=++++++-##########################*+++++++++############+",
+"+######$++++++++$########################=+++++++++-############+",
+"+######=+++++++++$######################-+++++++++&#############+",
+"+######=+++++++++*#####################$+++++++++&##############.",
+".@#####=++++++++++-###################-+++++++++=##############@.",
+".@@####=++++++++++%##################=+++++++++=#############@@@.",
+".@@@###=+++++++++++*###############$*++++++++%$##############@@@.",
+"....++++++++++++++++++++++++++++++++++++++++++++++++++++++++....."};
+
+const char *recOff_xpm[] = {
+"23 23 8 1",
+" c #242523",
+". c #342F2E",
+"+ c #3F3B3A",
+"@ c #594F4F",
+"# c #7A6663",
+"$ c #8C7170",
+"% c #A68384",
+"& c #B18E8F",
+" ",
+" ",
+" ",
+" ",
+" ",
+" @$%%$@ ",
+" .$&&&&&&$. ",
+" $&&&&&&&&$ ",
+" @&&&#++#&&&@ ",
+" $&&# #&&$ ",
+" %&&+ +&&% ",
+" %&&+ +&&% ",
+" $&&# #&&$ ",
+" @&&&#++#&&&@ ",
+" $&&&&&&&&$ ",
+" .$&&&&&&$. ",
+" @$%%$@ ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" "};
+
+const char *recOn_xpm[] = {
+"23 23 8 1",
+" c #4D4F4C",
+". c #5F4E50",
+"+ c #6E4F50",
+"@ c #8C5050",
+"# c #AE5454",
+"$ c #BB5253",
+"% c #C55352",
+"& c #E85557",
+" ",
+" ",
+" ",
+" ",
+" ",
+" @$&&$@ ",
+" .%&&&&&&%. ",
+" %&&&&&&&&% ",
+" @&&&#++#&&&@ ",
+" $&&# #&&$ ",
+" &&&+ +&&& ",
+" &&&+ +&&& ",
+" $&&# #&&$ ",
+" @&&&#++#&&&@ ",
+" %&&&&&&&&% ",
+" .%&&&&&&%. ",
+" @$&&$@ ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" "};
+
+const char *inputRecOn_xpm[] = {
+"23 23 8 1",
+" c #524D4C",
+". c #4D4F4C",
+"+ c #5D4F50",
+"@ c #8C5050",
+"# c #BB5253",
+"$ c #C45251",
+"% c #DD5256",
+"& c #EA5657",
+".......................",
+".......................",
+".......................",
+".......................",
+".......................",
+"........ @#%%#@ .......",
+".......+$&&&&&&$+......",
+"...... $&&&&&&&&$ .....",
+"......@&&&&&&&&&&@.....",
+"......#&&&&&&&&&&#.....",
+"......%&&&&&&&&&&%.....",
+"......%&&&&&&&&&&%.....",
+"......#&&&&&&&&&&#.....",
+"......@&&&&&&&&&&@.....",
+".......$&&&&&&&&$......",
+".......+$&&&&&&$+......",
+"........ @#%%#@ .......",
+".......................",
+".......................",
+".......................",
+".......................",
+".......................",
+"......................."};
+
+const char *inputRecOff_xpm[] = {
+"23 23 8 1",
+" c #242523",
+". c #252724",
+"+ c #332F2E",
+"@ c #594E4F",
+"# c #896E6D",
+"$ c #8D7271",
+"% c #A68384",
+"& c #B18E8F",
+" ",
+" ",
+" ",
+" ",
+" ",
+" .@#%%#@. ",
+" +$&&&&&&$+ ",
+" .$&&&&&&&&$. ",
+" @&&&&&&&&&&@ ",
+" #&&&&&&&&&&# ",
+" %&&&&&&&&&&% ",
+" %&&&&&&&&&&% ",
+" #&&&&&&&&&&# ",
+" @&&&&&&&&&&@ ",
+" $&&&&&&&&$ ",
+" +$&&&&&&$+ ",
+" .@#%%#@. ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" "};
+
+const char *muteOff_xpm[] = {
+"18 18 8 1",
+" c #242523",
+". c #2E2F2D",
+"+ c #3B3C3A",
+"@ c #525451",
+"# c #6F716E",
+"$ c #878986",
+"% c #ADAFAC",
+"& c #C6C8C5",
+" ",
+" ",
+" ",
+" ",
+" ++. .++ ",
+" +&&$ $&&+ ",
+" +&&% %&&+ ",
+" +&%&++&%&+ ",
+" +&$&##&$&+ ",
+" +&#%$$%#&+ ",
+" +&#$%%$#&+ ",
+" +&#@&&@#&+ ",
+" +&#+&&+#&+ ",
+" .#@ ## @#. ",
+" ",
+" ",
+" ",
+" "};
+
+const char *muteOn_xpm[] = {
+"18 18 8 1",
+" c #4D4F4C",
+". c #585A57",
+"+ c #616260",
+"@ c #7A7C79",
+"# c #888A87",
+"$ c #989A97",
+"% c #B2B4B1",
+"& c #C6C8C5",
+" ",
+" ",
+" ",
+" ",
+" .. .. ",
+" +&&$ $&&+ ",
+" +&&% %&&+ ",
+" +&%&++&%&+ ",
+" +&$&@@&$&+ ",
+" +&#%$$%#&+ ",
+" +&#$&&$#&+ ",
+" +&#@&&@#&+ ",
+" +&#.&&.#&+ ",
+" .#+ ## +#. ",
+" ",
+" ",
+" ",
+" "};
+
+
+const char *readActionOff_xpm[] = {
+"18 18 8 1",
+" c #242523",
+". c #393B38",
+"+ c #555754",
+"@ c #6B6D6A",
+"# c #7F807E",
+"$ c #9C9E9B",
+"% c #B1B3B0",
+"& c #C3C5C2",
+" ",
+" ",
+" ",
+" ",
+" .... ",
+" %&&&&%+ ",
+" %&@@@&& ",
+" %% $&. ",
+" %&@@#&$ ",
+" %&&&&@ ",
+" %% +&$ ",
+" %% #&# ",
+" %% %&+ ",
+" @@ .#+ ",
+" ",
+" ",
+" ",
+" "};
+
+
+const char *readActionOn_xpm[] = {
+"18 18 8 1",
+" c #4D4F4C",
+". c #696B68",
+"+ c #7A7C79",
+"@ c #888A87",
+"# c #939592",
+"$ c #A7A9A6",
+"% c #B7B9B6",
+"& c #C4C6C3",
+" ",
+" ",
+" ",
+" ",
+" ",
+" %&&&&%. ",
+" %&++@&& ",
+" %% $& ",
+" %&@@#&$ ",
+" %&&&&@ ",
+" %% +&$ ",
+" %% #&# ",
+" %% %&. ",
+" +@ .@+ ",
+" ",
+" ",
+" ",
+" "};
+
+
+const char *metronomeOff_xpm[] = {
+"13 13 8 1",
+" c #242523",
+". c #2D2928",
+"+ c #34302F",
+"@ c #443D3C",
+"# c #4F4445",
+"$ c #685659",
+"% c #826A68",
+"& c #A18282",
+" ",
+" ",
+" . . ",
+" #% %# ",
+" .&+ +&. ",
+" %$ $% ",
+" @& &@ ",
+" &@ @& ",
+" $% %$ ",
+" +&. .&+ ",
+" %# #% ",
+" . . ",
+" "};
+
+
+const char *metronomeOn_xpm[] = {
+"13 13 8 1",
+" c #4D4F4C",
+". c #565150",
+"+ c #645C5C",
+"@ c #716465",
+"# c #837070",
+"$ c #8F7775",
+"% c #977C7B",
+"& c #A68787",
+" ",
+" ",
+" . . ",
+" @% %@ ",
+" .&. .&. ",
+" $# #$ ",
+" +& &+ ",
+" &+ +& ",
+" #$ $# ",
+" .&. .&. ",
+" %@ @% ",
+" . . ",
+" "};
+
+
+const char *zoomInOff_xpm[] = {
+"18 18 8 1",
+" c None",
+". c #252525",
+"+ c #262626",
+"@ c #535353",
+"# c #ACACAC",
+"$ c #AEAEAE",
+"% c #B1B1B1",
+"& c #C4C4C4",
+"++++++++++++++++++",
+"+................+",
+"+................+",
+"+................+",
+"+................+",
+"+.......@@.......+",
+"+.......#$.......+",
+"+.......#$.......+",
+"+....@%%&&%%@....+",
+"+....@%%&&%%@....+",
+"+.......#$.......+",
+"+.......#$.......+",
+"+.......@@.......+",
+"+................+",
+"+................+",
+"+................+",
+"+................+",
+"++++++++++++++++++"};
+
+
+const char *zoomInOn_xpm[] = {
+"18 18 8 1",
+" c None",
+". c #4E4E4E",
+"+ c #707070",
+"@ c #717171",
+"# c #B3B3B3",
+"$ c #B5B5B5",
+"% c #B7B7B7",
+"& c #C5C5C5",
+"..................",
+"..................",
+"..................",
+"..................",
+"..................",
+"........++........",
+"........#$........",
+"........#$........",
+".....@%%&&%%@.....",
+".....@%%&&%%@.....",
+"........#$........",
+"........#$........",
+"........++........",
+"..................",
+"..................",
+"..................",
+"..................",
+".................."};
+
+
+const char *zoomOutOff_xpm[] = {
+"18 18 5 1",
+" c None",
+". c #252525",
+"+ c #262626",
+"@ c #9C9C9C",
+"# c #BBBBBB",
+"++++++++++++++++++",
+"+................+",
+"+................+",
+"+................+",
+"+................+",
+"+................+",
+"+................+",
+"+................+",
+"+......@##@......+",
+"+......@##@......+",
+"+................+",
+"+................+",
+"+................+",
+"+................+",
+"+................+",
+"+................+",
+"+................+",
+"++++++++++++++++++"};
+
+
+const char *zoomOutOn_xpm[] = {
+"18 18 4 1",
+" c None",
+". c #4E4E4E",
+"+ c #A7A7A7",
+"@ c #BEBEBE",
+"..................",
+"..................",
+"..................",
+"..................",
+"..................",
+"..................",
+"..................",
+"..................",
+".......+@@+.......",
+".......+@@+.......",
+"..................",
+"..................",
+"..................",
+"..................",
+"..................",
+"..................",
+"..................",
+".................."};
+
+
+
+const char *scrollRightOff_xpm[] = {
+"12 12 8 1",
+" c #181917",
+". c #242523",
+"+ c #2E2F2D",
+"@ c #4D4F4C",
+"# c #5D5F5C",
+"$ c #828481",
+"% c #9B9D9A",
+"& c #BCBEBB",
+"............",
+"............",
+"...+........",
+"...&$@......",
+"...$&&%@....",
+"....+#%&%...",
+"....+#%&%...",
+"...$&&%#....",
+"...&$@......",
+"...+........",
+"............",
+"............"};
+
+
+const char *scrollLeftOff_xpm[] = {
+"12 12 8 1",
+" c #181917",
+". c #242523",
+"+ c #2E2F2D",
+"@ c #4D4F4C",
+"# c #5D5F5C",
+"$ c #828481",
+"% c #9B9D9A",
+"& c #BCBEBB",
+"............",
+"............",
+"........+...",
+"......@$&...",
+"....@%&&$...",
+"...%&%#+....",
+"...%&%#+....",
+"....#%&&$...",
+"......@$&...",
+"........+...",
+"............",
+"............"};
+
+
+const char *scrollLeftOn_xpm[] = {
+"12 12 8 1",
+" c #4D4F4C",
+". c #6B6D6A",
+"+ c #7B7D7A",
+"@ c #969895",
+"# c #A6A8A5",
+"$ c #B4B6B3",
+"% c #C0C2BF",
+"& c #FEFFFC",
+" ",
+" ",
+" ",
+" .@$ ",
+" +#%%@ ",
+" $%#+ ",
+" %%#+ ",
+" +$%%@ ",
+" .#$ ",
+" ",
+" ",
+" "};
+
+
+const char *scrollRightOn_xpm[] = {
+"12 12 8 1",
+" c #4D4F4C",
+". c #6B6D6A",
+"+ c #7B7D7A",
+"@ c #969895",
+"# c #A6A8A5",
+"$ c #B4B6B3",
+"% c #C0C2BF",
+"& c #FEFFFC",
+" ",
+" ",
+" ",
+" %@. ",
+" @%%#. ",
+" +#%# ",
+" +#%# ",
+" @%%#+ ",
+" %@. ",
+" ",
+" ",
+" "};
+
+
+const char *soloOn_xpm[] = {
+"18 18 8 1",
+" c #4D4F4C",
+". c #616360",
+"+ c #737572",
+"@ c #838582",
+"# c #929491",
+"$ c #A5A7A4",
+"% c #B1B3B0",
+"& c #C6C8C5",
+" ",
+" ",
+" ",
+" ",
+" .@+. ",
+" #&&&&# ",
+" .&$ %&. ",
+" &%+ .. ",
+" #&&&$. ",
+" .@$&&. ",
+" .#. @&@ ",
+" .&$. #&+ ",
+" #&&&&$ ",
+" .+@+ ",
+" ",
+" ",
+" ",
+" "};
+
+
+const char *soloOff_xpm[] = {
+"18 18 8 1",
+" c #242523",
+". c #3D3F3D",
+"+ c #525451",
+"@ c #666865",
+"# c #80827F",
+"$ c #979996",
+"% c #A7A9A6",
+"& c #C6C8C5",
+" ",
+" ",
+" ",
+" ",
+" .@@. ",
+" #&&&&# ",
+" .&$ %&. ",
+" &%+ .. ",
+" #&&&$+ ",
+" .@%&&. ",
+" +#. @&@ ",
+" .&$..#&+ ",
+" #&&&&$ ",
+" .@@+ ",
+" ",
+" ",
+" ",
+" "};
+
+
+#ifdef WITH_VST
+
+
+const char *fxOff_xpm[] = {
+"18 18 8 1",
+" c #242523",
+". c #40423F",
+"+ c #4D4E4C",
+"@ c #686A67",
+"# c #7B7D7A",
+"$ c #919390",
+"% c #AEB0AD",
+"& c #C1C3C0",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ..... . . ",
+" $&&&$ $% @&. ",
+" $$ .&#&@ ",
+" $%##. @&$ ",
+" $%##. #&% ",
+" $$ .&@&# ",
+" $$ %$ @&. ",
+" .. + +. ",
+" ",
+" ",
+" ",
+" ",
+" "};
+
+
+const char *fxOn_xpm[] = {
+"18 18 8 1",
+" c #4D4F4C",
+". c #565855",
+"+ c #636562",
+"@ c #80827F",
+"# c #8E908D",
+"$ c #9FA19E",
+"% c #B1B3B0",
+"& c #C1C3C0",
+" ",
+" ",
+" ",
+" ",
+" ",
+" .++++ +. +. ",
+" $&&&$ $% @&. ",
+" $$ .&#&@ ",
+" $%##+ @&$ ",
+" $%##+ #&% ",
+" $$ +&@&# ",
+" $$ %$ @&+ ",
+" ++ .+. ++ ",
+" ",
+" ",
+" ",
+" ",
+" "};
+
+
+const char *fxShiftUpOff_xpm[] = {
+"18 18 7 1",
+" c #242523",
+". c #4D4F4C",
+"+ c #A3A5A2",
+"@ c #868885",
+"# c #C1C3C0",
+"$ c #313330",
+"% c #626361",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" .+@ ",
+" @+#. ",
+" $#%+@ ",
+" %# %#$ ",
+" +@ $#% ",
+" $#. @+ ",
+" $. $. ",
+" ",
+" ",
+" ",
+" ",
+" "};
+
+
+const char *fxShiftUpOn_xpm[] = {
+"18 18 5 1",
+" c #4D4F4C",
+". c #70726F",
+"+ c #A5A7A4",
+"@ c #C1C3BF",
+"# c #8E908D",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" .++ ",
+" +@@. ",
+" @.+# ",
+" .@ .@ ",
+" +# @. ",
+" .@. #+ ",
+" . . ",
+" ",
+" ",
+" ",
+" ",
+" "};
+
+
+const char *fxShiftDownOff_xpm[] = {
+"18 18 7 1",
+" c #242523",
+". c #4D4F4C",
+"+ c #A3A5A2",
+"@ c #313330",
+"# c #626361",
+"$ c #868885",
+"% c #C1C3C0",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" .+@ #$ ",
+" @%# +$ ",
+" $+ .%@ ",
+" .%@$+ ",
+" +$%# ",
+" #%%@ ",
+" @.. ",
+" ",
+" ",
+" ",
+" ",
+" "};
+
+
+const char *fxShiftDownOn_xpm[] = {
+"18 18 5 1",
+" c #4D4F4C",
+". c #70726F",
+"+ c #A5A7A4",
+"@ c #C1C3BF",
+"# c #8E908D",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" .+ .+ ",
+" @. +# ",
+" #+ .@. ",
+" .@.#+ ",
+" +#@. ",
+" #@@ ",
+" .. ",
+" ",
+" ",
+" ",
+" ",
+" "};
+
+
+const char *vstLogo_xpm[] = {
+"65 38 8 1",
+" c #161715",
+". c #2B2D2A",
+"+ c #474846",
+"@ c #6A6C69",
+"# c #8C8E8B",
+"$ c #A8AAA7",
+"% c #C7C9C6",
+"& c #EEF0ED",
+" @#############################################################+ ",
+"@#.............................................................$+",
+"#. .#",
+"#. .#",
+"#. ...... .. .#",
+"#. .@$$$####$%$#@.+&$ .#",
+"#. .#$$#+. +#$%%%%$ .#",
+"#. .$$#$ .#$$%$ .#",
+"#. ............. ....$$$$$ ++$$%$+@@@@@@@@@@@@@@@ .#",
+"#. ##$$$$$$%%%%@ %%&&&&%%$@ %&@#$$@@$%%&&&%%%&&&&& .#",
+"#. +$$$$$%@ .&%####%$@ $&$.$# #$%%%& @&%& .#",
+"#. +$$$$$% +&$###$%%&&$@. $&. #$%%%&. .%& .#",
+"#. @$$$$%$ %##$##$%&&&&&&%#.%# #$$%%&. @& .#",
+"#. $$$$$%+ #& #$$%%&&&&&&&%%$$@ #$$%%&. + .#",
+"#. .$$$$$% +&+ .#%&&&&&&&&%$$#$$# #$$%%&. .#",
+"#. @$$$$%$ %$ @%&&&&&&%$$###$$ #$$%%&. .#",
+"#. #$$$%%@ #& . +$&&&%$####$%$ #$$%%&. .#",
+"#. $$$%%% .&@ +%# .@$$$###$$% #$$$%&. .#",
+"#. +%$%%%$$% +$$+ #$#$$$% @$$$%&. .#",
+"#. #%%%%%&. +%$$ ##$$%$ @$$$%%. .#",
+"#. $$%%%@ +%$$$. #$$$%. @$$$$%. .#",
+"#. +%%%$ +%$$#$@ +$$%$ @#$$$%+ .#",
+"#. @%%. +%%%$$$$#@++.++@#$$$@ @@##$$$%%%$$@ .#",
+"#. #@ +&# .@@###$$$###@. @+++@@@@###$@ .#",
+"#. .#",
+"#. .#",
+"#. .#",
+"#. .#",
+"#. .#",
+"#. .@$$$$$$$$ .$%%%%%%# .#",
+"#. ....... .@@@@@@@@@. .#",
+"#. ........ @@@+@@@@@@@@@@+ .#",
+"@# ......... .####@@@@@@@@@@@+ #@",
+" @$$$$$$$$$$$$$$$.......... .@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$@ ",
+" ......... .@@@@@@@@@@@@@@@. ",
+" ........ @@@@@@@@@@. ",
+" ........... .@@@@@@@@@ ",
+" .......... .@@@@@@@@ "};
+
+
+const char *fxRemoveOff_xpm[] = {
+"18 18 9 1",
+" c None",
+". c #242623",
+"+ c #2F312E",
+"@ c #393A38",
+"# c #484A47",
+"$ c #5D5F5C",
+"% c #8E908D",
+"& c #9B9D9A",
+"* c #BDBFBC",
+"..................",
+"..................",
+"..................",
+"..................",
+"..................",
+".....+#@..@#+.....",
+"......&*++*&......",
+"......@*%%*@......",
+".......$**$.......",
+".......#**#.......",
+"......+*&&*+......",
+"......%*@@*%......",
+"......@@..@@......",
+"..................",
+"..................",
+"..................",
+"..................",
+".................."};
+
+
+const char *fxRemoveOn_xpm[] = {
+"18 18 9 1",
+" c None",
+". c #4D4F4C",
+"+ c #575956",
+"@ c #5C5D5B",
+"# c #666865",
+"$ c #787977",
+"% c #9C9E9B",
+"& c #A6A8A5",
+"* c #BFC1BE",
+"..................",
+"..................",
+"..................",
+"..................",
+"..................",
+"......#@..@#......",
+"......&*++*&......",
+"......@*%%*@......",
+".......$**$.......",
+".......#**#.......",
+"......+*&&*+......",
+"......%*@+*%......",
+"......@+..+@......",
+"..................",
+"..................",
+"..................",
+"..................",
+".................."};
+#endif // #ifdef WITH_VST
+
+
+const char *beatsDivideOn_xpm[] = {
+"13 13 13 1",
+" c None",
+". c #595B58",
+"+ c #5B5D5A",
+"@ c #5F615E",
+"# c #686967",
+"$ c #737572",
+"% c #787A77",
+"& c #80827F",
+"* c #8F918E",
+"= c #959794",
+"- c #9A9C99",
+"; c #C4C6C3",
+"> c #C7C9C6",
+".............",
+".............",
+".............",
+".............",
+".....#>#.....",
+"....+@%@+....",
+"...*>>>>>*...",
+"....+@%@+....",
+".....#>#.....",
+".............",
+".............",
+".............",
+"............."};
+
+
+const char *beatsDivideOff_xpm[] = {
+"13 13 13 1",
+" c None",
+". c #242523",
+"+ c #262825",
+"@ c #2D2E2C",
+"# c #3A3B39",
+"$ c #494B48",
+"% c #525451",
+"& c #595B58",
+"* c #5F615E",
+"= c #787A77",
+"- c #858784",
+"; c #C3C5C1",
+"> c #C7C9C6",
+".............",
+".............",
+".............",
+"......+......",
+".....#>#.....",
+"...++@%@++...",
+"...=>>>>>=...",
+"...++@%@++...",
+".....#>#.....",
+"......+......",
+".............",
+".............",
+"............."};
+
+
+const char *beatsMultiplyOn_xpm[] = {
+"13 13 13 1",
+" c None",
+". c #595B58",
+"+ c #5B5D5A",
+"@ c #5F615E",
+"# c #686967",
+"$ c #737572",
+"% c #787A77",
+"& c #80827F",
+"* c #8F918E",
+"= c #959794",
+"- c #9A9C99",
+"; c #C4C6C3",
+"> c #C7C9C6",
+".............",
+".............",
+".............",
+"....$...$....",
+"...$;&.&;$...",
+"....&;-;&....",
+".....->-.....",
+"....&;=;&....",
+"...$;&.&;$...",
+"...+$...$....",
+".............",
+".............",
+"............."};
+
+
+const char *beatsMultiplyOff_xpm[] = {
+"13 13 12 1",
+" c #242523",
+". c #262825",
+"+ c #2D2E2C",
+"@ c #3A3B39",
+"# c #494B48",
+"$ c #525451",
+"% c #595B58",
+"& c #5F615E",
+"* c #787A77",
+"= c #858784",
+"- c #C3C5C1",
+"; c #C7C9C6",
+" ",
+" ",
+" ",
+" .# #. ",
+" #-& &-# ",
+" &-=-& ",
+" =;= ",
+" %-*-& ",
+" #-% %-# ",
+" .# #. ",
+" ",
+" ",
+" "};
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * graphics
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+#ifndef GRAPHICS_H
+#define GRAPHICS_H
+
+extern const char *giada_logo_xpm[];
+
+extern const char *loopRepeat_xpm[];
+extern const char *loopBasic_xpm[];
+extern const char *loopOnce_xpm[];
+extern const char *loopOnceBar_xpm[];
+extern const char *oneshotBasic_xpm[];
+extern const char *oneshotRetrig_xpm[];
+extern const char *oneshotPress_xpm[];
+extern const char *oneshotEndless_xpm[];
+
+extern const char *updirOff_xpm[];
+extern const char *updirOn_xpm[];
+
+extern const char *pause_xpm[];
+extern const char *play_xpm[];
+
+extern const char *zoomInOff_xpm[];
+extern const char *zoomInOn_xpm[];
+extern const char *zoomOutOff_xpm[];
+extern const char *zoomOutOn_xpm[];
+
+extern const char *scrollLeftOff_xpm[];
+extern const char *scrollLeftOn_xpm[];
+extern const char *scrollRightOff_xpm[];
+extern const char *scrollRightOn_xpm[];
+
+extern const char *rewindOff_xpm[];
+extern const char *rewindOn_xpm[];
+
+extern const char *recOff_xpm[];
+extern const char *recOn_xpm[];
+
+extern const char *metronomeOff_xpm[];
+extern const char *metronomeOn_xpm[];
+
+extern const char *inputRecOn_xpm[];
+extern const char *inputRecOff_xpm[];
+
+extern const char *beatsDivideOn_xpm[];
+extern const char *beatsDivideOff_xpm[];
+extern const char *beatsMultiplyOn_xpm[];
+extern const char *beatsMultiplyOff_xpm[];
+
+extern const char *muteOff_xpm[];
+extern const char *muteOn_xpm[];
+
+extern const char *soloOff_xpm[];
+extern const char *soloOn_xpm[];
+
+extern const char *readActionOn_xpm[];
+extern const char *readActionOff_xpm[];
+
+#ifdef WITH_VST
+extern const char *fxOff_xpm[];
+extern const char *fxOn_xpm[];
+
+extern const char *fxShiftUpOn_xpm[];
+extern const char *fxShiftUpOff_xpm[];
+extern const char *fxShiftDownOn_xpm[];
+extern const char *fxShiftDownOff_xpm[];
+
+extern const char *fxRemoveOff_xpm[];
+extern const char *fxRemoveOn_xpm[];
+
+extern const char *vstLogo_xpm[];
+#endif
+
+extern const char *giada_icon[];
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gui_utils
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "mixer.h"
+#include "patch.h"
+#include "gui_utils.h"
+#include "graphics.h"
+#include "gd_warnings.h"
+#include "ge_window.h"
+#include "ge_channel.h"
+#include "gd_mainWindow.h"
+#include "gg_keyboard.h"
+#include "gd_actionEditor.h"
+#include "recorder.h"
+#include "wave.h"
+#include "pluginHost.h"
+#include "channel.h"
+#include "log.h"
+#include "conf.h"
+
+
+extern Mixer G_Mixer;
+extern unsigned G_beats;
+extern bool G_audio_status;
+extern Patch G_patch;
+extern Conf G_conf;
+extern uint32_t G_time;
+extern gdMainWindow *mainWin;
+#ifdef WITH_VST
+extern PluginHost G_PluginHost;
+#endif
+
+
+static int blinker = 0;
+
+
+void gu_refresh()
+{
+ Fl::lock();
+
+ /* update dynamic elements: in and out meters, beat meter and
+ * each channel */
+
+ mainWin->inOut->refresh();
+ mainWin->beatMeter->redraw();
+ mainWin->keyboard->refreshColumns();
+
+ /* compute timer for blinker */
+
+ blinker++;
+ if (blinker > 12)
+ blinker = 0;
+
+ /* redraw GUI */
+
+ Fl::unlock();
+ Fl::awake();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int gu_getBlinker()
+{
+ return blinker;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gu_updateControls()
+{
+ for (unsigned i=0; i<G_Mixer.channels.size; i++)
+ G_Mixer.channels.at(i)->guiChannel->update();
+
+ mainWin->inOut->setOutVol(G_Mixer.outVol);
+ mainWin->inOut->setInVol(G_Mixer.inVol);
+#ifdef WITH_VST
+ mainWin->inOut->setMasterFxOutFull(G_PluginHost.masterOut.size > 0);
+ mainWin->inOut->setMasterFxInFull(G_PluginHost.masterIn.size > 0);
+#endif
+
+ mainWin->timing->setMeter(G_Mixer.beats, G_Mixer.bars);
+ mainWin->timing->setBpm(G_Mixer.bpm);
+
+ /* if you reset to init state while the seq is in play: it's better to
+ * update the button status */
+
+ mainWin->controller->updatePlay(G_Mixer.running);
+ mainWin->controller->updateMetronome(0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gu_update_win_label(const char *c)
+{
+ std::string out = VERSIONE_STR;
+ out += " - ";
+ out += c;
+ mainWin->copy_label(out.c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gu_setFavicon(Fl_Window *w)
+{
+#if defined(__linux__)
+ fl_open_display();
+ Pixmap p, mask;
+ XpmCreatePixmapFromData(
+ fl_display,
+ DefaultRootWindow(fl_display),
+ (char **)giada_icon,
+ &p,
+ &mask,
+ NULL);
+ w->icon((char *)p);
+#elif defined(_WIN32)
+ w->icon((char *)LoadIcon(fl_display, MAKEINTRESOURCE(IDI_ICON1)));
+#endif
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gu_openSubWindow(gWindow *parent, gWindow *child, int id)
+{
+ if (parent->hasWindow(id)) {
+ gLog("[GU] parent has subwindow with id=%d, deleting\n", id);
+ parent->delSubWindow(id);
+ }
+ child->setId(id);
+ parent->addSubWindow(child);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gu_refreshActionEditor()
+{
+ /** TODO - why don't we simply call WID_ACTION_EDITOR->redraw()? */
+
+ gdActionEditor *aeditor = (gdActionEditor*) mainWin->getChild(WID_ACTION_EDITOR);
+ if (aeditor) {
+ Channel *chan = aeditor->chan;
+ mainWin->delSubWindow(WID_ACTION_EDITOR);
+ gu_openSubWindow(mainWin, new gdActionEditor(chan), WID_ACTION_EDITOR);
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+gWindow *gu_getSubwindow(gWindow *parent, int id)
+{
+ if (parent->hasWindow(id))
+ return parent->getChild(id);
+ else
+ return NULL;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gu_closeAllSubwindows()
+{
+ /* don't close WID_FILE_BROWSER, because it's the caller of this
+ * function */
+
+ mainWin->delSubWindow(WID_ACTION_EDITOR);
+ mainWin->delSubWindow(WID_SAMPLE_EDITOR);
+ mainWin->delSubWindow(WID_FX_LIST);
+ mainWin->delSubWindow(WID_FX);
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * gui_utils
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+#ifndef GUI_UTILS_H
+#define GUI_UTILS_H
+
+#include <dirent.h>
+#include <string>
+#include <FL/x.H>
+#include <FL/Fl.H>
+#ifdef __APPLE__
+ #include <libgen.h> // in osx, for basename() (but linux?)
+#endif
+
+/* including stuff for the favicon (or whatever called) */
+
+#if defined(_WIN32)
+ #include "resource.h"
+#elif defined(__linux__)
+ #include <X11/xpm.h>
+#endif
+
+
+/* refresh
+ * refresh all GUI elements. */
+
+void gu_refresh();
+
+/* getBlinker
+* return blinker value, used to make widgets blink. */
+
+int gu_getBlinker();
+
+/* updateControls
+ * update attributes of control elements (sample names, volumes, ...).
+ * Useful when loading a new patch. */
+
+void gu_updateControls();
+
+/* update_win_label
+ * update the name of the main window */
+
+void gu_update_win_label(const char *c);
+
+void gu_setFavicon(Fl_Window *w);
+
+void gu_openSubWindow(class gWindow *parent, gWindow *child, int id);
+
+/* refreshActionEditor
+ * reload the action editor window by closing and reopening it. It's used
+ * when you delete some actions from the mainWindow and the action editor
+ * window is open. */
+
+void gu_refreshActionEditor();
+
+
+/* closeAllSubwindows
+ * close all subwindows attached to mainWin. */
+
+void gu_closeAllSubwindows();
+
+
+/* getSubwindow
+ * return a pointer to an open subwindow, otherwise NULL. */
+
+gWindow *gu_getSubwindow(class gWindow *parent, int id);
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * init
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include <ctime>
+#include "init.h"
+#include "log.h"
+#include "mixer.h"
+#include "wave.h"
+#include "const.h"
+#include "utils.h"
+#include "mixerHandler.h"
+#include "patch.h"
+#include "conf.h"
+#include "pluginHost.h"
+#include "recorder.h"
+#include "gd_mainWindow.h"
+#include "gui_utils.h"
+#include "gd_warnings.h"
+#include "kernelMidi.h"
+
+
+extern Mixer G_Mixer;
+extern bool G_audio_status;
+extern bool G_quit;
+extern Patch G_Patch;
+extern Conf G_Conf;
+extern gdMainWindow *mainWin;
+
+#ifdef WITH_VST
+extern PluginHost G_PluginHost;
+#endif
+
+
+void init_prepareParser()
+{
+ G_Conf.read();
+ G_Patch.setDefault();
+ if (!gLog_init(G_Conf.logMode))
+ gLog("[init] log init failed! Using default stdout\n");
+ time_t t;
+ time (&t);
+ gLog("[init] Giada "VERSIONE" - %s", ctime(&t));
+ gLog("[init] configuration file ready\n");
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void init_prepareKernelAudio()
+{
+ kernelAudio::openDevice(
+ G_Conf.soundSystem,
+ G_Conf.soundDeviceOut,
+ G_Conf.soundDeviceIn,
+ G_Conf.channelsOut,
+ G_Conf.channelsIn,
+ G_Conf.samplerate,
+ G_Conf.buffersize);
+ G_Mixer.init();
+ recorder::init();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void init_prepareKernelMIDI()
+{
+ kernelMidi::setApi(G_Conf.midiSystem);
+ kernelMidi::openOutDevice(G_Conf.midiPortOut);
+ kernelMidi::openInDevice(G_Conf.midiPortIn);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void init_startGUI(int argc, char **argv)
+{
+ char win_label[32];
+ sprintf(win_label, "%s - %s",
+ VERSIONE_STR,
+ !strcmp(G_Patch.name, "") ? "(default patch)" : G_Patch.name);
+
+ mainWin = new gdMainWindow(GUI_WIDTH, GUI_HEIGHT, win_label, argc, argv);
+ mainWin->resize(G_Conf.mainWindowX, G_Conf.mainWindowY, G_Conf.mainWindowW, G_Conf.mainWindowH);
+
+ /* never update the GUI elements if G_audio_status is bad, segfaults
+ * are around the corner */
+
+ if (G_audio_status)
+ gu_updateControls();
+
+ if (!G_audio_status)
+ gdAlert(
+ "Your soundcard isn't configured correctly.\n"
+ "Check the configuration and restart Giada."
+ );
+}
+
+/* ------------------------------------------------------------------ */
+
+
+void init_startKernelAudio()
+{
+ if (G_audio_status)
+ kernelAudio::startStream();
+
+#ifdef WITH_VST
+ G_PluginHost.allocBuffers();
+#endif
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void init_shutdown()
+{
+ G_quit = true;
+
+ /* store position and size of the main window for the next startup */
+
+ G_Conf.mainWindowX = mainWin->x();
+ G_Conf.mainWindowY = mainWin->y();
+ G_Conf.mainWindowW = mainWin->w();
+ G_Conf.mainWindowH = mainWin->h();
+
+ /* close any open subwindow, especially before cleaning PluginHost to
+ * avoid mess */
+
+ gu_closeAllSubwindows();
+ gLog("[init] all subwindows closed\n");
+
+ /* write configuration file */
+
+ if (!G_Conf.write())
+ gLog("[init] error while saving configuration file!\n");
+ else
+ gLog("[init] configuration saved\n");
+
+ /* if G_audio_status we close the kernelAudio FIRST, THEN the mixer.
+ * The opposite could cause random segfaults (even now with RtAudio?). */
+
+ if (G_audio_status) {
+ kernelAudio::closeDevice();
+ G_Mixer.close();
+ gLog("[init] Mixer closed\n");
+ }
+
+ recorder::clearAll();
+ gLog("[init] Recorder cleaned up\n");
+
+#ifdef WITH_VST
+ G_PluginHost.freeAllStacks();
+ gLog("[init] Plugin Host cleaned up\n");
+#endif
+
+ gLog("[init] Giada "VERSIONE" closed\n\n");
+ gLog_close();
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * init
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef INIT_H
+#define INIT_H
+
+
+#include <cstdio>
+#include <stdint.h>
+#ifdef __APPLE__
+ #include <pwd.h>
+#endif
+
+
+void init_prepareParser();
+void init_startGUI(int argc, char **argv);
+void init_prepareKernelAudio();
+void init_prepareKernelMIDI();
+void init_startKernelAudio();
+void init_shutdown();
+
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * KernelAudio
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include <vector>
+#include "kernelAudio.h"
+#include "mixer.h"
+#include "glue.h"
+#include "conf.h"
+#include "log.h"
+
+
+extern Mixer G_Mixer;
+extern Conf G_Conf;
+extern bool G_audio_status;
+
+
+namespace kernelAudio {
+
+RtAudio *system = NULL;
+unsigned numDevs = 0;
+bool inputEnabled = 0;
+unsigned realBufsize = 0;
+int api = 0;
+
+int openDevice(
+ int _api,
+ int outDev,
+ int inDev,
+ int outChan,
+ int inChan,
+ int samplerate,
+ int buffersize)
+{
+ api = _api;
+ gLog("[KA] using system 0x%x\n", api);
+#if defined(__linux__)
+ if (api == SYS_API_JACK && hasAPI(RtAudio::UNIX_JACK))
+ system = new RtAudio(RtAudio::UNIX_JACK);
+ else
+ if (api == SYS_API_ALSA && hasAPI(RtAudio::LINUX_ALSA))
+ system = new RtAudio(RtAudio::LINUX_ALSA);
+ else
+ if (api == SYS_API_PULSE && hasAPI(RtAudio::LINUX_PULSE))
+ system = new RtAudio(RtAudio::LINUX_PULSE);
+#elif defined(_WIN32)
+ if (api == SYS_API_DS && hasAPI(RtAudio::WINDOWS_DS))
+ system = new RtAudio(RtAudio::WINDOWS_DS);
+ else
+ if (api == SYS_API_ASIO && hasAPI(RtAudio::WINDOWS_ASIO))
+ system = new RtAudio(RtAudio::WINDOWS_ASIO);
+#elif defined(__APPLE__)
+ if (api == SYS_API_CORE && hasAPI(RtAudio::MACOSX_CORE))
+ system = new RtAudio(RtAudio::MACOSX_CORE);
+#endif
+ else {
+ G_audio_status = false;
+ return 0;
+ }
+
+
+
+ //gLog("[KA] %d\n", sizeof(system->rtapi_));
+
+ gLog("[KA] Opening devices %d (out), %d (in), f=%d...\n", outDev, inDev, samplerate);
+
+ numDevs = system->getDeviceCount();
+
+ if (numDevs < 1) {
+ gLog("[KA] no devices found with this API\n");
+ closeDevice();
+ G_audio_status = false;
+ return 0;
+ }
+ else {
+ gLog("[KA] %d device(s) found\n", numDevs);
+ for (unsigned i=0; i<numDevs; i++)
+ gLog(" %d) %s\n", i, getDeviceName(i));
+ }
+
+
+ RtAudio::StreamParameters outParams;
+ RtAudio::StreamParameters inParams;
+
+ if (outDev == DEFAULT_SOUNDDEV_OUT)
+ outParams.deviceId = getDefaultOut();
+ else
+ outParams.deviceId = outDev;
+ outParams.nChannels = 2;
+ outParams.firstChannel = outChan*2; // chan 0=0, 1=2, 2=4, ...
+
+ /* inDevice can be disabled */
+
+ if (inDev != -1) {
+ inParams.deviceId = inDev;
+ inParams.nChannels = 2;
+ inParams.firstChannel = inChan*2; // chan 0=0, 1=2, 2=4, ...
+ inputEnabled = true;
+ }
+ else
+ inputEnabled = false;
+
+
+ RtAudio::StreamOptions options;
+ options.streamName = "Giada";
+ options.numberOfBuffers = 4;
+
+ realBufsize = buffersize;
+
+#if defined(__linux__) || defined(__APPLE__)
+ if (api == SYS_API_JACK) {
+ samplerate = getFreq(outDev, 0);
+ gLog("[KA] JACK in use, freq = %d\n", samplerate);
+ G_Conf.samplerate = samplerate;
+ }
+#endif
+
+ try {
+ if (inDev != -1) {
+ system->openStream(
+ &outParams, // output params
+ &inParams, // input params
+ RTAUDIO_FLOAT32, // audio format
+ samplerate, // sample rate
+ &realBufsize, // buffer size in byte
+ &G_Mixer.masterPlay, // audio callback
+ NULL, // user data (unused)
+ &options);
+ }
+ else {
+ system->openStream(
+ &outParams, // output params
+ NULL, // input params
+ RTAUDIO_FLOAT32, // audio format
+ samplerate, // sample rate
+ &realBufsize, // buffer size in byte
+ &G_Mixer.masterPlay, // audio callback
+ NULL, // user data (unused)
+ &options);
+ }
+ G_audio_status = true;
+
+#if defined(__linux__)
+ if (api == SYS_API_JACK)
+ jackSetSyncCb();
+#endif
+
+ return 1;
+ }
+ catch (RtAudioError &e) {
+ gLog("[KA] system init error: %s\n", e.getMessage().c_str());
+ closeDevice();
+ G_audio_status = false;
+ return 0;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int startStream() {
+ try {
+ system->startStream();
+ gLog("[KA] latency = %lu\n", system->getStreamLatency());
+ return 1;
+ }
+ catch (RtAudioError &e) {
+ gLog("[KA] Start stream error: %s\n", e.getMessage().c_str());
+ return 0;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int stopStream() {
+ try {
+ system->stopStream();
+ return 1;
+ }
+ catch (RtAudioError &e) {
+ gLog("[KA] Stop stream error\n");
+ return 0;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+const char *getDeviceName(unsigned dev) {
+ try {
+ return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).name.c_str();
+ }
+ catch (RtAudioError &e) {
+ gLog("[KA] invalid device ID = %d\n", dev);
+ return NULL;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int closeDevice() {
+ if (system->isStreamOpen()) {
+#if defined(__linux__) || defined(__APPLE__)
+ system->abortStream(); // stopStream seems to lock the thread
+#elif defined(_WIN32)
+ system->stopStream(); // on Windows it's the opposite
+#endif
+ system->closeStream();
+ delete system;
+ system = NULL;
+ }
+ return 1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+unsigned getMaxInChans(int dev) {
+
+ if (dev == -1) return 0;
+
+ try {
+ return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).inputChannels;
+ }
+ catch (RtAudioError &e) {
+ gLog("[KA] Unable to get input channels\n");
+ return 0;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+unsigned getMaxOutChans(unsigned dev) {
+ try {
+ return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).outputChannels;
+ }
+ catch (RtAudioError &e) {
+ gLog("[KA] Unable to get output channels\n");
+ return 0;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool isProbed(unsigned dev) {
+ try {
+ return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).probed;
+ }
+ catch (RtAudioError &e) {
+ return 0;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+unsigned getDuplexChans(unsigned dev) {
+ try {
+ return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).duplexChannels;
+ }
+ catch (RtAudioError &e) {
+ return 0;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool isDefaultIn(unsigned dev) {
+ try {
+ return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).isDefaultInput;
+ }
+ catch (RtAudioError &e) {
+ return 0;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool isDefaultOut(unsigned dev) {
+ try {
+ return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).isDefaultOutput;
+ }
+ catch (RtAudioError &e) {
+ return 0;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int getTotalFreqs(unsigned dev) {
+ try {
+ return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).sampleRates.size();
+ }
+ catch (RtAudioError &e) {
+ return 0;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int getFreq(unsigned dev, int i) {
+ try {
+ return ((RtAudio::DeviceInfo) system->getDeviceInfo(dev)).sampleRates.at(i);
+ }
+ catch (RtAudioError &e) {
+ return 0;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int getDefaultIn() {
+ return system->getDefaultInputDevice();
+}
+
+int getDefaultOut() {
+ return system->getDefaultOutputDevice();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int getDeviceByName(const char *name) {
+ for (unsigned i=0; i<numDevs; i++)
+ if (strcmp(name, getDeviceName(i))==0)
+ return i;
+ return -1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool hasAPI(int API) {
+ std::vector<RtAudio::Api> APIs;
+ RtAudio::getCompiledApi(APIs);
+ for (unsigned i=0; i<APIs.size(); i++)
+ if (APIs.at(i) == API)
+ return true;
+ return false;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+std::string getRtAudioVersion() {
+ return RtAudio::getVersion();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+#ifdef __linux__
+#include <jack/jack.h>
+#include <jack/intclient.h>
+#include <jack/transport.h>
+
+jack_client_t *jackGetHandle() {
+ return (jack_client_t*) system->rtapi_->__HACK__getJackClient();
+}
+
+void jackStart() {
+ if (api == SYS_API_JACK) {
+ jack_client_t *client = jackGetHandle();
+ jack_transport_start(client);
+ }
+}
+
+
+void jackStop() {
+ if (api == SYS_API_JACK) {
+ jack_client_t *client = jackGetHandle();
+ jack_transport_stop(client);
+ }
+}
+
+
+void jackSetSyncCb() {
+ jack_client_t *client = jackGetHandle();
+ jack_set_sync_callback(client, jackSyncCb, NULL);
+ //jack_set_sync_timeout(client, 8);
+}
+
+
+int jackSyncCb(jack_transport_state_t state, jack_position_t *pos,
+ void *arg)
+{
+ switch (state) {
+ case JackTransportStopped:
+ gLog("[KA] Jack transport stopped, frame=%d\n", pos->frame);
+ glue_stopSeq(false); // false = not from GUI
+ if (pos->frame == 0)
+ glue_rewindSeq();
+ break;
+
+ case JackTransportRolling:
+ gLog("[KA] Jack transport rolling\n");
+ break;
+
+ case JackTransportStarting:
+ gLog("[KA] Jack transport starting, frame=%d\n", pos->frame);
+ glue_startSeq(false); // false = not from GUI
+ if (pos->frame == 0)
+ glue_rewindSeq();
+ break;
+
+ default:
+ gLog("[KA] Jack transport [unknown]\n");
+ }
+ return 1;
+}
+
+#endif
+
+}
+
+
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * KernelAudio
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef KERNELAUDIO_H
+#define KERNELAUDIO_H
+
+
+#include "rtaudio-mod/RtAudio.h"
+#if defined(__linux__)
+ #include <jack/jack.h>
+ #include <jack/intclient.h>
+ #include <jack/transport.h>
+#endif
+
+
+namespace kernelAudio {
+
+ int openDevice(
+ int api,
+ int outDev,
+ int inDev,
+ int outChan,
+ int inChan,
+ int samplerate,
+ int buffersize);
+ int closeDevice();
+
+ int startStream();
+ int stopStream();
+
+ bool isProbed (unsigned dev);
+ bool isDefaultIn (unsigned dev);
+ bool isDefaultOut (unsigned dev);
+ const char *getDeviceName (unsigned dev);
+ unsigned getMaxInChans (int dev);
+ unsigned getMaxOutChans (unsigned dev);
+ unsigned getDuplexChans (unsigned dev);
+ int getTotalFreqs (unsigned dev);
+ int getFreq (unsigned dev, int i);
+ int getDeviceByName(const char *name);
+ int getDefaultOut ();
+ int getDefaultIn ();
+ bool hasAPI (int API);
+
+ std::string getRtAudioVersion();
+
+#ifdef __linux__
+ jack_client_t *jackGetHandle();
+ void jackStart();
+ void jackStop();
+ void jackSetSyncCb();
+ int jackSyncCb(jack_transport_state_t state, jack_position_t *pos, void *arg);
+#endif
+
+ /* *** how to avoid multiple definition of ***
+ * When you declare a variable in a header file, every source file that
+ * includes that header, either directly or indirectly, gets its own
+ * separate copy of the variable. Then when you go to link all the .o
+ * files together, the linker sees that the variable is instantiated
+ * in a bunch of .o files. Make it extern in the header file and
+ * instantiate it in memory.cpp. */
+
+ extern RtAudio *system;
+ extern unsigned numDevs;
+ extern bool inputEnabled;
+ extern unsigned realBufsize; // reale bufsize from the soundcard
+ extern int api;
+}
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * KernelMidi
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include <stdio.h>
+#include "kernelMidi.h"
+#include "glue.h"
+#include "mixer.h"
+#include "channel.h"
+#include "sampleChannel.h"
+#include "pluginHost.h"
+#include "conf.h"
+#include "log.h"
+
+
+extern bool G_midiStatus;
+extern Conf G_Conf;
+extern Mixer G_Mixer;
+
+#ifdef WITH_VST
+extern PluginHost G_PluginHost;
+#endif
+
+
+namespace kernelMidi
+{
+
+int api = 0; // one api for both in & out
+RtMidiOut *midiOut = NULL;
+RtMidiIn *midiIn = NULL;
+unsigned numOutPorts = 0;
+unsigned numInPorts = 0;
+
+cb_midiLearn *cb_learn = NULL;
+void *cb_data = NULL;
+
+
+/* ------------------------------------------------------------------ */
+
+
+void startMidiLearn(cb_midiLearn *cb, void *data)
+{
+ cb_learn = cb;
+ cb_data = data;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void stopMidiLearn()
+{
+ cb_learn = NULL;
+ cb_data = NULL;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void setApi(int _api)
+{
+ api = api;
+ gLog("[KM] using system 0x%x\n", api);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int openOutDevice(int port)
+{
+ try {
+ midiOut = new RtMidiOut((RtMidi::Api) api, "Giada MIDI Output");
+ G_midiStatus = true;
+ }
+ catch (RtMidiError &error) {
+ gLog("[KM] MIDI out device error: %s\n", error.getMessage().c_str());
+ G_midiStatus = false;
+ return 0;
+ }
+
+ /* print output ports */
+
+ numOutPorts = midiOut->getPortCount();
+ gLog("[KM] %d output MIDI ports found\n", numOutPorts);
+ for (unsigned i=0; i<numOutPorts; i++)
+ gLog(" %d) %s\n", i, getOutPortName(i));
+
+ /* try to open a port, if enabled */
+
+ if (port != -1 && numOutPorts > 0) {
+ try {
+ midiOut->openPort(port, getOutPortName(port));
+ gLog("[KM] MIDI out port %d open\n", port);
+ return 1;
+ }
+ catch (RtMidiError &error) {
+ gLog("[KM] unable to open MIDI out port %d: %s\n", port, error.getMessage().c_str());
+ G_midiStatus = false;
+ return 0;
+ }
+ }
+ else
+ return 2;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int openInDevice(int port)
+{
+ try {
+ midiIn = new RtMidiIn((RtMidi::Api) api, "Giada MIDI input");
+ G_midiStatus = true;
+ }
+ catch (RtMidiError &error) {
+ gLog("[KM] MIDI in device error: %s\n", error.getMessage().c_str());
+ G_midiStatus = false;
+ return 0;
+ }
+
+ /* print input ports */
+
+ numInPorts = midiIn->getPortCount();
+ gLog("[KM] %d input MIDI ports found\n", numInPorts);
+ for (unsigned i=0; i<numInPorts; i++)
+ gLog(" %d) %s\n", i, getInPortName(i));
+
+ /* try to open a port, if enabled */
+
+ if (port != -1 && numInPorts > 0) {
+ try {
+ midiIn->openPort(port, getInPortName(port));
+ midiIn->ignoreTypes(true, false, true); // ignore all system/time msgs, for now
+ gLog("[KM] MIDI in port %d open\n", port);
+ midiIn->setCallback(&callback);
+ return 1;
+ }
+ catch (RtMidiError &error) {
+ gLog("[KM] unable to open MIDI in port %d: %s\n", port, error.getMessage().c_str());
+ G_midiStatus = false;
+ return 0;
+ }
+ }
+ else
+ return 2;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool hasAPI(int API)
+{
+ std::vector<RtMidi::Api> APIs;
+ RtMidi::getCompiledApi(APIs);
+ for (unsigned i=0; i<APIs.size(); i++)
+ if (APIs.at(i) == API)
+ return true;
+ return false;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+const char *getOutPortName(unsigned p)
+{
+ try { return midiOut->getPortName(p).c_str(); }
+ catch (RtMidiError &error) { return NULL; }
+}
+
+const char *getInPortName(unsigned p)
+{
+ try { return midiIn->getPortName(p).c_str(); }
+ catch (RtMidiError &error) { return NULL; }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void send(uint32_t data)
+{
+ if (!G_midiStatus)
+ return;
+
+ //std::vector<unsigned char> msg(1, 0x00);
+ //msg[0] = getB1(data);
+ //msg[1] = getB2(data);
+ //msg[2] = getB3(data);
+
+ std::vector<unsigned char> msg(1, getB1(data));
+ msg.push_back(getB2(data));
+ msg.push_back(getB3(data));
+
+ midiOut->sendMessage(&msg);
+ //gLog("[KM] send msg=0x%X (%X %X %X)\n", data, msg[0], msg[1], msg[2]);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void send(int b1, int b2, int b3)
+{
+ if (!G_midiStatus)
+ return;
+
+ std::vector<unsigned char> msg(1, b1);
+
+ if (b2 != -1)
+ msg.push_back(b2);
+ if (b3 != -1)
+ msg.push_back(b3);
+
+ midiOut->sendMessage(&msg);
+ //gLog("[KM] send msg=(%X %X %X)\n", b1, b2, b3);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void callback(double t, std::vector<unsigned char> *msg, void *data)
+{
+ /* 0.8.0 - for now we handle other midi signals (common and real-time
+ * messages) as unknown, for debugging purposes */
+
+ if (msg->size() < 3) {
+ gLog("[KM] MIDI received - unknown signal - size=%d, value=0x", (int) msg->size());
+ for (unsigned i=0; i<msg->size(); i++)
+ gLog("%X", (int) msg->at(i));
+ gLog("\n");
+ return;
+ }
+
+ /* in this place we want to catch two things: a) note on/note off
+ * from a keyboard and b) knob/wheel/slider movements from a
+ * controller */
+
+ uint32_t input = getIValue(msg->at(0), msg->at(1), msg->at(2));
+ uint32_t chan = input & 0x0F000000;
+ uint32_t value = input & 0x0000FF00;
+ uint32_t pure = 0x00;
+ if (!G_Conf.noNoteOff)
+ pure = input & 0xFFFF0000; // input without 'value' byte
+ else
+ pure = input & 0xFFFFFF00; // input with 'value' byte
+
+ gLog("[KM] MIDI received - 0x%X (chan %d)", input, chan >> 24);
+
+ /* start dispatcher. If midi learn is on don't parse channels, just
+ * learn incoming midi signal. Otherwise process master events first,
+ * then each channel in the stack. This way incoming signals don't
+ * get processed by glue_* when midi learning is on. */
+
+ if (cb_learn) {
+ gLog("\n");
+ cb_learn(pure, cb_data);
+ }
+ else {
+
+ /* process master events */
+
+ if (pure == G_Conf.midiInRewind) {
+ gLog(" >>> rewind (global) (pure=0x%X)", pure);
+ glue_rewindSeq();
+ }
+ else if (pure == G_Conf.midiInStartStop) {
+ gLog(" >>> startStop (global) (pure=0x%X)", pure);
+ glue_startStopSeq();
+ }
+ else if (pure == G_Conf.midiInActionRec) {
+ gLog(" >>> actionRec (global) (pure=0x%X)", pure);
+ glue_startStopActionRec();
+ }
+ else if (pure == G_Conf.midiInInputRec) {
+ gLog(" >>> inputRec (global) (pure=0x%X)", pure);
+ glue_startStopInputRec(false, false); // update gui, no popup messages
+ }
+ else if (pure == G_Conf.midiInMetronome) {
+ gLog(" >>> metronome (global) (pure=0x%X)", pure);
+ glue_startStopMetronome(false);
+ }
+ else if (pure == G_Conf.midiInVolumeIn) {
+ float vf = (value >> 8)/127.0f;
+ gLog(" >>> input volume (global) (pure=0x%X, value=%d, float=%f)", pure, value >> 8, vf);
+ glue_setInVol(vf, false);
+ }
+ else if (pure == G_Conf.midiInVolumeOut) {
+ float vf = (value >> 8)/127.0f;
+ gLog(" >>> output volume (global) (pure=0x%X, value=%d, float=%f)", pure, value >> 8, vf);
+ glue_setOutVol(vf, false);
+ }
+ else if (pure == G_Conf.midiInBeatDouble) {
+ gLog(" >>> sequencer x2 (global) (pure=0x%X)", pure);
+ glue_beatsMultiply();
+ }
+ else if (pure == G_Conf.midiInBeatHalf) {
+ gLog(" >>> sequencer /2 (global) (pure=0x%X)", pure);
+ glue_beatsDivide();
+ }
+
+ /* process channels */
+
+ for (unsigned i=0; i<G_Mixer.channels.size; i++) {
+
+ Channel *ch = (Channel*) G_Mixer.channels.at(i);
+
+ if (!ch->midiIn) continue;
+
+ if (pure == ch->midiInKeyPress) {
+ gLog(" >>> keyPress, ch=%d (pure=0x%X)", ch->index, pure);
+ glue_keyPress(ch, false, false);
+ }
+ else if (pure == ch->midiInKeyRel) {
+ gLog(" >>> keyRel ch=%d (pure=0x%X)", ch->index, pure);
+ glue_keyRelease(ch, false, false);
+ }
+ else if (pure == ch->midiInMute) {
+ gLog(" >>> mute ch=%d (pure=0x%X)", ch->index, pure);
+ glue_setMute(ch, false);
+ }
+ else if (pure == ch->midiInSolo) {
+ gLog(" >>> solo ch=%d (pure=0x%X)", ch->index, pure);
+ ch->solo ? glue_setSoloOn(ch, false) : glue_setSoloOff(ch, false);
+ }
+ else if (pure == ch->midiInVolume) {
+ float vf = (value >> 8)/127.0f;
+ gLog(" >>> volume ch=%d (pure=0x%X, value=%d, float=%f)", ch->index, pure, value >> 8, vf);
+ glue_setChanVol(ch, vf, false);
+ }
+ else if (pure == ((SampleChannel*)ch)->midiInPitch) {
+ float vf = (value >> 8)/(127/4.0f); // [0-127] ~> [0.0 4.0]
+ gLog(" >>> pitch ch=%d (pure=0x%X, value=%d, float=%f)", ch->index, pure, value >> 8, vf);
+ glue_setPitch(NULL, (SampleChannel*)ch, vf, false);
+ }
+ else if (pure == ((SampleChannel*)ch)->midiInReadActions) {
+ gLog(" >>> start/stop read actions ch=%d (pure=0x%X)", ch->index, pure);
+ glue_startStopReadingRecs((SampleChannel*)ch, false);
+ }
+ }
+ gLog("\n");
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+std::string getRtMidiVersion()
+{
+ return midiOut->getVersion();
+}
+
+
+} // namespace
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * KernelMidi
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef KERNELMIDI_H
+#define KERNELMIDI_H
+
+
+#include <stdint.h>
+#include <RtMidi.h>
+#include "channel.h"
+
+
+namespace kernelMidi {
+
+ extern int api; // one api for both in & out
+ extern unsigned numOutPorts;
+ extern unsigned numInPorts;
+
+ typedef void (cb_midiLearn) (uint32_t, void *);
+
+ /* cb_learn
+ * callback prepared by the gdMidiGrabber window and called by
+ * kernelMidi. It contains things to do once the midi message has been
+ * stored. */
+
+ extern cb_midiLearn *cb_learn;
+ extern void *cb_data;
+
+ void startMidiLearn(cb_midiLearn *cb, void *data);
+ void stopMidiLearn();
+
+ inline int getB1(uint32_t iValue) { return (iValue >> 24) & 0xFF; }
+ inline int getB2(uint32_t iValue) { return (iValue >> 16) & 0xFF; }
+ inline int getB3(uint32_t iValue) { return (iValue >> 8) & 0xFF; }
+
+ inline uint32_t getIValue(int b1, int b2, int b3) {
+ return (b1 << 24) | (b2 << 16) | (b3 << 8) | (0x00);
+ }
+
+ /* send
+ * send a MIDI message 's' (uint32_t). */
+
+ void send(uint32_t s);
+
+ /* send (2)
+ * send separate bytes of MIDI message. */
+
+ void send(int b1, int b2=-1, int b3=-1);
+
+ /* setApi
+ * set the Api in use for both in & out messages. */
+
+ void setApi(int api);
+
+ /* open/close/in/outDevice */
+
+ int openOutDevice(int port);
+ int openInDevice(int port);
+ int closeInDevice();
+ int closeOutDevice();
+
+ /* getIn/OutPortName
+ * return the name of the port 'p'. */
+
+ const char *getInPortName(unsigned p);
+ const char *getOutPortName(unsigned p);
+
+ bool hasAPI(int API);
+
+ /* callback
+ * master callback for input events. */
+
+ void callback(double t, std::vector<unsigned char> *msg, void *data);
+
+ std::string getRtMidiVersion();
+}
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * log
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include <cstdio>
+#include <cstdarg>
+#include <string>
+#include "log.h"
+#include "const.h"
+#include "utils.h"
+
+
+static FILE *f;
+static int mode;
+static bool stat;
+
+
+int gLog_init(int m) {
+ mode = m;
+ stat = true;
+ if (mode == LOG_MODE_FILE) {
+ std::string fpath = gGetHomePath() + "/giada.log";
+ f = fopen(fpath.c_str(), "a");
+ if (!f) {
+ stat = false;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gLog_close() {
+ if (mode == LOG_MODE_FILE)
+ fclose(f);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void gLog(const char *format, ...) {
+ if (mode == LOG_MODE_MUTE)
+ return;
+ va_list args;
+ va_start(args, format);
+ if (mode == LOG_MODE_FILE && stat == true)
+ vfprintf(f, format, args);
+ else
+ vprintf(format, args);
+ va_end(args);
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * log
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef __LOG_H__
+#define __LOG_H__
+
+
+/* init
+ * init logger. Mode defines where to write the output: LOG_MODE_STDOUT,
+ * LOG_MODE_FILE and LOG_MODE_MUTE. */
+
+int gLog_init (int mode);
+
+void gLog_close();
+
+void gLog(const char *format, ...);
+
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include <pthread.h>
+#if defined(__linux__) || defined(__APPLE__)
+ #include <unistd.h>
+#endif
+#include "init.h"
+#include "const.h"
+#include "patch.h"
+#include "conf.h"
+#include "mixer.h"
+#include "mixerHandler.h"
+#include "kernelAudio.h"
+#include "recorder.h"
+#include "gui_utils.h"
+#include "gd_mainWindow.h"
+#ifdef WITH_VST
+#include "pluginHost.h"
+#endif
+
+
+/* global variables. Yeah, we are nasty */
+
+pthread_t t_video;
+Mixer G_Mixer;
+bool G_quit;
+bool G_audio_status;
+bool G_midiStatus;
+Patch G_Patch;
+Conf G_Conf;
+gdMainWindow *mainWin;
+
+#ifdef WITH_VST
+PluginHost G_PluginHost;
+#endif
+
+
+void *thread_video(void *arg);
+
+
+int main(int argc, char **argv) {
+
+ G_quit = false;
+
+ init_prepareParser();
+ init_prepareKernelAudio();
+ init_prepareKernelMIDI();
+ init_startGUI(argc, argv);
+ Fl::lock();
+ pthread_create(&t_video, NULL, thread_video, NULL);
+ init_startKernelAudio();
+
+ int ret = Fl::run();
+
+ pthread_join(t_video, NULL);
+ return ret;
+}
+
+
+
+void *thread_video(void *arg) {
+ if (G_audio_status)
+ while (!G_quit) {
+ gu_refresh();
+#ifdef _WIN32
+ Sleep(GUI_SLEEP);
+#else
+ usleep(GUI_SLEEP);
+#endif
+ }
+ pthread_exit(NULL);
+ return 0;
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * channel
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include "channel.h"
+#include "midiChannel.h"
+#include "pluginHost.h"
+#include "patch.h"
+#include "conf.h"
+#include "kernelMidi.h"
+#include "log.h"
+
+
+extern Patch G_Patch;
+extern Mixer G_Mixer;
+extern Conf G_Conf;
+#ifdef WITH_VST
+extern PluginHost G_PluginHost;
+#endif
+
+
+MidiChannel::MidiChannel(int bufferSize)
+ : Channel (CHANNEL_MIDI, STATUS_OFF, bufferSize),
+ midiOut (false),
+ midiOutChan(MIDI_CHANS[0])
+{
+#ifdef WITH_VST // init VstEvents stack
+ freeVstMidiEvents(true);
+#endif
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+MidiChannel::~MidiChannel() {}
+
+
+/* ------------------------------------------------------------------ */
+
+
+#ifdef WITH_VST
+
+void MidiChannel::freeVstMidiEvents(bool init) {
+ if (events.numEvents == 0 && !init)
+ return;
+ memset(events.events, 0, sizeof(VstEvent*) * MAX_VST_EVENTS);
+ events.numEvents = 0;
+ events.reserved = 0;
+}
+
+#endif
+
+
+/* ------------------------------------------------------------------ */
+
+
+#ifdef WITH_VST
+
+void MidiChannel::addVstMidiEvent(uint32_t msg) {
+ addVstMidiEvent(G_PluginHost.createVstMidiEvent(msg));
+}
+
+#endif
+
+
+/* ------------------------------------------------------------------ */
+
+
+#ifdef WITH_VST
+
+void MidiChannel::addVstMidiEvent(VstMidiEvent *e) {
+ if (events.numEvents < MAX_VST_EVENTS) {
+ events.events[events.numEvents] = (VstEvent*) e;
+ events.numEvents++;
+ /*
+ gLog("[MidiChannel] VstMidiEvent added - numEvents=%d offset=%d note=%d number=%d velo=%d\n",
+ events.numEvents,
+ e->deltaFrames,
+ e->midiData[0],
+ e->midiData[1],
+ e->midiData[2]
+ );*/
+ }
+ else
+ gLog("[MidiChannel] channel %d VstEvents = %d > MAX_VST_EVENTS, nothing to do\n", index, events.numEvents);
+}
+
+#endif
+
+
+/* ------------------------------------------------------------------ */
+
+
+void MidiChannel::onBar(int frame) {}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void MidiChannel::stop() {}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void MidiChannel::empty() {}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void MidiChannel::quantize(int index, int localFrame, int globalFrame) {}
+
+
+/* ------------------------------------------------------------------ */
+
+#ifdef WITH_VST
+
+VstEvents *MidiChannel::getVstEvents() {
+ return (VstEvents *) &events;
+}
+
+#endif
+
+
+/* ------------------------------------------------------------------ */
+
+
+void MidiChannel::parseAction(recorder::action *a, int localFrame, int globalFrame) {
+ if (a->type == ACTION_MIDI)
+ sendMidi(a, localFrame/2);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void MidiChannel::onZero(int frame) {
+ if (status == STATUS_ENDING)
+ status = STATUS_OFF;
+ else
+ if (status == STATUS_WAIT)
+ status = STATUS_PLAY;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void MidiChannel::setMute(bool internal) {
+ mute = true; // internal mute does not exist for midi (for now)
+ if (midiOut)
+ kernelMidi::send(MIDI_ALL_NOTES_OFF);
+#ifdef WITH_VST
+ addVstMidiEvent(MIDI_ALL_NOTES_OFF);
+#endif
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void MidiChannel::unsetMute(bool internal) {
+ mute = false; // internal mute does not exist for midi (for now)
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void MidiChannel::process(float *buffer) {
+#ifdef WITH_VST
+ G_PluginHost.processStack(vChan, PluginHost::CHANNEL, this);
+ freeVstMidiEvents();
+#endif
+
+ for (int j=0; j<bufferSize; j+=2) {
+ buffer[j] += vChan[j] * volume; // * panLeft; future?
+ buffer[j+1] += vChan[j+1] * volume; // * panRight; future?
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void MidiChannel::start(int frame, bool doQuantize) {
+ switch (status) {
+ case STATUS_PLAY:
+ status = STATUS_ENDING;
+ break;
+ case STATUS_ENDING:
+ case STATUS_WAIT:
+ status = STATUS_OFF;
+ break;
+ case STATUS_OFF:
+ status = STATUS_WAIT;
+ break;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void MidiChannel::stopBySeq() {
+ kill(0);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void MidiChannel::kill(int frame) {
+ if (status & (STATUS_PLAY | STATUS_ENDING)) {
+ if (midiOut)
+ kernelMidi::send(MIDI_ALL_NOTES_OFF);
+#ifdef WITH_VST
+ addVstMidiEvent(MIDI_ALL_NOTES_OFF);
+#endif
+ }
+ status = STATUS_OFF;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int MidiChannel::loadByPatch(const char *f, int i) {
+ volume = G_Patch.getVol(i);
+ index = G_Patch.getIndex(i);
+ mute = G_Patch.getMute(i);
+ mute_s = G_Patch.getMute_s(i);
+ solo = G_Patch.getSolo(i);
+ panLeft = G_Patch.getPanLeft(i);
+ panRight = G_Patch.getPanRight(i);
+
+ midiOut = G_Patch.getMidiValue(i, "Out");
+ midiOutChan = G_Patch.getMidiValue(i, "OutChan");
+
+ readPatchMidiIn(i);
+
+ return SAMPLE_LOADED_OK; /// TODO - change name, it's meaningless here
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void MidiChannel::sendMidi(recorder::action *a, int localFrame)
+{
+ if (status & (STATUS_PLAY | STATUS_ENDING) && !mute) {
+ if (midiOut)
+ kernelMidi::send(a->iValue | MIDI_CHANS[midiOutChan]);
+
+#ifdef WITH_VST
+ a->event->deltaFrames = localFrame;
+ addVstMidiEvent(a->event);
+#endif
+ }
+}
+
+
+void MidiChannel::sendMidi(uint32_t data)
+{
+ if (status & (STATUS_PLAY | STATUS_ENDING) && !mute) {
+ if (midiOut)
+ kernelMidi::send(data | MIDI_CHANS[midiOutChan]);
+#ifdef WITH_VST
+ addVstMidiEvent(data);
+#endif
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void MidiChannel::rewind() {
+ if (midiOut)
+ kernelMidi::send(MIDI_ALL_NOTES_OFF);
+#ifdef WITH_VST
+ addVstMidiEvent(MIDI_ALL_NOTES_OFF);
+#endif
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void MidiChannel::writePatch(FILE *fp, int i, bool isProject)
+{
+ Channel::writePatch(fp, i, isProject);
+
+ fprintf(fp, "chanMidiOut%d=%u\n", i, midiOut);
+ fprintf(fp, "chanMidiOutChan%d=%u\n", i, midiOutChan);
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * channel
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef MIDI_CHANNEL_H
+#define MIDI_CHANNEL_H
+
+
+#ifdef WITH_VST
+
+/* before including aeffetx(x).h we must define __cdecl, otherwise VST
+ * headers can't be compiled correctly. In windows __cdecl is already
+ * defined. */
+
+ #ifdef __GNUC__
+ #ifndef _WIN32
+ #define __cdecl
+ #endif
+ #endif
+ #include "vst/aeffectx.h"
+
+#endif
+
+
+class MidiChannel : public Channel {
+
+public:
+
+ MidiChannel(int bufferSize);
+ ~MidiChannel();
+
+ bool midiOut; // enable midi output
+ uint8_t midiOutChan; // midi output channel
+
+ void process (float *buffer);
+ void start (int frame, bool doQuantize);
+ void kill (int frame);
+ void empty ();
+ void stopBySeq ();
+ void stop ();
+ void rewind ();
+ void setMute (bool internal);
+ void unsetMute (bool internal);
+ int loadByPatch(const char *file, int i);
+ void writePatch (FILE *fp, int i, bool isProject);
+ void quantize (int index, int localFrame, int globalFrame);
+ void onZero (int frame);
+ void onBar (int frame);
+ void parseAction(recorder::action *a, int localFrame, int globalFrame);
+
+ /* ---------------------------------------------------------------- */
+
+ /* sendMidi
+ * send Midi event to the outside world. */
+
+ void sendMidi(recorder::action *a, int localFrame);
+ void sendMidi(uint32_t data);
+
+#ifdef WITH_VST
+
+ /* getVstEvents
+ * return a pointer to gVstEvents. */
+
+ VstEvents *getVstEvents();
+
+ /* freeVstMidiEvents
+ * empty vstEvents structure. Init: use the method for channel
+ * initialization. */
+
+ void freeVstMidiEvents(bool init=false);
+
+ /* addVstMidiEvent
+ * take a composite MIDI event, decompose it and add it to channel. The
+ * other version creates a VstMidiEvent on the fly. */
+
+ void addVstMidiEvent(struct VstMidiEvent *e);
+ void addVstMidiEvent(uint32_t msg);
+
+#endif
+
+ /* ---------------------------------------------------------------- */
+
+#ifdef WITH_VST
+
+ /* VST struct containing MIDI events. When ready, events are sent to
+ * each plugin in the channel.
+ *
+ * Anatomy of VstEvents
+ * --------------------
+ *
+ * VstInt32 numEvents = number of Events in array
+ * VstIntPtr reserved = zero (Reserved for future use)
+ * VstEvent *events[n] = event pointer array, variable size
+ *
+ * Note that by default VstEvents only holds three events- if you want
+ * it to hold more, create an equivalent struct with a larger array,
+ * and then cast it to a VstEvents object when you've populated it.
+ * That's what we do with gVstEvents! */
+
+ struct gVstEvents {
+ VstInt32 numEvents;
+ VstIntPtr reserved;
+ VstEvent *events[MAX_VST_EVENTS];
+ } events;
+
+#endif
+
+};
+
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * mixer
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include <math.h>
+#include "mixer.h"
+#include "init.h"
+#include "wave.h"
+#include "gui_utils.h"
+#include "recorder.h"
+#include "pluginHost.h"
+#include "patch.h"
+#include "conf.h"
+#include "mixerHandler.h"
+#include "channel.h"
+#include "sampleChannel.h"
+#include "midiChannel.h"
+#include "kernelMidi.h"
+#include "log.h"
+
+
+extern Mixer G_Mixer;
+extern Patch G_Patch;
+extern Conf G_Conf;
+#ifdef WITH_VST
+extern PluginHost G_PluginHost;
+#endif
+
+
+Mixer::Mixer() {}
+Mixer::~Mixer() {}
+
+
+#define TICKSIZE 38
+
+
+float Mixer::tock[TICKSIZE] = {
+ 0.059033, 0.117240, 0.173807, 0.227943, 0.278890, 0.325936,
+ 0.368423, 0.405755, 0.437413, 0.462951, 0.482013, 0.494333,
+ 0.499738, 0.498153, 0.489598, 0.474195, 0.452159, 0.423798,
+ 0.389509, 0.349771, 0.289883, 0.230617, 0.173194, 0.118739,
+ 0.068260, 0.022631, -0.017423, -0.051339, -0.078721, -0.099345,
+ -0.113163, -0.120295, -0.121028, -0.115804, -0.105209, -0.089954,
+ -0.070862, -0.048844
+};
+
+
+float Mixer::tick[TICKSIZE] = {
+ 0.175860, 0.341914, 0.488904, 0.608633, 0.694426, 0.741500,
+ 0.747229, 0.711293, 0.635697, 0.524656, 0.384362, 0.222636,
+ 0.048496, -0.128348, -0.298035, -0.451105, -0.579021, -0.674653,
+ -0.732667, -0.749830, -0.688924, -0.594091, -0.474481, -0.340160,
+ -0.201360, -0.067752, 0.052194, 0.151746, 0.226280, 0.273493,
+ 0.293425, 0.288307, 0.262252, 0.220811, 0.170435, 0.117887,
+ 0.069639, 0.031320
+};
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Mixer::init() {
+ quanto = 1;
+ docross = false;
+ rewindWait = false;
+ running = false;
+ ready = true;
+ waitRec = 0;
+ actualFrame = 0;
+ bpm = DEFAULT_BPM;
+ bars = DEFAULT_BARS;
+ beats = DEFAULT_BEATS;
+ quantize = DEFAULT_QUANTIZE;
+ metronome = false;
+
+ tickTracker = 0;
+ tockTracker = 0;
+ tickPlay = false;
+ tockPlay = false;
+
+ outVol = DEFAULT_OUT_VOL;
+ inVol = DEFAULT_IN_VOL;
+ peakOut = 0.0f;
+ peakIn = 0.0f;
+ chanInput = NULL;
+ inputTracker = 0;
+
+ actualBeat = 0;
+
+ midiTCstep = 0;
+ midiTCrate = (G_Conf.samplerate / G_Conf.midiTCfps) * 2; // dealing with stereo vals
+ midiTCframes = 0;
+ midiTCseconds = 0;
+ midiTCminutes = 0;
+ midiTChours = 0;
+
+ /* alloc virtual input channels. vChanInput malloc is done in
+ * updateFrameBars, because of its variable size */
+ /** TODO - set kernelAudio::realBufsize * 2 as private member */
+
+ vChanInput = NULL;
+ vChanInToOut = (float *) malloc(kernelAudio::realBufsize * 2 * sizeof(float));
+
+ pthread_mutex_init(&mutex_recs, NULL);
+ pthread_mutex_init(&mutex_chans, NULL);
+ pthread_mutex_init(&mutex_plugins, NULL);
+
+ updateFrameBars();
+ rewind();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+Channel *Mixer::addChannel(int type) {
+
+ Channel *ch;
+ int bufferSize = kernelAudio::realBufsize*2;
+
+ if (type == CHANNEL_SAMPLE)
+ ch = new SampleChannel(bufferSize);
+ else
+ ch = new MidiChannel(bufferSize);
+
+ while (true) {
+ int lockStatus = pthread_mutex_trylock(&mutex_chans);
+ if (lockStatus == 0) {
+ channels.add(ch);
+ pthread_mutex_unlock(&mutex_chans);
+ break;
+ }
+ }
+
+ ch->index = getNewIndex();
+ gLog("[mixer] channel index=%d added, type=%d, total=%d\n", ch->index, ch->type, channels.size);
+ return ch;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Mixer::getNewIndex() {
+
+ /* always skip last channel: it's the last one just added */
+
+ if (channels.size == 1)
+ return 0;
+
+ int index = 0;
+ for (unsigned i=0; i<channels.size-1; i++) {
+ if (channels.at(i)->index > index)
+ index = channels.at(i)->index;
+ }
+ index += 1;
+ return index;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Mixer::deleteChannel(Channel *ch) {
+ int lockStatus;
+ while (true) {
+ lockStatus = pthread_mutex_trylock(&mutex_chans);
+ if (lockStatus == 0) {
+ channels.del(ch);
+ delete ch;
+ pthread_mutex_unlock(&mutex_chans);
+ return 1;
+ }
+ //else
+ // gLog("[mixer::deleteChannel] waiting for mutex...\n");
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+Channel *Mixer::getChannelByIndex(int index) {
+ for (unsigned i=0; i<channels.size; i++)
+ if (channels.at(i)->index == index)
+ return channels.at(i);
+ gLog("[mixer::getChannelByIndex] channel at index %d not found!\n", index);
+ return NULL;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Mixer::sendMIDIsync() {
+
+ if (G_Conf.midiSync == MIDI_SYNC_CLOCK_M) {
+ if (actualFrame % (framesPerBeat/24) == 0)
+ kernelMidi::send(MIDI_CLOCK, -1, -1);
+ }
+ else
+ if (G_Conf.midiSync == MIDI_SYNC_MTC_M) {
+
+ /* check if a new timecode frame has passed. If so, send MIDI TC
+ * quarter frames. 8 quarter frames, divided in two branches:
+ * 1-4 and 5-8. We check timecode frame's parity: if even, send
+ * range 1-4, if odd send 5-8. */
+
+ if (actualFrame % midiTCrate == 0) {
+
+ /* frame low nibble
+ * frame high nibble
+ * seconds low nibble
+ * seconds high nibble */
+
+ if (midiTCframes % 2 == 0) {
+ kernelMidi::send(MIDI_MTC_QUARTER, (midiTCframes & 0x0F) | 0x00, -1);
+ kernelMidi::send(MIDI_MTC_QUARTER, (midiTCframes >> 4) | 0x10, -1);
+ kernelMidi::send(MIDI_MTC_QUARTER, (midiTCseconds & 0x0F) | 0x20, -1);
+ kernelMidi::send(MIDI_MTC_QUARTER, (midiTCseconds >> 4) | 0x30, -1);
+ }
+
+ /* minutes low nibble
+ * minutes high nibble
+ * hours low nibble
+ * hours high nibble SMPTE frame rate */
+
+ else {
+ kernelMidi::send(MIDI_MTC_QUARTER, (midiTCminutes & 0x0F) | 0x40, -1);
+ kernelMidi::send(MIDI_MTC_QUARTER, (midiTCminutes >> 4) | 0x50, -1);
+ kernelMidi::send(MIDI_MTC_QUARTER, (midiTChours & 0x0F) | 0x60, -1);
+ kernelMidi::send(MIDI_MTC_QUARTER, (midiTChours >> 4) | 0x70, -1);
+ }
+
+ midiTCframes++;
+
+ /* check if total timecode frames are greater than timecode fps:
+ * if so, a second has passed */
+
+ if (midiTCframes > G_Conf.midiTCfps) {
+ midiTCframes = 0;
+ midiTCseconds++;
+ if (midiTCseconds >= 60) {
+ midiTCminutes++;
+ midiTCseconds = 0;
+ if (midiTCminutes >= 60) {
+ midiTChours++;
+ midiTCminutes = 0;
+ }
+ }
+ //gLog("%d:%d:%d:%d\n", midiTChours, midiTCminutes, midiTCseconds, midiTCframes);
+ }
+ }
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Mixer::sendMIDIrewind() {
+
+ midiTCframes = 0;
+ midiTCseconds = 0;
+ midiTCminutes = 0;
+ midiTChours = 0;
+
+ /* For cueing the slave to a particular start point, Quarter Frame
+ * messages are not used. Instead, an MTC Full Frame message should
+ * be sent. The Full Frame is a SysEx message that encodes the entire
+ * SMPTE time in one message */
+
+ if (G_Conf.midiSync == MIDI_SYNC_MTC_M) {
+ kernelMidi::send(MIDI_SYSEX, 0x7F, 0x00); // send msg on channel 0
+ kernelMidi::send(0x01, 0x01, 0x00); // hours 0
+ kernelMidi::send(0x00, 0x00, 0x00); // mins, secs, frames 0
+ kernelMidi::send(MIDI_EOX, -1, -1); // end of sysex
+ }
+}
+
+/* ------------------------------------------------------------------ */
+
+
+int Mixer::masterPlay(
+ void *out_buf, void *in_buf, unsigned n_frames,
+ double streamTime, RtAudioStreamStatus status, void *userData) {
+ return G_Mixer.__masterPlay(out_buf, in_buf, n_frames);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Mixer::__masterPlay(void *out_buf, void *in_buf, unsigned bufferFrames) {
+
+ if (!ready)
+ return 0;
+
+ float *outBuf = ((float *) out_buf);
+ float *inBuf = ((float *) in_buf);
+ bufferFrames *= 2; // stereo
+ peakOut = 0.0f; // reset peak calculator
+ peakIn = 0.0f; // reset peak calculator
+
+ /* always clean each buffer */
+
+ memset(outBuf, 0, sizeof(float) * bufferFrames); // out
+ memset(vChanInToOut, 0, sizeof(float) * bufferFrames); // inToOut vChan
+
+ pthread_mutex_lock(&mutex_chans);
+ for (unsigned i=0; i<channels.size; i++)
+ if (channels.at(i)->type == CHANNEL_SAMPLE)
+ ((SampleChannel*)channels.at(i))->clear();
+ pthread_mutex_unlock(&mutex_chans);
+
+ for (unsigned j=0; j<bufferFrames; j+=2) {
+
+ if (kernelAudio::inputEnabled) {
+
+ /* input peak calculation (left chan only so far). */
+
+ if (inBuf[j] * inVol > peakIn)
+ peakIn = inBuf[j] * inVol;
+
+ /* "hear what you're playing" - process, copy and paste the input buffer
+ * onto the output buffer */
+
+ if (inToOut) {
+ vChanInToOut[j] = inBuf[j] * inVol;
+ vChanInToOut[j+1] = inBuf[j+1] * inVol;
+ }
+ }
+
+ /* operations to do if the sequencer is running:
+ * - compute quantizer
+ * - time check for LOOP_REPEAT
+ * - reset loops at beat 0
+ * - read recorded actions
+ * - reset actualFrame */
+
+ if (running) {
+
+ /* line in recording */
+
+ if (chanInput != NULL && kernelAudio::inputEnabled) {
+
+ /* delay comp: wait until waitRec reaches delayComp. WaitRec
+ * returns to 0 in mixerHandler, as soon as the recording ends */
+
+ if (waitRec < G_Conf.delayComp)
+ waitRec += 2;
+ else {
+ vChanInput[inputTracker] += inBuf[j] * inVol;
+ vChanInput[inputTracker+1] += inBuf[j+1] * inVol;
+ inputTracker += 2;
+ if (inputTracker >= totalFrames)
+ inputTracker = 0;
+ }
+ }
+
+ /* quantizer computations: quantize rewind and all channels. */
+
+ if (quantize > 0 && quanto > 0) {
+ if (actualFrame % (quanto) == 0) { // is quanto!
+ if (rewindWait) {
+ rewindWait = false;
+ rewind();
+ }
+ pthread_mutex_lock(&mutex_chans);
+ for (unsigned k=0; k<channels.size; k++)
+ channels.at(k)->quantize(k, j, actualFrame); // j == localFrame
+ pthread_mutex_unlock(&mutex_chans);
+ }
+ }
+
+ /* reset LOOP_REPEAT, if a bar has passed */
+
+ if (actualFrame % framesPerBar == 0 && actualFrame != 0) {
+ if (metronome)
+ tickPlay = true;
+
+ pthread_mutex_lock(&mutex_chans);
+ for (unsigned k=0; k<channels.size; k++)
+ channels.at(k)->onBar(j);
+ pthread_mutex_unlock(&mutex_chans);
+ }
+
+ /* reset loops on beat 0 */
+
+ if (actualFrame == 0) {
+ pthread_mutex_lock(&mutex_chans);
+ for (unsigned k=0; k<channels.size; k++)
+ channels.at(k)->onZero(j);
+ pthread_mutex_unlock(&mutex_chans);
+ }
+
+ /* reading all actions recorded */
+
+ pthread_mutex_lock(&mutex_recs);
+ for (unsigned y=0; y<recorder::frames.size; y++) {
+ if (recorder::frames.at(y) == actualFrame) {
+ for (unsigned z=0; z<recorder::global.at(y).size; z++) {
+ int index = recorder::global.at(y).at(z)->chan;
+ Channel *ch = getChannelByIndex(index);
+ ch->parseAction(recorder::global.at(y).at(z), j, actualFrame);
+ }
+ break;
+ }
+ }
+ pthread_mutex_unlock(&mutex_recs);
+
+ /* increase actualFrame */
+
+ actualFrame += 2;
+
+ /* if actualFrame > totalFrames the sequencer returns to frame 0,
+ * beat 0. This must be the last operation. */
+
+ if (actualFrame > totalFrames) {
+ actualFrame = 0;
+ actualBeat = 0;
+ }
+ else
+ if (actualFrame % framesPerBeat == 0 && actualFrame > 0) {
+ actualBeat++;
+
+ /* avoid tick and tock to overlap when a new bar has passed (which
+ * is also a beat) */
+
+ if (metronome && !tickPlay)
+ tockPlay = true;
+ }
+
+ sendMIDIsync();
+
+ } // if (running)
+
+ /* sum channels, CHANNEL_SAMPLE only */
+
+ pthread_mutex_lock(&mutex_chans);
+ for (unsigned k=0; k<channels.size; k++) {
+ if (channels.at(k)->type == CHANNEL_SAMPLE)
+ ((SampleChannel*)channels.at(k))->sum(j, running);
+ }
+ pthread_mutex_unlock(&mutex_chans);
+
+ /* metronome play */
+ /** FIXME - move this one after the peak meter calculation */
+
+ if (tockPlay) {
+ outBuf[j] += tock[tockTracker];
+ outBuf[j+1] += tock[tockTracker];
+ tockTracker++;
+ if (tockTracker >= TICKSIZE-1) {
+ tockPlay = false;
+ tockTracker = 0;
+ }
+ }
+ if (tickPlay) {
+ outBuf[j] += tick[tickTracker];
+ outBuf[j+1] += tick[tickTracker];
+ tickTracker++;
+ if (tickTracker >= TICKSIZE-1) {
+ tickPlay = false;
+ tickTracker = 0;
+ }
+ }
+ } // end loop J
+
+
+ /* final loop: sum virtual channels and process plugins. */
+
+ pthread_mutex_lock(&mutex_chans);
+ for (unsigned k=0; k<channels.size; k++)
+ channels.at(k)->process(outBuf);
+ pthread_mutex_unlock(&mutex_chans);
+
+ /* processing fxs master in & out, if any. */
+
+#ifdef WITH_VST
+ pthread_mutex_lock(&mutex_plugins);
+ G_PluginHost.processStack(outBuf, PluginHost::MASTER_OUT);
+ G_PluginHost.processStack(vChanInToOut, PluginHost::MASTER_IN);
+ pthread_mutex_unlock(&mutex_plugins);
+#endif
+
+ /* post processing master fx + peak calculation. */
+
+ for (unsigned j=0; j<bufferFrames; j+=2) {
+
+ /* merging vChanInToOut, if enabled */
+
+ if (inToOut) {
+ outBuf[j] += vChanInToOut[j];
+ outBuf[j+1] += vChanInToOut[j+1];
+ }
+
+ outBuf[j] *= outVol;
+ outBuf[j+1] *= outVol;
+
+ /* computes the peak for the left channel (so far). */
+
+ if (outBuf[j] > peakOut)
+ peakOut = outBuf[j];
+
+ if (G_Conf.limitOutput) {
+ if (outBuf[j] > 1.0f)
+ outBuf[j] = 1.0f;
+ else if (outBuf[j] < -1.0f)
+ outBuf[j] = -1.0f;
+
+ if (outBuf[j+1] > 1.0f)
+ outBuf[j+1] = 1.0f;
+ else if (outBuf[j+1] < -1.0f)
+ outBuf[j+1] = -1.0f;
+ }
+ }
+
+ return 0;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Mixer::updateFrameBars() {
+
+ /* seconds ....... total time of play (in seconds) of the whole
+ * sequencer. 60 / bpm == how many seconds lasts one bpm
+ * totalFrames ... number of frames in the whole sequencer, x2 because
+ * it's stereo
+ * framesPerBar .. n. of frames within a bar
+ * framesPerBeat . n. of frames within a beat */
+
+ float seconds = (60.0f / bpm) * beats;
+ totalFrames = G_Conf.samplerate * seconds * 2;
+ framesPerBar = totalFrames / bars;
+ framesPerBeat = totalFrames / beats;
+ framesInSequencer = framesPerBeat * MAX_BEATS;
+
+ /* big troubles if frames are odd. */
+
+ if (totalFrames % 2 != 0)
+ totalFrames--;
+ if (framesPerBar % 2 != 0)
+ framesPerBar--;
+ if (framesPerBeat % 2 != 0)
+ framesPerBeat--;
+
+ updateQuanto();
+
+ /* realloc input virtual channel, if not NULL. TotalFrames is changed! */
+
+ if (vChanInput != NULL)
+ free(vChanInput);
+ vChanInput = (float*) malloc(totalFrames * sizeof(float));
+ if (!vChanInput)
+ gLog("[Mixer] vChanInput realloc error!\n");
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Mixer::close() {
+ running = false;
+ while (channels.size > 0)
+ deleteChannel(channels.at(0));
+ free(vChanInput);
+ free(vChanInToOut);
+ return 1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool Mixer::isSilent() {
+ for (unsigned i=0; i<channels.size; i++)
+ if (channels.at(i)->status == STATUS_PLAY)
+ return false;
+ return true;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Mixer::rewind() {
+
+ actualFrame = 0;
+ actualBeat = 0;
+
+ if (running)
+ for (unsigned i=0; i<channels.size; i++)
+ channels.at(i)->rewind();
+
+ sendMIDIrewind();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Mixer::updateQuanto() {
+
+ /* big troubles if frames are odd. */
+
+ if (quantize != 0)
+ quanto = framesPerBeat / quantize;
+ if (quanto % 2 != 0)
+ quanto++;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool Mixer::hasLogicalSamples() {
+ for (unsigned i=0; i<channels.size; i++)
+ if (channels.at(i)->type == CHANNEL_SAMPLE)
+ if (((SampleChannel*)channels.at(i))->wave)
+ if (((SampleChannel*)channels.at(i))->wave->isLogical)
+ return true;
+ return false;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool Mixer::hasEditedSamples() {
+ for (unsigned i=0; i<channels.size; i++)
+ if (channels.at(i)->type == CHANNEL_SAMPLE)
+ if (((SampleChannel*)channels.at(i))->wave)
+ if (((SampleChannel*)channels.at(i))->wave->isEdited)
+ return true;
+ return false;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool Mixer::mergeVirtualInput() {
+ if (vChanInput == NULL) {
+ gLog("[Mixer] virtual input channel not alloc'd\n");
+ return false;
+ }
+ else {
+#ifdef WITH_VST
+ G_PluginHost.processStackOffline(vChanInput, PluginHost::MASTER_IN, 0, totalFrames);
+#endif
+ int numFrames = totalFrames*sizeof(float);
+ memcpy(chanInput->wave->data, vChanInput, numFrames);
+ memset(vChanInput, 0, numFrames); // clear vchan
+ return true;
+ }
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * mixer
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef MIXER_H
+#define MIXER_H
+
+#include <stdlib.h>
+#include <pthread.h>
+#include "const.h"
+#include "kernelAudio.h"
+#include "utils.h"
+
+
+class Mixer {
+
+public:
+
+ Mixer();
+ ~Mixer();
+
+ void init();
+ int close();
+
+ /* addChannel
+ * add a new channel without any wave inside of it. */
+
+ class Channel *addChannel(int type);
+
+ /* deleteChannel
+ * completely remove a channel from the stack. */
+
+ int deleteChannel(class Channel *ch);
+
+ /* masterPlay
+ * core method (callback) */
+
+ static int masterPlay(
+ void *out_buf, void *in_buf, unsigned n_frames,
+ double streamTime, RtAudioStreamStatus status, void *userData
+ );
+ int __masterPlay(void *out_buf, void *in_buf, unsigned n_frames);
+
+ /* updateFrameBars
+ * updates bpm, frames, beats and so on. */
+
+ void updateFrameBars();
+
+ /* isSilent
+ * is mixer silent? */
+
+ bool isSilent();
+
+ /* rewind
+ * rewind sequencer to sample 0. */
+
+ void rewind();
+
+ /* updateQuanto
+ * recomputes the quanto between two quantizations */
+
+ void updateQuanto();
+
+ /* hasLogicalSamples
+ * true if 1 or more samples are logical (memory only, such as takes) */
+
+ bool hasLogicalSamples();
+
+ /* hasEditedSamples
+ * true if 1 or more samples was edited via gEditor */
+
+ bool hasEditedSamples();
+
+ /* mergeVirtualInput
+ * memcpy the virtual channel input in the channel designed for input
+ * recording. Called by mixerHandler on stopInputRec() */
+
+ bool mergeVirtualInput();
+
+ /* getChannelByIndex
+ * return channel with given index 'i'. */
+
+ Channel *getChannelByIndex(int i);
+
+ inline Channel* getLastChannel() { return channels.at(channels.size-1); }
+
+
+ /* ---------------------------------------------------------------- */
+
+
+ enum { // const - what to do when a fadeout ends
+ DO_STOP = 0x01,
+ DO_MUTE = 0x02,
+ DO_MUTE_I = 0x04
+ };
+
+ enum { // const - fade types
+ FADEOUT = 0x01,
+ XFADE = 0x02
+ };
+
+ gVector<class Channel*> channels;
+
+ bool running;
+ bool ready;
+ float *vChanInput; // virtual channel for recording
+ float *vChanInToOut; // virtual channel in->out bridge (hear what you're playin)
+ int frameSize;
+ float outVol;
+ float inVol;
+ float peakOut;
+ float peakIn;
+ int quanto;
+ char quantize;
+ bool metronome;
+ float bpm;
+ int bars;
+ int beats;
+ int waitRec; // delayComp guard
+
+ bool docross; // crossfade guard
+ bool rewindWait; // rewind guard, if quantized
+
+ int framesPerBar; // frames in one bar
+ int framesPerBeat; // frames in one beat
+ int framesInSequencer; // frames in the whole sequencer
+ int totalFrames; // frames in the selected range (e.g. 4/4)
+ int actualFrame;
+ int actualBeat;
+
+#define TICKSIZE 38
+ static float tock[TICKSIZE];
+ static float tick[TICKSIZE];
+ int tickTracker, tockTracker;
+ bool tickPlay, tockPlay; // 1 = play, 0 = stop
+
+ /* chanInput
+ * the active channel during a recording. NULL = no channels active */
+
+ class SampleChannel *chanInput;
+
+ /* inputTracker
+ * position of the sample in the input side (recording) */
+
+ int inputTracker;
+
+ /* inToOut
+ * copy, process and paste the input into the output, in order to
+ * obtain a "hear what you're playing" feature. */
+
+ bool inToOut;
+
+ pthread_mutex_t mutex_recs;
+ pthread_mutex_t mutex_chans;
+ pthread_mutex_t mutex_plugins;
+
+
+private:
+
+ int midiTCstep; // part of MTC to send (0 to 7)
+ int midiTCrate; // send MTC data every midiTCrate frames
+ int midiTCframes;
+ int midiTCseconds;
+ int midiTCminutes;
+ int midiTChours;
+
+ /* getNewIndex
+ * compute new index value for new channels */
+
+ int getNewIndex();
+
+ /* sendMIDIsync
+ * generate MIDI sync output data */
+
+ void sendMIDIsync();
+
+ /* sendMIDIrewind
+ * rewind timecode to beat 0 and also send a MTC full frame to cue
+ * the slave */
+
+ void sendMIDIrewind();
+};
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * mixerHandler
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#if defined(__linux__)
+ #include <jack/jack.h>
+ #include <jack/intclient.h>
+ #include <jack/transport.h>
+#endif
+
+#include "mixerHandler.h"
+#include "kernelMidi.h"
+#include "mixer.h"
+#include "const.h"
+#include "utils.h"
+#include "init.h"
+#include "pluginHost.h"
+#include "plugin.h"
+#include "waveFx.h"
+#include "glue.h"
+#include "conf.h"
+#include "patch.h"
+#include "recorder.h"
+#include "channel.h"
+#include "sampleChannel.h"
+#include "wave.h"
+#include "log.h"
+
+
+extern Mixer G_Mixer;
+extern Patch G_Patch;
+extern Conf G_Conf;
+
+#ifdef WITH_VST
+extern PluginHost G_PluginHost;
+#endif
+
+
+void mh_stopSequencer()
+{
+ G_Mixer.running = false;
+ for (unsigned i=0; i<G_Mixer.channels.size; i++)
+ G_Mixer.channels.at(i)->stopBySeq();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void mh_clear()
+{
+ G_Mixer.running = false;
+ while (G_Mixer.channels.size > 0)
+ G_Mixer.channels.del(0U); // unsigned
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool mh_uniqueSolo(Channel *ch)
+{
+ int solos = 0;
+ for (unsigned i=0; i<G_Mixer.channels.size; i++) {
+ Channel *ch = G_Mixer.channels.at(i);
+ if (ch->solo) solos++;
+ if (solos > 1) return false;
+ }
+ return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+/** TODO - revision needed: mh should not call glue_addChannel */
+
+void mh_loadPatch(bool isProject, const char *projPath)
+{
+ G_Mixer.init();
+ G_Mixer.ready = false; // put it in wait mode
+
+ int numChans = G_Patch.getNumChans();
+ for (int i=0; i<numChans; i++) {
+
+ Channel *ch = glue_addChannel(G_Patch.getColumn(i), G_Patch.getType(i));
+
+ char smpPath[PATH_MAX];
+
+ /* projects < 0.6.3 version are not portable. Just use the regular
+ * samplePath */
+ /* TODO version >= 0.10.0 - old stuff, remove backward compatibility */
+
+ if (isProject && G_Patch.version >= 0.63f)
+ sprintf(smpPath, "%s%s%s", gDirname(projPath).c_str(), gGetSlash().c_str(), G_Patch.getSamplePath(i).c_str());
+ else
+ sprintf(smpPath, "%s", G_Patch.getSamplePath(i).c_str());
+
+ ch->loadByPatch(smpPath, i);
+ }
+
+ G_Mixer.outVol = G_Patch.getOutVol();
+ G_Mixer.inVol = G_Patch.getInVol();
+ G_Mixer.bpm = G_Patch.getBpm();
+ G_Mixer.bars = G_Patch.getBars();
+ G_Mixer.beats = G_Patch.getBeats();
+ G_Mixer.quantize = G_Patch.getQuantize();
+ G_Mixer.metronome = G_Patch.getMetronome();
+ G_Patch.lastTakeId = G_Patch.getLastTakeId();
+ G_Patch.samplerate = G_Patch.getSamplerate();
+
+ /* rewind and update frames in Mixer (it's vital) */
+
+ G_Mixer.rewind();
+ G_Mixer.updateFrameBars();
+ G_Mixer.ready = true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void mh_rewindSequencer()
+{
+ if (G_Mixer.quantize > 0 && G_Mixer.running) // quantize rewind
+ G_Mixer.rewindWait = true;
+ else
+ G_Mixer.rewind();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+SampleChannel *mh_startInputRec()
+{
+ /* search for the next available channel */
+
+ SampleChannel *chan = NULL;
+ for (unsigned i=0; i<G_Mixer.channels.size; i++) {
+ if (G_Mixer.channels.at(i)->type == CHANNEL_SAMPLE)
+ if (((SampleChannel*) G_Mixer.channels.at(i))->canInputRec()) {
+ chan = (SampleChannel*) G_Mixer.channels.at(i);
+ break;
+ }
+ }
+
+ /* no chans available? */
+
+ if (chan == NULL)
+ return NULL;
+
+ Wave *w = new Wave();
+ if (!w->allocEmpty(G_Mixer.totalFrames))
+ return NULL;
+
+ /* increase lastTakeId until the sample name TAKE-[n] is unique */
+
+ char name[32];
+ sprintf(name, "TAKE-%d", G_Patch.lastTakeId);
+ while (!mh_uniqueSamplename(chan, name)) {
+ G_Patch.lastTakeId++;
+ sprintf(name, "TAKE-%d", G_Patch.lastTakeId);
+ }
+
+ chan->allocEmpty(G_Mixer.totalFrames, G_Patch.lastTakeId);
+ G_Mixer.chanInput = chan;
+
+ /* start to write from the actualFrame, not the beginning */
+ /** FIXME: move this before wave allocation*/
+
+ G_Mixer.inputTracker = G_Mixer.actualFrame;
+
+ gLog(
+ "[mh] start input recs using chan %d with size %d, frame=%d\n",
+ chan->index, G_Mixer.totalFrames, G_Mixer.inputTracker
+ );
+
+ return chan;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+SampleChannel *mh_stopInputRec()
+{
+ gLog("[mh] stop input recs\n");
+ G_Mixer.mergeVirtualInput();
+ SampleChannel *ch = G_Mixer.chanInput;
+ G_Mixer.chanInput = NULL;
+ G_Mixer.waitRec = 0; // if delay compensation is in use
+ return ch;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool mh_uniqueSamplename(SampleChannel *ch, const char *name)
+{
+ for (unsigned i=0; i<G_Mixer.channels.size; i++) {
+ if (ch != G_Mixer.channels.at(i)) {
+ if (G_Mixer.channels.at(i)->type == CHANNEL_SAMPLE) {
+ SampleChannel *other = (SampleChannel*) G_Mixer.channels.at(i);
+ if (other->wave != NULL)
+ if (!strcmp(name, other->wave->name.c_str()))
+ return false;
+ }
+ }
+ }
+ return true;
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * mixerHandler
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef MIXERHANDLER_H
+#define MIXERHANDLER_H
+
+
+#include "recorder.h"
+
+
+/* stopSequencer
+ * stop the sequencer, with special case if samplesStopOnSeqHalt is
+ * true. */
+
+void mh_stopSequencer();
+
+void mh_rewindSequencer();
+
+/* clear
+ * stop everything and clear all channels. */
+
+void mh_clear();
+
+/* uniqueSolo
+ * true if ch is the only solo'd channel in mixer. */
+
+bool mh_uniqueSolo(class Channel *ch);
+
+/* loadPatch
+ * load a path or a project (if isProject) into Mixer. If isProject, path
+ * must contain the address of the project folder. */
+
+void mh_loadPatch(bool isProject, const char *projPath=0);
+
+/* startInputRec - record from line in
+ * creates a new empty wave in the first available channels and returns
+ * the chan number chosen, otherwise -1 if there are no more empty
+ * channels available. */
+
+SampleChannel *mh_startInputRec();
+
+SampleChannel *mh_stopInputRec();
+
+/* uniqueSamplename
+ * return true if samplename 'n' is unique. Requires SampleChannel *ch
+ * in order to skip check against itself. */
+
+bool mh_uniqueSamplename(class SampleChannel *ch, const char *name);
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * patch
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <stdint.h>
+#include "patch.h"
+#include "init.h"
+#include "recorder.h"
+#include "utils.h"
+#include "conf.h"
+#include "pluginHost.h"
+#include "wave.h"
+#include "mixer.h"
+#include "channel.h"
+#include "log.h"
+#include "gd_mainWindow.h"
+#include "gg_keyboard.h"
+
+
+extern Mixer G_Mixer;
+extern Conf G_Conf;
+#ifdef WITH_VST
+extern PluginHost G_PluginHost;
+#endif
+extern gdMainWindow *mainWin;
+
+
+int Patch::open(const char *file)
+{
+ fp = fopen(file, "r");
+ if (fp == NULL)
+ return PATCH_UNREADABLE;
+
+ if (getValue("header") != "GIADAPTC")
+ return PATCH_INVALID;
+
+ version = atof(getValue("versionf").c_str());
+ gLog("[PATCH] open patch version %f\n", version);
+ if (version == 0.0)
+ gLog("[PATCH] patch < 0.6.1, backward compatibility mode\n");
+
+ return PATCH_OPEN_OK;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Patch::setDefault()
+{
+ name[0] = '\0';
+ lastTakeId = 0;
+ samplerate = DEFAULT_SAMPLERATE;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch::close()
+{
+ return fclose(fp);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Patch::getName()
+{
+ std::string out = getValue("patchname");
+ strncpy(name, out.c_str(), MAX_PATCHNAME_LEN);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::string Patch::getSamplePath(int c)
+{
+ char tmp[16];
+ sprintf(tmp, "samplepath%d", c);
+ return getValue(tmp);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float Patch::getPitch(int c)
+{
+ char tmp[16];
+ sprintf(tmp, "chanPitch%d", c);
+ float out = atof(getValue(tmp).c_str());
+ if (out > 2.0f || out < 0.1f)
+ return 1.0f;
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch::getNumChans()
+{
+ if (version == 0.0) // backward compatibility with version < 0.6.1
+ return 32;
+ return atoi(getValue("channels").c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch::getNumColumns()
+{
+ return atoi(getValue("columns").c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch::getColumn(int c)
+{
+ if (version == 0.0) // backward compatibility with version < 0.6.1
+ return 0;
+ char tmp[16];
+ sprintf(tmp, "chanColumn%d", c);
+ return atoi(getValue(tmp).c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch::getIndex(int c)
+{
+ if (version == 0.0) // backward compatibility with version < 0.6.1
+ return c;
+
+ char tmp[16];
+ sprintf(tmp, "chanIndex%d", c);
+ return atoi(getValue(tmp).c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float Patch::getVol(int c)
+{
+ char tmp[16];
+ sprintf(tmp, "chanvol%d", c);
+ float out = atof(getValue(tmp).c_str());
+ if (out > 1.0f || out < 0.0f)
+ return DEFAULT_VOL;
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch::getMode(int c)
+{
+ char tmp[16];
+ sprintf(tmp, "chanmode%d", c);
+ int out = atoi(getValue(tmp).c_str());
+ if (out & (LOOP_ANY | SINGLE_ANY))
+ return out;
+ return DEFAULT_CHANMODE;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch::getMute(int c)
+{
+ char tmp[16];
+ sprintf(tmp, "chanMute%d", c);
+ return atoi(getValue(tmp).c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch::getMute_s(int c)
+{
+ char tmp[16];
+ sprintf(tmp, "chanMute_s%d", c);
+ return atoi(getValue(tmp).c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch::getSolo(int c)
+{
+ char tmp[16];
+ sprintf(tmp, "chanSolo%d", c);
+ return atoi(getValue(tmp).c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch::getType(int c)
+{
+ char tmp[16];
+ sprintf(tmp, "chanType%d", c);
+ int out = atoi(getValue(tmp).c_str());
+ if (out == 0)
+ return CHANNEL_SAMPLE;
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch::getBegin(int c)
+{
+ char tmp[16];
+ if (version < 0.73f)
+ sprintf(tmp, "chanstart%d", c);
+ else
+ sprintf(tmp, "chanBegin%d", c);
+ int out = atoi(getValue(tmp).c_str());
+ if (out < 0)
+ return 0;
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch::getEnd(int c, unsigned size)
+{
+ char tmp[16];
+ sprintf(tmp, "chanend%d", c);
+
+ /* if chanEnd doesn't exist, it returns an atoi(empty string) == 0.
+ * good in theory, a disaster in practice. */
+
+ std::string val = getValue(tmp);
+ if (val == "")
+ return size;
+
+ unsigned out = atoi(val.c_str());
+ if (out <= 0 || out > size)
+ return size;
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float Patch::getBoost(int c)
+{
+ char tmp[16];
+ sprintf(tmp, "chanBoost%d", c);
+ float out = atof(getValue(tmp).c_str());
+ if (out < 1.0f)
+ return DEFAULT_BOOST;
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float Patch::getPanLeft(int c)
+{
+ char tmp[16];
+ sprintf(tmp, "chanPanLeft%d", c);
+ std::string val = getValue(tmp);
+ if (val == "")
+ return 1.0f;
+
+ float out = atof(val.c_str());
+ if (out < 0.0f || out > 1.0f)
+ return 1.0f;
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch::getKey(int c)
+{
+ if (version == 0.0) // backward compatibility with version < 0.6.1
+ return 0;
+ char tmp[16];
+ sprintf(tmp, "chanKey%d", c);
+ return atoi(getValue(tmp).c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float Patch::getPanRight(int c)
+{
+ char tmp[16];
+ sprintf(tmp, "chanPanRight%d", c);
+ std::string val = getValue(tmp);
+ if (val == "")
+ return 1.0f;
+
+ float out = atof(val.c_str());
+ if (out < 0.0f || out > 1.0f)
+ return 1.0f;
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool Patch::getRecActive(int c)
+{
+ char tmp[16];
+ sprintf(tmp, "chanRecActive%d", c);
+ return atoi(getValue(tmp).c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float Patch::getOutVol()
+{
+ return atof(getValue("outVol").c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float Patch::getInVol()
+{
+ return atof(getValue("inVol").c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float Patch::getBpm()
+{
+ float out = atof(getValue("bpm").c_str());
+ if (out < 20.0f || out > 999.0f)
+ return DEFAULT_BPM;
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch::getBars()
+{
+ int out = atoi(getValue("bars").c_str());
+ if (out <= 0 || out > 32)
+ return DEFAULT_BARS;
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch::getBeats()
+{
+ int out = atoi(getValue("beats").c_str());
+ if (out <= 0 || out > 32)
+ return DEFAULT_BEATS;
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch::getQuantize()
+{
+ int out = atoi(getValue("quantize").c_str());
+ if (out < 0 || out > 8)
+ return DEFAULT_QUANTIZE;
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool Patch::getMetronome()
+{
+ bool out = atoi(getValue("metronome").c_str());
+ if (out != true || out != false)
+ return false;
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch::getLastTakeId()
+{
+ return atoi(getValue("lastTakeId").c_str());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch::getSamplerate()
+{
+ int out = atoi(getValue("samplerate").c_str());
+ if (out <= 0)
+ return DEFAULT_SAMPLERATE;
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+uint32_t Patch::getMidiValue(int i, const char *c)
+{
+ char tmp[32];
+ sprintf(tmp, "chanMidi%s%d", c, i);
+ return strtoul(getValue(tmp).c_str(), NULL, 10);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch::readRecs()
+{
+ gLog("[PATCH] Reading recs...\n");
+
+ unsigned numrecs = atoi(getValue("numrecs").c_str());
+
+ for (unsigned i=0; i<numrecs; i++) {
+ int frame, recPerFrame;
+
+ /* parsing 'dddddd d': [framenumber] [num. recs for that frame] */
+
+ char tmpbuf[16];
+ sprintf(tmpbuf, "recframe%d", i);
+ sscanf(getValue(tmpbuf).c_str(), "%d %d", &frame, &recPerFrame);
+
+//gLog("processing frame=%d, recPerFrame=%d\n", frame, recPerFrame);
+
+ for (int k=0; k<recPerFrame; k++) {
+ int chan = 0;
+ int type = 0;
+ float fValue = 0.0f;
+ int iValue_fix = 0;
+ uint32_t iValue = 0;
+
+ /* reading info for each frame: %d|%d */
+
+ char tmpbuf[16];
+ sprintf(tmpbuf, "f%da%d", i, k);
+
+ if (version < 0.61f) // no float and int values
+ sscanf(getValue(tmpbuf).c_str(), "%d|%d", &chan, &type);
+ else
+ if (version < 0.83f) // iValues were stored as signed int (wrong)
+ sscanf(getValue(tmpbuf).c_str(), "%d|%d|%f|%d", &chan, &type, &fValue, &iValue_fix);
+ else
+ sscanf(getValue(tmpbuf).c_str(), "%d|%d|%f|%u", &chan, &type, &fValue, &iValue);
+
+//gLog(" loading chan=%d, type=%d, fValue=%f, iValue=%u\n", chan, type, fValue, iValue);
+
+ Channel *ch = G_Mixer.getChannelByIndex(chan);
+ if (ch)
+ if (ch->status & ~(STATUS_WRONG | STATUS_MISSING | STATUS_EMPTY)) {
+ if (version < 0.83f)
+ recorder::rec(ch->index, type, frame, iValue_fix, fValue);
+ else
+ recorder::rec(ch->index, type, frame, iValue, fValue);
+ }
+ }
+ }
+ return 1;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+int Patch::readPlugins()
+{
+ gLog("[PATCH] Reading plugins...\n");
+
+ int globalOut = 1;
+
+ /* master plugins */
+
+ globalOut &= readMasterPlugins(PluginHost::MASTER_IN);
+ globalOut &= readMasterPlugins(PluginHost::MASTER_OUT);
+
+ /* channel plugins */
+
+ for (unsigned i=0; i<G_Mixer.channels.size; i++) {
+ Channel *ch = G_Mixer.channels.at(i);
+
+ char tmp[MAX_LINE_LEN];
+ sprintf(tmp, "chan%dPlugins", ch->index);
+ int np = atoi(getValue(tmp).c_str());
+
+ for (int j=0; j<np; j++) {
+ sprintf(tmp, "chan%d_p%dpathfile", ch->index, j);
+ int out = G_PluginHost.addPlugin(getValue(tmp).c_str(), PluginHost::CHANNEL, ch);
+ if (out != 0) {
+ sprintf(tmp, "chan%d_p%dnumParams", ch->index, j);
+ int nparam = atoi(getValue(tmp).c_str());
+ Plugin *pPlugin = G_PluginHost.getPluginByIndex(j, PluginHost::CHANNEL, ch);
+ sprintf(tmp, "chan%d_p%dbypass", ch->index, j);
+ pPlugin->bypass = atoi(getValue(tmp).c_str());
+ for (int k=0; k<nparam; k++) {
+ sprintf(tmp, "chan%d_p%dparam%dvalue", ch->index, j, k);
+ float pval = atof(getValue(tmp).c_str());
+ pPlugin->setParam(k, pval);
+ }
+ }
+ globalOut &= out;
+ }
+ }
+ return globalOut;
+}
+#endif
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Patch::write(const char *file, const char *name, bool project)
+{
+ fp = fopen(file, "w");
+ if (fp == NULL)
+ return 0;
+
+ fprintf(fp, "# --- Giada patch file --- \n");
+ fprintf(fp, "header=GIADAPTC\n");
+ fprintf(fp, "version=%s\n", VERSIONE);
+ fprintf(fp, "versionf=%f\n", VERSIONE_FLOAT);
+ fprintf(fp, "patchname=%s\n", name);
+ fprintf(fp, "bpm=%f\n", G_Mixer.bpm);
+ fprintf(fp, "bars=%d\n", G_Mixer.bars);
+ fprintf(fp, "beats=%d\n", G_Mixer.beats);
+ fprintf(fp, "quantize=%d\n", G_Mixer.quantize);
+ fprintf(fp, "outVol=%f\n", G_Mixer.outVol);
+ fprintf(fp, "inVol=%f\n", G_Mixer.inVol);
+ fprintf(fp, "metronome=%d\n", G_Mixer.metronome);
+ fprintf(fp, "lastTakeId=%d\n", lastTakeId);
+ fprintf(fp, "samplerate=%d\n", G_Conf.samplerate); // original samplerate when the patch was saved
+ fprintf(fp, "channels=%d\n", G_Mixer.channels.size);
+ fprintf(fp, "columns=%d\n", mainWin->keyboard->getTotalColumns());
+
+ for (unsigned i=0; i<G_Mixer.channels.size; i++) {
+ fprintf(fp, "# --- channel %d --- \n", i);
+ G_Mixer.channels.at(i)->writePatch(fp, i, project);
+ }
+
+ /* writing recs. Warning: channel index is not mixer.channels.at(chan),
+ * but mixer.channels.at(chan)->index! */
+
+ fprintf(fp, "# --- actions --- \n");
+ fprintf(fp, "numrecs=%d\n", recorder::global.size);
+ for (unsigned i=0; i<recorder::global.size; i++) {
+ fprintf(fp, "recframe%d=%d %d\n", i, recorder::frames.at(i), recorder::global.at(i).size);
+ for (unsigned k=0; k<recorder::global.at(i).size; k++) {
+ fprintf(fp, "f%da%d=%d|%d|%f|%u\n",
+ i, k,
+ recorder::global.at(i).at(k)->chan,
+ recorder::global.at(i).at(k)->type,
+ recorder::global.at(i).at(k)->fValue,
+ recorder::global.at(i).at(k)->iValue);
+ }
+ }
+
+#ifdef WITH_VST
+
+ /* writing master VST parameters */
+
+ writeMasterPlugins(PluginHost::MASTER_IN);
+ writeMasterPlugins(PluginHost::MASTER_OUT);
+
+ /* writing VST parameters, channels. chan%d is mixer::channels.at(%d)->index,
+ * not mixer::chanels.at(%d)! */
+
+ int numPlugs;
+ int numParams;
+ Plugin *pPlugin;
+
+ fprintf(fp, "# --- VST / channels --- \n");
+ for (unsigned i=0; i<G_Mixer.channels.size; i++) {
+ Channel *ch = G_Mixer.channels.at(i);
+ numPlugs = G_PluginHost.countPlugins(PluginHost::CHANNEL, ch);
+ fprintf(fp, "chan%dPlugins=%d\n", ch->index, numPlugs);
+
+ for (int j=0; j<numPlugs; j++) {
+ pPlugin = G_PluginHost.getPluginByIndex(j, PluginHost::CHANNEL, ch);
+ if (!pPlugin->status) {
+ gLog("[PATCH] Plugin %d is in a bad status, skip writing params\n", i);
+ continue;
+ }
+ fprintf(fp, "chan%d_p%dpathfile=%s\n", ch->index, j, pPlugin->pathfile);
+ fprintf(fp, "chan%d_p%dbypass=%d\n", ch->index, j, pPlugin->bypass);
+ numParams = pPlugin->getNumParams();
+ fprintf(fp, "chan%d_p%dnumParams=%d\n", ch->index, j, numParams);
+
+ for (int k=0; k<numParams; k++)
+ fprintf(fp, "chan%d_p%dparam%dvalue=%f\n", ch->index, j, k, pPlugin->getParam(k));
+ }
+ }
+
+#endif
+
+ fclose(fp);
+ return 1;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+#ifdef WITH_VST
+
+int Patch::readMasterPlugins(int type)
+{
+ int nmp;
+ char chr;
+ int res = 1;
+
+ if (type == PluginHost::MASTER_IN) {
+ chr = 'I';
+ nmp = atoi(getValue("masterIPlugins").c_str());
+ }
+ else {
+ chr = 'O';
+ nmp = atoi(getValue("masterOPlugins").c_str());
+ }
+
+ for (int i=0; i<nmp; i++) {
+ char tmp[MAX_LINE_LEN];
+ sprintf(tmp, "master%c_p%dpathfile", chr, i);
+ int out = G_PluginHost.addPlugin(getValue(tmp).c_str(), type);
+ if (out != 0) {
+ Plugin *pPlugin = G_PluginHost.getPluginByIndex(i, type);
+ sprintf(tmp, "master%c_p%dbypass", chr, i);
+ pPlugin->bypass = atoi(getValue(tmp).c_str());
+ sprintf(tmp, "master%c_p%dnumParams", chr, i);
+ int nparam = atoi(getValue(tmp).c_str());
+ for (int j=0; j<nparam; j++) {
+ sprintf(tmp, "master%c_p%dparam%dvalue", chr, i, j);
+ float pval = atof(getValue(tmp).c_str());
+ pPlugin->setParam(j, pval);
+ }
+ }
+ res &= out;
+ }
+
+ return res;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Patch::writeMasterPlugins(int type)
+{
+ char chr;
+
+ if (type == PluginHost::MASTER_IN) {
+ fprintf(fp, "# --- VST / master in --- \n");
+ chr = 'I';
+ }
+ else {
+ fprintf(fp, "# --- VST / master out --- \n");
+ chr = 'O';
+ }
+
+ int nmp = G_PluginHost.countPlugins(type);
+ fprintf(fp, "master%cPlugins=%d\n", chr, nmp);
+
+ for (int i=0; i<nmp; i++) {
+
+ Plugin *pPlugin = G_PluginHost.getPluginByIndex(i, type);
+ if (!pPlugin->status) {
+ gLog("[PATCH] Plugin %d is in a bad status, skip writing params\n", i);
+ continue;
+ }
+
+ fprintf(fp, "master%c_p%dpathfile=%s\n", chr, i, pPlugin->pathfile);
+ fprintf(fp, "master%c_p%dbypass=%d\n", chr, i, pPlugin->bypass);
+ int numParams = pPlugin->getNumParams();
+ fprintf(fp, "master%c_p%dnumParams=%d\n", chr, i, numParams);
+
+ for (int j=0; j<numParams; j++)
+ fprintf(fp, "master%c_p%dparam%dvalue=%f\n", chr, i, j, pPlugin->getParam(j));
+ }
+}
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * patch
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef __PATCH_H__
+#define __PATCH_H__
+
+#include <stdio.h>
+#include <string>
+#include <stdint.h>
+#include "dataStorage.h"
+#include "const.h"
+
+
+class Patch : public DataStorage {
+
+private:
+ int readMasterPlugins(int type);
+ void writeMasterPlugins(int type);
+
+public:
+
+ char name[MAX_PATCHNAME_LEN];
+ float version;
+ int lastTakeId;
+ int samplerate;
+
+ int open(const char *file);
+ void setDefault();
+ int close();
+
+ void getName ();
+ int getNumChans ();
+ int getNumColumns ();
+ std::string getSamplePath (int i);
+ float getVol (int i);
+ int getMode (int i);
+ int getMute (int i);
+ int getMute_s (int i);
+ int getSolo (int i);
+ int getBegin (int i);
+ int getEnd (int i, unsigned sampleSize);
+ float getBoost (int i);
+ float getPanLeft (int i);
+ float getPanRight (int i);
+ float getPitch (int i);
+ bool getRecActive (int i);
+ int getColumn (int i);
+ int getIndex (int i);
+ int getType (int i);
+ int getKey (int i);
+ uint32_t getMidiValue (int i, const char *c);
+ float getOutVol ();
+ float getInVol ();
+ float getBpm ();
+ int getBars ();
+ int getBeats ();
+ int getQuantize ();
+ bool getMetronome ();
+ int getLastTakeId ();
+ int getSamplerate ();
+
+ int write(const char *file, const char *name, bool isProject);
+ int readRecs();
+#ifdef WITH_VST
+ int readPlugins();
+#endif
+};
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifdef WITH_VST
+
+
+#include "plugin.h"
+#include "log.h"
+
+
+int Plugin::id_generator = 0;
+
+
+/* ------------------------------------------------------------------ */
+
+
+Plugin::Plugin()
+ : module (NULL),
+ entryPoint(NULL),
+ plugin (NULL),
+ id (id_generator++),
+ program (-1),
+ bypass (false),
+ suspended (false)
+{}
+
+
+/* ------------------------------------------------------------------ */
+
+
+Plugin::~Plugin() {
+ unload();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Plugin::unload() {
+
+ if (module == NULL)
+ return 1;
+
+#if defined(_WIN32)
+
+ FreeLibrary((HMODULE)module); // FIXME - error checking
+ return 1;
+
+#elif defined(__linux__)
+
+ return dlclose(module) == 0 ? 1 : 0;
+
+#elif defined(__APPLE__)
+
+ /* we must unload bundles but because bundles may be in use for other
+ plug-in types it is important (and mandatory on certain plug-ins,
+ e.g. Korg) to do a check on the retain count. */
+
+ CFIndex retainCount = CFGetRetainCount(module);
+
+ if (retainCount == 1) {
+ gLog("[plugin] retainCount == 1, can unload dlyb\n");
+ CFBundleUnloadExecutable(module);
+ CFRelease(module);
+ }
+ else
+ gLog("[plugin] retainCount > 1 (%d), leave dlyb alone\n", (int) retainCount);
+
+ return 1;
+
+#endif
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Plugin::load(const char *fname) {
+
+ strcpy(pathfile, fname);
+
+#if defined(_WIN32)
+
+ module = LoadLibrary(pathfile);
+
+#elif defined(__linux__)
+
+ module = dlopen(pathfile, RTLD_LAZY);
+
+#elif defined(__APPLE__)
+
+ /* creates the path to the bundle. In OSX vsts are stored inside the
+ * so-called bundles, just a directory with '.vst' extension. Finally
+ * we open the bundle with CFBundleCreate. */
+
+ CFStringRef pathStr = CFStringCreateWithCString(NULL, pathfile, kCFStringEncodingASCII);
+ CFURLRef bundleUrl = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, pathStr, kCFURLPOSIXPathStyle, true);
+ if(bundleUrl == NULL) {
+ gLog("[plugin] unable to create URL reference for plugin\n");
+ status = 0;
+ return 0;
+ }
+ module = CFBundleCreate(kCFAllocatorDefault, bundleUrl);
+
+#endif
+
+ if (module) {
+
+ /* release (free) any old string */
+
+#ifdef __APPLE__
+ CFRelease(pathStr);
+ CFRelease(bundleUrl);
+#endif
+ //strcpy(pathfile, fname); ???????????
+ status = 1;
+ return 1;
+ }
+ else {
+
+#if defined(_WIN32)
+
+ gLog("[plugin] unable to load %s, error: %d\n", fname, (int) GetLastError());
+
+#elif defined(__linux__)
+
+ gLog("[plugin] unable to load %s, error: %s\n", fname, dlerror());
+
+#elif defined(__APPLE__)
+
+ gLog("[plugin] unable to create bundle reference\n");
+ CFRelease(pathStr);
+ CFRelease(bundleUrl);
+
+#endif
+ status = 0;
+ return 0;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Plugin::init(VstIntPtr VSTCALLBACK (*HostCallback) (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt)) {
+
+#if defined(_WIN32)
+
+ entryPoint = (vstPluginFuncPtr) GetProcAddress((HMODULE)module, "VSTPluginMain");
+ if (!entryPoint)
+ entryPoint = (vstPluginFuncPtr) GetProcAddress((HMODULE)module, "main");
+
+#elif defined(__linux__)
+
+ /* bad stuff here: main() is a function pointer, dlsym(module, "main")
+ * returns a pointer to an object (void*) which should be casted to
+ * a pointer to function (main(), precisely). Unfortunately the standard
+ * forbids the conversion from void* to function pointer. So we do a raw
+ * mem copy from tmp to entryPoint. */
+
+ void *tmp;
+ tmp = dlsym(module, "VSTPluginMain");
+ if (!tmp)
+ tmp = dlsym(module, "main");
+ memcpy(&entryPoint, &tmp, sizeof(tmp));
+
+#elif defined(__APPLE__)
+
+ /* same also for Unix/OSX. */
+
+ void *tmp = NULL;
+ tmp = CFBundleGetFunctionPointerForName(module, CFSTR("VSTPluginMain"));
+
+ if (!tmp) {
+ gLog("[plugin] entryPoint 'VSTPluginMain' not found\n");
+ tmp = CFBundleGetFunctionPointerForName(module, CFSTR("main_macho")); // VST SDK < 2.4
+ }
+ if (!tmp) {
+ gLog("[plugin] entryPoint 'main_macho' not found\n");
+ tmp = CFBundleGetFunctionPointerForName(module, CFSTR("main"));
+ }
+ if (tmp)
+ memcpy(&entryPoint, &tmp, sizeof(tmp));
+ else
+ gLog("[plugin] entryPoint 'main' not found\n");
+
+#endif
+
+ /* if entry point is found, add to plugin a pointer to hostCallback. Or
+ * in other words bind the callback to the plugin. */
+
+ if (entryPoint) {
+ gLog("[plugin] entryPoint found\n");
+ plugin = entryPoint(HostCallback);
+ if (!plugin) {
+ gLog("[plugin] failed to create effect instance!\n");
+ return 0;
+ }
+ }
+ else {
+ gLog("[plugin] entryPoint not found, unable to proceed\n");
+ return 0;
+ }
+
+
+ /* check the magicNumber */
+ /** WARNING: on Windows one can load any DLL! Why!?! */
+
+ if(plugin->magic == kEffectMagic) {
+ gLog("[plugin] magic number OK\n");
+ return 1;
+ }
+ else {
+ gLog("[plugin] magic number is bad\n");
+ return 0;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Plugin::setup(int samplerate, int frames) {
+
+ /* init plugin through the dispatcher with some basic infos */
+
+ plugin->dispatcher(plugin, effOpen, 0, 0, 0, 0);
+ plugin->dispatcher(plugin, effSetSampleRate, 0, 0, 0, samplerate);
+ plugin->dispatcher(plugin, effSetBlockSize, 0, frames, 0, 0);
+
+ /* check SDK compatibility */
+
+ if (getSDKVersion() != kVstVersion)
+ gLog("[plugin] warning: different VST version (host: %d, plugin: %d)\n", kVstVersion, getSDKVersion());
+
+ return 1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+AEffect *Plugin::getPlugin() {
+ return plugin;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Plugin::getId() { return id; }
+
+
+/* ------------------------------------------------------------------ */
+
+int Plugin::getSDKVersion() {
+ return plugin->dispatcher(plugin, effGetVstVersion, 0, 0, 0, 0);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Plugin::getName(char *out) {
+ char tmp[128] = "\0";
+ plugin->dispatcher(plugin, effGetEffectName, 0, 0, tmp, 0);
+ tmp[kVstMaxEffectNameLen-1] = '\0';
+ strncpy(out, tmp, kVstMaxEffectNameLen);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Plugin::getVendor(char *out) {
+ char tmp[128] = "\0";
+ plugin->dispatcher(plugin, effGetVendorString, 0, 0, tmp, 0);
+ tmp[kVstMaxVendorStrLen-1] = '\0';
+ strncpy(out, tmp, kVstMaxVendorStrLen);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Plugin::getProduct(char *out) {
+ char tmp[128] = "\0";
+ plugin->dispatcher(plugin, effGetProductString, 0, 0, tmp, 0);
+ tmp[kVstMaxProductStrLen-1] = '\0';
+ strncpy(out, tmp, kVstMaxProductStrLen);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Plugin::getNumPrograms() { return plugin->numPrograms; }
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Plugin::setProgram(int index) {
+ plugin->dispatcher(plugin, effBeginSetProgram, 0, 0, 0, 0);
+ plugin->dispatcher(plugin, effSetProgram, 0, index, 0, 0);
+ gLog("[plugin] program changed, index %d\n", index);
+ program = index;
+ return plugin->dispatcher(plugin, effEndSetProgram, 0, 0, 0, 0);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Plugin::getNumParams() { return plugin->numParams; }
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Plugin::getNumInputs() { return plugin->numInputs; }
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Plugin::getNumOutputs() { return plugin->numOutputs; }
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Plugin::getProgramName(int index, char *out) {
+ char tmp[128] = "\0";
+ plugin->dispatcher(plugin, effGetProgramNameIndexed, index, 0, tmp, 0);
+ tmp[kVstMaxProgNameLen-1] = '\0';
+ strncpy(out, tmp, kVstMaxProgNameLen);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Plugin::getParamName(int index, char *out) {
+ char tmp[128] = "\0";
+ plugin->dispatcher(plugin, effGetParamName, index, 0, tmp, 0);
+ tmp[kVstMaxParamStrLen-1] = '\0';
+ strncpy(out, tmp, kVstMaxParamStrLen);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Plugin::getParamLabel(int index, char *out) {
+ char tmp[128] = "\0";
+ plugin->dispatcher(plugin, effGetParamLabel, index, 0, tmp, 0);
+ tmp[kVstMaxParamStrLen-1] = '\0';
+ strncpy(out, tmp, kVstMaxParamStrLen);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Plugin::getParamDisplay(int index, char *out) {
+ char tmp[128] = "\0";
+ plugin->dispatcher(plugin, effGetParamDisplay, index, 0, tmp, 0);
+ tmp[kVstMaxParamStrLen-1] = '\0';
+ strncpy(out, tmp, kVstMaxParamStrLen);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+float Plugin::getParam(int index) {
+ return plugin->getParameter(plugin, index);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Plugin::setParam(int index, float value) {
+ plugin->setParameter(plugin, index, value);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool Plugin::hasGui() {
+ return plugin->flags & effFlagsHasEditor;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Plugin::openGui(void *w) {
+ long val = 0;
+#ifdef __linux__
+ val = (long) w;
+#endif
+ plugin->dispatcher(plugin, effEditOpen, 0, val, w, 0);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Plugin::closeGui() {
+ plugin->dispatcher(plugin, effEditClose, 0, 0, 0, 0);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Plugin::getGuiWidth() {
+ ERect *pErect = NULL;
+ plugin->dispatcher(plugin, effEditGetRect, 0, 0, &pErect, 0);
+ return pErect->top + pErect->right;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Plugin::getGuiHeight() {
+ ERect *pErect = NULL;
+ plugin->dispatcher(plugin, effEditGetRect, 0, 0, &pErect, 0);
+ return pErect->top + pErect->bottom;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Plugin::idle() {
+ plugin->dispatcher(plugin, effEditIdle, 0, 0, NULL, 0);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Plugin::processAudio(float **in, float **out, long frames) {
+ plugin->processReplacing(plugin, in, out, frames);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Plugin::processEvents(VstEvents *events) {
+ plugin->dispatcher(plugin, effProcessEvents, 0, 0, events, 0.0);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Plugin::resume() {
+ plugin->dispatcher(plugin, effMainsChanged, 0, 1, 0, 0);
+ suspended = false;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Plugin::suspend() {
+ plugin->dispatcher(plugin, effMainsChanged, 0, 0, 0, 0);
+ suspended = true;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Plugin::close() {
+ plugin->dispatcher(plugin, effClose, 0, 0, 0, 0);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Plugin::getRect(ERect **out) {
+ plugin->dispatcher(plugin, effEditGetRect, 0, 0, out, 0);
+}
+
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * plugin
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+#ifdef WITH_VST
+
+#ifndef __PLUGIN_H
+#define __PLUGIN_H
+
+#include <cstdio>
+
+/* before including aeffetx(x).h we must define __cdecl, otherwise VST
+ * headers can't be compiled correctly. In windows __cdecl is already
+ * defined. */
+
+#ifdef __GNUC__
+ #ifndef _WIN32
+ #define __cdecl
+ #endif
+#endif
+
+#include "vst/aeffectx.h"
+
+#if defined(_WIN32)
+ #include <windows.h>
+#elif defined(__linux__)
+ #include <dlfcn.h>
+ #include <X11/Xlib.h>
+#elif defined(__APPLE__)
+ #include <CoreFoundation/CFBundle.h>
+#endif
+
+#include <limits.h> // PATH_MAX
+
+
+// Plugin's entry point
+typedef AEffect* (*vstPluginFuncPtr)(audioMasterCallback host);
+
+
+class Plugin {
+
+private:
+
+#if defined(_WIN32) || defined(__linux__)
+ void *module; // dll, so, ...
+#elif defined(__APPLE__)
+ CFBundleRef module; // OSX bundle
+#endif
+
+ vstPluginFuncPtr entryPoint; // VST entry point
+ AEffect *plugin; // real plugin
+
+ /* each plugin has an unique ID */
+
+ static int id_generator;
+ int id;
+
+ /* program
+ * selected program. -1 if no program available */
+
+ int program;
+
+ /* unload
+ * free plugin from memory. Calls dlclose and similars. */
+
+ int unload();
+
+public:
+ Plugin();
+ ~Plugin();
+
+ int load(const char *fname);
+ int init(VstIntPtr VSTCALLBACK (*HostCallback)(AEffect*, VstInt32, VstInt32, VstIntPtr, void*, float));
+ int setup(int samplerate, int frames);
+
+ AEffect *getPlugin();
+
+ /* get[Item].
+ * Wrappers called by host when it wants info from the plugin. */
+
+ int getId();
+ int getSDKVersion();
+ void getName (char *out);
+ void getVendor (char *out);
+ void getProduct(char *out);
+ int getNumPrograms(); // list all programs
+ int setProgram(int index); // load a program
+ int getNumParams();
+ int getNumInputs();
+ int getNumOutputs();
+ void getProgramName(int index, char *out); // program = preset
+ void getParamName(int index, char *out);
+ void getParamLabel(int index, char *out); // parameter's value(0, -39, ...)
+ void getParamDisplay(int index, char *out); // parameter's unit measurement (dB, Pan, ...)
+ float getParam(int index);
+ void getRect(ERect **out);
+ void setParam(int index, float value);
+
+ bool hasGui();
+ void openGui(void *w);
+ void closeGui();
+ int getGuiWidth();
+ int getGuiHeight();
+ void idle();
+
+ void processAudio (float **in, float **out, long frames);
+ void processEvents(VstEvents *events);
+ void resume();
+ void suspend();
+ void close();
+
+ inline int getProgram() { return program; }
+
+ /* there's a specific opcode for the bypass, but we don't trust the
+ * plugin's developers. */
+
+ bool bypass;
+
+ /* the status of the plugin:
+ * 1: ok
+ * 0: missing (file not found) */
+
+ int status;
+
+ /* suspended
+ * true after suspend(), false after resume(). A suspended plugin isn't
+ * processed by pluginHost. */
+
+ bool suspended;
+
+ /* pathfile
+ * full filename path */
+
+ char pathfile[PATH_MAX];
+
+ /* window
+ * plugin must know its window in case of a resize via opcode */
+
+ class gWindow *window;
+};
+
+#endif
+
+#endif // #ifdef WITH_VST
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * pluginHost
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifdef WITH_VST
+
+
+#include "pluginHost.h"
+#include "conf.h"
+#include "const.h"
+#include "mixer.h"
+#include "gd_mainWindow.h"
+#include "channel.h"
+#include "sampleChannel.h"
+#include "midiChannel.h"
+#include "kernelMidi.h"
+#include "log.h"
+
+
+extern Conf G_Conf;
+extern Mixer G_Mixer;
+extern PluginHost G_PluginHost;
+extern unsigned G_beats;
+extern gdMainWindow *mainWin;
+
+
+PluginHost::PluginHost() {
+
+ /* initially we fill vstTimeInfo with trash. Only when the plugin requests
+ * the opcode we load the right infos from G_Mixer. */
+
+ vstTimeInfo.samplePos = 0.0;
+ vstTimeInfo.sampleRate = G_Conf.samplerate;
+ vstTimeInfo.nanoSeconds = 0.0;
+ vstTimeInfo.ppqPos = 0.0;
+ vstTimeInfo.tempo = 120.0;
+ vstTimeInfo.barStartPos = 0.0;
+ vstTimeInfo.cycleStartPos = 0.0;
+ vstTimeInfo.cycleEndPos = 0.0;
+ vstTimeInfo.timeSigNumerator = 4;
+ vstTimeInfo.timeSigDenominator = 4;
+ vstTimeInfo.smpteOffset = 0;
+ vstTimeInfo.smpteFrameRate = 1;
+ vstTimeInfo.samplesToNextClock = 0;
+ vstTimeInfo.flags = 0;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+PluginHost::~PluginHost() {}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int PluginHost::allocBuffers() {
+
+ /** FIXME - ERROR CHECKING! */
+
+ /* never, ever use G_Conf.buffersize to alloc these chunks of memory.
+ * If you use JACK, that value would be meaningless. Always refer to
+ * kernelAudio::realBufsize. */
+
+ int bufSize = kernelAudio::realBufsize*sizeof(float);
+
+ bufferI = (float **) malloc(2 * sizeof(float*));
+ bufferI[0] = (float *) malloc(bufSize);
+ bufferI[1] = (float *) malloc(bufSize);
+
+ bufferO = (float **) malloc(2 * sizeof(float*));
+ bufferO[0] = (float *) malloc(bufSize);
+ bufferO[1] = (float *) malloc(bufSize);
+
+ memset(bufferI[0], 0, bufSize);
+ memset(bufferI[1], 0, bufSize);
+ memset(bufferO[0], 0, bufSize);
+ memset(bufferO[1], 0, bufSize);
+
+ gLog("[pluginHost] buffers allocated, buffersize = %d\n", 2*kernelAudio::realBufsize);
+
+ //printOpcodes();
+
+ return 1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+VstIntPtr VSTCALLBACK PluginHost::HostCallback(AEffect *effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void *ptr, float opt) {
+ return G_PluginHost.gHostCallback(effect, opcode, index, value, ptr, opt);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+VstIntPtr PluginHost::gHostCallback(AEffect *effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void *ptr, float opt) {
+
+ /* warning: VST headers compiled with DECLARE_VST_DEPRECATED. */
+
+ switch (opcode) {
+
+ /* 0 - Called after a control has changed in the editor and when
+ * the associated parameter should be automated. Index contains the
+ * param, opt the value. Thanks, but we don't need it now. It will
+ * be useful when recording actions from VST (in the future). */
+
+ case audioMasterAutomate:
+ return 0;
+
+ /* 1 - host version (2.4) */
+
+ case audioMasterVersion:
+ return kVstVersion;
+
+ /* 3 - Give idle time to Host application, e.g. if plug-in editor is
+ * doing mouse tracking in a modal loop. This a is multithread app,
+ * we don't need it. */
+
+ case audioMasterIdle:
+ return 0;
+
+ /* 6 - tells the host that the plugin is an instrument. Deprecated. */
+
+ case DECLARE_VST_DEPRECATED(audioMasterWantMidi):
+ return 0;
+
+ /* 7 - time infos */
+
+ case audioMasterGetTime:
+ vstTimeInfo.samplePos = G_Mixer.actualFrame;
+ vstTimeInfo.sampleRate = G_Conf.samplerate;
+ vstTimeInfo.tempo = G_Mixer.bpm;
+ vstTimeInfo.timeSigNumerator = G_Mixer.beats;
+ vstTimeInfo.timeSigDenominator = G_Mixer.bars;
+ vstTimeInfo.ppqPos = (G_Mixer.actualFrame / (float) G_Conf.samplerate) * (float) G_Mixer.bpm / 60.0f;
+ return (VstIntPtr) &vstTimeInfo;
+
+ /* ? - requires a pointer to VstEvents. No vstEvents so far (v0.5.4) */
+
+ case audioMasterProcessEvents:
+ return 0;
+
+ /* 13 - tells that numInputs/numOutputs are changed. Not supported and
+ * not needed. */
+
+ case audioMasterIOChanged:
+ return false;
+
+ /* 14 - plugin needs idle calls (outside its editor window). Deprecated */
+
+ case DECLARE_VST_DEPRECATED(audioMasterNeedIdle):
+ return 0;
+
+ /* 15 - requests to resize the editor window. w = index, h = value*/
+
+ case audioMasterSizeWindow: {
+ gWindow *window = NULL;
+ for (unsigned i=0; i<masterOut.size && !window; i++)
+ if (masterOut.at(i)->getPlugin() == effect)
+ window = masterOut.at(i)->window;
+
+ for (unsigned i=0; i<masterIn.size && !window; i++)
+ if (masterIn.at(i)->getPlugin() == effect)
+ window = masterIn.at(i)->window;
+
+ for (unsigned i=0; i<G_Mixer.channels.size && !window; i++) {
+ Channel *ch = G_Mixer.channels.at(i);
+ for (unsigned j=0; j<ch->plugins.size && !window; j++)
+ if (ch->plugins.at(j)->getPlugin() == effect)
+ window = ch->plugins.at(j)->window;
+ }
+
+ if (window) {
+ gLog("[pluginHost] audioMasterSizeWindow: resizing window from plugin %p\n", (void*) effect);
+ if (index == 1 || value == 1)
+ gLog("[pluginHost] warning: non-sense values!\n");
+ else
+ window->size((int)index, (int)value);
+ return 1;
+ }
+ else {
+ gLog("[pluginHost] audioMasterSizeWindow: window from plugin %p not found\n", (void*) effect);
+ return 0;
+ }
+ }
+
+ /* 16 - sample rate */
+
+ case audioMasterGetSampleRate:
+ return G_Conf.samplerate;
+
+ /* ?? - buffer size */
+
+ case audioMasterGetBlockSize:
+ return kernelAudio::realBufsize;
+
+ case audioMasterGetInputLatency:
+ gLog("[pluginHost] requested opcode 'audioMasterGetInputLatency' (%d)\n", opcode);
+ return 0;
+
+ case audioMasterGetOutputLatency:
+ gLog("[pluginHost] requested opcode 'audioMasterGetOutputLatency' (%d)\n", opcode);
+ return 0;
+
+ /* 23 - wants to know what kind of process is that.
+ * kVstProcessLevelRealtime = currently in audio thread (where
+ * process is called). */
+
+ case audioMasterGetCurrentProcessLevel:
+ return kVstProcessLevelRealtime;
+
+ /* 32 - vendor name */
+
+ case audioMasterGetVendorString:
+ strcpy((char*)ptr, "Monocasual");
+ return 1;
+
+ /* 32 - product name */
+
+ case audioMasterGetProductString:
+ strcpy((char*)ptr, "Giada");
+ return 1;
+
+ /* 33 - product version */
+
+ case audioMasterGetVendorVersion:
+ return (int) VERSIONE_FLOAT * 100;
+
+
+ /* 37 - Plugin asks Host if it implements the feature text. */
+
+ case audioMasterCanDo:
+ gLog("[pluginHost] audioMasterCanDo: %s\n", (char*)ptr);
+ if (!strcmp((char*)ptr, "sizeWindow") ||
+ !strcmp((char*)ptr, "sendVstTimeInfo") ||
+ !strcmp((char*)ptr, "sendVstMidiEvent") ||
+ !strcmp((char*)ptr, "sendVstMidiEventFlagIsRealtime"))
+ return 1; // we can do all of that
+ else
+ return 0;
+
+ /* 42 - Something has changed, update the host's 'multi-fx' display.
+ * Not supported right now, return 0. This opcode deals with the program
+ * changes, more infos http://www.asseca.com/vst-24-specs/amUpdateDisplay.html */
+
+ case audioMasterUpdateDisplay:
+ return 0;
+
+ case audioMasterGetLanguage:
+ return kVstLangEnglish;
+
+ /* ?? */
+
+ case audioMasterGetAutomationState:
+ gLog("[pluginHost] requested opcode 'audioMasterGetAutomationState' (%d)\n", opcode);
+ return 0;
+
+ /* 43 - It tells the Host that if it needs to, it has to record
+ * automation data for this control. In other words this opcode is fired
+ * when the user starts to tweak a parameter with the mouse.
+ * Useful when the plugin actions will be recorded. */
+
+ case audioMasterBeginEdit:
+ return 0;
+
+ /* 44 - no more interaction for the user, started with the previous
+ * opcode. */
+
+ case audioMasterEndEdit:
+ return 0;
+
+ default:
+ gLog("[pluginHost] FIXME: host callback called with opcode %d\n", opcode);
+ return 0;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int PluginHost::addPlugin(const char *fname, int stackType, Channel *ch) {
+
+ Plugin *p = new Plugin();
+ bool success = true;
+
+ gVector <Plugin *> *pStack;
+ pStack = getStack(stackType, ch);
+
+ if (!p->load(fname)) {
+ //delete p;
+ //return 0;
+ success = false;
+ }
+
+ /* if the load failed we add a 'dead' plugin into the stack. This is
+ * useful to report a missing plugin. */
+
+ if (!success) {
+ pStack->add(p);
+ return 0;
+ }
+
+ /* otherwise let's try to initialize it. */
+
+ else {
+
+ /* try to init the plugin. If fails, delete it and return error. */
+
+ if (!p->init(&PluginHost::HostCallback)) {
+ delete p;
+ return 0;
+ }
+
+ /* plugin setup */
+
+ p->setup(G_Conf.samplerate, kernelAudio::realBufsize);
+
+ /* try to add the new plugin until succeed */
+
+ int lockStatus;
+ while (true) {
+ lockStatus = pthread_mutex_trylock(&G_Mixer.mutex_plugins);
+ if (lockStatus == 0) {
+ pStack->add(p);
+ pthread_mutex_unlock(&G_Mixer.mutex_plugins);
+ break;
+ }
+ }
+
+ char name[256]; p->getName(name);
+ gLog("[pluginHost] plugin id=%d loaded (%s), stack type=%d, stack size=%d\n", p->getId(), name, stackType, pStack->size);
+
+ /* p->resume() is suggested. Who knows... */
+
+ p->resume();
+
+ return 1;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void PluginHost::processStack(float *buffer, int stackType, Channel *ch) {
+
+ gVector <Plugin *> *pStack = getStack(stackType, ch);
+
+ /* empty stack, stack not found or mixer not ready: do nothing */
+ /// TODO - join evaluation
+
+ if (!G_Mixer.ready)
+ return;
+ if (pStack == NULL)
+ return;
+ if (pStack->size == 0)
+ return;
+
+ /* converting buffer from Giada to VST */
+
+ for (unsigned i=0; i<kernelAudio::realBufsize; i++) {
+ bufferI[0][i] = buffer[i*2];
+ bufferI[1][i] = buffer[(i*2)+1];
+ }
+
+ /* hardcore processing. At the end we swap input and output, so that
+ * the N-th plugin will process the result of the plugin N-1. */
+
+ for (unsigned i=0; i<pStack->size; i++) {
+ /// TODO - join evaluation
+
+ if (pStack->at(i)->status != 1)
+ continue;
+ if (pStack->at(i)->suspended)
+ continue;
+ if (pStack->at(i)->bypass)
+ continue;
+ if (ch) { // process events if it's a channel stack
+ if (ch->type == CHANNEL_MIDI) {
+ ///gLog("events: %d\n", (((MidiChannel*)ch)->getVstEvents())->numEvents);
+ pStack->at(i)->processEvents(((MidiChannel*)ch)->getVstEvents());
+ }
+ }
+ pStack->at(i)->processAudio(bufferI, bufferO, kernelAudio::realBufsize);
+ bufferI = bufferO;
+ }
+
+ /* converting buffer from VST to Giada. A note for the future: if we
+ * overwrite (=) (as we do now) it's SEND, if we add (+) it's INSERT. */
+
+ for (unsigned i=0; i<kernelAudio::realBufsize; i++) {
+ buffer[i*2] = bufferO[0][i];
+ buffer[(i*2)+1] = bufferO[1][i];
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void PluginHost::processStackOffline(float *buffer, int stackType, Channel *ch, int size) {
+
+ /* call processStack on the entire size of the buffer. How many cycles?
+ * size / (kernelAudio::realBufsize*2) (ie. internal bufsize) */
+
+ /** FIXME 1 - calling processStack is slow, due to its internal buffer
+ * conversions. We should also call processOffline from VST sdk */
+
+ int index = 0;
+ int step = kernelAudio::realBufsize*2;
+
+ while (index <= size) {
+ int left = index+step-size;
+ if (left < 0)
+ processStack(&buffer[index], stackType, ch);
+
+ /** FIXME 2 - we left out the last part of buffer, because size % step != 0.
+ * we should process the last chunk in a separate buffer, padded with 0 */
+
+ //else
+ // gLog("chunk of buffer left, size=%d\n", left);
+
+ index+=step;
+ }
+
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+Plugin *PluginHost::getPluginById(int id, int stackType, Channel *ch) {
+ gVector <Plugin *> *pStack = getStack(stackType, ch);
+ for (unsigned i=0; i<pStack->size; i++) {
+ if (pStack->at(i)->getId() == id)
+ return pStack->at(i);
+ }
+ return NULL;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+Plugin *PluginHost::getPluginByIndex(int index, int stackType, Channel *ch) {
+ gVector <Plugin *> *pStack = getStack(stackType, ch);
+ if (pStack->size == 0)
+ return NULL;
+ if ((unsigned) index >= pStack->size)
+ return NULL;
+ return pStack->at(index);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void PluginHost::freeStack(int stackType, Channel *ch) {
+
+ gVector <Plugin *> *pStack;
+ pStack = getStack(stackType, ch);
+
+ if (pStack->size == 0)
+ return;
+
+ int lockStatus;
+ while (true) {
+ lockStatus = pthread_mutex_trylock(&G_Mixer.mutex_plugins);
+ if (lockStatus == 0) {
+ for (unsigned i=0; i<pStack->size; i++) {
+ if (pStack->at(i)->status == 1) { // only if plugin is ok
+ pStack->at(i)->suspend();
+ pStack->at(i)->close();
+ }
+ delete pStack->at(i);
+ }
+ pStack->clear();
+ pthread_mutex_unlock(&G_Mixer.mutex_plugins);
+ break;
+ }
+ }
+
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void PluginHost::freeAllStacks() {
+ freeStack(PluginHost::MASTER_OUT);
+ freeStack(PluginHost::MASTER_IN);
+ for (unsigned i=0; i<G_Mixer.channels.size; i++)
+ freeStack(PluginHost::CHANNEL, G_Mixer.channels.at(i));
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void PluginHost::freePlugin(int id, int stackType, Channel *ch) {
+
+ gVector <Plugin *> *pStack;
+ pStack = getStack(stackType, ch);
+
+ /* try to delete the plugin until succeed. G_Mixer has priority. */
+
+ for (unsigned i=0; i<pStack->size; i++)
+ if (pStack->at(i)->getId() == id) {
+
+ if (pStack->at(i)->status == 0) { // no frills if plugin is missing
+ delete pStack->at(i);
+ pStack->del(i);
+ return;
+ }
+ else {
+ int lockStatus;
+ while (true) {
+ lockStatus = pthread_mutex_trylock(&G_Mixer.mutex_plugins);
+ if (lockStatus == 0) {
+ pStack->at(i)->suspend();
+ pStack->at(i)->close();
+ delete pStack->at(i);
+ pStack->del(i);
+ pthread_mutex_unlock(&G_Mixer.mutex_plugins);
+ gLog("[pluginHost] plugin id=%d removed\n", id);
+ return;
+ }
+ //else
+ //gLog("[pluginHost] waiting for mutex...\n");
+ }
+ }
+ }
+ gLog("[pluginHost] plugin id=%d not found\n", id);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void PluginHost::swapPlugin(unsigned indexA, unsigned indexB, int stackType, Channel *ch) {
+
+ gVector <Plugin *> *pStack = getStack(stackType, ch);
+
+ int lockStatus;
+ while (true) {
+ lockStatus = pthread_mutex_trylock(&G_Mixer.mutex_plugins);
+ if (lockStatus == 0) {
+ pStack->swap(indexA, indexB);
+ pthread_mutex_unlock(&G_Mixer.mutex_plugins);
+ gLog("[pluginHost] plugin at index %d and %d swapped\n", indexA, indexB);
+ return;
+ }
+ //else
+ //gLog("[pluginHost] waiting for mutex...\n");
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int PluginHost::getPluginIndex(int id, int stackType, Channel *ch) {
+
+ gVector <Plugin *> *pStack = getStack(stackType, ch);
+
+ for (unsigned i=0; i<pStack->size; i++)
+ if (pStack->at(i)->getId() == id)
+ return i;
+ return -1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+gVector <Plugin *> *PluginHost::getStack(int stackType, Channel *ch) {
+ switch(stackType) {
+ case MASTER_OUT:
+ return &masterOut;
+ case MASTER_IN:
+ return &masterIn;
+ case CHANNEL:
+ return &ch->plugins;
+ default:
+ return NULL;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+VstMidiEvent *PluginHost::createVstMidiEvent(uint32_t msg)
+{
+ VstMidiEvent *e = (VstMidiEvent*) malloc(sizeof(VstMidiEvent));
+
+ /* type = two types of events: MIDI event and MIDI system exclusive
+ * (aka sysex, not implemented). */
+
+ e->type = kVstMidiType;
+ e->byteSize = sizeof(VstMidiEvent);
+
+ /* deltaFrames = sample frames related to the current block start
+ * sample position. */
+
+ e->deltaFrames = 0;
+
+ /* flags = kVstMidiEventIsRealtime means that this event is played
+ * live (not in playback from a sequencer track). This allows the
+ * Plug-In to handle these flagged events with higher priority,
+ * especially when the Plug-In has a big latency */
+
+ e->flags = kVstMidiEventIsRealtime;
+
+ /* midiData = 1 to 3 MIDI bytes; midiData[3] is reserved (zero) */
+
+ e->midiData[0] = kernelMidi::getB1(msg); // note on/off + channel
+ e->midiData[1] = kernelMidi::getB2(msg); // note number
+ e->midiData[2] = kernelMidi::getB3(msg); // velocity
+ e->midiData[3] = 0;
+
+ /* noteLength = (in sample frames) of entire note, if available,
+ * else 0 */
+
+ e->noteLength = 0;
+
+ /* noteOffset = offset (in sample frames) into note from note start
+ * if available, else 0 */
+
+ e->noteOffset = 0;
+
+ /* noteOffVelocity = Note Off Velocity [0, 127]. */
+
+ e->noteOffVelocity = 0;
+
+ return e;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+unsigned PluginHost::countPlugins(int stackType, Channel *ch) {
+ gVector <Plugin *> *pStack = getStack(stackType, ch);
+ return pStack->size;
+}
+
+
+#endif // #ifdef WITH_VST
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * pluginHost
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+#ifdef WITH_VST
+
+#ifndef __PLUGIN_HOST_
+#define __PLUGIN_HOST_
+
+#include "ge_window.h"
+#include "plugin.h"
+#include "utils.h"
+#include "init.h"
+#include "const.h"
+
+
+class PluginHost {
+
+private:
+
+ /* VSTs have a different buffer model:
+ *
+ * buffer[0] = channel left
+ * buffer[1] = channel right
+ * buffer[0][....] = all signals from left chan
+ * buffer[1][....] = all signals from right chan */
+
+ float **bufferI;
+ float **bufferO;
+
+ /* VST struct containing infos on tempo (bpm, freq, smtpe, ...). */
+
+ VstTimeInfo vstTimeInfo;
+
+public:
+
+ /* stack types. Use them together with getStack() in order to geta
+ * pointer to the right stack. */
+
+ enum stackType {
+ MASTER_OUT,
+ MASTER_IN,
+ CHANNEL
+ };
+
+ /* stack of Plugins */
+
+ gVector <Plugin *> masterOut;
+ gVector <Plugin *> masterIn;
+
+ PluginHost();
+ ~PluginHost();
+
+ int allocBuffers();
+
+ /* The plugin can ask the host if it supports a given capability,
+ * which is done through the HostCallback() function.
+ *
+ * Why static? This is a callback attached to each plugin in the stack
+ * and C++ callback functions need to be static when declared in class.
+ *
+ * OPCODE LIST:
+ * base version: vstsdk2.4/pluginterfaces/aeffect.h (vst 1.x)
+ * enhanced v. : vstsdk2.4/pluginterfaces/effectx.h (vst 2.x) */
+
+ static VstIntPtr VSTCALLBACK HostCallback(AEffect *effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void *ptr, float opt);
+ VstIntPtr gHostCallback(AEffect *effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void *ptr, float opt);
+
+ int addPlugin(const char *fname, int stackType, class Channel *ch=NULL);
+
+ void processEvents(float *buffer, class Channel *ch);
+
+ /* processStack
+ * apply the fx list to the buffer. */
+
+ void processStack(float *buffer, int stackType, class Channel *ch=NULL);
+
+ /* processStackOffline
+ * apply the fx list to a longer chunk of data */
+
+ void processStackOffline(float *buffer, int stackType, class Channel *ch, int size);
+
+ /* createVstMidiEvent
+ * return a pointer to a new VstMidiEvent structure. */
+
+ VstMidiEvent *createVstMidiEvent(uint32_t msg);
+
+ gVector <Plugin *> *getStack(int stackType, class Channel *ch=NULL);
+
+ Plugin *getPluginById(int id, int stackType, class Channel *ch=NULL);
+
+ Plugin *getPluginByIndex(int index, int stackType, class Channel *ch=NULL);
+
+ int getPluginIndex(int id, int stackType, class Channel *ch=NULL);
+
+ unsigned countPlugins(int stackType, class Channel *ch=NULL);
+
+ void freeStack(int stackType, class Channel *ch=NULL);
+
+ void freeAllStacks();
+
+ void freePlugin(int id, int stackType, class Channel *ch=NULL);
+
+ void swapPlugin(unsigned indexA, unsigned indexB, int stackType, class Channel *ch=NULL);
+};
+#endif
+
+#endif // #ifdef WITH_VST
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * recorder
+ * Action recorder.
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include <math.h>
+#include "recorder.h"
+#include "const.h"
+#include "utils.h"
+#include "mixer.h"
+#include "mixerHandler.h"
+#include "kernelAudio.h"
+#include "pluginHost.h"
+#include "kernelMidi.h"
+#include "utils.h"
+#include "patch.h"
+#include "conf.h"
+#include "channel.h"
+#include "sampleChannel.h"
+#include "log.h"
+
+
+#ifdef WITH_VST
+extern PluginHost G_PluginHost;
+#endif
+
+
+extern Mixer G_Mixer;
+extern Patch f_patch;
+extern Conf G_Conf;
+
+
+namespace recorder
+{
+gVector<int> frames;
+gVector< gVector<action*> > global;
+gVector<action*> actions;
+
+bool active = false;
+bool sortedActions = false;
+
+composite cmp;
+
+
+/* ------------------------------------------------------------------ */
+
+
+void init()
+{
+ sortedActions = false;
+ active = false;
+ clearAll();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool canRec(Channel *ch)
+{
+ /* NO recording if:
+ * recorder is inactive
+ * mixer is not running
+ * mixer is recording a take in this channel ch
+ * channel is empty */
+
+ if (!active || !G_Mixer.running || G_Mixer.chanInput == ch || (ch->type == CHANNEL_SAMPLE && ((SampleChannel*)ch)->wave == NULL))
+ return 0;
+ return 1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void rec(int index, int type, int frame, uint32_t iValue, float fValue)
+{
+ /* make sure frame is even */
+
+ if (frame % 2 != 0)
+ frame++;
+
+ /* allocating the action */
+
+ action *a = (action*) malloc(sizeof(action));
+ a->chan = index;
+ a->type = type;
+ a->frame = frame;
+ a->iValue = iValue;
+ a->fValue = fValue;
+
+ /* check if the frame exists in the stack. If it exists, we don't extend
+ * the stack, but we add (or push) a new action to it. */
+
+ int frameToExpand = frames.size;
+ for (int i=0; i<frameToExpand; i++)
+ if (frames.at(i) == frame) {
+ frameToExpand = i;
+ break;
+ }
+
+ /* espansione dello stack frames nel caso l'azione ricada in frame
+ * non precedentemente memorizzati (frameToExpand == frames.size).
+ * Espandere frames è facile, basta aggiungere un frame in coda.
+ * Espandere global è più complesso: bisogna prima allocare una
+ * cella in global (per renderlo parallelo a frames) e poi
+ * inizializzare il suo sub-stack (di action). */
+
+ if (frameToExpand == (int) frames.size) {
+ frames.add(frame);
+ global.add(actions); // array of actions added
+ global.at(global.size-1).add(a); // action added
+ }
+ else {
+
+ /* no duplicates, please */
+
+ for (unsigned t=0; t<global.at(frameToExpand).size; t++) {
+ action *ac = global.at(frameToExpand).at(t);
+ if (ac->chan == index &&
+ ac->type == type &&
+ ac->frame == frame &&
+ ac->iValue == iValue &&
+ ac->fValue == fValue)
+ return;
+ }
+
+ global.at(frameToExpand).add(a); // expand array
+ }
+
+ /* if WITH_VST create a new VST event and attach it to our action.
+ * Nota bene: the VST event occurs on localFrame=0: this is a
+ * user-generated event after all! */
+
+#ifdef WITH_VST
+ if (type == ACTION_MIDI)
+ a->event = G_PluginHost.createVstMidiEvent(a->iValue);
+#endif
+
+ /* don't activate the channel (readActions == false), it's up to
+ * the other layers */
+
+ Channel *ch = G_Mixer.getChannelByIndex(index);
+ ch->hasActions = true;
+
+ sortedActions = false;
+
+ gLog("[REC] action recorded, type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n",
+ a->type, a->frame, a->chan, a->iValue, a->iValue, a->fValue);
+ //print();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void clearChan(int index)
+{
+ gLog("[REC] clearing chan %d...\n", index);
+
+ for (unsigned i=0; i<global.size; i++) { // for each frame i
+ unsigned j=0;
+ while (true) {
+ if (j == global.at(i).size) break; // for each action j of frame i
+ action *a = global.at(i).at(j);
+ if (a->chan == index) {
+#ifdef WITH_VST
+ if (a->type == ACTION_MIDI)
+ free(a->event);
+#endif
+ free(a);
+ global.at(i).del(j);
+ }
+ else
+ j++;
+ }
+ }
+
+ Channel *ch = G_Mixer.getChannelByIndex(index);
+ ch->hasActions = false;
+ optimize();
+ //print();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void clearAction(int index, char act)
+{
+ gLog("[REC] clearing action %d from chan %d...\n", act, index);
+ for (unsigned i=0; i<global.size; i++) { // for each frame i
+ unsigned j=0;
+ while (true) { // for each action j of frame i
+ if (j == global.at(i).size)
+ break;
+ action *a = global.at(i).at(j);
+ if (a->chan == index && (act & a->type) == a->type) { // bitmask
+ free(a);
+ global.at(i).del(j);
+ }
+ else
+ j++;
+ }
+ }
+ Channel *ch = G_Mixer.getChannelByIndex(index);
+ ch->hasActions = false; /// FIXME - why this? Isn't it useless if we call chanHasActions?
+ optimize();
+ chanHasActions(index); /// FIXME
+ //print();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void deleteAction(int chan, int frame, char type, bool checkValues, uint32_t iValue, float fValue)
+{
+ /* make sure frame is even */
+
+ if (frame % 2 != 0)
+ frame++;
+
+ /* find the frame 'frame' */
+
+ bool found = false;
+ for (unsigned i=0; i<frames.size && !found; i++) {
+ if (frames.at(i) == frame) {
+
+ /* find the action in frame i */
+
+ for (unsigned j=0; j<global.at(i).size; j++) {
+ action *a = global.at(i).at(j);
+
+ /* action comparison logic */
+
+ bool doit = (a->chan == chan && a->type == (type & a->type));
+ if (checkValues)
+ doit &= (a->iValue == iValue && a->fValue == fValue);
+
+ if (doit) {
+ int lockStatus = 0;
+ while (lockStatus == 0) {
+ lockStatus = pthread_mutex_trylock(&G_Mixer.mutex_recs);
+ if (lockStatus == 0) {
+#ifdef WITH_VST
+ if (type == ACTION_MIDI)
+ free(a->event);
+#endif
+ free(a);
+ global.at(i).del(j);
+ pthread_mutex_unlock(&G_Mixer.mutex_recs);
+ found = true;
+ break;
+ }
+ else
+ gLog("[REC] delete action: waiting for mutex...\n");
+ }
+ }
+ }
+ }
+ }
+ if (found) {
+ optimize();
+ chanHasActions(chan);
+ gLog("[REC] action deleted, type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n",
+ type, frame, chan, iValue, iValue, fValue);
+ }
+ else
+ gLog("[REC] unable to delete action, not found! type=%d frame=%d chan=%d iValue=%d (%X) fValue=%f\n",
+ type, frame, chan, iValue, iValue, fValue);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void deleteActions(int chan, int frame_a, int frame_b, char type)
+{
+ sortActions();
+ gVector<int> dels;
+
+ for (unsigned i=0; i<frames.size; i++)
+ if (frames.at(i) > frame_a && frames.at(i) < frame_b)
+ dels.add(frames.at(i));
+
+ for (unsigned i=0; i<dels.size; i++)
+ deleteAction(chan, dels.at(i), type, false); // false == don't check values
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void clearAll()
+{
+ while (global.size > 0) {
+ for (unsigned i=0; i<global.size; i++) {
+ for (unsigned k=0; k<global.at(i).size; k++) {
+#ifdef WITH_VST
+ if (global.at(i).at(k)->type == ACTION_MIDI)
+ free(global.at(i).at(k)->event);
+#endif
+ free(global.at(i).at(k)); // free action
+ }
+ global.at(i).clear(); // free action container
+ global.del(i);
+ }
+ }
+
+ for (unsigned i=0; i<G_Mixer.channels.size; i++) {
+ G_Mixer.channels.at(i)->hasActions = false;
+ if (G_Mixer.channels.at(i)->type == CHANNEL_SAMPLE)
+ ((SampleChannel*)G_Mixer.channels.at(i))->readActions = false;
+ }
+
+ global.clear();
+ frames.clear();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void optimize()
+{
+ /* do something until the i frame is empty. */
+
+ unsigned i = 0;
+ while (true) {
+ if (i == global.size) return;
+ if (global.at(i).size == 0) {
+ global.del(i);
+ frames.del(i);
+ }
+ else
+ i++;
+ }
+
+ sortActions();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void sortActions()
+{
+ if (sortedActions)
+ return;
+ for (unsigned i=0; i<frames.size; i++)
+ for (unsigned j=0; j<frames.size; j++)
+ if (frames.at(j) > frames.at(i)) {
+ frames.swap(j, i);
+ global.swap(j, i);
+ }
+ sortedActions = true;
+ //print();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void updateBpm(float oldval, float newval, int oldquanto)
+{
+ for (unsigned i=0; i<frames.size; i++) {
+
+ float frame = ((float) frames.at(i)/newval) * oldval;
+ frames.at(i) = (int) frame;
+
+ /* the division up here cannot be precise. A new frame can be 44099
+ * and the quantizer set to 44100. That would mean two recs completely
+ * useless. So we compute a reject value ('scarto'): if it's lower
+ * than 6 frames the new frame is collapsed with a quantized frame. */
+ /** CHECKME - maybe 6 frames are too low */
+
+ if (frames.at(i) != 0) {
+ int scarto = oldquanto % frames.at(i);
+ if (scarto > 0 && scarto <= 6)
+ frames.at(i) = frames.at(i) + scarto;
+ }
+
+ /* never ever have odd frames. */
+
+ if (frames.at(i) % 2 != 0)
+ frames.at(i)++;
+ }
+
+ /* update structs */
+
+ for (unsigned i=0; i<frames.size; i++) {
+ for (unsigned j=0; j<global.at(i).size; j++) {
+ action *a = global.at(i).at(j);
+ a->frame = frames.at(i);
+ }
+ }
+
+ //print();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void updateSamplerate(int systemRate, int patchRate)
+{
+ /* diff ratio: systemRate / patchRate
+ * e.g. 44100 / 96000 = 0.4... */
+
+ if (systemRate == patchRate)
+ return;
+
+ gLog("[REC] systemRate (%d) != patchRate (%d), converting...\n", systemRate, patchRate);
+
+ float ratio = systemRate / (float) patchRate;
+ for (unsigned i=0; i<frames.size; i++) {
+
+ gLog("[REC] oldFrame = %d", frames.at(i));
+
+ float newFrame = frames.at(i);
+ newFrame = floorf(newFrame * ratio);
+
+ frames.at(i) = (int) newFrame;
+
+ if (frames.at(i) % 2 != 0)
+ frames.at(i)++;
+
+ gLog(", newFrame = %d\n", frames.at(i));
+ }
+
+ /* update structs */
+
+ for (unsigned i=0; i<frames.size; i++) {
+ for (unsigned j=0; j<global.at(i).size; j++) {
+ action *a = global.at(i).at(j);
+ a->frame = frames.at(i);
+ }
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void expand(int old_fpb, int new_fpb)
+{
+ /* this algorithm requires multiple passages if we expand from e.g. 2
+ * to 16 beats, precisely 16 / 2 - 1 = 7 times (-1 is the first group,
+ * which exists yet). If we expand by a non-multiple, the result is zero,
+ * due to float->int implicit cast */
+
+ unsigned pass = (int) (new_fpb / old_fpb) - 1;
+ if (pass == 0) pass = 1;
+
+ unsigned init_fs = frames.size;
+
+ for (unsigned z=1; z<=pass; z++) {
+ for (unsigned i=0; i<init_fs; i++) {
+ unsigned newframe = frames.at(i) + (old_fpb*z);
+ frames.add(newframe);
+ global.add(actions);
+ for (unsigned k=0; k<global.at(i).size; k++) {
+ action *a = global.at(i).at(k);
+ rec(a->chan, a->type, newframe, a->iValue, a->fValue);
+ }
+ }
+ }
+ gLog("[REC] expanded recs\n");
+ //print();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void shrink(int new_fpb)
+{
+ /* easier than expand(): here we delete eveything beyond old_framesPerBars. */
+
+ unsigned i=0;
+ while (true) {
+ if (i == frames.size) break;
+
+ if (frames.at(i) >= new_fpb) {
+ for (unsigned k=0; k<global.at(i).size; k++)
+ free(global.at(i).at(k)); // free action
+ global.at(i).clear(); // free action container
+ global.del(i); // shrink global
+ frames.del(i); // shrink frames
+ }
+ else
+ i++;
+ }
+ optimize();
+ gLog("[REC] shrinked recs\n");
+ //print();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void chanHasActions(int index)
+{
+ Channel *ch = G_Mixer.getChannelByIndex(index);
+ if (global.size == 0) {
+ ch->hasActions = false;
+ return;
+ }
+ for (unsigned i=0; i<global.size && !ch->hasActions; i++) {
+ for (unsigned j=0; j<global.at(i).size && !ch->hasActions; j++) {
+ if (global.at(i).at(j)->chan == index)
+ ch->hasActions = true;
+ }
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int getNextAction(int chan, char type, int frame, action **out, uint32_t iValue)
+{
+ sortActions(); // mandatory
+
+ unsigned i=0;
+ while (i < frames.size && frames.at(i) <= frame) i++;
+
+ if (i == frames.size) // no further actions past 'frame'
+ return -1;
+
+ for (; i<global.size; i++)
+ for (unsigned j=0; j<global.at(i).size; j++) {
+ action *a = global.at(i).at(j);
+ if (a->chan == chan && (type & a->type) == a->type) {
+ if (iValue == 0 || (iValue != 0 && a->iValue == iValue)) {
+ *out = global.at(i).at(j);
+ return 1;
+ }
+ }
+ }
+
+ return -2; // no 'type' actions found
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int getAction(int chan, char action, int frame, struct action **out)
+{
+ for (unsigned i=0; i<global.size; i++)
+ for (unsigned j=0; j<global.at(i).size; j++)
+ if (frame == global.at(i).at(j)->frame &&
+ action == global.at(i).at(j)->type &&
+ chan == global.at(i).at(j)->chan)
+ {
+ *out = global.at(i).at(j);
+ return 1;
+ }
+ return 0;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void startOverdub(int index, char actionMask, int frame)
+{
+ /* prepare the composite struct */
+
+ if (actionMask == ACTION_KEYS) {
+ cmp.a1.type = ACTION_KEYPRESS;
+ cmp.a2.type = ACTION_KEYREL;
+ }
+ else {
+ cmp.a1.type = ACTION_MUTEON;
+ cmp.a2.type = ACTION_MUTEOFF;
+ }
+ cmp.a1.chan = index;
+ cmp.a2.chan = index;
+ cmp.a1.frame = frame;
+ // cmp.a2.frame doesn't exist yet
+
+ /* avoid underlying action truncation: if action2.type == nextAction:
+ * you are in the middle of a composite action, truncation needed */
+
+ rec(index, cmp.a1.type, frame);
+
+ action *act = NULL;
+ int res = getNextAction(index, cmp.a1.type | cmp.a2.type, cmp.a1.frame, &act);
+ if (res == 1) {
+ if (act->type == cmp.a2.type) {
+ int truncFrame = cmp.a1.frame-kernelAudio::realBufsize;
+ if (truncFrame < 0)
+ truncFrame = 0;
+ gLog("[REC] add truncation at frame %d, type=%d\n", truncFrame, cmp.a2.type);
+ rec(index, cmp.a2.type, truncFrame);
+ }
+ }
+
+ SampleChannel *ch = (SampleChannel*) G_Mixer.getChannelByIndex(index);
+ ch->readActions = false; // don't use disableRead()
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void stopOverdub(int frame)
+{
+ cmp.a2.frame = frame;
+ bool ringLoop = false;
+ bool nullLoop = false;
+
+ /* ring loop verification, i.e. a composite action with key_press at
+ * frame N and key_release at frame M, with M <= N */
+
+ if (cmp.a2.frame < cmp.a1.frame) {
+ ringLoop = true;
+ gLog("[REC] ring loop! frame1=%d < frame2=%d\n", cmp.a1.frame, cmp.a2.frame);
+ rec(cmp.a2.chan, cmp.a2.type, G_Mixer.totalFrames); // record at the end of the sequencer
+ }
+ else
+ if (cmp.a2.frame == cmp.a1.frame) {
+ nullLoop = true;
+ gLog("[REC] null loop! frame1=%d == frame2=%d\n", cmp.a1.frame, cmp.a2.frame);
+ deleteAction(cmp.a1.chan, cmp.a1.frame, cmp.a1.type, false); // false == don't check values
+ }
+
+ SampleChannel *ch = (SampleChannel*) G_Mixer.getChannelByIndex(cmp.a2.chan);
+ ch->readActions = false; // don't use disableRead()
+
+ /* remove any nested action between keypress----keyrel, then record */
+
+ if (!nullLoop)
+ deleteActions(cmp.a2.chan, cmp.a1.frame, cmp.a2.frame, cmp.a1.type);
+ deleteActions(cmp.a2.chan, cmp.a1.frame, cmp.a2.frame, cmp.a2.type);
+
+ if (!ringLoop && !nullLoop) {
+ rec(cmp.a2.chan, cmp.a2.type, cmp.a2.frame);
+
+ /* avoid underlying action truncation, if keyrel happens inside a
+ * composite action */
+
+ action *act = NULL;
+ int res = getNextAction(cmp.a2.chan, cmp.a1.type | cmp.a2.type, cmp.a2.frame, &act);
+ if (res == 1) {
+ if (act->type == cmp.a2.type) {
+ gLog("[REC] add truncation at frame %d, type=%d\n", act->frame, act->type);
+ deleteAction(act->chan, act->frame, act->type, false); // false == don't check values
+ }
+ }
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void print()
+{
+ gLog("[REC] ** print debug **\n");
+ for (unsigned i=0; i<global.size; i++) {
+ gLog(" frame %d\n", frames.at(i));
+ for (unsigned j=0; j<global.at(i).size; j++) {
+ gLog(" action %d | chan %d | frame %d\n", global.at(i).at(j)->type, global.at(i).at(j)->chan, global.at(i).at(j)->frame);
+ }
+ }
+}
+
+} // namespace
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * recorder
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+#ifndef RECORDER_H
+#define RECORDER_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "utils.h"
+#include "const.h"
+#include "mixer.h"
+
+#ifdef WITH_VST
+
+/* before including aeffetx(x).h we must define __cdecl, otherwise VST
+ * headers can't be compiled correctly. In windows __cdecl is already
+ * defined. */
+
+ #ifdef __GNUC__
+ #ifndef _WIN32
+ #define __cdecl
+ #endif
+ #endif
+ #include "vst/aeffectx.h"
+#endif
+
+/*
+ * [global0]-->[gVector<_action*>0]-->[a0][a1][a2] 0[frames1]
+ * [global1]-->[gVector<_action*>1]-->[a0][a1][a2] 1[frames2]
+ * [global2]-->[gVector<_action*>2]-->[a0][a1][a2] 2[frames3]
+ * [global3]-->[gVector<_action*>3]-->[a0][a1][a2] 3[frames4]
+ * */
+
+namespace recorder {
+
+/* action
+ * struct containing fields to describe an atomic action. Note from
+ * VST sdk: parameter values, like all VST parameters, are declared as
+ * floats with an inclusive range of 0.0 to 1.0 (fValue). */
+
+struct action {
+ int chan; // channel index, i.e. Channel->index
+ int type;
+ int frame; // redundant info, used by helper functions
+ float fValue; // used only for envelopes (volumes, vst params).
+ uint32_t iValue; // used only for MIDI events
+
+ /* if VST store here a pointer to a vstEvent. */
+
+#ifdef WITH_VST
+ VstMidiEvent *event;
+#endif
+};
+
+/* composite
+ * a group of two actions (keypress+keyrel, muteon+muteoff) used during
+ * the overdub process */
+
+struct composite {
+ action a1;
+ action a2;
+};
+
+extern gVector<int> frames; // frame counter (sentinel) frames.size == global.size
+extern gVector< gVector<action*> > global; // container of containers of actions
+extern gVector<action*> actions; // container of actions
+
+extern bool active;
+extern bool sortedActions; // are actions sorted via sortActions()?
+
+/* init
+ * everything starts from here. */
+
+void init();
+
+/* chanHasActions
+ * Check if the channel has at least one action recorded. If false, sets
+ * ch->hasActions = false. Used after an action deletion. */
+
+void chanHasActions(int chan);
+
+/* canRec
+ * can a channel rec an action? Call this one BEFORE rec(). */
+
+bool canRec(Channel *ch);
+
+/* rec
+ * record an action. */
+
+void rec(int chan, int action, int frame, uint32_t iValue=0, float fValue=0.0f);
+
+/* clearChan
+ * clear all actions from a channel. */
+
+void clearChan(int chan);
+
+/* clearAction
+ * clear the 'action' action type from a channel. */
+
+void clearAction(int chan, char action);
+
+/* deleteAction
+ * delete ONE action. Useful in the action editor. 'type' can be a mask. */
+
+void deleteAction(int chan, int frame, char type, bool checkValues, uint32_t iValue=0, float fValue=0.0);
+
+/* deleteActions
+ * delete A RANGE of actions from frame_a to frame_b in channel 'chan'.
+ * 'type' can be a bitmask. Exclusive range (frame_a, frame_b). */
+
+void deleteActions(int chan, int frame_a, int frame_b, char type);
+
+/* clearAll
+ * delete everything. */
+
+void clearAll();
+
+/* optimize
+ * clear frames without actions. */
+
+void optimize();
+
+/* sortActions
+ * sorts actions by frame, asc mode. */
+
+void sortActions();
+
+/* updateBpm
+ * reassign frames by calculating the new bpm value. */
+
+void updateBpm(float oldval, float newval, int oldquanto);
+
+/* updateSamplerate
+ * reassign frames taking in account the samplerate. If f_system ==
+ * f_patch nothing changes, otherwise the conversion is mandatory. */
+
+void updateSamplerate(int systemRate, int patchRate);
+
+void expand(int old_fpb, int new_fpb);
+void shrink(int new_fpb);
+
+/* getNextAction
+ * return the nearest action in chan 'chan' of type 'action' starting
+ * from 'frame'. Action can be a bitmask. If iValue != -1 search for
+ * next action with iValue == iValue: useful for MIDI key_release. */
+
+int getNextAction(int chan, char action, int frame, struct action **out, uint32_t iValue=0);
+
+/* getAction
+ * return a pointer to action in chan 'chan' of type 'action' at frame
+ * 'frame'. */
+
+int getAction(int chan, char action, int frame, struct action **out);
+
+/* start/endOverdub */
+
+void startOverdub(int chan, char action, int frame);
+void stopOverdub(int frame);
+
+/* print
+ * debug of the frame stack. */
+
+void print();
+
+} // namespace
+
+#endif
--- /dev/null
+#define IDI_ICON1 101
\ No newline at end of file
--- /dev/null
+#include "resource.h"
+IDI_ICON1 ICON DISCARDABLE "giada.ico"
\ No newline at end of file
--- /dev/null
+### Do not edit -- Generated by 'configure --with-whatever' from Makefile.in
+### RtAudio library Makefile
+
+RM = /bin/rm
+LN = /bin/ln
+
+OBJECTS = RtAudio.o @objects@
+
+LIBNAME = librtaudio
+STATIC = $(LIBNAME).a
+SHARED = @sharedlib@
+RELEASE = 4.1.1
+MAJOR = 4
+LIBRARIES = $(STATIC) $(SHARED)
+
+CC = @CXX@
+AR = @AR@
+RANLIB = @RANLIB@
+
+DEFS = @CPPFLAGS@
+CFLAGS = @CXXFLAGS@ -Iinclude -fPIC
+
+PREFIX = @prefix@
+
+all : $(LIBRARIES)
+
+tests:
+ cd tests && $(MAKE) all
+
+$(LIBRARIES): $(OBJECTS)
+ $(AR) ruv $(STATIC) $(OBJECTS)
+ ranlib $(STATIC)
+ $(CC) -fPIC @libflags@ $(OBJECTS) @LIBS@
+ $(LN) -sf @sharedname@ $(SHARED)
+ $(LN) -sf @sharedname@ $(SHARED).$(MAJOR)
+
+%.o : %.cpp
+ $(CC) $(CFLAGS) $(DEFS) -c $(<) -o $@
+
+%.o : include/%.cpp
+ $(CC) $(CFLAGS) $(DEFS) -c $(<) -o $@
+
+install:
+ install --mode=755 $(STATIC) $(PREFIX)/lib/
+ install --mode=755 @sharedname@ $(PREFIX)/lib/
+ $(LN) -sf @sharedname@ $(PREFIX)/lib/$(SHARED)
+ $(LN) -sf @sharedname@ $(PREFIX)/lib/$(SHARED).$(MAJOR)
+ install --mode=644 $(LIBNAME).pc $(PREFIX)/lib/pkgconfig
+ install --mode=644 RtAudio.h $(PREFIX)/include/
+ install --mode=755 rtaudio-config $(PREFIX)/bin/
+
+uninstall:
+ -@rm -vf $(patsubst %,$(PREFIX)/lib/%, $(LIBRARIES) $(SHARED).$(MAJOR) $(SHARED).$(RELEASE))
+ -@rm -vf $(PREFIX)/lib/pkgconfig/$(LIBNAME).pc
+ -@rm -vf $(PREFIX)/bin/rtaudio-config
+
+clean :
+ $(RM) -f $(LIBRARIES) @sharedname@ $(SHARED)*
+ $(RM) -f $(OBJECTS)
+ $(RM) -f *~
+ cd tests && $(MAKE) clean
+
+distclean:
+ $(RM) -f $(LIBRARIES) @sharedname@ $(SHARED)*
+ $(RM) -f $(OBJECTS)
+ $(RM) -f *~
+ $(RM) -rf config.log config.status autom4te.cache Makefile rtaudio-config $(LIBNAME).pc
+ cd tests && $(MAKE) distclean
+
+strip :
+ strip $(LIBRARIES)
+ ranlib $(LIBRARIES)
+ cd tests && $(MAKE) strip
+
+.PHONY: clean distclean strip install uninstall
--- /dev/null
+/************************************************************************/\r
+/*! \class RtAudio\r
+ \brief Realtime audio i/o C++ classes.\r
+\r
+ RtAudio provides a common API (Application Programming Interface)\r
+ for realtime audio input/output across Linux (native ALSA, Jack,\r
+ and OSS), Macintosh OS X (CoreAudio and Jack), and Windows\r
+ (DirectSound, ASIO and WASAPI) operating systems.\r
+\r
+ RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/\r
+\r
+ RtAudio: realtime audio i/o C++ classes\r
+ Copyright (c) 2001-2014 Gary P. Scavone\r
+\r
+ Permission is hereby granted, free of charge, to any person\r
+ obtaining a copy of this software and associated documentation files\r
+ (the "Software"), to deal in the Software without restriction,\r
+ including without limitation the rights to use, copy, modify, merge,\r
+ publish, distribute, sublicense, and/or sell copies of the Software,\r
+ and to permit persons to whom the Software is furnished to do so,\r
+ subject to the following conditions:\r
+\r
+ The above copyright notice and this permission notice shall be\r
+ included in all copies or substantial portions of the Software.\r
+\r
+ Any person wishing to distribute modifications to the Software is\r
+ asked to send the modifications to the original developer so that\r
+ they can be incorporated into the canonical version. This is,\r
+ however, not a binding provision of this license.\r
+\r
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r
+ ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
+ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+*/\r
+/************************************************************************/\r
+\r
+// RtAudio: Version 4.1.1\r
+\r
+#include "RtAudio.h"\r
+#include <iostream>\r
+#include <cstdlib>\r
+#include <cstring>\r
+#include <climits>\r
+\r
+// Static variable definitions.\r
+const unsigned int RtApi::MAX_SAMPLE_RATES = 14;\r
+const unsigned int RtApi::SAMPLE_RATES[] = {\r
+ 4000, 5512, 8000, 9600, 11025, 16000, 22050,\r
+ 32000, 44100, 48000, 88200, 96000, 176400, 192000\r
+};\r
+\r
+#if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) || defined(__WINDOWS_WASAPI__)\r
+ #define MUTEX_INITIALIZE(A) InitializeCriticalSection(A)\r
+ #define MUTEX_DESTROY(A) DeleteCriticalSection(A)\r
+ #define MUTEX_LOCK(A) EnterCriticalSection(A)\r
+ #define MUTEX_UNLOCK(A) LeaveCriticalSection(A)\r
+#elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)\r
+ // pthread API\r
+ #define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL)\r
+ #define MUTEX_DESTROY(A) pthread_mutex_destroy(A)\r
+ #define MUTEX_LOCK(A) pthread_mutex_lock(A)\r
+ #define MUTEX_UNLOCK(A) pthread_mutex_unlock(A)\r
+#else\r
+ #define MUTEX_INITIALIZE(A) abs(*A) // dummy definitions\r
+ #define MUTEX_DESTROY(A) abs(*A) // dummy definitions\r
+#endif\r
+\r
+// *************************************************** //\r
+//\r
+// RtAudio definitions.\r
+//\r
+// *************************************************** //\r
+\r
+std::string RtAudio :: getVersion( void ) throw()\r
+{\r
+ return RTAUDIO_VERSION;\r
+}\r
+\r
+void RtAudio :: getCompiledApi( std::vector<RtAudio::Api> &apis ) throw()\r
+{\r
+ apis.clear();\r
+\r
+ // The order here will control the order of RtAudio's API search in\r
+ // the constructor.\r
+#if defined(__UNIX_JACK__)\r
+ apis.push_back( UNIX_JACK );\r
+#endif\r
+#if defined(__LINUX_ALSA__)\r
+ apis.push_back( LINUX_ALSA );\r
+#endif\r
+#if defined(__LINUX_PULSE__)\r
+ apis.push_back( LINUX_PULSE );\r
+#endif\r
+#if defined(__LINUX_OSS__)\r
+ apis.push_back( LINUX_OSS );\r
+#endif\r
+#if defined(__WINDOWS_ASIO__)\r
+ apis.push_back( WINDOWS_ASIO );\r
+#endif\r
+#if defined(__WINDOWS_WASAPI__)\r
+ apis.push_back( WINDOWS_WASAPI );\r
+#endif\r
+#if defined(__WINDOWS_DS__)\r
+ apis.push_back( WINDOWS_DS );\r
+#endif\r
+#if defined(__MACOSX_CORE__)\r
+ apis.push_back( MACOSX_CORE );\r
+#endif\r
+#if defined(__RTAUDIO_DUMMY__)\r
+ apis.push_back( RTAUDIO_DUMMY );\r
+#endif\r
+}\r
+\r
+void RtAudio :: openRtApi( RtAudio::Api api )\r
+{\r
+ if ( rtapi_ )\r
+ delete rtapi_;\r
+ rtapi_ = 0;\r
+\r
+#if defined(__UNIX_JACK__)\r
+ if ( api == UNIX_JACK )\r
+ rtapi_ = new RtApiJack();\r
+#endif\r
+#if defined(__LINUX_ALSA__)\r
+ if ( api == LINUX_ALSA )\r
+ rtapi_ = new RtApiAlsa();\r
+#endif\r
+#if defined(__LINUX_PULSE__)\r
+ if ( api == LINUX_PULSE )\r
+ rtapi_ = new RtApiPulse();\r
+#endif\r
+#if defined(__LINUX_OSS__)\r
+ if ( api == LINUX_OSS )\r
+ rtapi_ = new RtApiOss();\r
+#endif\r
+#if defined(__WINDOWS_ASIO__)\r
+ if ( api == WINDOWS_ASIO )\r
+ rtapi_ = new RtApiAsio();\r
+#endif\r
+#if defined(__WINDOWS_WASAPI__)\r
+ if ( api == WINDOWS_WASAPI )\r
+ rtapi_ = new RtApiWasapi();\r
+#endif\r
+#if defined(__WINDOWS_DS__)\r
+ if ( api == WINDOWS_DS )\r
+ rtapi_ = new RtApiDs();\r
+#endif\r
+#if defined(__MACOSX_CORE__)\r
+ if ( api == MACOSX_CORE )\r
+ rtapi_ = new RtApiCore();\r
+#endif\r
+#if defined(__RTAUDIO_DUMMY__)\r
+ if ( api == RTAUDIO_DUMMY )\r
+ rtapi_ = new RtApiDummy();\r
+#endif\r
+}\r
+\r
+RtAudio :: RtAudio( RtAudio::Api api )\r
+{\r
+ rtapi_ = 0;\r
+\r
+ if ( api != UNSPECIFIED ) {\r
+ // Attempt to open the specified API.\r
+ openRtApi( api );\r
+ if ( rtapi_ ) return;\r
+\r
+ // No compiled support for specified API value. Issue a debug\r
+ // warning and continue as if no API was specified.\r
+ std::cerr << "\nRtAudio: no compiled support for specified API argument!\n" << std::endl;\r
+ }\r
+\r
+ // Iterate through the compiled APIs and return as soon as we find\r
+ // one with at least one device or we reach the end of the list.\r
+ std::vector< RtAudio::Api > apis;\r
+ getCompiledApi( apis );\r
+ for ( unsigned int i=0; i<apis.size(); i++ ) {\r
+ openRtApi( apis[i] );\r
+ if ( rtapi_->getDeviceCount() ) break;\r
+ }\r
+\r
+ if ( rtapi_ ) return;\r
+\r
+ // It should not be possible to get here because the preprocessor\r
+ // definition __RTAUDIO_DUMMY__ is automatically defined if no\r
+ // API-specific definitions are passed to the compiler. But just in\r
+ // case something weird happens, we'll thow an error.\r
+ std::string errorText = "\nRtAudio: no compiled API support found ... critical error!!\n\n";\r
+ throw( RtAudioError( errorText, RtAudioError::UNSPECIFIED ) );\r
+}\r
+\r
+RtAudio :: ~RtAudio() throw()\r
+{\r
+ if ( rtapi_ )\r
+ delete rtapi_;\r
+}\r
+\r
+void RtAudio :: openStream( RtAudio::StreamParameters *outputParameters,\r
+ RtAudio::StreamParameters *inputParameters,\r
+ RtAudioFormat format, unsigned int sampleRate,\r
+ unsigned int *bufferFrames,\r
+ RtAudioCallback callback, void *userData,\r
+ RtAudio::StreamOptions *options,\r
+ RtAudioErrorCallback errorCallback )\r
+{\r
+ return rtapi_->openStream( outputParameters, inputParameters, format,\r
+ sampleRate, bufferFrames, callback,\r
+ userData, options, errorCallback );\r
+}\r
+\r
+// *************************************************** //\r
+//\r
+// Public RtApi definitions (see end of file for\r
+// private or protected utility functions).\r
+//\r
+// *************************************************** //\r
+\r
+RtApi :: RtApi()\r
+{\r
+ stream_.state = STREAM_CLOSED;\r
+ stream_.mode = UNINITIALIZED;\r
+ stream_.apiHandle = 0;\r
+ stream_.userBuffer[0] = 0;\r
+ stream_.userBuffer[1] = 0;\r
+ MUTEX_INITIALIZE( &stream_.mutex );\r
+ showWarnings_ = true;\r
+ firstErrorOccurred_ = false;\r
+}\r
+\r
+RtApi :: ~RtApi()\r
+{\r
+ MUTEX_DESTROY( &stream_.mutex );\r
+}\r
+\r
+void RtApi :: openStream( RtAudio::StreamParameters *oParams,\r
+ RtAudio::StreamParameters *iParams,\r
+ RtAudioFormat format, unsigned int sampleRate,\r
+ unsigned int *bufferFrames,\r
+ RtAudioCallback callback, void *userData,\r
+ RtAudio::StreamOptions *options,\r
+ RtAudioErrorCallback errorCallback )\r
+{\r
+ if ( stream_.state != STREAM_CLOSED ) {\r
+ errorText_ = "RtApi::openStream: a stream is already open!";\r
+ error( RtAudioError::INVALID_USE );\r
+ return;\r
+ }\r
+\r
+ // Clear stream information potentially left from a previously open stream.\r
+ clearStreamInfo();\r
+\r
+ if ( oParams && oParams->nChannels < 1 ) {\r
+ errorText_ = "RtApi::openStream: a non-NULL output StreamParameters structure cannot have an nChannels value less than one.";\r
+ error( RtAudioError::INVALID_USE );\r
+ return;\r
+ }\r
+\r
+ if ( iParams && iParams->nChannels < 1 ) {\r
+ errorText_ = "RtApi::openStream: a non-NULL input StreamParameters structure cannot have an nChannels value less than one.";\r
+ error( RtAudioError::INVALID_USE );\r
+ return;\r
+ }\r
+\r
+ if ( oParams == NULL && iParams == NULL ) {\r
+ errorText_ = "RtApi::openStream: input and output StreamParameters structures are both NULL!";\r
+ error( RtAudioError::INVALID_USE );\r
+ return;\r
+ }\r
+\r
+ if ( formatBytes(format) == 0 ) {\r
+ errorText_ = "RtApi::openStream: 'format' parameter value is undefined.";\r
+ error( RtAudioError::INVALID_USE );\r
+ return;\r
+ }\r
+\r
+ unsigned int nDevices = getDeviceCount();\r
+ unsigned int oChannels = 0;\r
+ if ( oParams ) {\r
+ oChannels = oParams->nChannels;\r
+ if ( oParams->deviceId >= nDevices ) {\r
+ errorText_ = "RtApi::openStream: output device parameter value is invalid.";\r
+ error( RtAudioError::INVALID_USE );\r
+ return;\r
+ }\r
+ }\r
+\r
+ unsigned int iChannels = 0;\r
+ if ( iParams ) {\r
+ iChannels = iParams->nChannels;\r
+ if ( iParams->deviceId >= nDevices ) {\r
+ errorText_ = "RtApi::openStream: input device parameter value is invalid.";\r
+ error( RtAudioError::INVALID_USE );\r
+ return;\r
+ }\r
+ }\r
+\r
+ bool result;\r
+\r
+ if ( oChannels > 0 ) {\r
+\r
+ result = probeDeviceOpen( oParams->deviceId, OUTPUT, oChannels, oParams->firstChannel,\r
+ sampleRate, format, bufferFrames, options );\r
+ if ( result == false ) {\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ }\r
+\r
+ if ( iChannels > 0 ) {\r
+\r
+ result = probeDeviceOpen( iParams->deviceId, INPUT, iChannels, iParams->firstChannel,\r
+ sampleRate, format, bufferFrames, options );\r
+ if ( result == false ) {\r
+ if ( oChannels > 0 ) closeStream();\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ }\r
+\r
+ stream_.callbackInfo.callback = (void *) callback;\r
+ stream_.callbackInfo.userData = userData;\r
+ stream_.callbackInfo.errorCallback = (void *) errorCallback;\r
+\r
+ if ( options ) options->numberOfBuffers = stream_.nBuffers;\r
+ stream_.state = STREAM_STOPPED;\r
+}\r
+\r
+unsigned int RtApi :: getDefaultInputDevice( void )\r
+{\r
+ // Should be implemented in subclasses if possible.\r
+ return 0;\r
+}\r
+\r
+unsigned int RtApi :: getDefaultOutputDevice( void )\r
+{\r
+ // Should be implemented in subclasses if possible.\r
+ return 0;\r
+}\r
+\r
+void RtApi :: closeStream( void )\r
+{\r
+ // MUST be implemented in subclasses!\r
+ return;\r
+}\r
+\r
+bool RtApi :: probeDeviceOpen( unsigned int /*device*/, StreamMode /*mode*/, unsigned int /*channels*/,\r
+ unsigned int /*firstChannel*/, unsigned int /*sampleRate*/,\r
+ RtAudioFormat /*format*/, unsigned int * /*bufferSize*/,\r
+ RtAudio::StreamOptions * /*options*/ )\r
+{\r
+ // MUST be implemented in subclasses!\r
+ return FAILURE;\r
+}\r
+\r
+void RtApi :: tickStreamTime( void )\r
+{\r
+ // Subclasses that do not provide their own implementation of\r
+ // getStreamTime should call this function once per buffer I/O to\r
+ // provide basic stream time support.\r
+\r
+ stream_.streamTime += ( stream_.bufferSize * 1.0 / stream_.sampleRate );\r
+\r
+#if defined( HAVE_GETTIMEOFDAY )\r
+ gettimeofday( &stream_.lastTickTimestamp, NULL );\r
+#endif\r
+}\r
+\r
+long RtApi :: getStreamLatency( void )\r
+{\r
+ verifyStream();\r
+\r
+ long totalLatency = 0;\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )\r
+ totalLatency = stream_.latency[0];\r
+ if ( stream_.mode == INPUT || stream_.mode == DUPLEX )\r
+ totalLatency += stream_.latency[1];\r
+\r
+ return totalLatency;\r
+}\r
+\r
+double RtApi :: getStreamTime( void )\r
+{\r
+ verifyStream();\r
+\r
+#if defined( HAVE_GETTIMEOFDAY )\r
+ // Return a very accurate estimate of the stream time by\r
+ // adding in the elapsed time since the last tick.\r
+ struct timeval then;\r
+ struct timeval now;\r
+\r
+ if ( stream_.state != STREAM_RUNNING || stream_.streamTime == 0.0 )\r
+ return stream_.streamTime;\r
+\r
+ gettimeofday( &now, NULL );\r
+ then = stream_.lastTickTimestamp;\r
+ return stream_.streamTime +\r
+ ((now.tv_sec + 0.000001 * now.tv_usec) -\r
+ (then.tv_sec + 0.000001 * then.tv_usec)); \r
+#else\r
+ return stream_.streamTime;\r
+#endif\r
+}\r
+\r
+void RtApi :: setStreamTime( double time )\r
+{\r
+ verifyStream();\r
+\r
+ if ( time >= 0.0 )\r
+ stream_.streamTime = time;\r
+}\r
+\r
+unsigned int RtApi :: getStreamSampleRate( void )\r
+{\r
+ verifyStream();\r
+\r
+ return stream_.sampleRate;\r
+}\r
+\r
+\r
+// *************************************************** //\r
+//\r
+// OS/API-specific methods.\r
+//\r
+// *************************************************** //\r
+\r
+#if defined(__MACOSX_CORE__)\r
+\r
+// The OS X CoreAudio API is designed to use a separate callback\r
+// procedure for each of its audio devices. A single RtAudio duplex\r
+// stream using two different devices is supported here, though it\r
+// cannot be guaranteed to always behave correctly because we cannot\r
+// synchronize these two callbacks.\r
+//\r
+// A property listener is installed for over/underrun information.\r
+// However, no functionality is currently provided to allow property\r
+// listeners to trigger user handlers because it is unclear what could\r
+// be done if a critical stream parameter (buffer size, sample rate,\r
+// device disconnect) notification arrived. The listeners entail\r
+// quite a bit of extra code and most likely, a user program wouldn't\r
+// be prepared for the result anyway. However, we do provide a flag\r
+// to the client callback function to inform of an over/underrun.\r
+\r
+// A structure to hold various information related to the CoreAudio API\r
+// implementation.\r
+struct CoreHandle {\r
+ AudioDeviceID id[2]; // device ids\r
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
+ AudioDeviceIOProcID procId[2];\r
+#endif\r
+ UInt32 iStream[2]; // device stream index (or first if using multiple)\r
+ UInt32 nStreams[2]; // number of streams to use\r
+ bool xrun[2];\r
+ char *deviceBuffer;\r
+ pthread_cond_t condition;\r
+ int drainCounter; // Tracks callback counts when draining\r
+ bool internalDrain; // Indicates if stop is initiated from callback or not.\r
+\r
+ CoreHandle()\r
+ :deviceBuffer(0), drainCounter(0), internalDrain(false) { nStreams[0] = 1; nStreams[1] = 1; id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }\r
+};\r
+\r
+RtApiCore:: RtApiCore()\r
+{\r
+#if defined( AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER )\r
+ // This is a largely undocumented but absolutely necessary\r
+ // requirement starting with OS-X 10.6. If not called, queries and\r
+ // updates to various audio device properties are not handled\r
+ // correctly.\r
+ CFRunLoopRef theRunLoop = NULL;\r
+ AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop,\r
+ kAudioObjectPropertyScopeGlobal,\r
+ kAudioObjectPropertyElementMaster };\r
+ OSStatus result = AudioObjectSetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);\r
+ if ( result != noErr ) {\r
+ errorText_ = "RtApiCore::RtApiCore: error setting run loop property!";\r
+ error( RtAudioError::WARNING );\r
+ }\r
+#endif\r
+}\r
+\r
+RtApiCore :: ~RtApiCore()\r
+{\r
+ // The subclass destructor gets called before the base class\r
+ // destructor, so close an existing stream before deallocating\r
+ // apiDeviceId memory.\r
+ if ( stream_.state != STREAM_CLOSED ) closeStream();\r
+}\r
+\r
+unsigned int RtApiCore :: getDeviceCount( void )\r
+{\r
+ // Find out how many audio devices there are, if any.\r
+ UInt32 dataSize;\r
+ AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
+ OSStatus result = AudioObjectGetPropertyDataSize( kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize );\r
+ if ( result != noErr ) {\r
+ errorText_ = "RtApiCore::getDeviceCount: OS-X error getting device info!";\r
+ error( RtAudioError::WARNING );\r
+ return 0;\r
+ }\r
+\r
+ return dataSize / sizeof( AudioDeviceID );\r
+}\r
+\r
+unsigned int RtApiCore :: getDefaultInputDevice( void )\r
+{\r
+ unsigned int nDevices = getDeviceCount();\r
+ if ( nDevices <= 1 ) return 0;\r
+\r
+ AudioDeviceID id;\r
+ UInt32 dataSize = sizeof( AudioDeviceID );\r
+ AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
+ OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );\r
+ if ( result != noErr ) {\r
+ errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device.";\r
+ error( RtAudioError::WARNING );\r
+ return 0;\r
+ }\r
+\r
+ dataSize *= nDevices;\r
+ AudioDeviceID deviceList[ nDevices ];\r
+ property.mSelector = kAudioHardwarePropertyDevices;\r
+ result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );\r
+ if ( result != noErr ) {\r
+ errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device IDs.";\r
+ error( RtAudioError::WARNING );\r
+ return 0;\r
+ }\r
+\r
+ for ( unsigned int i=0; i<nDevices; i++ )\r
+ if ( id == deviceList[i] ) return i;\r
+\r
+ errorText_ = "RtApiCore::getDefaultInputDevice: No default device found!";\r
+ error( RtAudioError::WARNING );\r
+ return 0;\r
+}\r
+\r
+unsigned int RtApiCore :: getDefaultOutputDevice( void )\r
+{\r
+ unsigned int nDevices = getDeviceCount();\r
+ if ( nDevices <= 1 ) return 0;\r
+\r
+ AudioDeviceID id;\r
+ UInt32 dataSize = sizeof( AudioDeviceID );\r
+ AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
+ OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );\r
+ if ( result != noErr ) {\r
+ errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device.";\r
+ error( RtAudioError::WARNING );\r
+ return 0;\r
+ }\r
+\r
+ dataSize = sizeof( AudioDeviceID ) * nDevices;\r
+ AudioDeviceID deviceList[ nDevices ];\r
+ property.mSelector = kAudioHardwarePropertyDevices;\r
+ result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );\r
+ if ( result != noErr ) {\r
+ errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device IDs.";\r
+ error( RtAudioError::WARNING );\r
+ return 0;\r
+ }\r
+\r
+ for ( unsigned int i=0; i<nDevices; i++ )\r
+ if ( id == deviceList[i] ) return i;\r
+\r
+ errorText_ = "RtApiCore::getDefaultOutputDevice: No default device found!";\r
+ error( RtAudioError::WARNING );\r
+ return 0;\r
+}\r
+\r
+RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )\r
+{\r
+ RtAudio::DeviceInfo info;\r
+ info.probed = false;\r
+\r
+ // Get device ID\r
+ unsigned int nDevices = getDeviceCount();\r
+ if ( nDevices == 0 ) {\r
+ errorText_ = "RtApiCore::getDeviceInfo: no devices found!";\r
+ error( RtAudioError::INVALID_USE );\r
+ return info;\r
+ }\r
+\r
+ if ( device >= nDevices ) {\r
+ errorText_ = "RtApiCore::getDeviceInfo: device ID is invalid!";\r
+ error( RtAudioError::INVALID_USE );\r
+ return info;\r
+ }\r
+\r
+ AudioDeviceID deviceList[ nDevices ];\r
+ UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;\r
+ AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,\r
+ kAudioObjectPropertyScopeGlobal,\r
+ kAudioObjectPropertyElementMaster };\r
+ OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property,\r
+ 0, NULL, &dataSize, (void *) &deviceList );\r
+ if ( result != noErr ) {\r
+ errorText_ = "RtApiCore::getDeviceInfo: OS-X system error getting device IDs.";\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ AudioDeviceID id = deviceList[ device ];\r
+\r
+ // Get the device name.\r
+ info.name.erase();\r
+ CFStringRef cfname;\r
+ dataSize = sizeof( CFStringRef );\r
+ property.mSelector = kAudioObjectPropertyManufacturer;\r
+ result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname );\r
+ if ( result != noErr ) {\r
+ errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device manufacturer.";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ //const char *mname = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );\r
+ int length = CFStringGetLength(cfname);\r
+ char *mname = (char *)malloc(length * 3 + 1);\r
+#if defined( UNICODE ) || defined( _UNICODE )\r
+ CFStringGetCString(cfname, mname, length * 3 + 1, kCFStringEncodingUTF8);\r
+#else\r
+ CFStringGetCString(cfname, mname, length * 3 + 1, CFStringGetSystemEncoding());\r
+#endif\r
+ info.name.append( (const char *)mname, strlen(mname) );\r
+ info.name.append( ": " );\r
+ CFRelease( cfname );\r
+ free(mname);\r
+\r
+ property.mSelector = kAudioObjectPropertyName;\r
+ result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname );\r
+ if ( result != noErr ) {\r
+ errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device name.";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ //const char *name = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );\r
+ length = CFStringGetLength(cfname);\r
+ char *name = (char *)malloc(length * 3 + 1);\r
+#if defined( UNICODE ) || defined( _UNICODE )\r
+ CFStringGetCString(cfname, name, length * 3 + 1, kCFStringEncodingUTF8);\r
+#else\r
+ CFStringGetCString(cfname, name, length * 3 + 1, CFStringGetSystemEncoding());\r
+#endif\r
+ info.name.append( (const char *)name, strlen(name) );\r
+ CFRelease( cfname );\r
+ free(name);\r
+\r
+ // Get the output stream "configuration".\r
+ AudioBufferList *bufferList = nil;\r
+ property.mSelector = kAudioDevicePropertyStreamConfiguration;\r
+ property.mScope = kAudioDevicePropertyScopeOutput;\r
+ // property.mElement = kAudioObjectPropertyElementWildcard;\r
+ dataSize = 0;\r
+ result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
+ if ( result != noErr || dataSize == 0 ) {\r
+ errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration info for device (" << device << ").";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ // Allocate the AudioBufferList.\r
+ bufferList = (AudioBufferList *) malloc( dataSize );\r
+ if ( bufferList == NULL ) {\r
+ errorText_ = "RtApiCore::getDeviceInfo: memory error allocating output AudioBufferList.";\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );\r
+ if ( result != noErr || dataSize == 0 ) {\r
+ free( bufferList );\r
+ errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration for device (" << device << ").";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ // Get output channel information.\r
+ unsigned int i, nStreams = bufferList->mNumberBuffers;\r
+ for ( i=0; i<nStreams; i++ )\r
+ info.outputChannels += bufferList->mBuffers[i].mNumberChannels;\r
+ free( bufferList );\r
+\r
+ // Get the input stream "configuration".\r
+ property.mScope = kAudioDevicePropertyScopeInput;\r
+ result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
+ if ( result != noErr || dataSize == 0 ) {\r
+ errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration info for device (" << device << ").";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ // Allocate the AudioBufferList.\r
+ bufferList = (AudioBufferList *) malloc( dataSize );\r
+ if ( bufferList == NULL ) {\r
+ errorText_ = "RtApiCore::getDeviceInfo: memory error allocating input AudioBufferList.";\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );\r
+ if (result != noErr || dataSize == 0) {\r
+ free( bufferList );\r
+ errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration for device (" << device << ").";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ // Get input channel information.\r
+ nStreams = bufferList->mNumberBuffers;\r
+ for ( i=0; i<nStreams; i++ )\r
+ info.inputChannels += bufferList->mBuffers[i].mNumberChannels;\r
+ free( bufferList );\r
+\r
+ // If device opens for both playback and capture, we determine the channels.\r
+ if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
+ info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
+\r
+ // Probe the device sample rates.\r
+ bool isInput = false;\r
+ if ( info.outputChannels == 0 ) isInput = true;\r
+\r
+ // Determine the supported sample rates.\r
+ property.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;\r
+ if ( isInput == false ) property.mScope = kAudioDevicePropertyScopeOutput;\r
+ result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
+ if ( result != kAudioHardwareNoError || dataSize == 0 ) {\r
+ errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rate info.";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ UInt32 nRanges = dataSize / sizeof( AudioValueRange );\r
+ AudioValueRange rangeList[ nRanges ];\r
+ result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &rangeList );\r
+ if ( result != kAudioHardwareNoError ) {\r
+ errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rates.";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ // The sample rate reporting mechanism is a bit of a mystery. It\r
+ // seems that it can either return individual rates or a range of\r
+ // rates. I assume that if the min / max range values are the same,\r
+ // then that represents a single supported rate and if the min / max\r
+ // range values are different, the device supports an arbitrary\r
+ // range of values (though there might be multiple ranges, so we'll\r
+ // use the most conservative range).\r
+ Float64 minimumRate = 1.0, maximumRate = 10000000000.0;\r
+ bool haveValueRange = false;\r
+ info.sampleRates.clear();\r
+ for ( UInt32 i=0; i<nRanges; i++ ) {\r
+ if ( rangeList[i].mMinimum == rangeList[i].mMaximum )\r
+ info.sampleRates.push_back( (unsigned int) rangeList[i].mMinimum );\r
+ else {\r
+ haveValueRange = true;\r
+ if ( rangeList[i].mMinimum > minimumRate ) minimumRate = rangeList[i].mMinimum;\r
+ if ( rangeList[i].mMaximum < maximumRate ) maximumRate = rangeList[i].mMaximum;\r
+ }\r
+ }\r
+\r
+ if ( haveValueRange ) {\r
+ for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
+ if ( SAMPLE_RATES[k] >= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate )\r
+ info.sampleRates.push_back( SAMPLE_RATES[k] );\r
+ }\r
+ }\r
+\r
+ // Sort and remove any redundant values\r
+ std::sort( info.sampleRates.begin(), info.sampleRates.end() );\r
+ info.sampleRates.erase( unique( info.sampleRates.begin(), info.sampleRates.end() ), info.sampleRates.end() );\r
+\r
+ if ( info.sampleRates.size() == 0 ) {\r
+ errorStream_ << "RtApiCore::probeDeviceInfo: No supported sample rates found for device (" << device << ").";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ // CoreAudio always uses 32-bit floating point data for PCM streams.\r
+ // Thus, any other "physical" formats supported by the device are of\r
+ // no interest to the client.\r
+ info.nativeFormats = RTAUDIO_FLOAT32;\r
+\r
+ if ( info.outputChannels > 0 )\r
+ if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;\r
+ if ( info.inputChannels > 0 )\r
+ if ( getDefaultInputDevice() == device ) info.isDefaultInput = true;\r
+\r
+ info.probed = true;\r
+ return info;\r
+}\r
+\r
+static OSStatus callbackHandler( AudioDeviceID inDevice,\r
+ const AudioTimeStamp* /*inNow*/,\r
+ const AudioBufferList* inInputData,\r
+ const AudioTimeStamp* /*inInputTime*/,\r
+ AudioBufferList* outOutputData,\r
+ const AudioTimeStamp* /*inOutputTime*/,\r
+ void* infoPointer )\r
+{\r
+ CallbackInfo *info = (CallbackInfo *) infoPointer;\r
+\r
+ RtApiCore *object = (RtApiCore *) info->object;\r
+ if ( object->callbackEvent( inDevice, inInputData, outOutputData ) == false )\r
+ return kAudioHardwareUnspecifiedError;\r
+ else\r
+ return kAudioHardwareNoError;\r
+}\r
+\r
+static OSStatus xrunListener( AudioObjectID /*inDevice*/,\r
+ UInt32 nAddresses,\r
+ const AudioObjectPropertyAddress properties[],\r
+ void* handlePointer )\r
+{\r
+ CoreHandle *handle = (CoreHandle *) handlePointer;\r
+ for ( UInt32 i=0; i<nAddresses; i++ ) {\r
+ if ( properties[i].mSelector == kAudioDeviceProcessorOverload ) {\r
+ if ( properties[i].mScope == kAudioDevicePropertyScopeInput )\r
+ handle->xrun[1] = true;\r
+ else\r
+ handle->xrun[0] = true;\r
+ }\r
+ }\r
+\r
+ return kAudioHardwareNoError;\r
+}\r
+\r
+static OSStatus rateListener( AudioObjectID inDevice,\r
+ UInt32 /*nAddresses*/,\r
+ const AudioObjectPropertyAddress /*properties*/[],\r
+ void* ratePointer )\r
+{\r
+ Float64 *rate = (Float64 *) ratePointer;\r
+ UInt32 dataSize = sizeof( Float64 );\r
+ AudioObjectPropertyAddress property = { kAudioDevicePropertyNominalSampleRate,\r
+ kAudioObjectPropertyScopeGlobal,\r
+ kAudioObjectPropertyElementMaster };\r
+ AudioObjectGetPropertyData( inDevice, &property, 0, NULL, &dataSize, rate );\r
+ return kAudioHardwareNoError;\r
+}\r
+\r
+bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
+ unsigned int firstChannel, unsigned int sampleRate,\r
+ RtAudioFormat format, unsigned int *bufferSize,\r
+ RtAudio::StreamOptions *options )\r
+{\r
+ // Get device ID\r
+ unsigned int nDevices = getDeviceCount();\r
+ if ( nDevices == 0 ) {\r
+ // This should not happen because a check is made before this function is called.\r
+ errorText_ = "RtApiCore::probeDeviceOpen: no devices found!";\r
+ return FAILURE;\r
+ }\r
+\r
+ if ( device >= nDevices ) {\r
+ // This should not happen because a check is made before this function is called.\r
+ errorText_ = "RtApiCore::probeDeviceOpen: device ID is invalid!";\r
+ return FAILURE;\r
+ }\r
+\r
+ AudioDeviceID deviceList[ nDevices ];\r
+ UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;\r
+ AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,\r
+ kAudioObjectPropertyScopeGlobal,\r
+ kAudioObjectPropertyElementMaster };\r
+ OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property,\r
+ 0, NULL, &dataSize, (void *) &deviceList );\r
+ if ( result != noErr ) {\r
+ errorText_ = "RtApiCore::probeDeviceOpen: OS-X system error getting device IDs.";\r
+ return FAILURE;\r
+ }\r
+\r
+ AudioDeviceID id = deviceList[ device ];\r
+\r
+ // Setup for stream mode.\r
+ bool isInput = false;\r
+ if ( mode == INPUT ) {\r
+ isInput = true;\r
+ property.mScope = kAudioDevicePropertyScopeInput;\r
+ }\r
+ else\r
+ property.mScope = kAudioDevicePropertyScopeOutput;\r
+\r
+ // Get the stream "configuration".\r
+ AudioBufferList *bufferList = nil;\r
+ dataSize = 0;\r
+ property.mSelector = kAudioDevicePropertyStreamConfiguration;\r
+ result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );\r
+ if ( result != noErr || dataSize == 0 ) {\r
+ errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration info for device (" << device << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Allocate the AudioBufferList.\r
+ bufferList = (AudioBufferList *) malloc( dataSize );\r
+ if ( bufferList == NULL ) {\r
+ errorText_ = "RtApiCore::probeDeviceOpen: memory error allocating AudioBufferList.";\r
+ return FAILURE;\r
+ }\r
+\r
+ result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );\r
+ if (result != noErr || dataSize == 0) {\r
+ free( bufferList );\r
+ errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration for device (" << device << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Search for one or more streams that contain the desired number of\r
+ // channels. CoreAudio devices can have an arbitrary number of\r
+ // streams and each stream can have an arbitrary number of channels.\r
+ // For each stream, a single buffer of interleaved samples is\r
+ // provided. RtAudio prefers the use of one stream of interleaved\r
+ // data or multiple consecutive single-channel streams. However, we\r
+ // now support multiple consecutive multi-channel streams of\r
+ // interleaved data as well.\r
+ UInt32 iStream, offsetCounter = firstChannel;\r
+ UInt32 nStreams = bufferList->mNumberBuffers;\r
+ bool monoMode = false;\r
+ bool foundStream = false;\r
+\r
+ // First check that the device supports the requested number of\r
+ // channels.\r
+ UInt32 deviceChannels = 0;\r
+ for ( iStream=0; iStream<nStreams; iStream++ )\r
+ deviceChannels += bufferList->mBuffers[iStream].mNumberChannels;\r
+\r
+ if ( deviceChannels < ( channels + firstChannel ) ) {\r
+ free( bufferList );\r
+ errorStream_ << "RtApiCore::probeDeviceOpen: the device (" << device << ") does not support the requested channel count.";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Look for a single stream meeting our needs.\r
+ UInt32 firstStream, streamCount = 1, streamChannels = 0, channelOffset = 0;\r
+ for ( iStream=0; iStream<nStreams; iStream++ ) {\r
+ streamChannels = bufferList->mBuffers[iStream].mNumberChannels;\r
+ if ( streamChannels >= channels + offsetCounter ) {\r
+ firstStream = iStream;\r
+ channelOffset = offsetCounter;\r
+ foundStream = true;\r
+ break;\r
+ }\r
+ if ( streamChannels > offsetCounter ) break;\r
+ offsetCounter -= streamChannels;\r
+ }\r
+\r
+ // If we didn't find a single stream above, then we should be able\r
+ // to meet the channel specification with multiple streams.\r
+ if ( foundStream == false ) {\r
+ monoMode = true;\r
+ offsetCounter = firstChannel;\r
+ for ( iStream=0; iStream<nStreams; iStream++ ) {\r
+ streamChannels = bufferList->mBuffers[iStream].mNumberChannels;\r
+ if ( streamChannels > offsetCounter ) break;\r
+ offsetCounter -= streamChannels;\r
+ }\r
+\r
+ firstStream = iStream;\r
+ channelOffset = offsetCounter;\r
+ Int32 channelCounter = channels + offsetCounter - streamChannels;\r
+\r
+ if ( streamChannels > 1 ) monoMode = false;\r
+ while ( channelCounter > 0 ) {\r
+ streamChannels = bufferList->mBuffers[++iStream].mNumberChannels;\r
+ if ( streamChannels > 1 ) monoMode = false;\r
+ channelCounter -= streamChannels;\r
+ streamCount++;\r
+ }\r
+ }\r
+\r
+ free( bufferList );\r
+\r
+ // Determine the buffer size.\r
+ AudioValueRange bufferRange;\r
+ dataSize = sizeof( AudioValueRange );\r
+ property.mSelector = kAudioDevicePropertyBufferFrameSizeRange;\r
+ result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &bufferRange );\r
+\r
+ if ( result != noErr ) {\r
+ errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting buffer size range for device (" << device << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ if ( bufferRange.mMinimum > *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMinimum;\r
+ else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMaximum;\r
+ if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned long) bufferRange.mMinimum;\r
+\r
+ // Set the buffer size. For multiple streams, I'm assuming we only\r
+ // need to make this setting for the master channel.\r
+ UInt32 theSize = (UInt32) *bufferSize;\r
+ dataSize = sizeof( UInt32 );\r
+ property.mSelector = kAudioDevicePropertyBufferFrameSize;\r
+ result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &theSize );\r
+\r
+ if ( result != noErr ) {\r
+ errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting the buffer size for device (" << device << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // If attempting to setup a duplex stream, the bufferSize parameter\r
+ // MUST be the same in both directions!\r
+ *bufferSize = theSize;\r
+ if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {\r
+ errorStream_ << "RtApiCore::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << device << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ stream_.bufferSize = *bufferSize;\r
+ stream_.nBuffers = 1;\r
+\r
+ // Try to set "hog" mode ... it's not clear to me this is working.\r
+ if ( options && options->flags & RTAUDIO_HOG_DEVICE ) {\r
+ pid_t hog_pid;\r
+ dataSize = sizeof( hog_pid );\r
+ property.mSelector = kAudioDevicePropertyHogMode;\r
+ result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &hog_pid );\r
+ if ( result != noErr ) {\r
+ errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting 'hog' state!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ if ( hog_pid != getpid() ) {\r
+ hog_pid = getpid();\r
+ result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &hog_pid );\r
+ if ( result != noErr ) {\r
+ errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting 'hog' state!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ }\r
+ }\r
+\r
+ // Check and if necessary, change the sample rate for the device.\r
+ Float64 nominalRate;\r
+ dataSize = sizeof( Float64 );\r
+ property.mSelector = kAudioDevicePropertyNominalSampleRate;\r
+ result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &nominalRate );\r
+ if ( result != noErr ) {\r
+ errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting current sample rate.";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Only change the sample rate if off by more than 1 Hz.\r
+ if ( fabs( nominalRate - (double)sampleRate ) > 1.0 ) {\r
+\r
+ // Set a property listener for the sample rate change\r
+ Float64 reportedRate = 0.0;\r
+ AudioObjectPropertyAddress tmp = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
+ result = AudioObjectAddPropertyListener( id, &tmp, rateListener, (void *) &reportedRate );\r
+ if ( result != noErr ) {\r
+ errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate property listener for device (" << device << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ nominalRate = (Float64) sampleRate;\r
+ result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &nominalRate );\r
+ if ( result != noErr ) {\r
+ AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate );\r
+ errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate for device (" << device << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Now wait until the reported nominal rate is what we just set.\r
+ UInt32 microCounter = 0;\r
+ while ( reportedRate != nominalRate ) {\r
+ microCounter += 5000;\r
+ if ( microCounter > 5000000 ) break;\r
+ usleep( 5000 );\r
+ }\r
+\r
+ // Remove the property listener.\r
+ AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate );\r
+\r
+ if ( microCounter > 5000000 ) {\r
+ errorStream_ << "RtApiCore::probeDeviceOpen: timeout waiting for sample rate update for device (" << device << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ }\r
+\r
+ // Now set the stream format for all streams. Also, check the\r
+ // physical format of the device and change that if necessary.\r
+ AudioStreamBasicDescription description;\r
+ dataSize = sizeof( AudioStreamBasicDescription );\r
+ property.mSelector = kAudioStreamPropertyVirtualFormat;\r
+ result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &description );\r
+ if ( result != noErr ) {\r
+ errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream format for device (" << device << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Set the sample rate and data format id. However, only make the\r
+ // change if the sample rate is not within 1.0 of the desired\r
+ // rate and the format is not linear pcm.\r
+ bool updateFormat = false;\r
+ if ( fabs( description.mSampleRate - (Float64)sampleRate ) > 1.0 ) {\r
+ description.mSampleRate = (Float64) sampleRate;\r
+ updateFormat = true;\r
+ }\r
+\r
+ if ( description.mFormatID != kAudioFormatLinearPCM ) {\r
+ description.mFormatID = kAudioFormatLinearPCM;\r
+ updateFormat = true;\r
+ }\r
+\r
+ if ( updateFormat ) {\r
+ result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &description );\r
+ if ( result != noErr ) {\r
+ errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate or data format for device (" << device << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ }\r
+\r
+ // Now check the physical format.\r
+ property.mSelector = kAudioStreamPropertyPhysicalFormat;\r
+ result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &description );\r
+ if ( result != noErr ) {\r
+ errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream physical format for device (" << device << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ //std::cout << "Current physical stream format:" << std::endl;\r
+ //std::cout << " mBitsPerChan = " << description.mBitsPerChannel << std::endl;\r
+ //std::cout << " aligned high = " << (description.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (description.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl;\r
+ //std::cout << " bytesPerFrame = " << description.mBytesPerFrame << std::endl;\r
+ //std::cout << " sample rate = " << description.mSampleRate << std::endl;\r
+\r
+ if ( description.mFormatID != kAudioFormatLinearPCM || description.mBitsPerChannel < 16 ) {\r
+ description.mFormatID = kAudioFormatLinearPCM;\r
+ //description.mSampleRate = (Float64) sampleRate;\r
+ AudioStreamBasicDescription testDescription = description;\r
+ UInt32 formatFlags;\r
+\r
+ // We'll try higher bit rates first and then work our way down.\r
+ std::vector< std::pair<UInt32, UInt32> > physicalFormats;\r
+ formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsFloat) & ~kLinearPCMFormatFlagIsSignedInteger;\r
+ physicalFormats.push_back( std::pair<Float32, UInt32>( 32, formatFlags ) );\r
+ formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat;\r
+ physicalFormats.push_back( std::pair<Float32, UInt32>( 32, formatFlags ) );\r
+ physicalFormats.push_back( std::pair<Float32, UInt32>( 24, formatFlags ) ); // 24-bit packed\r
+ formatFlags &= ~( kAudioFormatFlagIsPacked | kAudioFormatFlagIsAlignedHigh );\r
+ physicalFormats.push_back( std::pair<Float32, UInt32>( 24.2, formatFlags ) ); // 24-bit in 4 bytes, aligned low\r
+ formatFlags |= kAudioFormatFlagIsAlignedHigh;\r
+ physicalFormats.push_back( std::pair<Float32, UInt32>( 24.4, formatFlags ) ); // 24-bit in 4 bytes, aligned high\r
+ formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat;\r
+ physicalFormats.push_back( std::pair<Float32, UInt32>( 16, formatFlags ) );\r
+ physicalFormats.push_back( std::pair<Float32, UInt32>( 8, formatFlags ) );\r
+\r
+ bool setPhysicalFormat = false;\r
+ for( unsigned int i=0; i<physicalFormats.size(); i++ ) {\r
+ testDescription = description;\r
+ testDescription.mBitsPerChannel = (UInt32) physicalFormats[i].first;\r
+ testDescription.mFormatFlags = physicalFormats[i].second;\r
+ if ( (24 == (UInt32)physicalFormats[i].first) && ~( physicalFormats[i].second & kAudioFormatFlagIsPacked ) )\r
+ testDescription.mBytesPerFrame = 4 * testDescription.mChannelsPerFrame;\r
+ else\r
+ testDescription.mBytesPerFrame = testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame;\r
+ testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket;\r
+ result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &testDescription );\r
+ if ( result == noErr ) {\r
+ setPhysicalFormat = true;\r
+ //std::cout << "Updated physical stream format:" << std::endl;\r
+ //std::cout << " mBitsPerChan = " << testDescription.mBitsPerChannel << std::endl;\r
+ //std::cout << " aligned high = " << (testDescription.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (testDescription.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl;\r
+ //std::cout << " bytesPerFrame = " << testDescription.mBytesPerFrame << std::endl;\r
+ //std::cout << " sample rate = " << testDescription.mSampleRate << std::endl;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if ( !setPhysicalFormat ) {\r
+ errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting physical data format for device (" << device << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ } // done setting virtual/physical formats.\r
+\r
+ // Get the stream / device latency.\r
+ UInt32 latency;\r
+ dataSize = sizeof( UInt32 );\r
+ property.mSelector = kAudioDevicePropertyLatency;\r
+ if ( AudioObjectHasProperty( id, &property ) == true ) {\r
+ result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &latency );\r
+ if ( result == kAudioHardwareNoError ) stream_.latency[ mode ] = latency;\r
+ else {\r
+ errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting device latency for device (" << device << ").";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ }\r
+ }\r
+\r
+ // Byte-swapping: According to AudioHardware.h, the stream data will\r
+ // always be presented in native-endian format, so we should never\r
+ // need to byte swap.\r
+ stream_.doByteSwap[mode] = false;\r
+\r
+ // From the CoreAudio documentation, PCM data must be supplied as\r
+ // 32-bit floats.\r
+ stream_.userFormat = format;\r
+ stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
+\r
+ if ( streamCount == 1 )\r
+ stream_.nDeviceChannels[mode] = description.mChannelsPerFrame;\r
+ else // multiple streams\r
+ stream_.nDeviceChannels[mode] = channels;\r
+ stream_.nUserChannels[mode] = channels;\r
+ stream_.channelOffset[mode] = channelOffset; // offset within a CoreAudio stream\r
+ if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
+ else stream_.userInterleaved = true;\r
+ stream_.deviceInterleaved[mode] = true;\r
+ if ( monoMode == true ) stream_.deviceInterleaved[mode] = false;\r
+\r
+ // Set flags for buffer conversion.\r
+ stream_.doConvertBuffer[mode] = false;\r
+ if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
+ stream_.doConvertBuffer[mode] = true;\r
+ if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
+ stream_.doConvertBuffer[mode] = true;\r
+ if ( streamCount == 1 ) {\r
+ if ( stream_.nUserChannels[mode] > 1 &&\r
+ stream_.userInterleaved != stream_.deviceInterleaved[mode] )\r
+ stream_.doConvertBuffer[mode] = true;\r
+ }\r
+ else if ( monoMode && stream_.userInterleaved )\r
+ stream_.doConvertBuffer[mode] = true;\r
+\r
+ // Allocate our CoreHandle structure for the stream.\r
+ CoreHandle *handle = 0;\r
+ if ( stream_.apiHandle == 0 ) {\r
+ try {\r
+ handle = new CoreHandle;\r
+ }\r
+ catch ( std::bad_alloc& ) {\r
+ errorText_ = "RtApiCore::probeDeviceOpen: error allocating CoreHandle memory.";\r
+ goto error;\r
+ }\r
+\r
+ if ( pthread_cond_init( &handle->condition, NULL ) ) {\r
+ errorText_ = "RtApiCore::probeDeviceOpen: error initializing pthread condition variable.";\r
+ goto error;\r
+ }\r
+ stream_.apiHandle = (void *) handle;\r
+ }\r
+ else\r
+ handle = (CoreHandle *) stream_.apiHandle;\r
+ handle->iStream[mode] = firstStream;\r
+ handle->nStreams[mode] = streamCount;\r
+ handle->id[mode] = id;\r
+\r
+ // Allocate necessary internal buffers.\r
+ unsigned long bufferBytes;\r
+ bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
+ // stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
+ stream_.userBuffer[mode] = (char *) malloc( bufferBytes * sizeof(char) );\r
+ memset( stream_.userBuffer[mode], 0, bufferBytes * sizeof(char) );\r
+ if ( stream_.userBuffer[mode] == NULL ) {\r
+ errorText_ = "RtApiCore::probeDeviceOpen: error allocating user buffer memory.";\r
+ goto error;\r
+ }\r
+\r
+ // If possible, we will make use of the CoreAudio stream buffers as\r
+ // "device buffers". However, we can't do this if using multiple\r
+ // streams.\r
+ if ( stream_.doConvertBuffer[mode] && handle->nStreams[mode] > 1 ) {\r
+\r
+ bool makeBuffer = true;\r
+ bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
+ if ( mode == INPUT ) {\r
+ if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
+ unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
+ if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
+ }\r
+ }\r
+\r
+ if ( makeBuffer ) {\r
+ bufferBytes *= *bufferSize;\r
+ if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
+ if ( stream_.deviceBuffer == NULL ) {\r
+ errorText_ = "RtApiCore::probeDeviceOpen: error allocating device buffer memory.";\r
+ goto error;\r
+ }\r
+ }\r
+ }\r
+\r
+ stream_.sampleRate = sampleRate;\r
+ stream_.device[mode] = device;\r
+ stream_.state = STREAM_STOPPED;\r
+ stream_.callbackInfo.object = (void *) this;\r
+\r
+ // Setup the buffer conversion information structure.\r
+ if ( stream_.doConvertBuffer[mode] ) {\r
+ if ( streamCount > 1 ) setConvertInfo( mode, 0 );\r
+ else setConvertInfo( mode, channelOffset );\r
+ }\r
+\r
+ if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device )\r
+ // Only one callback procedure per device.\r
+ stream_.mode = DUPLEX;\r
+ else {\r
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
+ result = AudioDeviceCreateIOProcID( id, callbackHandler, (void *) &stream_.callbackInfo, &handle->procId[mode] );\r
+#else\r
+ // deprecated in favor of AudioDeviceCreateIOProcID()\r
+ result = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream_.callbackInfo );\r
+#endif\r
+ if ( result != noErr ) {\r
+ errorStream_ << "RtApiCore::probeDeviceOpen: system error setting callback for device (" << device << ").";\r
+ errorText_ = errorStream_.str();\r
+ goto error;\r
+ }\r
+ if ( stream_.mode == OUTPUT && mode == INPUT )\r
+ stream_.mode = DUPLEX;\r
+ else\r
+ stream_.mode = mode;\r
+ }\r
+\r
+ // Setup the device property listener for over/underload.\r
+ property.mSelector = kAudioDeviceProcessorOverload;\r
+ property.mScope = kAudioObjectPropertyScopeGlobal;\r
+ result = AudioObjectAddPropertyListener( id, &property, xrunListener, (void *) handle );\r
+\r
+ return SUCCESS;\r
+\r
+ error:\r
+ if ( handle ) {\r
+ pthread_cond_destroy( &handle->condition );\r
+ delete handle;\r
+ stream_.apiHandle = 0;\r
+ }\r
+\r
+ for ( int i=0; i<2; i++ ) {\r
+ if ( stream_.userBuffer[i] ) {\r
+ free( stream_.userBuffer[i] );\r
+ stream_.userBuffer[i] = 0;\r
+ }\r
+ }\r
+\r
+ if ( stream_.deviceBuffer ) {\r
+ free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = 0;\r
+ }\r
+\r
+ stream_.state = STREAM_CLOSED;\r
+ return FAILURE;\r
+}\r
+\r
+void RtApiCore :: closeStream( void )\r
+{\r
+ if ( stream_.state == STREAM_CLOSED ) {\r
+ errorText_ = "RtApiCore::closeStream(): no open stream to close!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+ if ( stream_.state == STREAM_RUNNING )\r
+ AudioDeviceStop( handle->id[0], callbackHandler );\r
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
+ AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] );\r
+#else\r
+ // deprecated in favor of AudioDeviceDestroyIOProcID()\r
+ AudioDeviceRemoveIOProc( handle->id[0], callbackHandler );\r
+#endif\r
+ }\r
+\r
+ if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {\r
+ if ( stream_.state == STREAM_RUNNING )\r
+ AudioDeviceStop( handle->id[1], callbackHandler );\r
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
+ AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] );\r
+#else\r
+ // deprecated in favor of AudioDeviceDestroyIOProcID()\r
+ AudioDeviceRemoveIOProc( handle->id[1], callbackHandler );\r
+#endif\r
+ }\r
+\r
+ for ( int i=0; i<2; i++ ) {\r
+ if ( stream_.userBuffer[i] ) {\r
+ free( stream_.userBuffer[i] );\r
+ stream_.userBuffer[i] = 0;\r
+ }\r
+ }\r
+\r
+ if ( stream_.deviceBuffer ) {\r
+ free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = 0;\r
+ }\r
+\r
+ // Destroy pthread condition variable.\r
+ pthread_cond_destroy( &handle->condition );\r
+ delete handle;\r
+ stream_.apiHandle = 0;\r
+\r
+ stream_.mode = UNINITIALIZED;\r
+ stream_.state = STREAM_CLOSED;\r
+}\r
+\r
+void RtApiCore :: startStream( void )\r
+{\r
+ verifyStream();\r
+ if ( stream_.state == STREAM_RUNNING ) {\r
+ errorText_ = "RtApiCore::startStream(): the stream is already running!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ OSStatus result = noErr;\r
+ CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+\r
+ result = AudioDeviceStart( handle->id[0], callbackHandler );\r
+ if ( result != noErr ) {\r
+ errorStream_ << "RtApiCore::startStream: system error (" << getErrorCode( result ) << ") starting callback procedure on device (" << stream_.device[0] << ").";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+ }\r
+\r
+ if ( stream_.mode == INPUT ||\r
+ ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {\r
+\r
+ result = AudioDeviceStart( handle->id[1], callbackHandler );\r
+ if ( result != noErr ) {\r
+ errorStream_ << "RtApiCore::startStream: system error starting input callback procedure on device (" << stream_.device[1] << ").";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+ }\r
+\r
+ handle->drainCounter = 0;\r
+ handle->internalDrain = false;\r
+ stream_.state = STREAM_RUNNING;\r
+\r
+ unlock:\r
+ if ( result == noErr ) return;\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+}\r
+\r
+void RtApiCore :: stopStream( void )\r
+{\r
+ verifyStream();\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ errorText_ = "RtApiCore::stopStream(): the stream is already stopped!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ OSStatus result = noErr;\r
+ CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+\r
+ if ( handle->drainCounter == 0 ) {\r
+ handle->drainCounter = 2;\r
+ pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled\r
+ }\r
+\r
+ result = AudioDeviceStop( handle->id[0], callbackHandler );\r
+ if ( result != noErr ) {\r
+ errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping callback procedure on device (" << stream_.device[0] << ").";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+ }\r
+\r
+ if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {\r
+\r
+ result = AudioDeviceStop( handle->id[1], callbackHandler );\r
+ if ( result != noErr ) {\r
+ errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping input callback procedure on device (" << stream_.device[1] << ").";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+ }\r
+\r
+ stream_.state = STREAM_STOPPED;\r
+\r
+ unlock:\r
+ if ( result == noErr ) return;\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+}\r
+\r
+void RtApiCore :: abortStream( void )\r
+{\r
+ verifyStream();\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ errorText_ = "RtApiCore::abortStream(): the stream is already stopped!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
+ handle->drainCounter = 2;\r
+\r
+ stopStream();\r
+}\r
+\r
+// This function will be called by a spawned thread when the user\r
+// callback function signals that the stream should be stopped or\r
+// aborted. It is better to handle it this way because the\r
+// callbackEvent() function probably should return before the AudioDeviceStop()\r
+// function is called.\r
+static void *coreStopStream( void *ptr )\r
+{\r
+ CallbackInfo *info = (CallbackInfo *) ptr;\r
+ RtApiCore *object = (RtApiCore *) info->object;\r
+\r
+ object->stopStream();\r
+ pthread_exit( NULL );\r
+}\r
+\r
+bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,\r
+ const AudioBufferList *inBufferList,\r
+ const AudioBufferList *outBufferList )\r
+{\r
+ if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;\r
+ if ( stream_.state == STREAM_CLOSED ) {\r
+ errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
+ error( RtAudioError::WARNING );\r
+ return FAILURE;\r
+ }\r
+\r
+ CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
+ CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
+\r
+ // Check if we were draining the stream and signal is finished.\r
+ if ( handle->drainCounter > 3 ) {\r
+ ThreadHandle threadId;\r
+\r
+ stream_.state = STREAM_STOPPING;\r
+ if ( handle->internalDrain == true )\r
+ pthread_create( &threadId, NULL, coreStopStream, info );\r
+ else // external call to stopStream()\r
+ pthread_cond_signal( &handle->condition );\r
+ return SUCCESS;\r
+ }\r
+\r
+ AudioDeviceID outputDevice = handle->id[0];\r
+\r
+ // Invoke user callback to get fresh output data UNLESS we are\r
+ // draining stream or duplex mode AND the input/output devices are\r
+ // different AND this function is called for the input device.\r
+ if ( handle->drainCounter == 0 && ( stream_.mode != DUPLEX || deviceId == outputDevice ) ) {\r
+ RtAudioCallback callback = (RtAudioCallback) info->callback;\r
+ double streamTime = getStreamTime();\r
+ RtAudioStreamStatus status = 0;\r
+ if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
+ status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
+ handle->xrun[0] = false;\r
+ }\r
+ if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
+ status |= RTAUDIO_INPUT_OVERFLOW;\r
+ handle->xrun[1] = false;\r
+ }\r
+\r
+ int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
+ stream_.bufferSize, streamTime, status, info->userData );\r
+ if ( cbReturnValue == 2 ) {\r
+ stream_.state = STREAM_STOPPING;\r
+ handle->drainCounter = 2;\r
+ abortStream();\r
+ return SUCCESS;\r
+ }\r
+ else if ( cbReturnValue == 1 ) {\r
+ handle->drainCounter = 1;\r
+ handle->internalDrain = true;\r
+ }\r
+ }\r
+\r
+ if ( stream_.mode == OUTPUT || ( stream_.mode == DUPLEX && deviceId == outputDevice ) ) {\r
+\r
+ if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
+\r
+ if ( handle->nStreams[0] == 1 ) {\r
+ memset( outBufferList->mBuffers[handle->iStream[0]].mData,\r
+ 0,\r
+ outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );\r
+ }\r
+ else { // fill multiple streams with zeros\r
+ for ( unsigned int i=0; i<handle->nStreams[0]; i++ ) {\r
+ memset( outBufferList->mBuffers[handle->iStream[0]+i].mData,\r
+ 0,\r
+ outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize );\r
+ }\r
+ }\r
+ }\r
+ else if ( handle->nStreams[0] == 1 ) {\r
+ if ( stream_.doConvertBuffer[0] ) { // convert directly to CoreAudio stream buffer\r
+ convertBuffer( (char *) outBufferList->mBuffers[handle->iStream[0]].mData,\r
+ stream_.userBuffer[0], stream_.convertInfo[0] );\r
+ }\r
+ else { // copy from user buffer\r
+ memcpy( outBufferList->mBuffers[handle->iStream[0]].mData,\r
+ stream_.userBuffer[0],\r
+ outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );\r
+ }\r
+ }\r
+ else { // fill multiple streams\r
+ Float32 *inBuffer = (Float32 *) stream_.userBuffer[0];\r
+ if ( stream_.doConvertBuffer[0] ) {\r
+ convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
+ inBuffer = (Float32 *) stream_.deviceBuffer;\r
+ }\r
+\r
+ if ( stream_.deviceInterleaved[0] == false ) { // mono mode\r
+ UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;\r
+ for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
+ memcpy( outBufferList->mBuffers[handle->iStream[0]+i].mData,\r
+ (void *)&inBuffer[i*stream_.bufferSize], bufferBytes );\r
+ }\r
+ }\r
+ else { // fill multiple multi-channel streams with interleaved data\r
+ UInt32 streamChannels, channelsLeft, inJump, outJump, inOffset;\r
+ Float32 *out, *in;\r
+\r
+ bool inInterleaved = ( stream_.userInterleaved ) ? true : false;\r
+ UInt32 inChannels = stream_.nUserChannels[0];\r
+ if ( stream_.doConvertBuffer[0] ) {\r
+ inInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode\r
+ inChannels = stream_.nDeviceChannels[0];\r
+ }\r
+\r
+ if ( inInterleaved ) inOffset = 1;\r
+ else inOffset = stream_.bufferSize;\r
+\r
+ channelsLeft = inChannels;\r
+ for ( unsigned int i=0; i<handle->nStreams[0]; i++ ) {\r
+ in = inBuffer;\r
+ out = (Float32 *) outBufferList->mBuffers[handle->iStream[0]+i].mData;\r
+ streamChannels = outBufferList->mBuffers[handle->iStream[0]+i].mNumberChannels;\r
+\r
+ outJump = 0;\r
+ // Account for possible channel offset in first stream\r
+ if ( i == 0 && stream_.channelOffset[0] > 0 ) {\r
+ streamChannels -= stream_.channelOffset[0];\r
+ outJump = stream_.channelOffset[0];\r
+ out += outJump;\r
+ }\r
+\r
+ // Account for possible unfilled channels at end of the last stream\r
+ if ( streamChannels > channelsLeft ) {\r
+ outJump = streamChannels - channelsLeft;\r
+ streamChannels = channelsLeft;\r
+ }\r
+\r
+ // Determine input buffer offsets and skips\r
+ if ( inInterleaved ) {\r
+ inJump = inChannels;\r
+ in += inChannels - channelsLeft;\r
+ }\r
+ else {\r
+ inJump = 1;\r
+ in += (inChannels - channelsLeft) * inOffset;\r
+ }\r
+\r
+ for ( unsigned int i=0; i<stream_.bufferSize; i++ ) {\r
+ for ( unsigned int j=0; j<streamChannels; j++ ) {\r
+ *out++ = in[j*inOffset];\r
+ }\r
+ out += outJump;\r
+ in += inJump;\r
+ }\r
+ channelsLeft -= streamChannels;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ // Don't bother draining input\r
+ if ( handle->drainCounter ) {\r
+ handle->drainCounter++;\r
+ goto unlock;\r
+ }\r
+\r
+ AudioDeviceID inputDevice;\r
+ inputDevice = handle->id[1];\r
+ if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == inputDevice ) ) {\r
+\r
+ if ( handle->nStreams[1] == 1 ) {\r
+ if ( stream_.doConvertBuffer[1] ) { // convert directly from CoreAudio stream buffer\r
+ convertBuffer( stream_.userBuffer[1],\r
+ (char *) inBufferList->mBuffers[handle->iStream[1]].mData,\r
+ stream_.convertInfo[1] );\r
+ }\r
+ else { // copy to user buffer\r
+ memcpy( stream_.userBuffer[1],\r
+ inBufferList->mBuffers[handle->iStream[1]].mData,\r
+ inBufferList->mBuffers[handle->iStream[1]].mDataByteSize );\r
+ }\r
+ }\r
+ else { // read from multiple streams\r
+ Float32 *outBuffer = (Float32 *) stream_.userBuffer[1];\r
+ if ( stream_.doConvertBuffer[1] ) outBuffer = (Float32 *) stream_.deviceBuffer;\r
+\r
+ if ( stream_.deviceInterleaved[1] == false ) { // mono mode\r
+ UInt32 bufferBytes = inBufferList->mBuffers[handle->iStream[1]].mDataByteSize;\r
+ for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
+ memcpy( (void *)&outBuffer[i*stream_.bufferSize],\r
+ inBufferList->mBuffers[handle->iStream[1]+i].mData, bufferBytes );\r
+ }\r
+ }\r
+ else { // read from multiple multi-channel streams\r
+ UInt32 streamChannels, channelsLeft, inJump, outJump, outOffset;\r
+ Float32 *out, *in;\r
+\r
+ bool outInterleaved = ( stream_.userInterleaved ) ? true : false;\r
+ UInt32 outChannels = stream_.nUserChannels[1];\r
+ if ( stream_.doConvertBuffer[1] ) {\r
+ outInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode\r
+ outChannels = stream_.nDeviceChannels[1];\r
+ }\r
+\r
+ if ( outInterleaved ) outOffset = 1;\r
+ else outOffset = stream_.bufferSize;\r
+\r
+ channelsLeft = outChannels;\r
+ for ( unsigned int i=0; i<handle->nStreams[1]; i++ ) {\r
+ out = outBuffer;\r
+ in = (Float32 *) inBufferList->mBuffers[handle->iStream[1]+i].mData;\r
+ streamChannels = inBufferList->mBuffers[handle->iStream[1]+i].mNumberChannels;\r
+\r
+ inJump = 0;\r
+ // Account for possible channel offset in first stream\r
+ if ( i == 0 && stream_.channelOffset[1] > 0 ) {\r
+ streamChannels -= stream_.channelOffset[1];\r
+ inJump = stream_.channelOffset[1];\r
+ in += inJump;\r
+ }\r
+\r
+ // Account for possible unread channels at end of the last stream\r
+ if ( streamChannels > channelsLeft ) {\r
+ inJump = streamChannels - channelsLeft;\r
+ streamChannels = channelsLeft;\r
+ }\r
+\r
+ // Determine output buffer offsets and skips\r
+ if ( outInterleaved ) {\r
+ outJump = outChannels;\r
+ out += outChannels - channelsLeft;\r
+ }\r
+ else {\r
+ outJump = 1;\r
+ out += (outChannels - channelsLeft) * outOffset;\r
+ }\r
+\r
+ for ( unsigned int i=0; i<stream_.bufferSize; i++ ) {\r
+ for ( unsigned int j=0; j<streamChannels; j++ ) {\r
+ out[j*outOffset] = *in++;\r
+ }\r
+ out += outJump;\r
+ in += inJump;\r
+ }\r
+ channelsLeft -= streamChannels;\r
+ }\r
+ }\r
+ \r
+ if ( stream_.doConvertBuffer[1] ) { // convert from our internal "device" buffer\r
+ convertBuffer( stream_.userBuffer[1],\r
+ stream_.deviceBuffer,\r
+ stream_.convertInfo[1] );\r
+ }\r
+ }\r
+ }\r
+\r
+ unlock:\r
+ //MUTEX_UNLOCK( &stream_.mutex );\r
+\r
+ RtApi::tickStreamTime();\r
+ return SUCCESS;\r
+}\r
+\r
+const char* RtApiCore :: getErrorCode( OSStatus code )\r
+{\r
+ switch( code ) {\r
+\r
+ case kAudioHardwareNotRunningError:\r
+ return "kAudioHardwareNotRunningError";\r
+\r
+ case kAudioHardwareUnspecifiedError:\r
+ return "kAudioHardwareUnspecifiedError";\r
+\r
+ case kAudioHardwareUnknownPropertyError:\r
+ return "kAudioHardwareUnknownPropertyError";\r
+\r
+ case kAudioHardwareBadPropertySizeError:\r
+ return "kAudioHardwareBadPropertySizeError";\r
+\r
+ case kAudioHardwareIllegalOperationError:\r
+ return "kAudioHardwareIllegalOperationError";\r
+\r
+ case kAudioHardwareBadObjectError:\r
+ return "kAudioHardwareBadObjectError";\r
+\r
+ case kAudioHardwareBadDeviceError:\r
+ return "kAudioHardwareBadDeviceError";\r
+\r
+ case kAudioHardwareBadStreamError:\r
+ return "kAudioHardwareBadStreamError";\r
+\r
+ case kAudioHardwareUnsupportedOperationError:\r
+ return "kAudioHardwareUnsupportedOperationError";\r
+\r
+ case kAudioDeviceUnsupportedFormatError:\r
+ return "kAudioDeviceUnsupportedFormatError";\r
+\r
+ case kAudioDevicePermissionsError:\r
+ return "kAudioDevicePermissionsError";\r
+\r
+ default:\r
+ return "CoreAudio unknown error";\r
+ }\r
+}\r
+\r
+ //******************** End of __MACOSX_CORE__ *********************//\r
+#endif\r
+\r
+#if defined(__UNIX_JACK__)\r
+\r
+// JACK is a low-latency audio server, originally written for the\r
+// GNU/Linux operating system and now also ported to OS-X. It can\r
+// connect a number of different applications to an audio device, as\r
+// well as allowing them to share audio between themselves.\r
+//\r
+// When using JACK with RtAudio, "devices" refer to JACK clients that\r
+// have ports connected to the server. The JACK server is typically\r
+// started in a terminal as follows:\r
+//\r
+// .jackd -d alsa -d hw:0\r
+//\r
+// or through an interface program such as qjackctl. Many of the\r
+// parameters normally set for a stream are fixed by the JACK server\r
+// and can be specified when the JACK server is started. In\r
+// particular,\r
+//\r
+// .jackd -d alsa -d hw:0 -r 44100 -p 512 -n 4\r
+//\r
+// specifies a sample rate of 44100 Hz, a buffer size of 512 sample\r
+// frames, and number of buffers = 4. Once the server is running, it\r
+// is not possible to override these values. If the values are not\r
+// specified in the command-line, the JACK server uses default values.\r
+//\r
+// The JACK server does not have to be running when an instance of\r
+// RtApiJack is created, though the function getDeviceCount() will\r
+// report 0 devices found until JACK has been started. When no\r
+// devices are available (i.e., the JACK server is not running), a\r
+// stream cannot be opened.\r
+\r
+#include <jack/jack.h>\r
+#include <unistd.h>\r
+#include <cstdio>\r
+\r
+// A structure to hold various information related to the Jack API\r
+// implementation.\r
+struct JackHandle {\r
+ jack_client_t *client;\r
+ jack_port_t **ports[2];\r
+ std::string deviceName[2];\r
+ bool xrun[2];\r
+ pthread_cond_t condition;\r
+ int drainCounter; // Tracks callback counts when draining\r
+ bool internalDrain; // Indicates if stop is initiated from callback or not.\r
+\r
+ JackHandle()\r
+ :client(0), drainCounter(0), internalDrain(false) { ports[0] = 0; ports[1] = 0; xrun[0] = false; xrun[1] = false; }\r
+};\r
+\r
+/* --- Monocasual hack ---------------------------------------------- */\r
+#ifdef __linux__\r
+void *RtApi :: __HACK__getJackClient() {\r
+ JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
+ return (void*) handle->client;\r
+}\r
+#endif\r
+/* ------------------------------------------------------------------ */\r
+\r
+static void jackSilentError( const char * ) {};\r
+\r
+RtApiJack :: RtApiJack()\r
+{\r
+ // Nothing to do here.\r
+#if !defined(__RTAUDIO_DEBUG__)\r
+ // Turn off Jack's internal error reporting.\r
+ jack_set_error_function( &jackSilentError );\r
+#endif\r
+}\r
+\r
+RtApiJack :: ~RtApiJack()\r
+{\r
+ if ( stream_.state != STREAM_CLOSED ) closeStream();\r
+}\r
+\r
+unsigned int RtApiJack :: getDeviceCount( void )\r
+{\r
+ // See if we can become a jack client.\r
+ jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption;\r
+ jack_status_t *status = NULL;\r
+ jack_client_t *client = jack_client_open( "RtApiJackCount", options, status );\r
+ if ( client == 0 ) return 0;\r
+\r
+ const char **ports;\r
+ std::string port, previousPort;\r
+ unsigned int nChannels = 0, nDevices = 0;\r
+ ports = jack_get_ports( client, NULL, NULL, 0 );\r
+ if ( ports ) {\r
+ // Parse the port names up to the first colon (:).\r
+ size_t iColon = 0;\r
+ do {\r
+ port = (char *) ports[ nChannels ];\r
+ iColon = port.find(":");\r
+ if ( iColon != std::string::npos ) {\r
+ port = port.substr( 0, iColon + 1 );\r
+ if ( port != previousPort ) {\r
+ nDevices++;\r
+ previousPort = port;\r
+ }\r
+ }\r
+ } while ( ports[++nChannels] );\r
+ free( ports );\r
+ }\r
+\r
+ jack_client_close( client );\r
+ return nDevices;\r
+}\r
+\r
+RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )\r
+{\r
+ RtAudio::DeviceInfo info;\r
+ info.probed = false;\r
+\r
+ jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption\r
+ jack_status_t *status = NULL;\r
+ jack_client_t *client = jack_client_open( "RtApiJackInfo", options, status );\r
+ if ( client == 0 ) {\r
+ errorText_ = "RtApiJack::getDeviceInfo: Jack server not found or connection error!";\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ const char **ports;\r
+ std::string port, previousPort;\r
+ unsigned int nPorts = 0, nDevices = 0;\r
+ ports = jack_get_ports( client, NULL, NULL, 0 );\r
+ if ( ports ) {\r
+ // Parse the port names up to the first colon (:).\r
+ size_t iColon = 0;\r
+ do {\r
+ port = (char *) ports[ nPorts ];\r
+ iColon = port.find(":");\r
+ if ( iColon != std::string::npos ) {\r
+ port = port.substr( 0, iColon );\r
+ if ( port != previousPort ) {\r
+ if ( nDevices == device ) info.name = port;\r
+ nDevices++;\r
+ previousPort = port;\r
+ }\r
+ }\r
+ } while ( ports[++nPorts] );\r
+ free( ports );\r
+ }\r
+\r
+ if ( device >= nDevices ) {\r
+ jack_client_close( client );\r
+ errorText_ = "RtApiJack::getDeviceInfo: device ID is invalid!";\r
+ error( RtAudioError::INVALID_USE );\r
+ return info;\r
+ }\r
+\r
+ // Get the current jack server sample rate.\r
+ info.sampleRates.clear();\r
+ info.sampleRates.push_back( jack_get_sample_rate( client ) );\r
+\r
+ // Count the available ports containing the client name as device\r
+ // channels. Jack "input ports" equal RtAudio output channels.\r
+ unsigned int nChannels = 0;\r
+ ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsInput );\r
+ if ( ports ) {\r
+ while ( ports[ nChannels ] ) nChannels++;\r
+ free( ports );\r
+ info.outputChannels = nChannels;\r
+ }\r
+\r
+ // Jack "output ports" equal RtAudio input channels.\r
+ nChannels = 0;\r
+ ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsOutput );\r
+ if ( ports ) {\r
+ while ( ports[ nChannels ] ) nChannels++;\r
+ free( ports );\r
+ info.inputChannels = nChannels;\r
+ }\r
+\r
+ if ( info.outputChannels == 0 && info.inputChannels == 0 ) {\r
+ jack_client_close(client);\r
+ errorText_ = "RtApiJack::getDeviceInfo: error determining Jack input/output channels!";\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ // If device opens for both playback and capture, we determine the channels.\r
+ if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
+ info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
+\r
+ // Jack always uses 32-bit floats.\r
+ info.nativeFormats = RTAUDIO_FLOAT32;\r
+\r
+ // Jack doesn't provide default devices so we'll use the first available one.\r
+ if ( device == 0 && info.outputChannels > 0 )\r
+ info.isDefaultOutput = true;\r
+ if ( device == 0 && info.inputChannels > 0 )\r
+ info.isDefaultInput = true;\r
+\r
+ jack_client_close(client);\r
+ info.probed = true;\r
+ return info;\r
+}\r
+\r
+static int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer )\r
+{\r
+ CallbackInfo *info = (CallbackInfo *) infoPointer;\r
+\r
+ RtApiJack *object = (RtApiJack *) info->object;\r
+ if ( object->callbackEvent( (unsigned long) nframes ) == false ) return 1;\r
+\r
+ return 0;\r
+}\r
+\r
+// This function will be called by a spawned thread when the Jack\r
+// server signals that it is shutting down. It is necessary to handle\r
+// it this way because the jackShutdown() function must return before\r
+// the jack_deactivate() function (in closeStream()) will return.\r
+static void *jackCloseStream( void *ptr )\r
+{\r
+ CallbackInfo *info = (CallbackInfo *) ptr;\r
+ RtApiJack *object = (RtApiJack *) info->object;\r
+\r
+ object->closeStream();\r
+\r
+ pthread_exit( NULL );\r
+}\r
+static void jackShutdown( void *infoPointer )\r
+{\r
+ CallbackInfo *info = (CallbackInfo *) infoPointer;\r
+ RtApiJack *object = (RtApiJack *) info->object;\r
+\r
+ // Check current stream state. If stopped, then we'll assume this\r
+ // was called as a result of a call to RtApiJack::stopStream (the\r
+ // deactivation of a client handle causes this function to be called).\r
+ // If not, we'll assume the Jack server is shutting down or some\r
+ // other problem occurred and we should close the stream.\r
+ if ( object->isStreamRunning() == false ) return;\r
+\r
+ ThreadHandle threadId;\r
+ pthread_create( &threadId, NULL, jackCloseStream, info );\r
+ std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl;\r
+}\r
+\r
+static int jackXrun( void *infoPointer )\r
+{\r
+ JackHandle *handle = (JackHandle *) infoPointer;\r
+\r
+ if ( handle->ports[0] ) handle->xrun[0] = true;\r
+ if ( handle->ports[1] ) handle->xrun[1] = true;\r
+\r
+ return 0;\r
+}\r
+\r
+bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
+ unsigned int firstChannel, unsigned int sampleRate,\r
+ RtAudioFormat format, unsigned int *bufferSize,\r
+ RtAudio::StreamOptions *options )\r
+{\r
+ JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
+\r
+ // Look for jack server and try to become a client (only do once per stream).\r
+ jack_client_t *client = 0;\r
+ if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) {\r
+ jack_options_t jackoptions = (jack_options_t) ( JackNoStartServer ); //JackNullOption;\r
+ jack_status_t *status = NULL;\r
+ if ( options && !options->streamName.empty() )\r
+ client = jack_client_open( options->streamName.c_str(), jackoptions, status );\r
+ else\r
+ client = jack_client_open( "RtApiJack", jackoptions, status );\r
+ if ( client == 0 ) {\r
+ errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!";\r
+ error( RtAudioError::WARNING );\r
+ return FAILURE;\r
+ }\r
+ }\r
+ else {\r
+ // The handle must have been created on an earlier pass.\r
+ client = handle->client;\r
+ }\r
+\r
+ const char **ports;\r
+ std::string port, previousPort, deviceName;\r
+ unsigned int nPorts = 0, nDevices = 0;\r
+ ports = jack_get_ports( client, NULL, NULL, 0 );\r
+ if ( ports ) {\r
+ // Parse the port names up to the first colon (:).\r
+ size_t iColon = 0;\r
+ do {\r
+ port = (char *) ports[ nPorts ];\r
+ iColon = port.find(":");\r
+ if ( iColon != std::string::npos ) {\r
+ port = port.substr( 0, iColon );\r
+ if ( port != previousPort ) {\r
+ if ( nDevices == device ) deviceName = port;\r
+ nDevices++;\r
+ previousPort = port;\r
+ }\r
+ }\r
+ } while ( ports[++nPorts] );\r
+ free( ports );\r
+ }\r
+\r
+ if ( device >= nDevices ) {\r
+ errorText_ = "RtApiJack::probeDeviceOpen: device ID is invalid!";\r
+ return FAILURE;\r
+ }\r
+\r
+ // Count the available ports containing the client name as device\r
+ // channels. Jack "input ports" equal RtAudio output channels.\r
+ unsigned int nChannels = 0;\r
+ unsigned long flag = JackPortIsInput;\r
+ if ( mode == INPUT ) flag = JackPortIsOutput;\r
+ ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );\r
+ if ( ports ) {\r
+ while ( ports[ nChannels ] ) nChannels++;\r
+ free( ports );\r
+ }\r
+\r
+ // Compare the jack ports for specified client to the requested number of channels.\r
+ if ( nChannels < (channels + firstChannel) ) {\r
+ errorStream_ << "RtApiJack::probeDeviceOpen: requested number of channels (" << channels << ") + offset (" << firstChannel << ") not found for specified device (" << device << ":" << deviceName << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Check the jack server sample rate.\r
+ unsigned int jackRate = jack_get_sample_rate( client );\r
+ if ( sampleRate != jackRate ) {\r
+ jack_client_close( client );\r
+ errorStream_ << "RtApiJack::probeDeviceOpen: the requested sample rate (" << sampleRate << ") is different than the JACK server rate (" << jackRate << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ stream_.sampleRate = jackRate;\r
+\r
+ // Get the latency of the JACK port.\r
+ ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );\r
+ if ( ports[ firstChannel ] ) {\r
+ // Added by Ge Wang\r
+ jack_latency_callback_mode_t cbmode = (mode == INPUT ? JackCaptureLatency : JackPlaybackLatency);\r
+ // the range (usually the min and max are equal)\r
+ jack_latency_range_t latrange; latrange.min = latrange.max = 0;\r
+ // get the latency range\r
+ jack_port_get_latency_range( jack_port_by_name( client, ports[firstChannel] ), cbmode, &latrange );\r
+ // be optimistic, use the min!\r
+ stream_.latency[mode] = latrange.min;\r
+ //stream_.latency[mode] = jack_port_get_latency( jack_port_by_name( client, ports[ firstChannel ] ) );\r
+ }\r
+ free( ports );\r
+\r
+ // The jack server always uses 32-bit floating-point data.\r
+ stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
+ stream_.userFormat = format;\r
+\r
+ if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
+ else stream_.userInterleaved = true;\r
+\r
+ // Jack always uses non-interleaved buffers.\r
+ stream_.deviceInterleaved[mode] = false;\r
+\r
+ // Jack always provides host byte-ordered data.\r
+ stream_.doByteSwap[mode] = false;\r
+\r
+ // Get the buffer size. The buffer size and number of buffers\r
+ // (periods) is set when the jack server is started.\r
+ stream_.bufferSize = (int) jack_get_buffer_size( client );\r
+ *bufferSize = stream_.bufferSize;\r
+\r
+ stream_.nDeviceChannels[mode] = channels;\r
+ stream_.nUserChannels[mode] = channels;\r
+\r
+ // Set flags for buffer conversion.\r
+ stream_.doConvertBuffer[mode] = false;\r
+ if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
+ stream_.doConvertBuffer[mode] = true;\r
+ if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
+ stream_.nUserChannels[mode] > 1 )\r
+ stream_.doConvertBuffer[mode] = true;\r
+\r
+ // Allocate our JackHandle structure for the stream.\r
+ if ( handle == 0 ) {\r
+ try {\r
+ handle = new JackHandle;\r
+ }\r
+ catch ( std::bad_alloc& ) {\r
+ errorText_ = "RtApiJack::probeDeviceOpen: error allocating JackHandle memory.";\r
+ goto error;\r
+ }\r
+\r
+ if ( pthread_cond_init(&handle->condition, NULL) ) {\r
+ errorText_ = "RtApiJack::probeDeviceOpen: error initializing pthread condition variable.";\r
+ goto error;\r
+ }\r
+ stream_.apiHandle = (void *) handle;\r
+ handle->client = client;\r
+ }\r
+ handle->deviceName[mode] = deviceName;\r
+\r
+ // Allocate necessary internal buffers.\r
+ unsigned long bufferBytes;\r
+ bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
+ stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
+ if ( stream_.userBuffer[mode] == NULL ) {\r
+ errorText_ = "RtApiJack::probeDeviceOpen: error allocating user buffer memory.";\r
+ goto error;\r
+ }\r
+\r
+ if ( stream_.doConvertBuffer[mode] ) {\r
+\r
+ bool makeBuffer = true;\r
+ if ( mode == OUTPUT )\r
+ bufferBytes = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
+ else { // mode == INPUT\r
+ bufferBytes = stream_.nDeviceChannels[1] * formatBytes( stream_.deviceFormat[1] );\r
+ if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
+ unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);\r
+ if ( bufferBytes < bytesOut ) makeBuffer = false;\r
+ }\r
+ }\r
+\r
+ if ( makeBuffer ) {\r
+ bufferBytes *= *bufferSize;\r
+ if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
+ if ( stream_.deviceBuffer == NULL ) {\r
+ errorText_ = "RtApiJack::probeDeviceOpen: error allocating device buffer memory.";\r
+ goto error;\r
+ }\r
+ }\r
+ }\r
+\r
+ // Allocate memory for the Jack ports (channels) identifiers.\r
+ handle->ports[mode] = (jack_port_t **) malloc ( sizeof (jack_port_t *) * channels );\r
+ if ( handle->ports[mode] == NULL ) {\r
+ errorText_ = "RtApiJack::probeDeviceOpen: error allocating port memory.";\r
+ goto error;\r
+ }\r
+\r
+ stream_.device[mode] = device;\r
+ stream_.channelOffset[mode] = firstChannel;\r
+ stream_.state = STREAM_STOPPED;\r
+ stream_.callbackInfo.object = (void *) this;\r
+\r
+ if ( stream_.mode == OUTPUT && mode == INPUT )\r
+ // We had already set up the stream for output.\r
+ stream_.mode = DUPLEX;\r
+ else {\r
+ stream_.mode = mode;\r
+ jack_set_process_callback( handle->client, jackCallbackHandler, (void *) &stream_.callbackInfo );\r
+ jack_set_xrun_callback( handle->client, jackXrun, (void *) &handle );\r
+ jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo );\r
+ }\r
+\r
+ // Register our ports.\r
+ char label[64];\r
+ if ( mode == OUTPUT ) {\r
+ for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
+ snprintf( label, 64, "outport %d", i );\r
+ handle->ports[0][i] = jack_port_register( handle->client, (const char *)label,\r
+ JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );\r
+ }\r
+ }\r
+ else {\r
+ for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
+ snprintf( label, 64, "inport %d", i );\r
+ handle->ports[1][i] = jack_port_register( handle->client, (const char *)label,\r
+ JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 );\r
+ }\r
+ }\r
+\r
+ // Setup the buffer conversion information structure. We don't use\r
+ // buffers to do channel offsets, so we override that parameter\r
+ // here.\r
+ if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );\r
+\r
+ return SUCCESS;\r
+\r
+ error:\r
+ if ( handle ) {\r
+ pthread_cond_destroy( &handle->condition );\r
+ jack_client_close( handle->client );\r
+\r
+ if ( handle->ports[0] ) free( handle->ports[0] );\r
+ if ( handle->ports[1] ) free( handle->ports[1] );\r
+\r
+ delete handle;\r
+ stream_.apiHandle = 0;\r
+ }\r
+\r
+ for ( int i=0; i<2; i++ ) {\r
+ if ( stream_.userBuffer[i] ) {\r
+ free( stream_.userBuffer[i] );\r
+ stream_.userBuffer[i] = 0;\r
+ }\r
+ }\r
+\r
+ if ( stream_.deviceBuffer ) {\r
+ free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = 0;\r
+ }\r
+\r
+ return FAILURE;\r
+}\r
+\r
+void RtApiJack :: closeStream( void )\r
+{\r
+ if ( stream_.state == STREAM_CLOSED ) {\r
+ errorText_ = "RtApiJack::closeStream(): no open stream to close!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
+ if ( handle ) {\r
+\r
+ if ( stream_.state == STREAM_RUNNING )\r
+ jack_deactivate( handle->client );\r
+\r
+ jack_client_close( handle->client );\r
+ }\r
+\r
+ if ( handle ) {\r
+ if ( handle->ports[0] ) free( handle->ports[0] );\r
+ if ( handle->ports[1] ) free( handle->ports[1] );\r
+ pthread_cond_destroy( &handle->condition );\r
+ delete handle;\r
+ stream_.apiHandle = 0;\r
+ }\r
+\r
+ for ( int i=0; i<2; i++ ) {\r
+ if ( stream_.userBuffer[i] ) {\r
+ free( stream_.userBuffer[i] );\r
+ stream_.userBuffer[i] = 0;\r
+ }\r
+ }\r
+\r
+ if ( stream_.deviceBuffer ) {\r
+ free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = 0;\r
+ }\r
+\r
+ stream_.mode = UNINITIALIZED;\r
+ stream_.state = STREAM_CLOSED;\r
+}\r
+\r
+void RtApiJack :: startStream( void )\r
+{\r
+ verifyStream();\r
+ if ( stream_.state == STREAM_RUNNING ) {\r
+ errorText_ = "RtApiJack::startStream(): the stream is already running!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
+ int result = jack_activate( handle->client );\r
+ if ( result ) {\r
+ errorText_ = "RtApiJack::startStream(): unable to activate JACK client!";\r
+ goto unlock;\r
+ }\r
+\r
+ const char **ports;\r
+\r
+ // Get the list of available ports.\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+ result = 1;\r
+ ports = jack_get_ports( handle->client, handle->deviceName[0].c_str(), NULL, JackPortIsInput);\r
+ if ( ports == NULL) {\r
+ errorText_ = "RtApiJack::startStream(): error determining available JACK input ports!";\r
+ goto unlock;\r
+ }\r
+\r
+ // Now make the port connections. Since RtAudio wasn't designed to\r
+ // allow the user to select particular channels of a device, we'll\r
+ // just open the first "nChannels" ports with offset.\r
+ for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
+ result = 1;\r
+ if ( ports[ stream_.channelOffset[0] + i ] )\r
+ result = jack_connect( handle->client, jack_port_name( handle->ports[0][i] ), ports[ stream_.channelOffset[0] + i ] );\r
+ if ( result ) {\r
+ free( ports );\r
+ errorText_ = "RtApiJack::startStream(): error connecting output ports!";\r
+ goto unlock;\r
+ }\r
+ }\r
+ free(ports);\r
+ }\r
+\r
+ if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
+ result = 1;\r
+ ports = jack_get_ports( handle->client, handle->deviceName[1].c_str(), NULL, JackPortIsOutput );\r
+ if ( ports == NULL) {\r
+ errorText_ = "RtApiJack::startStream(): error determining available JACK output ports!";\r
+ goto unlock;\r
+ }\r
+\r
+ // Now make the port connections. See note above.\r
+ for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
+ result = 1;\r
+ if ( ports[ stream_.channelOffset[1] + i ] )\r
+ result = jack_connect( handle->client, ports[ stream_.channelOffset[1] + i ], jack_port_name( handle->ports[1][i] ) );\r
+ if ( result ) {\r
+ free( ports );\r
+ errorText_ = "RtApiJack::startStream(): error connecting input ports!";\r
+ goto unlock;\r
+ }\r
+ }\r
+ free(ports);\r
+ }\r
+\r
+ handle->drainCounter = 0;\r
+ handle->internalDrain = false;\r
+ stream_.state = STREAM_RUNNING;\r
+\r
+ unlock:\r
+ if ( result == 0 ) return;\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+}\r
+\r
+void RtApiJack :: stopStream( void )\r
+{\r
+ verifyStream();\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ errorText_ = "RtApiJack::stopStream(): the stream is already stopped!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+\r
+ if ( handle->drainCounter == 0 ) {\r
+ handle->drainCounter = 2;\r
+ pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled\r
+ }\r
+ }\r
+\r
+ jack_deactivate( handle->client );\r
+ stream_.state = STREAM_STOPPED;\r
+}\r
+\r
+void RtApiJack :: abortStream( void )\r
+{\r
+ verifyStream();\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ errorText_ = "RtApiJack::abortStream(): the stream is already stopped!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
+ handle->drainCounter = 2;\r
+\r
+ stopStream();\r
+}\r
+\r
+// This function will be called by a spawned thread when the user\r
+// callback function signals that the stream should be stopped or\r
+// aborted. It is necessary to handle it this way because the\r
+// callbackEvent() function must return before the jack_deactivate()\r
+// function will return.\r
+static void *jackStopStream( void *ptr )\r
+{\r
+ CallbackInfo *info = (CallbackInfo *) ptr;\r
+ RtApiJack *object = (RtApiJack *) info->object;\r
+\r
+ object->stopStream();\r
+ pthread_exit( NULL );\r
+}\r
+\r
+bool RtApiJack :: callbackEvent( unsigned long nframes )\r
+{\r
+ if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;\r
+ if ( stream_.state == STREAM_CLOSED ) {\r
+ errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
+ error( RtAudioError::WARNING );\r
+ return FAILURE;\r
+ }\r
+ if ( stream_.bufferSize != nframes ) {\r
+ errorText_ = "RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process!";\r
+ error( RtAudioError::WARNING );\r
+ return FAILURE;\r
+ }\r
+\r
+ CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
+ JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
+\r
+ // Check if we were draining the stream and signal is finished.\r
+ if ( handle->drainCounter > 3 ) {\r
+ ThreadHandle threadId;\r
+\r
+ stream_.state = STREAM_STOPPING;\r
+ if ( handle->internalDrain == true )\r
+ pthread_create( &threadId, NULL, jackStopStream, info );\r
+ else\r
+ pthread_cond_signal( &handle->condition );\r
+ return SUCCESS;\r
+ }\r
+\r
+ // Invoke user callback first, to get fresh output data.\r
+ if ( handle->drainCounter == 0 ) {\r
+ RtAudioCallback callback = (RtAudioCallback) info->callback;\r
+ double streamTime = getStreamTime();\r
+ RtAudioStreamStatus status = 0;\r
+ if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
+ status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
+ handle->xrun[0] = false;\r
+ }\r
+ if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
+ status |= RTAUDIO_INPUT_OVERFLOW;\r
+ handle->xrun[1] = false;\r
+ }\r
+ int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
+ stream_.bufferSize, streamTime, status, info->userData );\r
+ if ( cbReturnValue == 2 ) {\r
+ stream_.state = STREAM_STOPPING;\r
+ handle->drainCounter = 2;\r
+ ThreadHandle id;\r
+ pthread_create( &id, NULL, jackStopStream, info );\r
+ return SUCCESS;\r
+ }\r
+ else if ( cbReturnValue == 1 ) {\r
+ handle->drainCounter = 1;\r
+ handle->internalDrain = true;\r
+ }\r
+ }\r
+\r
+ jack_default_audio_sample_t *jackbuffer;\r
+ unsigned long bufferBytes = nframes * sizeof( jack_default_audio_sample_t );\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+\r
+ if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
+\r
+ for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {\r
+ jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );\r
+ memset( jackbuffer, 0, bufferBytes );\r
+ }\r
+\r
+ }\r
+ else if ( stream_.doConvertBuffer[0] ) {\r
+\r
+ convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
+\r
+ for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {\r
+ jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );\r
+ memcpy( jackbuffer, &stream_.deviceBuffer[i*bufferBytes], bufferBytes );\r
+ }\r
+ }\r
+ else { // no buffer conversion\r
+ for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {\r
+ jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );\r
+ memcpy( jackbuffer, &stream_.userBuffer[0][i*bufferBytes], bufferBytes );\r
+ }\r
+ }\r
+ }\r
+\r
+ // Don't bother draining input\r
+ if ( handle->drainCounter ) {\r
+ handle->drainCounter++;\r
+ goto unlock;\r
+ }\r
+\r
+ if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
+\r
+ if ( stream_.doConvertBuffer[1] ) {\r
+ for ( unsigned int i=0; i<stream_.nDeviceChannels[1]; i++ ) {\r
+ jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );\r
+ memcpy( &stream_.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes );\r
+ }\r
+ convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
+ }\r
+ else { // no buffer conversion\r
+ for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {\r
+ jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );\r
+ memcpy( &stream_.userBuffer[1][i*bufferBytes], jackbuffer, bufferBytes );\r
+ }\r
+ }\r
+ }\r
+\r
+ unlock:\r
+ RtApi::tickStreamTime();\r
+ return SUCCESS;\r
+}\r
+ //******************** End of __UNIX_JACK__ *********************//\r
+#endif\r
+\r
+#if defined(__WINDOWS_ASIO__) // ASIO API on Windows\r
+\r
+// The ASIO API is designed around a callback scheme, so this\r
+// implementation is similar to that used for OS-X CoreAudio and Linux\r
+// Jack. The primary constraint with ASIO is that it only allows\r
+// access to a single driver at a time. Thus, it is not possible to\r
+// have more than one simultaneous RtAudio stream.\r
+//\r
+// This implementation also requires a number of external ASIO files\r
+// and a few global variables. The ASIO callback scheme does not\r
+// allow for the passing of user data, so we must create a global\r
+// pointer to our callbackInfo structure.\r
+//\r
+// On unix systems, we make use of a pthread condition variable.\r
+// Since there is no equivalent in Windows, I hacked something based\r
+// on information found in\r
+// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html.\r
+\r
+#include "asiosys.h"\r
+#include "asio.h"\r
+#include "iasiothiscallresolver.h"\r
+#include "asiodrivers.h"\r
+#include <cmath>\r
+\r
+static AsioDrivers drivers;\r
+static ASIOCallbacks asioCallbacks;\r
+static ASIODriverInfo driverInfo;\r
+static CallbackInfo *asioCallbackInfo;\r
+static bool asioXRun;\r
+\r
+struct AsioHandle {\r
+ int drainCounter; // Tracks callback counts when draining\r
+ bool internalDrain; // Indicates if stop is initiated from callback or not.\r
+ ASIOBufferInfo *bufferInfos;\r
+ HANDLE condition;\r
+\r
+ AsioHandle()\r
+ :drainCounter(0), internalDrain(false), bufferInfos(0) {}\r
+};\r
+\r
+// Function declarations (definitions at end of section)\r
+static const char* getAsioErrorString( ASIOError result );\r
+static void sampleRateChanged( ASIOSampleRate sRate );\r
+static long asioMessages( long selector, long value, void* message, double* opt );\r
+\r
+RtApiAsio :: RtApiAsio()\r
+{\r
+ // ASIO cannot run on a multi-threaded appartment. You can call\r
+ // CoInitialize beforehand, but it must be for appartment threading\r
+ // (in which case, CoInitilialize will return S_FALSE here).\r
+ coInitialized_ = false;\r
+ HRESULT hr = CoInitialize( NULL ); \r
+ if ( FAILED(hr) ) {\r
+ errorText_ = "RtApiAsio::ASIO requires a single-threaded appartment. Call CoInitializeEx(0,COINIT_APARTMENTTHREADED)";\r
+ error( RtAudioError::WARNING );\r
+ }\r
+ coInitialized_ = true;\r
+\r
+ drivers.removeCurrentDriver();\r
+ driverInfo.asioVersion = 2;\r
+\r
+ // See note in DirectSound implementation about GetDesktopWindow().\r
+ driverInfo.sysRef = GetForegroundWindow();\r
+}\r
+\r
+RtApiAsio :: ~RtApiAsio()\r
+{\r
+ if ( stream_.state != STREAM_CLOSED ) closeStream();\r
+ if ( coInitialized_ ) CoUninitialize();\r
+}\r
+\r
+unsigned int RtApiAsio :: getDeviceCount( void )\r
+{\r
+ return (unsigned int) drivers.asioGetNumDev();\r
+}\r
+\r
+RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )\r
+{\r
+ RtAudio::DeviceInfo info;\r
+ info.probed = false;\r
+\r
+ // Get device ID\r
+ unsigned int nDevices = getDeviceCount();\r
+ if ( nDevices == 0 ) {\r
+ errorText_ = "RtApiAsio::getDeviceInfo: no devices found!";\r
+ error( RtAudioError::INVALID_USE );\r
+ return info;\r
+ }\r
+\r
+ if ( device >= nDevices ) {\r
+ errorText_ = "RtApiAsio::getDeviceInfo: device ID is invalid!";\r
+ error( RtAudioError::INVALID_USE );\r
+ return info;\r
+ }\r
+\r
+ // If a stream is already open, we cannot probe other devices. Thus, use the saved results.\r
+ if ( stream_.state != STREAM_CLOSED ) {\r
+ if ( device >= devices_.size() ) {\r
+ errorText_ = "RtApiAsio::getDeviceInfo: device ID was not present before stream was opened.";\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+ return devices_[ device ];\r
+ }\r
+\r
+ char driverName[32];\r
+ ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );\r
+ if ( result != ASE_OK ) {\r
+ errorStream_ << "RtApiAsio::getDeviceInfo: unable to get driver name (" << getAsioErrorString( result ) << ").";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ info.name = driverName;\r
+\r
+ if ( !drivers.loadDriver( driverName ) ) {\r
+ errorStream_ << "RtApiAsio::getDeviceInfo: unable to load driver (" << driverName << ").";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ result = ASIOInit( &driverInfo );\r
+ if ( result != ASE_OK ) {\r
+ errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ // Determine the device channel information.\r
+ long inputChannels, outputChannels;\r
+ result = ASIOGetChannels( &inputChannels, &outputChannels );\r
+ if ( result != ASE_OK ) {\r
+ drivers.removeCurrentDriver();\r
+ errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ info.outputChannels = outputChannels;\r
+ info.inputChannels = inputChannels;\r
+ if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
+ info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
+\r
+ // Determine the supported sample rates.\r
+ info.sampleRates.clear();\r
+ for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {\r
+ result = ASIOCanSampleRate( (ASIOSampleRate) SAMPLE_RATES[i] );\r
+ if ( result == ASE_OK )\r
+ info.sampleRates.push_back( SAMPLE_RATES[i] );\r
+ }\r
+\r
+ // Determine supported data types ... just check first channel and assume rest are the same.\r
+ ASIOChannelInfo channelInfo;\r
+ channelInfo.channel = 0;\r
+ channelInfo.isInput = true;\r
+ if ( info.inputChannels <= 0 ) channelInfo.isInput = false;\r
+ result = ASIOGetChannelInfo( &channelInfo );\r
+ if ( result != ASE_OK ) {\r
+ drivers.removeCurrentDriver();\r
+ errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting driver channel info (" << driverName << ").";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ info.nativeFormats = 0;\r
+ if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB )\r
+ info.nativeFormats |= RTAUDIO_SINT16;\r
+ else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB )\r
+ info.nativeFormats |= RTAUDIO_SINT32;\r
+ else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB )\r
+ info.nativeFormats |= RTAUDIO_FLOAT32;\r
+ else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB )\r
+ info.nativeFormats |= RTAUDIO_FLOAT64;\r
+ else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB )\r
+ info.nativeFormats |= RTAUDIO_SINT24;\r
+\r
+ if ( info.outputChannels > 0 )\r
+ if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;\r
+ if ( info.inputChannels > 0 )\r
+ if ( getDefaultInputDevice() == device ) info.isDefaultInput = true;\r
+\r
+ info.probed = true;\r
+ drivers.removeCurrentDriver();\r
+ return info;\r
+}\r
+\r
+static void bufferSwitch( long index, ASIOBool /*processNow*/ )\r
+{\r
+ RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object;\r
+ object->callbackEvent( index );\r
+}\r
+\r
+void RtApiAsio :: saveDeviceInfo( void )\r
+{\r
+ devices_.clear();\r
+\r
+ unsigned int nDevices = getDeviceCount();\r
+ devices_.resize( nDevices );\r
+ for ( unsigned int i=0; i<nDevices; i++ )\r
+ devices_[i] = getDeviceInfo( i );\r
+}\r
+\r
+bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
+ unsigned int firstChannel, unsigned int sampleRate,\r
+ RtAudioFormat format, unsigned int *bufferSize,\r
+ RtAudio::StreamOptions *options )\r
+{\r
+ // For ASIO, a duplex stream MUST use the same driver.\r
+ if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] != device ) {\r
+ errorText_ = "RtApiAsio::probeDeviceOpen: an ASIO duplex stream must use the same device for input and output!";\r
+ return FAILURE;\r
+ }\r
+\r
+ char driverName[32];\r
+ ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );\r
+ if ( result != ASE_OK ) {\r
+ errorStream_ << "RtApiAsio::probeDeviceOpen: unable to get driver name (" << getAsioErrorString( result ) << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Only load the driver once for duplex stream.\r
+ if ( mode != INPUT || stream_.mode != OUTPUT ) {\r
+ // The getDeviceInfo() function will not work when a stream is open\r
+ // because ASIO does not allow multiple devices to run at the same\r
+ // time. Thus, we'll probe the system before opening a stream and\r
+ // save the results for use by getDeviceInfo().\r
+ this->saveDeviceInfo();\r
+\r
+ if ( !drivers.loadDriver( driverName ) ) {\r
+ errorStream_ << "RtApiAsio::probeDeviceOpen: unable to load driver (" << driverName << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ result = ASIOInit( &driverInfo );\r
+ if ( result != ASE_OK ) {\r
+ errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ }\r
+\r
+ // Check the device channel count.\r
+ long inputChannels, outputChannels;\r
+ result = ASIOGetChannels( &inputChannels, &outputChannels );\r
+ if ( result != ASE_OK ) {\r
+ drivers.removeCurrentDriver();\r
+ errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) ||\r
+ ( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) {\r
+ drivers.removeCurrentDriver();\r
+ errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ stream_.nDeviceChannels[mode] = channels;\r
+ stream_.nUserChannels[mode] = channels;\r
+ stream_.channelOffset[mode] = firstChannel;\r
+\r
+ // Verify the sample rate is supported.\r
+ result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate );\r
+ if ( result != ASE_OK ) {\r
+ drivers.removeCurrentDriver();\r
+ errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Get the current sample rate\r
+ ASIOSampleRate currentRate;\r
+ result = ASIOGetSampleRate( ¤tRate );\r
+ if ( result != ASE_OK ) {\r
+ drivers.removeCurrentDriver();\r
+ errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error getting sample rate.";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Set the sample rate only if necessary\r
+ if ( currentRate != sampleRate ) {\r
+ result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate );\r
+ if ( result != ASE_OK ) {\r
+ drivers.removeCurrentDriver();\r
+ errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ }\r
+\r
+ // Determine the driver data type.\r
+ ASIOChannelInfo channelInfo;\r
+ channelInfo.channel = 0;\r
+ if ( mode == OUTPUT ) channelInfo.isInput = false;\r
+ else channelInfo.isInput = true;\r
+ result = ASIOGetChannelInfo( &channelInfo );\r
+ if ( result != ASE_OK ) {\r
+ drivers.removeCurrentDriver();\r
+ errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format.";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Assuming WINDOWS host is always little-endian.\r
+ stream_.doByteSwap[mode] = false;\r
+ stream_.userFormat = format;\r
+ stream_.deviceFormat[mode] = 0;\r
+ if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) {\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
+ if ( channelInfo.type == ASIOSTInt16MSB ) stream_.doByteSwap[mode] = true;\r
+ }\r
+ else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) {\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
+ if ( channelInfo.type == ASIOSTInt32MSB ) stream_.doByteSwap[mode] = true;\r
+ }\r
+ else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) {\r
+ stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
+ if ( channelInfo.type == ASIOSTFloat32MSB ) stream_.doByteSwap[mode] = true;\r
+ }\r
+ else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) {\r
+ stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;\r
+ if ( channelInfo.type == ASIOSTFloat64MSB ) stream_.doByteSwap[mode] = true;\r
+ }\r
+ else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB ) {\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
+ if ( channelInfo.type == ASIOSTInt24MSB ) stream_.doByteSwap[mode] = true;\r
+ }\r
+\r
+ if ( stream_.deviceFormat[mode] == 0 ) {\r
+ drivers.removeCurrentDriver();\r
+ errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio.";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Set the buffer size. For a duplex stream, this will end up\r
+ // setting the buffer size based on the input constraints, which\r
+ // should be ok.\r
+ long minSize, maxSize, preferSize, granularity;\r
+ result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity );\r
+ if ( result != ASE_OK ) {\r
+ drivers.removeCurrentDriver();\r
+ errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size.";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;\r
+ else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;\r
+ else if ( granularity == -1 ) {\r
+ // Make sure bufferSize is a power of two.\r
+ int log2_of_min_size = 0;\r
+ int log2_of_max_size = 0;\r
+\r
+ for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) {\r
+ if ( minSize & ((long)1 << i) ) log2_of_min_size = i;\r
+ if ( maxSize & ((long)1 << i) ) log2_of_max_size = i;\r
+ }\r
+\r
+ long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) );\r
+ int min_delta_num = log2_of_min_size;\r
+\r
+ for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) {\r
+ long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) );\r
+ if (current_delta < min_delta) {\r
+ min_delta = current_delta;\r
+ min_delta_num = i;\r
+ }\r
+ }\r
+\r
+ *bufferSize = ( (unsigned int)1 << min_delta_num );\r
+ if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;\r
+ else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;\r
+ }\r
+ else if ( granularity != 0 ) {\r
+ // Set to an even multiple of granularity, rounding up.\r
+ *bufferSize = (*bufferSize + granularity-1) / granularity * granularity;\r
+ }\r
+\r
+ if ( mode == INPUT && stream_.mode == OUTPUT && stream_.bufferSize != *bufferSize ) {\r
+ drivers.removeCurrentDriver();\r
+ errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!";\r
+ return FAILURE;\r
+ }\r
+\r
+ stream_.bufferSize = *bufferSize;\r
+ stream_.nBuffers = 2;\r
+\r
+ if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
+ else stream_.userInterleaved = true;\r
+\r
+ // ASIO always uses non-interleaved buffers.\r
+ stream_.deviceInterleaved[mode] = false;\r
+\r
+ // Allocate, if necessary, our AsioHandle structure for the stream.\r
+ AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
+ if ( handle == 0 ) {\r
+ try {\r
+ handle = new AsioHandle;\r
+ }\r
+ catch ( std::bad_alloc& ) {\r
+ //if ( handle == NULL ) { \r
+ drivers.removeCurrentDriver();\r
+ errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory.";\r
+ return FAILURE;\r
+ }\r
+ handle->bufferInfos = 0;\r
+\r
+ // Create a manual-reset event.\r
+ handle->condition = CreateEvent( NULL, // no security\r
+ TRUE, // manual-reset\r
+ FALSE, // non-signaled initially\r
+ NULL ); // unnamed\r
+ stream_.apiHandle = (void *) handle;\r
+ }\r
+\r
+ // Create the ASIO internal buffers. Since RtAudio sets up input\r
+ // and output separately, we'll have to dispose of previously\r
+ // created output buffers for a duplex stream.\r
+ long inputLatency, outputLatency;\r
+ if ( mode == INPUT && stream_.mode == OUTPUT ) {\r
+ ASIODisposeBuffers();\r
+ if ( handle->bufferInfos ) free( handle->bufferInfos );\r
+ }\r
+\r
+ // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.\r
+ bool buffersAllocated = false;\r
+ unsigned int i, nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];\r
+ handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) );\r
+ if ( handle->bufferInfos == NULL ) {\r
+ errorStream_ << "RtApiAsio::probeDeviceOpen: error allocating bufferInfo memory for driver (" << driverName << ").";\r
+ errorText_ = errorStream_.str();\r
+ goto error;\r
+ }\r
+\r
+ ASIOBufferInfo *infos;\r
+ infos = handle->bufferInfos;\r
+ for ( i=0; i<stream_.nDeviceChannels[0]; i++, infos++ ) {\r
+ infos->isInput = ASIOFalse;\r
+ infos->channelNum = i + stream_.channelOffset[0];\r
+ infos->buffers[0] = infos->buffers[1] = 0;\r
+ }\r
+ for ( i=0; i<stream_.nDeviceChannels[1]; i++, infos++ ) {\r
+ infos->isInput = ASIOTrue;\r
+ infos->channelNum = i + stream_.channelOffset[1];\r
+ infos->buffers[0] = infos->buffers[1] = 0;\r
+ }\r
+\r
+ // Set up the ASIO callback structure and create the ASIO data buffers.\r
+ asioCallbacks.bufferSwitch = &bufferSwitch;\r
+ asioCallbacks.sampleRateDidChange = &sampleRateChanged;\r
+ asioCallbacks.asioMessage = &asioMessages;\r
+ asioCallbacks.bufferSwitchTimeInfo = NULL;\r
+ result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );\r
+ if ( result != ASE_OK ) {\r
+ errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers.";\r
+ errorText_ = errorStream_.str();\r
+ goto error;\r
+ }\r
+ buffersAllocated = true;\r
+\r
+ // Set flags for buffer conversion.\r
+ stream_.doConvertBuffer[mode] = false;\r
+ if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
+ stream_.doConvertBuffer[mode] = true;\r
+ if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
+ stream_.nUserChannels[mode] > 1 )\r
+ stream_.doConvertBuffer[mode] = true;\r
+\r
+ // Allocate necessary internal buffers\r
+ unsigned long bufferBytes;\r
+ bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
+ stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
+ if ( stream_.userBuffer[mode] == NULL ) {\r
+ errorText_ = "RtApiAsio::probeDeviceOpen: error allocating user buffer memory.";\r
+ goto error;\r
+ }\r
+\r
+ if ( stream_.doConvertBuffer[mode] ) {\r
+\r
+ bool makeBuffer = true;\r
+ bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
+ if ( mode == INPUT ) {\r
+ if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
+ unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
+ if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
+ }\r
+ }\r
+\r
+ if ( makeBuffer ) {\r
+ bufferBytes *= *bufferSize;\r
+ if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
+ if ( stream_.deviceBuffer == NULL ) {\r
+ errorText_ = "RtApiAsio::probeDeviceOpen: error allocating device buffer memory.";\r
+ goto error;\r
+ }\r
+ }\r
+ }\r
+\r
+ stream_.sampleRate = sampleRate;\r
+ stream_.device[mode] = device;\r
+ stream_.state = STREAM_STOPPED;\r
+ asioCallbackInfo = &stream_.callbackInfo;\r
+ stream_.callbackInfo.object = (void *) this;\r
+ if ( stream_.mode == OUTPUT && mode == INPUT )\r
+ // We had already set up an output stream.\r
+ stream_.mode = DUPLEX;\r
+ else\r
+ stream_.mode = mode;\r
+\r
+ // Determine device latencies\r
+ result = ASIOGetLatencies( &inputLatency, &outputLatency );\r
+ if ( result != ASE_OK ) {\r
+ errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting latency.";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING); // warn but don't fail\r
+ }\r
+ else {\r
+ stream_.latency[0] = outputLatency;\r
+ stream_.latency[1] = inputLatency;\r
+ }\r
+\r
+ // Setup the buffer conversion information structure. We don't use\r
+ // buffers to do channel offsets, so we override that parameter\r
+ // here.\r
+ if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );\r
+\r
+ return SUCCESS;\r
+\r
+ error:\r
+ if ( buffersAllocated )\r
+ ASIODisposeBuffers();\r
+ drivers.removeCurrentDriver();\r
+\r
+ if ( handle ) {\r
+ CloseHandle( handle->condition );\r
+ if ( handle->bufferInfos )\r
+ free( handle->bufferInfos );\r
+ delete handle;\r
+ stream_.apiHandle = 0;\r
+ }\r
+\r
+ for ( int i=0; i<2; i++ ) {\r
+ if ( stream_.userBuffer[i] ) {\r
+ free( stream_.userBuffer[i] );\r
+ stream_.userBuffer[i] = 0;\r
+ }\r
+ }\r
+\r
+ if ( stream_.deviceBuffer ) {\r
+ free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = 0;\r
+ }\r
+\r
+ return FAILURE;\r
+}\r
+\r
+void RtApiAsio :: closeStream()\r
+{\r
+ if ( stream_.state == STREAM_CLOSED ) {\r
+ errorText_ = "RtApiAsio::closeStream(): no open stream to close!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ if ( stream_.state == STREAM_RUNNING ) {\r
+ stream_.state = STREAM_STOPPED;\r
+ ASIOStop();\r
+ }\r
+ ASIODisposeBuffers();\r
+ drivers.removeCurrentDriver();\r
+\r
+ AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
+ if ( handle ) {\r
+ CloseHandle( handle->condition );\r
+ if ( handle->bufferInfos )\r
+ free( handle->bufferInfos );\r
+ delete handle;\r
+ stream_.apiHandle = 0;\r
+ }\r
+\r
+ for ( int i=0; i<2; i++ ) {\r
+ if ( stream_.userBuffer[i] ) {\r
+ free( stream_.userBuffer[i] );\r
+ stream_.userBuffer[i] = 0;\r
+ }\r
+ }\r
+\r
+ if ( stream_.deviceBuffer ) {\r
+ free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = 0;\r
+ }\r
+\r
+ stream_.mode = UNINITIALIZED;\r
+ stream_.state = STREAM_CLOSED;\r
+}\r
+\r
+bool stopThreadCalled = false;\r
+\r
+void RtApiAsio :: startStream()\r
+{\r
+ verifyStream();\r
+ if ( stream_.state == STREAM_RUNNING ) {\r
+ errorText_ = "RtApiAsio::startStream(): the stream is already running!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
+ ASIOError result = ASIOStart();\r
+ if ( result != ASE_OK ) {\r
+ errorStream_ << "RtApiAsio::startStream: error (" << getAsioErrorString( result ) << ") starting device.";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+\r
+ handle->drainCounter = 0;\r
+ handle->internalDrain = false;\r
+ ResetEvent( handle->condition );\r
+ stream_.state = STREAM_RUNNING;\r
+ asioXRun = false;\r
+\r
+ unlock:\r
+ stopThreadCalled = false;\r
+\r
+ if ( result == ASE_OK ) return;\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+}\r
+\r
+void RtApiAsio :: stopStream()\r
+{\r
+ verifyStream();\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ errorText_ = "RtApiAsio::stopStream(): the stream is already stopped!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+ if ( handle->drainCounter == 0 ) {\r
+ handle->drainCounter = 2;\r
+ WaitForSingleObject( handle->condition, INFINITE ); // block until signaled\r
+ }\r
+ }\r
+\r
+ stream_.state = STREAM_STOPPED;\r
+\r
+ ASIOError result = ASIOStop();\r
+ if ( result != ASE_OK ) {\r
+ errorStream_ << "RtApiAsio::stopStream: error (" << getAsioErrorString( result ) << ") stopping device.";\r
+ errorText_ = errorStream_.str();\r
+ }\r
+\r
+ if ( result == ASE_OK ) return;\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+}\r
+\r
+void RtApiAsio :: abortStream()\r
+{\r
+ verifyStream();\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ errorText_ = "RtApiAsio::abortStream(): the stream is already stopped!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ // The following lines were commented-out because some behavior was\r
+ // noted where the device buffers need to be zeroed to avoid\r
+ // continuing sound, even when the device buffers are completely\r
+ // disposed. So now, calling abort is the same as calling stop.\r
+ // AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
+ // handle->drainCounter = 2;\r
+ stopStream();\r
+}\r
+\r
+// This function will be called by a spawned thread when the user\r
+// callback function signals that the stream should be stopped or\r
+// aborted. It is necessary to handle it this way because the\r
+// callbackEvent() function must return before the ASIOStop()\r
+// function will return.\r
+static unsigned __stdcall asioStopStream( void *ptr )\r
+{\r
+ CallbackInfo *info = (CallbackInfo *) ptr;\r
+ RtApiAsio *object = (RtApiAsio *) info->object;\r
+\r
+ object->stopStream();\r
+ _endthreadex( 0 );\r
+ return 0;\r
+}\r
+\r
+bool RtApiAsio :: callbackEvent( long bufferIndex )\r
+{\r
+ if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;\r
+ if ( stream_.state == STREAM_CLOSED ) {\r
+ errorText_ = "RtApiAsio::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
+ error( RtAudioError::WARNING );\r
+ return FAILURE;\r
+ }\r
+\r
+ CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
+ AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
+\r
+ // Check if we were draining the stream and signal if finished.\r
+ if ( handle->drainCounter > 3 ) {\r
+\r
+ stream_.state = STREAM_STOPPING;\r
+ if ( handle->internalDrain == false )\r
+ SetEvent( handle->condition );\r
+ else { // spawn a thread to stop the stream\r
+ unsigned threadId;\r
+ stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream,\r
+ &stream_.callbackInfo, 0, &threadId );\r
+ }\r
+ return SUCCESS;\r
+ }\r
+\r
+ // Invoke user callback to get fresh output data UNLESS we are\r
+ // draining stream.\r
+ if ( handle->drainCounter == 0 ) {\r
+ RtAudioCallback callback = (RtAudioCallback) info->callback;\r
+ double streamTime = getStreamTime();\r
+ RtAudioStreamStatus status = 0;\r
+ if ( stream_.mode != INPUT && asioXRun == true ) {\r
+ status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
+ asioXRun = false;\r
+ }\r
+ if ( stream_.mode != OUTPUT && asioXRun == true ) {\r
+ status |= RTAUDIO_INPUT_OVERFLOW;\r
+ asioXRun = false;\r
+ }\r
+ int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
+ stream_.bufferSize, streamTime, status, info->userData );\r
+ if ( cbReturnValue == 2 ) {\r
+ stream_.state = STREAM_STOPPING;\r
+ handle->drainCounter = 2;\r
+ unsigned threadId;\r
+ stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream,\r
+ &stream_.callbackInfo, 0, &threadId );\r
+ return SUCCESS;\r
+ }\r
+ else if ( cbReturnValue == 1 ) {\r
+ handle->drainCounter = 1;\r
+ handle->internalDrain = true;\r
+ }\r
+ }\r
+\r
+ unsigned int nChannels, bufferBytes, i, j;\r
+ nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+\r
+ bufferBytes = stream_.bufferSize * formatBytes( stream_.deviceFormat[0] );\r
+\r
+ if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
+\r
+ for ( i=0, j=0; i<nChannels; i++ ) {\r
+ if ( handle->bufferInfos[i].isInput != ASIOTrue )\r
+ memset( handle->bufferInfos[i].buffers[bufferIndex], 0, bufferBytes );\r
+ }\r
+\r
+ }\r
+ else if ( stream_.doConvertBuffer[0] ) {\r
+\r
+ convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
+ if ( stream_.doByteSwap[0] )\r
+ byteSwapBuffer( stream_.deviceBuffer,\r
+ stream_.bufferSize * stream_.nDeviceChannels[0],\r
+ stream_.deviceFormat[0] );\r
+\r
+ for ( i=0, j=0; i<nChannels; i++ ) {\r
+ if ( handle->bufferInfos[i].isInput != ASIOTrue )\r
+ memcpy( handle->bufferInfos[i].buffers[bufferIndex],\r
+ &stream_.deviceBuffer[j++*bufferBytes], bufferBytes );\r
+ }\r
+\r
+ }\r
+ else {\r
+\r
+ if ( stream_.doByteSwap[0] )\r
+ byteSwapBuffer( stream_.userBuffer[0],\r
+ stream_.bufferSize * stream_.nUserChannels[0],\r
+ stream_.userFormat );\r
+\r
+ for ( i=0, j=0; i<nChannels; i++ ) {\r
+ if ( handle->bufferInfos[i].isInput != ASIOTrue )\r
+ memcpy( handle->bufferInfos[i].buffers[bufferIndex],\r
+ &stream_.userBuffer[0][bufferBytes*j++], bufferBytes );\r
+ }\r
+\r
+ }\r
+ }\r
+\r
+ // Don't bother draining input\r
+ if ( handle->drainCounter ) {\r
+ handle->drainCounter++;\r
+ goto unlock;\r
+ }\r
+\r
+ if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
+\r
+ bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[1]);\r
+\r
+ if (stream_.doConvertBuffer[1]) {\r
+\r
+ // Always interleave ASIO input data.\r
+ for ( i=0, j=0; i<nChannels; i++ ) {\r
+ if ( handle->bufferInfos[i].isInput == ASIOTrue )\r
+ memcpy( &stream_.deviceBuffer[j++*bufferBytes],\r
+ handle->bufferInfos[i].buffers[bufferIndex],\r
+ bufferBytes );\r
+ }\r
+\r
+ if ( stream_.doByteSwap[1] )\r
+ byteSwapBuffer( stream_.deviceBuffer,\r
+ stream_.bufferSize * stream_.nDeviceChannels[1],\r
+ stream_.deviceFormat[1] );\r
+ convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
+\r
+ }\r
+ else {\r
+ for ( i=0, j=0; i<nChannels; i++ ) {\r
+ if ( handle->bufferInfos[i].isInput == ASIOTrue ) {\r
+ memcpy( &stream_.userBuffer[1][bufferBytes*j++],\r
+ handle->bufferInfos[i].buffers[bufferIndex],\r
+ bufferBytes );\r
+ }\r
+ }\r
+\r
+ if ( stream_.doByteSwap[1] )\r
+ byteSwapBuffer( stream_.userBuffer[1],\r
+ stream_.bufferSize * stream_.nUserChannels[1],\r
+ stream_.userFormat );\r
+ }\r
+ }\r
+\r
+ unlock:\r
+ // The following call was suggested by Malte Clasen. While the API\r
+ // documentation indicates it should not be required, some device\r
+ // drivers apparently do not function correctly without it.\r
+ ASIOOutputReady();\r
+\r
+ RtApi::tickStreamTime();\r
+ return SUCCESS;\r
+}\r
+\r
+static void sampleRateChanged( ASIOSampleRate sRate )\r
+{\r
+ // The ASIO documentation says that this usually only happens during\r
+ // external sync. Audio processing is not stopped by the driver,\r
+ // actual sample rate might not have even changed, maybe only the\r
+ // sample rate status of an AES/EBU or S/PDIF digital input at the\r
+ // audio device.\r
+\r
+ RtApi *object = (RtApi *) asioCallbackInfo->object;\r
+ try {\r
+ object->stopStream();\r
+ }\r
+ catch ( RtAudioError &exception ) {\r
+ std::cerr << "\nRtApiAsio: sampleRateChanged() error (" << exception.getMessage() << ")!\n" << std::endl;\r
+ return;\r
+ }\r
+\r
+ std::cerr << "\nRtApiAsio: driver reports sample rate changed to " << sRate << " ... stream stopped!!!\n" << std::endl;\r
+}\r
+\r
+static long asioMessages( long selector, long value, void* /*message*/, double* /*opt*/ )\r
+{\r
+ long ret = 0;\r
+\r
+ switch( selector ) {\r
+ case kAsioSelectorSupported:\r
+ if ( value == kAsioResetRequest\r
+ || value == kAsioEngineVersion\r
+ || value == kAsioResyncRequest\r
+ || value == kAsioLatenciesChanged\r
+ // The following three were added for ASIO 2.0, you don't\r
+ // necessarily have to support them.\r
+ || value == kAsioSupportsTimeInfo\r
+ || value == kAsioSupportsTimeCode\r
+ || value == kAsioSupportsInputMonitor)\r
+ ret = 1L;\r
+ break;\r
+ case kAsioResetRequest:\r
+ // Defer the task and perform the reset of the driver during the\r
+ // next "safe" situation. You cannot reset the driver right now,\r
+ // as this code is called from the driver. Reset the driver is\r
+ // done by completely destruct is. I.e. ASIOStop(),\r
+ // ASIODisposeBuffers(), Destruction Afterwards you initialize the\r
+ // driver again.\r
+ std::cerr << "\nRtApiAsio: driver reset requested!!!" << std::endl;\r
+ ret = 1L;\r
+ break;\r
+ case kAsioResyncRequest:\r
+ // This informs the application that the driver encountered some\r
+ // non-fatal data loss. It is used for synchronization purposes\r
+ // of different media. Added mainly to work around the Win16Mutex\r
+ // problems in Windows 95/98 with the Windows Multimedia system,\r
+ // which could lose data because the Mutex was held too long by\r
+ // another thread. However a driver can issue it in other\r
+ // situations, too.\r
+ // std::cerr << "\nRtApiAsio: driver resync requested!!!" << std::endl;\r
+ asioXRun = true;\r
+ ret = 1L;\r
+ break;\r
+ case kAsioLatenciesChanged:\r
+ // This will inform the host application that the drivers were\r
+ // latencies changed. Beware, it this does not mean that the\r
+ // buffer sizes have changed! You might need to update internal\r
+ // delay data.\r
+ std::cerr << "\nRtApiAsio: driver latency may have changed!!!" << std::endl;\r
+ ret = 1L;\r
+ break;\r
+ case kAsioEngineVersion:\r
+ // Return the supported ASIO version of the host application. If\r
+ // a host application does not implement this selector, ASIO 1.0\r
+ // is assumed by the driver.\r
+ ret = 2L;\r
+ break;\r
+ case kAsioSupportsTimeInfo:\r
+ // Informs the driver whether the\r
+ // asioCallbacks.bufferSwitchTimeInfo() callback is supported.\r
+ // For compatibility with ASIO 1.0 drivers the host application\r
+ // should always support the "old" bufferSwitch method, too.\r
+ ret = 0;\r
+ break;\r
+ case kAsioSupportsTimeCode:\r
+ // Informs the driver whether application is interested in time\r
+ // code info. If an application does not need to know about time\r
+ // code, the driver has less work to do.\r
+ ret = 0;\r
+ break;\r
+ }\r
+ return ret;\r
+}\r
+\r
+static const char* getAsioErrorString( ASIOError result )\r
+{\r
+ struct Messages \r
+ {\r
+ ASIOError value;\r
+ const char*message;\r
+ };\r
+\r
+ static const Messages m[] = \r
+ {\r
+ { ASE_NotPresent, "Hardware input or output is not present or available." },\r
+ { ASE_HWMalfunction, "Hardware is malfunctioning." },\r
+ { ASE_InvalidParameter, "Invalid input parameter." },\r
+ { ASE_InvalidMode, "Invalid mode." },\r
+ { ASE_SPNotAdvancing, "Sample position not advancing." },\r
+ { ASE_NoClock, "Sample clock or rate cannot be determined or is not present." },\r
+ { ASE_NoMemory, "Not enough memory to complete the request." }\r
+ };\r
+\r
+ for ( unsigned int i = 0; i < sizeof(m)/sizeof(m[0]); ++i )\r
+ if ( m[i].value == result ) return m[i].message;\r
+\r
+ return "Unknown error.";\r
+}\r
+\r
+//******************** End of __WINDOWS_ASIO__ *********************//\r
+#endif\r
+\r
+\r
+#if defined(__WINDOWS_WASAPI__) // Windows WASAPI API\r
+\r
+// Authored by Marcus Tomlinson <themarcustomlinson@gmail.com>, April 2014\r
+// - Introduces support for the Windows WASAPI API\r
+// - Aims to deliver bit streams to and from hardware at the lowest possible latency, via the absolute minimum buffer sizes required\r
+// - Provides flexible stream configuration to an otherwise strict and inflexible WASAPI interface\r
+// - Includes automatic internal conversion of sample rate and buffer size between hardware and the user\r
+\r
+#ifndef INITGUID\r
+ #define INITGUID\r
+#endif\r
+#include <audioclient.h>\r
+#include <avrt.h>\r
+#include <mmdeviceapi.h>\r
+#include <functiondiscoverykeys_devpkey.h>\r
+\r
+//=============================================================================\r
+\r
+#define SAFE_RELEASE( objectPtr )\\r
+if ( objectPtr )\\r
+{\\r
+ objectPtr->Release();\\r
+ objectPtr = NULL;\\r
+}\r
+\r
+typedef HANDLE ( __stdcall *TAvSetMmThreadCharacteristicsPtr )( LPCWSTR TaskName, LPDWORD TaskIndex );\r
+\r
+//-----------------------------------------------------------------------------\r
+\r
+// WASAPI dictates stream sample rate, format, channel count, and in some cases, buffer size.\r
+// Therefore we must perform all necessary conversions to user buffers in order to satisfy these\r
+// requirements. WasapiBuffer ring buffers are used between HwIn->UserIn and UserOut->HwOut to\r
+// provide intermediate storage for read / write synchronization.\r
+class WasapiBuffer\r
+{\r
+public:\r
+ WasapiBuffer()\r
+ : buffer_( NULL ),\r
+ bufferSize_( 0 ),\r
+ inIndex_( 0 ),\r
+ outIndex_( 0 ) {}\r
+\r
+ ~WasapiBuffer() {\r
+ delete buffer_;\r
+ }\r
+\r
+ // sets the length of the internal ring buffer\r
+ void setBufferSize( unsigned int bufferSize, unsigned int formatBytes ) {\r
+ delete buffer_;\r
+\r
+ buffer_ = ( char* ) calloc( bufferSize, formatBytes );\r
+\r
+ bufferSize_ = bufferSize;\r
+ inIndex_ = 0;\r
+ outIndex_ = 0;\r
+ }\r
+\r
+ // attempt to push a buffer into the ring buffer at the current "in" index\r
+ bool pushBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format )\r
+ {\r
+ if ( !buffer || // incoming buffer is NULL\r
+ bufferSize == 0 || // incoming buffer has no data\r
+ bufferSize > bufferSize_ ) // incoming buffer too large\r
+ {\r
+ return false;\r
+ }\r
+\r
+ unsigned int relOutIndex = outIndex_;\r
+ unsigned int inIndexEnd = inIndex_ + bufferSize;\r
+ if ( relOutIndex < inIndex_ && inIndexEnd >= bufferSize_ ) {\r
+ relOutIndex += bufferSize_;\r
+ }\r
+\r
+ // "in" index can end on the "out" index but cannot begin at it\r
+ if ( inIndex_ <= relOutIndex && inIndexEnd > relOutIndex ) {\r
+ return false; // not enough space between "in" index and "out" index\r
+ }\r
+\r
+ // copy buffer from external to internal\r
+ int fromZeroSize = inIndex_ + bufferSize - bufferSize_;\r
+ fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize;\r
+ int fromInSize = bufferSize - fromZeroSize;\r
+\r
+ switch( format )\r
+ {\r
+ case RTAUDIO_SINT8:\r
+ memcpy( &( ( char* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( char ) );\r
+ memcpy( buffer_, &( ( char* ) buffer )[fromInSize], fromZeroSize * sizeof( char ) );\r
+ break;\r
+ case RTAUDIO_SINT16:\r
+ memcpy( &( ( short* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( short ) );\r
+ memcpy( buffer_, &( ( short* ) buffer )[fromInSize], fromZeroSize * sizeof( short ) );\r
+ break;\r
+ case RTAUDIO_SINT24:\r
+ memcpy( &( ( S24* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( S24 ) );\r
+ memcpy( buffer_, &( ( S24* ) buffer )[fromInSize], fromZeroSize * sizeof( S24 ) );\r
+ break;\r
+ case RTAUDIO_SINT32:\r
+ memcpy( &( ( int* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( int ) );\r
+ memcpy( buffer_, &( ( int* ) buffer )[fromInSize], fromZeroSize * sizeof( int ) );\r
+ break;\r
+ case RTAUDIO_FLOAT32:\r
+ memcpy( &( ( float* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( float ) );\r
+ memcpy( buffer_, &( ( float* ) buffer )[fromInSize], fromZeroSize * sizeof( float ) );\r
+ break;\r
+ case RTAUDIO_FLOAT64:\r
+ memcpy( &( ( double* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( double ) );\r
+ memcpy( buffer_, &( ( double* ) buffer )[fromInSize], fromZeroSize * sizeof( double ) );\r
+ break;\r
+ }\r
+\r
+ // update "in" index\r
+ inIndex_ += bufferSize;\r
+ inIndex_ %= bufferSize_;\r
+\r
+ return true;\r
+ }\r
+\r
+ // attempt to pull a buffer from the ring buffer from the current "out" index\r
+ bool pullBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format )\r
+ {\r
+ if ( !buffer || // incoming buffer is NULL\r
+ bufferSize == 0 || // incoming buffer has no data\r
+ bufferSize > bufferSize_ ) // incoming buffer too large\r
+ {\r
+ return false;\r
+ }\r
+\r
+ unsigned int relInIndex = inIndex_;\r
+ unsigned int outIndexEnd = outIndex_ + bufferSize;\r
+ if ( relInIndex < outIndex_ && outIndexEnd >= bufferSize_ ) {\r
+ relInIndex += bufferSize_;\r
+ }\r
+\r
+ // "out" index can begin at and end on the "in" index\r
+ if ( outIndex_ < relInIndex && outIndexEnd > relInIndex ) {\r
+ return false; // not enough space between "out" index and "in" index\r
+ }\r
+\r
+ // copy buffer from internal to external\r
+ int fromZeroSize = outIndex_ + bufferSize - bufferSize_;\r
+ fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize;\r
+ int fromOutSize = bufferSize - fromZeroSize;\r
+\r
+ switch( format )\r
+ {\r
+ case RTAUDIO_SINT8:\r
+ memcpy( buffer, &( ( char* ) buffer_ )[outIndex_], fromOutSize * sizeof( char ) );\r
+ memcpy( &( ( char* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( char ) );\r
+ break;\r
+ case RTAUDIO_SINT16:\r
+ memcpy( buffer, &( ( short* ) buffer_ )[outIndex_], fromOutSize * sizeof( short ) );\r
+ memcpy( &( ( short* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( short ) );\r
+ break;\r
+ case RTAUDIO_SINT24:\r
+ memcpy( buffer, &( ( S24* ) buffer_ )[outIndex_], fromOutSize * sizeof( S24 ) );\r
+ memcpy( &( ( S24* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( S24 ) );\r
+ break;\r
+ case RTAUDIO_SINT32:\r
+ memcpy( buffer, &( ( int* ) buffer_ )[outIndex_], fromOutSize * sizeof( int ) );\r
+ memcpy( &( ( int* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( int ) );\r
+ break;\r
+ case RTAUDIO_FLOAT32:\r
+ memcpy( buffer, &( ( float* ) buffer_ )[outIndex_], fromOutSize * sizeof( float ) );\r
+ memcpy( &( ( float* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( float ) );\r
+ break;\r
+ case RTAUDIO_FLOAT64:\r
+ memcpy( buffer, &( ( double* ) buffer_ )[outIndex_], fromOutSize * sizeof( double ) );\r
+ memcpy( &( ( double* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( double ) );\r
+ break;\r
+ }\r
+\r
+ // update "out" index\r
+ outIndex_ += bufferSize;\r
+ outIndex_ %= bufferSize_;\r
+\r
+ return true;\r
+ }\r
+\r
+private:\r
+ char* buffer_;\r
+ unsigned int bufferSize_;\r
+ unsigned int inIndex_;\r
+ unsigned int outIndex_;\r
+};\r
+\r
+//-----------------------------------------------------------------------------\r
+\r
+// In order to satisfy WASAPI's buffer requirements, we need a means of converting sample rate\r
+// between HW and the user. The convertBufferWasapi function is used to perform this conversion\r
+// between HwIn->UserIn and UserOut->HwOut during the stream callback loop.\r
+// This sample rate converter favors speed over quality, and works best with conversions between\r
+// one rate and its multiple.\r
+void convertBufferWasapi( char* outBuffer,\r
+ const char* inBuffer,\r
+ const unsigned int& channelCount,\r
+ const unsigned int& inSampleRate,\r
+ const unsigned int& outSampleRate,\r
+ const unsigned int& inSampleCount,\r
+ unsigned int& outSampleCount,\r
+ const RtAudioFormat& format )\r
+{\r
+ // calculate the new outSampleCount and relative sampleStep\r
+ float sampleRatio = ( float ) outSampleRate / inSampleRate;\r
+ float sampleStep = 1.0f / sampleRatio;\r
+ float inSampleFraction = 0.0f;\r
+\r
+ outSampleCount = ( unsigned int ) ( inSampleCount * sampleRatio );\r
+\r
+ // frame-by-frame, copy each relative input sample into it's corresponding output sample\r
+ for ( unsigned int outSample = 0; outSample < outSampleCount; outSample++ )\r
+ {\r
+ unsigned int inSample = ( unsigned int ) inSampleFraction;\r
+\r
+ switch ( format )\r
+ {\r
+ case RTAUDIO_SINT8:\r
+ memcpy( &( ( char* ) outBuffer )[ outSample * channelCount ], &( ( char* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( char ) );\r
+ break;\r
+ case RTAUDIO_SINT16:\r
+ memcpy( &( ( short* ) outBuffer )[ outSample * channelCount ], &( ( short* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( short ) );\r
+ break;\r
+ case RTAUDIO_SINT24:\r
+ memcpy( &( ( S24* ) outBuffer )[ outSample * channelCount ], &( ( S24* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( S24 ) );\r
+ break;\r
+ case RTAUDIO_SINT32:\r
+ memcpy( &( ( int* ) outBuffer )[ outSample * channelCount ], &( ( int* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( int ) );\r
+ break;\r
+ case RTAUDIO_FLOAT32:\r
+ memcpy( &( ( float* ) outBuffer )[ outSample * channelCount ], &( ( float* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( float ) );\r
+ break;\r
+ case RTAUDIO_FLOAT64:\r
+ memcpy( &( ( double* ) outBuffer )[ outSample * channelCount ], &( ( double* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( double ) );\r
+ break;\r
+ }\r
+\r
+ // jump to next in sample\r
+ inSampleFraction += sampleStep;\r
+ }\r
+}\r
+\r
+//-----------------------------------------------------------------------------\r
+\r
+// A structure to hold various information related to the WASAPI implementation.\r
+struct WasapiHandle\r
+{\r
+ IAudioClient* captureAudioClient;\r
+ IAudioClient* renderAudioClient;\r
+ IAudioCaptureClient* captureClient;\r
+ IAudioRenderClient* renderClient;\r
+ HANDLE captureEvent;\r
+ HANDLE renderEvent;\r
+\r
+ WasapiHandle()\r
+ : captureAudioClient( NULL ),\r
+ renderAudioClient( NULL ),\r
+ captureClient( NULL ),\r
+ renderClient( NULL ),\r
+ captureEvent( NULL ),\r
+ renderEvent( NULL ) {}\r
+};\r
+\r
+//=============================================================================\r
+\r
+RtApiWasapi::RtApiWasapi()\r
+ : coInitialized_( false ), deviceEnumerator_( NULL )\r
+{\r
+ // WASAPI can run either apartment or multi-threaded\r
+ HRESULT hr = CoInitialize( NULL );\r
+ if ( !FAILED( hr ) )\r
+ coInitialized_ = true;\r
+\r
+ // Instantiate device enumerator\r
+ hr = CoCreateInstance( __uuidof( MMDeviceEnumerator ), NULL,\r
+ CLSCTX_ALL, __uuidof( IMMDeviceEnumerator ),\r
+ ( void** ) &deviceEnumerator_ );\r
+\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::RtApiWasapi: Unable to instantiate device enumerator";\r
+ error( RtAudioError::DRIVER_ERROR );\r
+ }\r
+}\r
+\r
+//-----------------------------------------------------------------------------\r
+\r
+RtApiWasapi::~RtApiWasapi()\r
+{\r
+ if ( stream_.state != STREAM_CLOSED )\r
+ closeStream();\r
+\r
+ SAFE_RELEASE( deviceEnumerator_ );\r
+\r
+ // If this object previously called CoInitialize()\r
+ if ( coInitialized_ )\r
+ CoUninitialize();\r
+}\r
+\r
+//=============================================================================\r
+\r
+unsigned int RtApiWasapi::getDeviceCount( void )\r
+{\r
+ unsigned int captureDeviceCount = 0;\r
+ unsigned int renderDeviceCount = 0;\r
+\r
+ IMMDeviceCollection* captureDevices = NULL;\r
+ IMMDeviceCollection* renderDevices = NULL;\r
+\r
+ // Count capture devices\r
+ errorText_.clear();\r
+ HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device collection.";\r
+ goto Exit;\r
+ }\r
+\r
+ hr = captureDevices->GetCount( &captureDeviceCount );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device count.";\r
+ goto Exit;\r
+ }\r
+\r
+ // Count render devices\r
+ hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device collection.";\r
+ goto Exit;\r
+ }\r
+\r
+ hr = renderDevices->GetCount( &renderDeviceCount );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device count.";\r
+ goto Exit;\r
+ }\r
+\r
+Exit:\r
+ // release all references\r
+ SAFE_RELEASE( captureDevices );\r
+ SAFE_RELEASE( renderDevices );\r
+\r
+ if ( errorText_.empty() )\r
+ return captureDeviceCount + renderDeviceCount;\r
+\r
+ error( RtAudioError::DRIVER_ERROR );\r
+ return 0;\r
+}\r
+\r
+//-----------------------------------------------------------------------------\r
+\r
+RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )\r
+{\r
+ RtAudio::DeviceInfo info;\r
+ unsigned int captureDeviceCount = 0;\r
+ unsigned int renderDeviceCount = 0;\r
+ std::wstring deviceName;\r
+ std::string defaultDeviceName;\r
+ bool isCaptureDevice = false;\r
+\r
+ PROPVARIANT deviceNameProp;\r
+ PROPVARIANT defaultDeviceNameProp;\r
+\r
+ IMMDeviceCollection* captureDevices = NULL;\r
+ IMMDeviceCollection* renderDevices = NULL;\r
+ IMMDevice* devicePtr = NULL;\r
+ IMMDevice* defaultDevicePtr = NULL;\r
+ IAudioClient* audioClient = NULL;\r
+ IPropertyStore* devicePropStore = NULL;\r
+ IPropertyStore* defaultDevicePropStore = NULL;\r
+\r
+ WAVEFORMATEX* deviceFormat = NULL;\r
+ WAVEFORMATEX* closestMatchFormat = NULL;\r
+\r
+ // probed\r
+ info.probed = false;\r
+\r
+ // Count capture devices\r
+ errorText_.clear();\r
+ RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
+ HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device collection.";\r
+ goto Exit;\r
+ }\r
+\r
+ hr = captureDevices->GetCount( &captureDeviceCount );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device count.";\r
+ goto Exit;\r
+ }\r
+\r
+ // Count render devices\r
+ hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device collection.";\r
+ goto Exit;\r
+ }\r
+\r
+ hr = renderDevices->GetCount( &renderDeviceCount );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device count.";\r
+ goto Exit;\r
+ }\r
+\r
+ // validate device index\r
+ if ( device >= captureDeviceCount + renderDeviceCount ) {\r
+ errorText_ = "RtApiWasapi::getDeviceInfo: Invalid device index.";\r
+ errorType = RtAudioError::INVALID_USE;\r
+ goto Exit;\r
+ }\r
+\r
+ // determine whether index falls within capture or render devices\r
+ if ( device >= renderDeviceCount ) {\r
+ hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device handle.";\r
+ goto Exit;\r
+ }\r
+ isCaptureDevice = true;\r
+ }\r
+ else {\r
+ hr = renderDevices->Item( device, &devicePtr );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device handle.";\r
+ goto Exit;\r
+ }\r
+ isCaptureDevice = false;\r
+ }\r
+\r
+ // get default device name\r
+ if ( isCaptureDevice ) {\r
+ hr = deviceEnumerator_->GetDefaultAudioEndpoint( eCapture, eConsole, &defaultDevicePtr );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default capture device handle.";\r
+ goto Exit;\r
+ }\r
+ }\r
+ else {\r
+ hr = deviceEnumerator_->GetDefaultAudioEndpoint( eRender, eConsole, &defaultDevicePtr );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default render device handle.";\r
+ goto Exit;\r
+ }\r
+ }\r
+\r
+ hr = defaultDevicePtr->OpenPropertyStore( STGM_READ, &defaultDevicePropStore );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open default device property store.";\r
+ goto Exit;\r
+ }\r
+ PropVariantInit( &defaultDeviceNameProp );\r
+\r
+ hr = defaultDevicePropStore->GetValue( PKEY_Device_FriendlyName, &defaultDeviceNameProp );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default device property: PKEY_Device_FriendlyName.";\r
+ goto Exit;\r
+ }\r
+\r
+ deviceName = defaultDeviceNameProp.pwszVal;\r
+ defaultDeviceName = std::string( deviceName.begin(), deviceName.end() );\r
+\r
+ // name\r
+ hr = devicePtr->OpenPropertyStore( STGM_READ, &devicePropStore );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open device property store.";\r
+ goto Exit;\r
+ }\r
+\r
+ PropVariantInit( &deviceNameProp );\r
+\r
+ hr = devicePropStore->GetValue( PKEY_Device_FriendlyName, &deviceNameProp );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device property: PKEY_Device_FriendlyName.";\r
+ goto Exit;\r
+ }\r
+\r
+ deviceName = deviceNameProp.pwszVal;\r
+ info.name = std::string( deviceName.begin(), deviceName.end() );\r
+\r
+ // is default\r
+ if ( isCaptureDevice ) {\r
+ info.isDefaultInput = info.name == defaultDeviceName;\r
+ info.isDefaultOutput = false;\r
+ }\r
+ else {\r
+ info.isDefaultInput = false;\r
+ info.isDefaultOutput = info.name == defaultDeviceName;\r
+ }\r
+\r
+ // channel count\r
+ hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, NULL, ( void** ) &audioClient );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device audio client.";\r
+ goto Exit;\r
+ }\r
+\r
+ hr = audioClient->GetMixFormat( &deviceFormat );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device mix format.";\r
+ goto Exit;\r
+ }\r
+\r
+ if ( isCaptureDevice ) {\r
+ info.inputChannels = deviceFormat->nChannels;\r
+ info.outputChannels = 0;\r
+ info.duplexChannels = 0;\r
+ }\r
+ else {\r
+ info.inputChannels = 0;\r
+ info.outputChannels = deviceFormat->nChannels;\r
+ info.duplexChannels = 0;\r
+ }\r
+\r
+ // sample rates\r
+ info.sampleRates.clear();\r
+\r
+ // allow support for all sample rates as we have a built-in sample rate converter\r
+ for ( unsigned int i = 0; i < MAX_SAMPLE_RATES; i++ ) {\r
+ info.sampleRates.push_back( SAMPLE_RATES[i] );\r
+ }\r
+\r
+ // native format\r
+ info.nativeFormats = 0;\r
+\r
+ if ( deviceFormat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||\r
+ ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&\r
+ ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT ) )\r
+ {\r
+ if ( deviceFormat->wBitsPerSample == 32 ) {\r
+ info.nativeFormats |= RTAUDIO_FLOAT32;\r
+ }\r
+ else if ( deviceFormat->wBitsPerSample == 64 ) {\r
+ info.nativeFormats |= RTAUDIO_FLOAT64;\r
+ }\r
+ }\r
+ else if ( deviceFormat->wFormatTag == WAVE_FORMAT_PCM ||\r
+ ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&\r
+ ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_PCM ) )\r
+ {\r
+ if ( deviceFormat->wBitsPerSample == 8 ) {\r
+ info.nativeFormats |= RTAUDIO_SINT8;\r
+ }\r
+ else if ( deviceFormat->wBitsPerSample == 16 ) {\r
+ info.nativeFormats |= RTAUDIO_SINT16;\r
+ }\r
+ else if ( deviceFormat->wBitsPerSample == 24 ) {\r
+ info.nativeFormats |= RTAUDIO_SINT24;\r
+ }\r
+ else if ( deviceFormat->wBitsPerSample == 32 ) {\r
+ info.nativeFormats |= RTAUDIO_SINT32;\r
+ }\r
+ }\r
+\r
+ // probed\r
+ info.probed = true;\r
+\r
+Exit:\r
+ // release all references\r
+ PropVariantClear( &deviceNameProp );\r
+ PropVariantClear( &defaultDeviceNameProp );\r
+\r
+ SAFE_RELEASE( captureDevices );\r
+ SAFE_RELEASE( renderDevices );\r
+ SAFE_RELEASE( devicePtr );\r
+ SAFE_RELEASE( defaultDevicePtr );\r
+ SAFE_RELEASE( audioClient );\r
+ SAFE_RELEASE( devicePropStore );\r
+ SAFE_RELEASE( defaultDevicePropStore );\r
+\r
+ CoTaskMemFree( deviceFormat );\r
+ CoTaskMemFree( closestMatchFormat );\r
+\r
+ if ( !errorText_.empty() )\r
+ error( errorType );\r
+ return info;\r
+}\r
+\r
+//-----------------------------------------------------------------------------\r
+\r
+unsigned int RtApiWasapi::getDefaultOutputDevice( void )\r
+{\r
+ for ( unsigned int i = 0; i < getDeviceCount(); i++ ) {\r
+ if ( getDeviceInfo( i ).isDefaultOutput ) {\r
+ return i;\r
+ }\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+//-----------------------------------------------------------------------------\r
+\r
+unsigned int RtApiWasapi::getDefaultInputDevice( void )\r
+{\r
+ for ( unsigned int i = 0; i < getDeviceCount(); i++ ) {\r
+ if ( getDeviceInfo( i ).isDefaultInput ) {\r
+ return i;\r
+ }\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+//-----------------------------------------------------------------------------\r
+\r
+void RtApiWasapi::closeStream( void )\r
+{\r
+ if ( stream_.state == STREAM_CLOSED ) {\r
+ errorText_ = "RtApiWasapi::closeStream: No open stream to close.";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ if ( stream_.state != STREAM_STOPPED )\r
+ stopStream();\r
+\r
+ // clean up stream memory\r
+ SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient )\r
+ SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient )\r
+\r
+ SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureClient )\r
+ SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderClient )\r
+\r
+ if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent )\r
+ CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent );\r
+\r
+ if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent )\r
+ CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent );\r
+\r
+ delete ( WasapiHandle* ) stream_.apiHandle;\r
+ stream_.apiHandle = NULL;\r
+\r
+ for ( int i = 0; i < 2; i++ ) {\r
+ if ( stream_.userBuffer[i] ) {\r
+ free( stream_.userBuffer[i] );\r
+ stream_.userBuffer[i] = 0;\r
+ }\r
+ }\r
+\r
+ if ( stream_.deviceBuffer ) {\r
+ free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = 0;\r
+ }\r
+\r
+ // update stream state\r
+ stream_.state = STREAM_CLOSED;\r
+}\r
+\r
+//-----------------------------------------------------------------------------\r
+\r
+void RtApiWasapi::startStream( void )\r
+{\r
+ verifyStream();\r
+\r
+ if ( stream_.state == STREAM_RUNNING ) {\r
+ errorText_ = "RtApiWasapi::startStream: The stream is already running.";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ // update stream state\r
+ stream_.state = STREAM_RUNNING;\r
+\r
+ // create WASAPI stream thread\r
+ stream_.callbackInfo.thread = ( ThreadHandle ) CreateThread( NULL, 0, runWasapiThread, this, CREATE_SUSPENDED, NULL );\r
+\r
+ if ( !stream_.callbackInfo.thread ) {\r
+ errorText_ = "RtApiWasapi::startStream: Unable to instantiate callback thread.";\r
+ error( RtAudioError::THREAD_ERROR );\r
+ }\r
+ else {\r
+ SetThreadPriority( ( void* ) stream_.callbackInfo.thread, stream_.callbackInfo.priority );\r
+ ResumeThread( ( void* ) stream_.callbackInfo.thread );\r
+ }\r
+}\r
+\r
+//-----------------------------------------------------------------------------\r
+\r
+void RtApiWasapi::stopStream( void )\r
+{\r
+ verifyStream();\r
+\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ errorText_ = "RtApiWasapi::stopStream: The stream is already stopped.";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ // inform stream thread by setting stream state to STREAM_STOPPING\r
+ stream_.state = STREAM_STOPPING;\r
+\r
+ // wait until stream thread is stopped\r
+ while( stream_.state != STREAM_STOPPED ) {\r
+ Sleep( 1 );\r
+ }\r
+\r
+ // Wait for the last buffer to play before stopping.\r
+ Sleep( 1000 * stream_.bufferSize / stream_.sampleRate );\r
+\r
+ // stop capture client if applicable\r
+ if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {\r
+ HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::stopStream: Unable to stop capture stream.";\r
+ error( RtAudioError::DRIVER_ERROR );\r
+ return;\r
+ }\r
+ }\r
+\r
+ // stop render client if applicable\r
+ if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {\r
+ HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::stopStream: Unable to stop render stream.";\r
+ error( RtAudioError::DRIVER_ERROR );\r
+ return;\r
+ }\r
+ }\r
+\r
+ // close thread handle\r
+ if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {\r
+ errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread.";\r
+ error( RtAudioError::THREAD_ERROR );\r
+ return;\r
+ }\r
+\r
+ stream_.callbackInfo.thread = (ThreadHandle) NULL;\r
+}\r
+\r
+//-----------------------------------------------------------------------------\r
+\r
+void RtApiWasapi::abortStream( void )\r
+{\r
+ verifyStream();\r
+\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ errorText_ = "RtApiWasapi::abortStream: The stream is already stopped.";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ // inform stream thread by setting stream state to STREAM_STOPPING\r
+ stream_.state = STREAM_STOPPING;\r
+\r
+ // wait until stream thread is stopped\r
+ while ( stream_.state != STREAM_STOPPED ) {\r
+ Sleep( 1 );\r
+ }\r
+\r
+ // stop capture client if applicable\r
+ if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {\r
+ HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::abortStream: Unable to stop capture stream.";\r
+ error( RtAudioError::DRIVER_ERROR );\r
+ return;\r
+ }\r
+ }\r
+\r
+ // stop render client if applicable\r
+ if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {\r
+ HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::abortStream: Unable to stop render stream.";\r
+ error( RtAudioError::DRIVER_ERROR );\r
+ return;\r
+ }\r
+ }\r
+\r
+ // close thread handle\r
+ if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {\r
+ errorText_ = "RtApiWasapi::abortStream: Unable to close callback thread.";\r
+ error( RtAudioError::THREAD_ERROR );\r
+ return;\r
+ }\r
+\r
+ stream_.callbackInfo.thread = (ThreadHandle) NULL;\r
+}\r
+\r
+//-----------------------------------------------------------------------------\r
+\r
+bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
+ unsigned int firstChannel, unsigned int sampleRate,\r
+ RtAudioFormat format, unsigned int* bufferSize,\r
+ RtAudio::StreamOptions* options )\r
+{\r
+ bool methodResult = FAILURE;\r
+ unsigned int captureDeviceCount = 0;\r
+ unsigned int renderDeviceCount = 0;\r
+\r
+ IMMDeviceCollection* captureDevices = NULL;\r
+ IMMDeviceCollection* renderDevices = NULL;\r
+ IMMDevice* devicePtr = NULL;\r
+ WAVEFORMATEX* deviceFormat = NULL;\r
+ unsigned int bufferBytes;\r
+ stream_.state = STREAM_STOPPED;\r
+\r
+ // create API Handle if not already created\r
+ if ( !stream_.apiHandle )\r
+ stream_.apiHandle = ( void* ) new WasapiHandle();\r
+\r
+ // Count capture devices\r
+ errorText_.clear();\r
+ RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
+ HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device collection.";\r
+ goto Exit;\r
+ }\r
+\r
+ hr = captureDevices->GetCount( &captureDeviceCount );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device count.";\r
+ goto Exit;\r
+ }\r
+\r
+ // Count render devices\r
+ hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device collection.";\r
+ goto Exit;\r
+ }\r
+\r
+ hr = renderDevices->GetCount( &renderDeviceCount );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device count.";\r
+ goto Exit;\r
+ }\r
+\r
+ // validate device index\r
+ if ( device >= captureDeviceCount + renderDeviceCount ) {\r
+ errorType = RtAudioError::INVALID_USE;\r
+ errorText_ = "RtApiWasapi::probeDeviceOpen: Invalid device index.";\r
+ goto Exit;\r
+ }\r
+\r
+ // determine whether index falls within capture or render devices\r
+ if ( device >= renderDeviceCount ) {\r
+ if ( mode != INPUT ) {\r
+ errorType = RtAudioError::INVALID_USE;\r
+ errorText_ = "RtApiWasapi::probeDeviceOpen: Capture device selected as output device.";\r
+ goto Exit;\r
+ }\r
+\r
+ // retrieve captureAudioClient from devicePtr\r
+ IAudioClient*& captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;\r
+\r
+ hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device handle.";\r
+ goto Exit;\r
+ }\r
+\r
+ hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,\r
+ NULL, ( void** ) &captureAudioClient );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client.";\r
+ goto Exit;\r
+ }\r
+\r
+ hr = captureAudioClient->GetMixFormat( &deviceFormat );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format.";\r
+ goto Exit;\r
+ }\r
+\r
+ stream_.nDeviceChannels[mode] = deviceFormat->nChannels;\r
+ captureAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );\r
+ }\r
+ else {\r
+ if ( mode != OUTPUT ) {\r
+ errorType = RtAudioError::INVALID_USE;\r
+ errorText_ = "RtApiWasapi::probeDeviceOpen: Render device selected as input device.";\r
+ goto Exit;\r
+ }\r
+\r
+ // retrieve renderAudioClient from devicePtr\r
+ IAudioClient*& renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;\r
+\r
+ hr = renderDevices->Item( device, &devicePtr );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device handle.";\r
+ goto Exit;\r
+ }\r
+\r
+ hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,\r
+ NULL, ( void** ) &renderAudioClient );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client.";\r
+ goto Exit;\r
+ }\r
+\r
+ hr = renderAudioClient->GetMixFormat( &deviceFormat );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format.";\r
+ goto Exit;\r
+ }\r
+\r
+ stream_.nDeviceChannels[mode] = deviceFormat->nChannels;\r
+ renderAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );\r
+ }\r
+\r
+ // fill stream data\r
+ if ( ( stream_.mode == OUTPUT && mode == INPUT ) ||\r
+ ( stream_.mode == INPUT && mode == OUTPUT ) ) {\r
+ stream_.mode = DUPLEX;\r
+ }\r
+ else {\r
+ stream_.mode = mode;\r
+ }\r
+\r
+ stream_.device[mode] = device;\r
+ stream_.doByteSwap[mode] = false;\r
+ stream_.sampleRate = sampleRate;\r
+ stream_.bufferSize = *bufferSize;\r
+ stream_.nBuffers = 1;\r
+ stream_.nUserChannels[mode] = channels;\r
+ stream_.channelOffset[mode] = firstChannel;\r
+ stream_.userFormat = format;\r
+ stream_.deviceFormat[mode] = getDeviceInfo( device ).nativeFormats;\r
+\r
+ if ( options && options->flags & RTAUDIO_NONINTERLEAVED )\r
+ stream_.userInterleaved = false;\r
+ else\r
+ stream_.userInterleaved = true;\r
+ stream_.deviceInterleaved[mode] = true;\r
+\r
+ // Set flags for buffer conversion.\r
+ stream_.doConvertBuffer[mode] = false;\r
+ if ( stream_.userFormat != stream_.deviceFormat[mode] ||\r
+ stream_.nUserChannels != stream_.nDeviceChannels )\r
+ stream_.doConvertBuffer[mode] = true;\r
+ else if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
+ stream_.nUserChannels[mode] > 1 )\r
+ stream_.doConvertBuffer[mode] = true;\r
+\r
+ if ( stream_.doConvertBuffer[mode] )\r
+ setConvertInfo( mode, 0 );\r
+\r
+ // Allocate necessary internal buffers\r
+ bufferBytes = stream_.nUserChannels[mode] * stream_.bufferSize * formatBytes( stream_.userFormat );\r
+\r
+ stream_.userBuffer[mode] = ( char* ) calloc( bufferBytes, 1 );\r
+ if ( !stream_.userBuffer[mode] ) {\r
+ errorType = RtAudioError::MEMORY_ERROR;\r
+ errorText_ = "RtApiWasapi::probeDeviceOpen: Error allocating user buffer memory.";\r
+ goto Exit;\r
+ }\r
+\r
+ if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME )\r
+ stream_.callbackInfo.priority = 15;\r
+ else\r
+ stream_.callbackInfo.priority = 0;\r
+\r
+ ///! TODO: RTAUDIO_MINIMIZE_LATENCY // Provide stream buffers directly to callback\r
+ ///! TODO: RTAUDIO_HOG_DEVICE // Exclusive mode\r
+\r
+ methodResult = SUCCESS;\r
+\r
+Exit:\r
+ //clean up\r
+ SAFE_RELEASE( captureDevices );\r
+ SAFE_RELEASE( renderDevices );\r
+ SAFE_RELEASE( devicePtr );\r
+ CoTaskMemFree( deviceFormat );\r
+\r
+ // if method failed, close the stream\r
+ if ( methodResult == FAILURE )\r
+ closeStream();\r
+\r
+ if ( !errorText_.empty() )\r
+ error( errorType );\r
+ return methodResult;\r
+}\r
+\r
+//=============================================================================\r
+\r
+DWORD WINAPI RtApiWasapi::runWasapiThread( void* wasapiPtr )\r
+{\r
+ if ( wasapiPtr )\r
+ ( ( RtApiWasapi* ) wasapiPtr )->wasapiThread();\r
+\r
+ return 0;\r
+}\r
+\r
+DWORD WINAPI RtApiWasapi::stopWasapiThread( void* wasapiPtr )\r
+{\r
+ if ( wasapiPtr )\r
+ ( ( RtApiWasapi* ) wasapiPtr )->stopStream();\r
+\r
+ return 0;\r
+}\r
+\r
+DWORD WINAPI RtApiWasapi::abortWasapiThread( void* wasapiPtr )\r
+{\r
+ if ( wasapiPtr )\r
+ ( ( RtApiWasapi* ) wasapiPtr )->abortStream();\r
+\r
+ return 0;\r
+}\r
+\r
+//-----------------------------------------------------------------------------\r
+\r
+void RtApiWasapi::wasapiThread()\r
+{\r
+ // as this is a new thread, we must CoInitialize it\r
+ CoInitialize( NULL );\r
+\r
+ HRESULT hr;\r
+\r
+ IAudioClient* captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;\r
+ IAudioClient* renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;\r
+ IAudioCaptureClient* captureClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureClient;\r
+ IAudioRenderClient* renderClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderClient;\r
+ HANDLE captureEvent = ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent;\r
+ HANDLE renderEvent = ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent;\r
+\r
+ WAVEFORMATEX* captureFormat = NULL;\r
+ WAVEFORMATEX* renderFormat = NULL;\r
+ float captureSrRatio = 0.0f;\r
+ float renderSrRatio = 0.0f;\r
+ WasapiBuffer captureBuffer;\r
+ WasapiBuffer renderBuffer;\r
+\r
+ // declare local stream variables\r
+ RtAudioCallback callback = ( RtAudioCallback ) stream_.callbackInfo.callback;\r
+ BYTE* streamBuffer = NULL;\r
+ unsigned long captureFlags = 0;\r
+ unsigned int bufferFrameCount = 0;\r
+ unsigned int numFramesPadding = 0;\r
+ unsigned int convBufferSize = 0;\r
+ bool callbackPushed = false;\r
+ bool callbackPulled = false;\r
+ bool callbackStopped = false;\r
+ int callbackResult = 0;\r
+\r
+ // convBuffer is used to store converted buffers between WASAPI and the user\r
+ char* convBuffer = NULL;\r
+ unsigned int convBuffSize = 0;\r
+ unsigned int deviceBuffSize = 0;\r
+\r
+ errorText_.clear();\r
+ RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
+\r
+ // Attempt to assign "Pro Audio" characteristic to thread\r
+ HMODULE AvrtDll = LoadLibrary( (LPCTSTR) "AVRT.dll" );\r
+ if ( AvrtDll ) {\r
+ DWORD taskIndex = 0;\r
+ TAvSetMmThreadCharacteristicsPtr AvSetMmThreadCharacteristicsPtr = ( TAvSetMmThreadCharacteristicsPtr ) GetProcAddress( AvrtDll, "AvSetMmThreadCharacteristicsW" );\r
+ AvSetMmThreadCharacteristicsPtr( L"Pro Audio", &taskIndex );\r
+ FreeLibrary( AvrtDll );\r
+ }\r
+\r
+ // start capture stream if applicable\r
+ if ( captureAudioClient ) {\r
+ hr = captureAudioClient->GetMixFormat( &captureFormat );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";\r
+ goto Exit;\r
+ }\r
+\r
+ captureSrRatio = ( ( float ) captureFormat->nSamplesPerSec / stream_.sampleRate );\r
+\r
+ // initialize capture stream according to desire buffer size\r
+ float desiredBufferSize = stream_.bufferSize * captureSrRatio;\r
+ REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / captureFormat->nSamplesPerSec );\r
+\r
+ if ( !captureClient ) {\r
+ hr = captureAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,\r
+ AUDCLNT_STREAMFLAGS_EVENTCALLBACK,\r
+ desiredBufferPeriod,\r
+ desiredBufferPeriod,\r
+ captureFormat,\r
+ NULL );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize capture audio client.";\r
+ goto Exit;\r
+ }\r
+\r
+ hr = captureAudioClient->GetService( __uuidof( IAudioCaptureClient ),\r
+ ( void** ) &captureClient );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture client handle.";\r
+ goto Exit;\r
+ }\r
+\r
+ // configure captureEvent to trigger on every available capture buffer\r
+ captureEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
+ if ( !captureEvent ) {\r
+ errorType = RtAudioError::SYSTEM_ERROR;\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to create capture event.";\r
+ goto Exit;\r
+ }\r
+\r
+ hr = captureAudioClient->SetEventHandle( captureEvent );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to set capture event handle.";\r
+ goto Exit;\r
+ }\r
+\r
+ ( ( WasapiHandle* ) stream_.apiHandle )->captureClient = captureClient;\r
+ ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent = captureEvent;\r
+ }\r
+\r
+ unsigned int inBufferSize = 0;\r
+ hr = captureAudioClient->GetBufferSize( &inBufferSize );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to get capture buffer size.";\r
+ goto Exit;\r
+ }\r
+\r
+ // scale outBufferSize according to stream->user sample rate ratio\r
+ unsigned int outBufferSize = ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT];\r
+ inBufferSize *= stream_.nDeviceChannels[INPUT];\r
+\r
+ // set captureBuffer size\r
+ captureBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[INPUT] ) );\r
+\r
+ // reset the capture stream\r
+ hr = captureAudioClient->Reset();\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to reset capture stream.";\r
+ goto Exit;\r
+ }\r
+\r
+ // start the capture stream\r
+ hr = captureAudioClient->Start();\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to start capture stream.";\r
+ goto Exit;\r
+ }\r
+ }\r
+\r
+ // start render stream if applicable\r
+ if ( renderAudioClient ) {\r
+ hr = renderAudioClient->GetMixFormat( &renderFormat );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";\r
+ goto Exit;\r
+ }\r
+\r
+ renderSrRatio = ( ( float ) renderFormat->nSamplesPerSec / stream_.sampleRate );\r
+\r
+ // initialize render stream according to desire buffer size\r
+ float desiredBufferSize = stream_.bufferSize * renderSrRatio;\r
+ REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / renderFormat->nSamplesPerSec );\r
+\r
+ if ( !renderClient ) {\r
+ hr = renderAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,\r
+ AUDCLNT_STREAMFLAGS_EVENTCALLBACK,\r
+ desiredBufferPeriod,\r
+ desiredBufferPeriod,\r
+ renderFormat,\r
+ NULL );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize render audio client.";\r
+ goto Exit;\r
+ }\r
+\r
+ hr = renderAudioClient->GetService( __uuidof( IAudioRenderClient ),\r
+ ( void** ) &renderClient );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render client handle.";\r
+ goto Exit;\r
+ }\r
+\r
+ // configure renderEvent to trigger on every available render buffer\r
+ renderEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
+ if ( !renderEvent ) {\r
+ errorType = RtAudioError::SYSTEM_ERROR;\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to create render event.";\r
+ goto Exit;\r
+ }\r
+\r
+ hr = renderAudioClient->SetEventHandle( renderEvent );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to set render event handle.";\r
+ goto Exit;\r
+ }\r
+\r
+ ( ( WasapiHandle* ) stream_.apiHandle )->renderClient = renderClient;\r
+ ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent = renderEvent;\r
+ }\r
+\r
+ unsigned int outBufferSize = 0;\r
+ hr = renderAudioClient->GetBufferSize( &outBufferSize );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to get render buffer size.";\r
+ goto Exit;\r
+ }\r
+\r
+ // scale inBufferSize according to user->stream sample rate ratio\r
+ unsigned int inBufferSize = ( unsigned int ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT];\r
+ outBufferSize *= stream_.nDeviceChannels[OUTPUT];\r
+\r
+ // set renderBuffer size\r
+ renderBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
+\r
+ // reset the render stream\r
+ hr = renderAudioClient->Reset();\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to reset render stream.";\r
+ goto Exit;\r
+ }\r
+\r
+ // start the render stream\r
+ hr = renderAudioClient->Start();\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to start render stream.";\r
+ goto Exit;\r
+ }\r
+ }\r
+\r
+ if ( stream_.mode == INPUT ) {\r
+ convBuffSize = ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );\r
+ deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );\r
+ }\r
+ else if ( stream_.mode == OUTPUT ) {\r
+ convBuffSize = ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] );\r
+ deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] );\r
+ }\r
+ else if ( stream_.mode == DUPLEX ) {\r
+ convBuffSize = std::max( ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ),\r
+ ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
+ deviceBuffSize = std::max( stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ),\r
+ stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
+ }\r
+\r
+ convBuffer = ( char* ) malloc( convBuffSize );\r
+ stream_.deviceBuffer = ( char* ) malloc( deviceBuffSize );\r
+ if ( !convBuffer || !stream_.deviceBuffer ) {\r
+ errorType = RtAudioError::MEMORY_ERROR;\r
+ errorText_ = "RtApiWasapi::wasapiThread: Error allocating device buffer memory.";\r
+ goto Exit;\r
+ }\r
+\r
+ // stream process loop\r
+ while ( stream_.state != STREAM_STOPPING ) {\r
+ if ( !callbackPulled ) {\r
+ // Callback Input\r
+ // ==============\r
+ // 1. Pull callback buffer from inputBuffer\r
+ // 2. If 1. was successful: Convert callback buffer to user sample rate and channel count\r
+ // Convert callback buffer to user format\r
+\r
+ if ( captureAudioClient ) {\r
+ // Pull callback buffer from inputBuffer\r
+ callbackPulled = captureBuffer.pullBuffer( convBuffer,\r
+ ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT],\r
+ stream_.deviceFormat[INPUT] );\r
+\r
+ if ( callbackPulled ) {\r
+ // Convert callback buffer to user sample rate\r
+ convertBufferWasapi( stream_.deviceBuffer,\r
+ convBuffer,\r
+ stream_.nDeviceChannels[INPUT],\r
+ captureFormat->nSamplesPerSec,\r
+ stream_.sampleRate,\r
+ ( unsigned int ) ( stream_.bufferSize * captureSrRatio ),\r
+ convBufferSize,\r
+ stream_.deviceFormat[INPUT] );\r
+\r
+ if ( stream_.doConvertBuffer[INPUT] ) {\r
+ // Convert callback buffer to user format\r
+ convertBuffer( stream_.userBuffer[INPUT],\r
+ stream_.deviceBuffer,\r
+ stream_.convertInfo[INPUT] );\r
+ }\r
+ else {\r
+ // no further conversion, simple copy deviceBuffer to userBuffer\r
+ memcpy( stream_.userBuffer[INPUT],\r
+ stream_.deviceBuffer,\r
+ stream_.bufferSize * stream_.nUserChannels[INPUT] * formatBytes( stream_.userFormat ) );\r
+ }\r
+ }\r
+ }\r
+ else {\r
+ // if there is no capture stream, set callbackPulled flag\r
+ callbackPulled = true;\r
+ }\r
+\r
+ // Execute Callback\r
+ // ================\r
+ // 1. Execute user callback method\r
+ // 2. Handle return value from callback\r
+\r
+ // if callback has not requested the stream to stop\r
+ if ( callbackPulled && !callbackStopped ) {\r
+ // Execute user callback method\r
+ callbackResult = callback( stream_.userBuffer[OUTPUT],\r
+ stream_.userBuffer[INPUT],\r
+ stream_.bufferSize,\r
+ getStreamTime(),\r
+ captureFlags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY ? RTAUDIO_INPUT_OVERFLOW : 0,\r
+ stream_.callbackInfo.userData );\r
+\r
+ // Handle return value from callback\r
+ if ( callbackResult == 1 ) {\r
+ // instantiate a thread to stop this thread\r
+ HANDLE threadHandle = CreateThread( NULL, 0, stopWasapiThread, this, 0, NULL );\r
+ if ( !threadHandle ) {\r
+ errorType = RtAudioError::THREAD_ERROR;\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream stop thread.";\r
+ goto Exit;\r
+ }\r
+ else if ( !CloseHandle( threadHandle ) ) {\r
+ errorType = RtAudioError::THREAD_ERROR;\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream stop thread handle.";\r
+ goto Exit;\r
+ }\r
+\r
+ callbackStopped = true;\r
+ }\r
+ else if ( callbackResult == 2 ) {\r
+ // instantiate a thread to stop this thread\r
+ HANDLE threadHandle = CreateThread( NULL, 0, abortWasapiThread, this, 0, NULL );\r
+ if ( !threadHandle ) {\r
+ errorType = RtAudioError::THREAD_ERROR;\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream abort thread.";\r
+ goto Exit;\r
+ }\r
+ else if ( !CloseHandle( threadHandle ) ) {\r
+ errorType = RtAudioError::THREAD_ERROR;\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream abort thread handle.";\r
+ goto Exit;\r
+ }\r
+\r
+ callbackStopped = true;\r
+ }\r
+ }\r
+ }\r
+\r
+ // Callback Output\r
+ // ===============\r
+ // 1. Convert callback buffer to stream format\r
+ // 2. Convert callback buffer to stream sample rate and channel count\r
+ // 3. Push callback buffer into outputBuffer\r
+\r
+ if ( renderAudioClient && callbackPulled ) {\r
+ if ( stream_.doConvertBuffer[OUTPUT] ) {\r
+ // Convert callback buffer to stream format\r
+ convertBuffer( stream_.deviceBuffer,\r
+ stream_.userBuffer[OUTPUT],\r
+ stream_.convertInfo[OUTPUT] );\r
+\r
+ }\r
+\r
+ // Convert callback buffer to stream sample rate\r
+ convertBufferWasapi( convBuffer,\r
+ stream_.deviceBuffer,\r
+ stream_.nDeviceChannels[OUTPUT],\r
+ stream_.sampleRate,\r
+ renderFormat->nSamplesPerSec,\r
+ stream_.bufferSize,\r
+ convBufferSize,\r
+ stream_.deviceFormat[OUTPUT] );\r
+\r
+ // Push callback buffer into outputBuffer\r
+ callbackPushed = renderBuffer.pushBuffer( convBuffer,\r
+ convBufferSize * stream_.nDeviceChannels[OUTPUT],\r
+ stream_.deviceFormat[OUTPUT] );\r
+ }\r
+ else {\r
+ // if there is no render stream, set callbackPushed flag\r
+ callbackPushed = true;\r
+ }\r
+\r
+ // Stream Capture\r
+ // ==============\r
+ // 1. Get capture buffer from stream\r
+ // 2. Push capture buffer into inputBuffer\r
+ // 3. If 2. was successful: Release capture buffer\r
+\r
+ if ( captureAudioClient ) {\r
+ // if the callback input buffer was not pulled from captureBuffer, wait for next capture event\r
+ if ( !callbackPulled ) {\r
+ WaitForSingleObject( captureEvent, INFINITE );\r
+ }\r
+\r
+ // Get capture buffer from stream\r
+ hr = captureClient->GetBuffer( &streamBuffer,\r
+ &bufferFrameCount,\r
+ &captureFlags, NULL, NULL );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture buffer.";\r
+ goto Exit;\r
+ }\r
+\r
+ if ( bufferFrameCount != 0 ) {\r
+ // Push capture buffer into inputBuffer\r
+ if ( captureBuffer.pushBuffer( ( char* ) streamBuffer,\r
+ bufferFrameCount * stream_.nDeviceChannels[INPUT],\r
+ stream_.deviceFormat[INPUT] ) )\r
+ {\r
+ // Release capture buffer\r
+ hr = captureClient->ReleaseBuffer( bufferFrameCount );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
+ goto Exit;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // Inform WASAPI that capture was unsuccessful\r
+ hr = captureClient->ReleaseBuffer( 0 );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
+ goto Exit;\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // Inform WASAPI that capture was unsuccessful\r
+ hr = captureClient->ReleaseBuffer( 0 );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
+ goto Exit;\r
+ }\r
+ }\r
+ }\r
+\r
+ // Stream Render\r
+ // =============\r
+ // 1. Get render buffer from stream\r
+ // 2. Pull next buffer from outputBuffer\r
+ // 3. If 2. was successful: Fill render buffer with next buffer\r
+ // Release render buffer\r
+\r
+ if ( renderAudioClient ) {\r
+ // if the callback output buffer was not pushed to renderBuffer, wait for next render event\r
+ if ( callbackPulled && !callbackPushed ) {\r
+ WaitForSingleObject( renderEvent, INFINITE );\r
+ }\r
+\r
+ // Get render buffer from stream\r
+ hr = renderAudioClient->GetBufferSize( &bufferFrameCount );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer size.";\r
+ goto Exit;\r
+ }\r
+\r
+ hr = renderAudioClient->GetCurrentPadding( &numFramesPadding );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer padding.";\r
+ goto Exit;\r
+ }\r
+\r
+ bufferFrameCount -= numFramesPadding;\r
+\r
+ if ( bufferFrameCount != 0 ) {\r
+ hr = renderClient->GetBuffer( bufferFrameCount, &streamBuffer );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer.";\r
+ goto Exit;\r
+ }\r
+\r
+ // Pull next buffer from outputBuffer\r
+ // Fill render buffer with next buffer\r
+ if ( renderBuffer.pullBuffer( ( char* ) streamBuffer,\r
+ bufferFrameCount * stream_.nDeviceChannels[OUTPUT],\r
+ stream_.deviceFormat[OUTPUT] ) )\r
+ {\r
+ // Release render buffer\r
+ hr = renderClient->ReleaseBuffer( bufferFrameCount, 0 );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
+ goto Exit;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // Inform WASAPI that render was unsuccessful\r
+ hr = renderClient->ReleaseBuffer( 0, 0 );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
+ goto Exit;\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // Inform WASAPI that render was unsuccessful\r
+ hr = renderClient->ReleaseBuffer( 0, 0 );\r
+ if ( FAILED( hr ) ) {\r
+ errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
+ goto Exit;\r
+ }\r
+ }\r
+ }\r
+\r
+ // if the callback buffer was pushed renderBuffer reset callbackPulled flag\r
+ if ( callbackPushed ) {\r
+ callbackPulled = false;\r
+ }\r
+\r
+ // tick stream time\r
+ RtApi::tickStreamTime();\r
+ }\r
+\r
+Exit:\r
+ // clean up\r
+ CoTaskMemFree( captureFormat );\r
+ CoTaskMemFree( renderFormat );\r
+\r
+ free ( convBuffer );\r
+\r
+ CoUninitialize();\r
+\r
+ // update stream state\r
+ stream_.state = STREAM_STOPPED;\r
+\r
+ if ( errorText_.empty() )\r
+ return;\r
+ else\r
+ error( errorType );\r
+}\r
+\r
+//******************** End of __WINDOWS_WASAPI__ *********************//\r
+#endif\r
+\r
+\r
+#if defined(__WINDOWS_DS__) // Windows DirectSound API\r
+\r
+// Modified by Robin Davies, October 2005\r
+// - Improvements to DirectX pointer chasing. \r
+// - Bug fix for non-power-of-two Asio granularity used by Edirol PCR-A30.\r
+// - Auto-call CoInitialize for DSOUND and ASIO platforms.\r
+// Various revisions for RtAudio 4.0 by Gary Scavone, April 2007\r
+// Changed device query structure for RtAudio 4.0.7, January 2010\r
+\r
+#include <dsound.h>\r
+#include <assert.h>\r
+#include <algorithm>\r
+\r
+#if defined(__MINGW32__)\r
+ // missing from latest mingw winapi\r
+#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */\r
+#define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */\r
+#define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */\r
+#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */\r
+#endif\r
+\r
+#define MINIMUM_DEVICE_BUFFER_SIZE 32768\r
+\r
+#ifdef _MSC_VER // if Microsoft Visual C++\r
+#pragma comment( lib, "winmm.lib" ) // then, auto-link winmm.lib. Otherwise, it has to be added manually.\r
+#endif\r
+\r
+static inline DWORD dsPointerBetween( DWORD pointer, DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize )\r
+{\r
+ if ( pointer > bufferSize ) pointer -= bufferSize;\r
+ if ( laterPointer < earlierPointer ) laterPointer += bufferSize;\r
+ if ( pointer < earlierPointer ) pointer += bufferSize;\r
+ return pointer >= earlierPointer && pointer < laterPointer;\r
+}\r
+\r
+// A structure to hold various information related to the DirectSound\r
+// API implementation.\r
+struct DsHandle {\r
+ unsigned int drainCounter; // Tracks callback counts when draining\r
+ bool internalDrain; // Indicates if stop is initiated from callback or not.\r
+ void *id[2];\r
+ void *buffer[2];\r
+ bool xrun[2];\r
+ UINT bufferPointer[2]; \r
+ DWORD dsBufferSize[2];\r
+ DWORD dsPointerLeadTime[2]; // the number of bytes ahead of the safe pointer to lead by.\r
+ HANDLE condition;\r
+\r
+ DsHandle()\r
+ :drainCounter(0), internalDrain(false) { id[0] = 0; id[1] = 0; buffer[0] = 0; buffer[1] = 0; xrun[0] = false; xrun[1] = false; bufferPointer[0] = 0; bufferPointer[1] = 0; }\r
+};\r
+\r
+// Declarations for utility functions, callbacks, and structures\r
+// specific to the DirectSound implementation.\r
+static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,\r
+ LPCTSTR description,\r
+ LPCTSTR module,\r
+ LPVOID lpContext );\r
+\r
+static const char* getErrorString( int code );\r
+\r
+static unsigned __stdcall callbackHandler( void *ptr );\r
+\r
+struct DsDevice {\r
+ LPGUID id[2];\r
+ bool validId[2];\r
+ bool found;\r
+ std::string name;\r
+\r
+ DsDevice()\r
+ : found(false) { validId[0] = false; validId[1] = false; }\r
+};\r
+\r
+struct DsProbeData {\r
+ bool isInput;\r
+ std::vector<struct DsDevice>* dsDevices;\r
+};\r
+\r
+RtApiDs :: RtApiDs()\r
+{\r
+ // Dsound will run both-threaded. If CoInitialize fails, then just\r
+ // accept whatever the mainline chose for a threading model.\r
+ coInitialized_ = false;\r
+ HRESULT hr = CoInitialize( NULL );\r
+ if ( !FAILED( hr ) ) coInitialized_ = true;\r
+}\r
+\r
+RtApiDs :: ~RtApiDs()\r
+{\r
+ if ( coInitialized_ ) CoUninitialize(); // balanced call.\r
+ if ( stream_.state != STREAM_CLOSED ) closeStream();\r
+}\r
+\r
+// The DirectSound default output is always the first device.\r
+unsigned int RtApiDs :: getDefaultOutputDevice( void )\r
+{\r
+ return 0;\r
+}\r
+\r
+// The DirectSound default input is always the first input device,\r
+// which is the first capture device enumerated.\r
+unsigned int RtApiDs :: getDefaultInputDevice( void )\r
+{\r
+ return 0;\r
+}\r
+\r
+unsigned int RtApiDs :: getDeviceCount( void )\r
+{\r
+ // Set query flag for previously found devices to false, so that we\r
+ // can check for any devices that have disappeared.\r
+ for ( unsigned int i=0; i<dsDevices.size(); i++ )\r
+ dsDevices[i].found = false;\r
+\r
+ // Query DirectSound devices.\r
+ struct DsProbeData probeInfo;\r
+ probeInfo.isInput = false;\r
+ probeInfo.dsDevices = &dsDevices;\r
+ HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating output devices!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ }\r
+\r
+ // Query DirectSoundCapture devices.\r
+ probeInfo.isInput = true;\r
+ result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating input devices!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ }\r
+\r
+ // Clean out any devices that may have disappeared.\r
+ std::vector< int > indices;\r
+ for ( unsigned int i=0; i<dsDevices.size(); i++ )\r
+ if ( dsDevices[i].found == false ) indices.push_back( i );\r
+ //unsigned int nErased = 0;\r
+ for ( unsigned int i=0; i<indices.size(); i++ )\r
+ dsDevices.erase( dsDevices.begin()+indices[i] );\r
+ //dsDevices.erase( dsDevices.begin()-nErased++ );\r
+\r
+ return static_cast<unsigned int>(dsDevices.size());\r
+}\r
+\r
+RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )\r
+{\r
+ RtAudio::DeviceInfo info;\r
+ info.probed = false;\r
+\r
+ if ( dsDevices.size() == 0 ) {\r
+ // Force a query of all devices\r
+ getDeviceCount();\r
+ if ( dsDevices.size() == 0 ) {\r
+ errorText_ = "RtApiDs::getDeviceInfo: no devices found!";\r
+ error( RtAudioError::INVALID_USE );\r
+ return info;\r
+ }\r
+ }\r
+\r
+ if ( device >= dsDevices.size() ) {\r
+ errorText_ = "RtApiDs::getDeviceInfo: device ID is invalid!";\r
+ error( RtAudioError::INVALID_USE );\r
+ return info;\r
+ }\r
+\r
+ HRESULT result;\r
+ if ( dsDevices[ device ].validId[0] == false ) goto probeInput;\r
+\r
+ LPDIRECTSOUND output;\r
+ DSCAPS outCaps;\r
+ result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ goto probeInput;\r
+ }\r
+\r
+ outCaps.dwSize = sizeof( outCaps );\r
+ result = output->GetCaps( &outCaps );\r
+ if ( FAILED( result ) ) {\r
+ output->Release();\r
+ errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ goto probeInput;\r
+ }\r
+\r
+ // Get output channel information.\r
+ info.outputChannels = ( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;\r
+\r
+ // Get sample rate information.\r
+ info.sampleRates.clear();\r
+ for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
+ if ( SAMPLE_RATES[k] >= (unsigned int) outCaps.dwMinSecondarySampleRate &&\r
+ SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate )\r
+ info.sampleRates.push_back( SAMPLE_RATES[k] );\r
+ }\r
+\r
+ // Get format information.\r
+ if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT ) info.nativeFormats |= RTAUDIO_SINT16;\r
+ if ( outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) info.nativeFormats |= RTAUDIO_SINT8;\r
+\r
+ output->Release();\r
+\r
+ if ( getDefaultOutputDevice() == device )\r
+ info.isDefaultOutput = true;\r
+\r
+ if ( dsDevices[ device ].validId[1] == false ) {\r
+ info.name = dsDevices[ device ].name;\r
+ info.probed = true;\r
+ return info;\r
+ }\r
+\r
+ probeInput:\r
+\r
+ LPDIRECTSOUNDCAPTURE input;\r
+ result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ DSCCAPS inCaps;\r
+ inCaps.dwSize = sizeof( inCaps );\r
+ result = input->GetCaps( &inCaps );\r
+ if ( FAILED( result ) ) {\r
+ input->Release();\r
+ errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ // Get input channel information.\r
+ info.inputChannels = inCaps.dwChannels;\r
+\r
+ // Get sample rate and format information.\r
+ std::vector<unsigned int> rates;\r
+ if ( inCaps.dwChannels >= 2 ) {\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
+\r
+ if ( info.nativeFormats & RTAUDIO_SINT16 ) {\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) rates.push_back( 11025 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) rates.push_back( 22050 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) rates.push_back( 44100 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) rates.push_back( 96000 );\r
+ }\r
+ else if ( info.nativeFormats & RTAUDIO_SINT8 ) {\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) rates.push_back( 11025 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) rates.push_back( 22050 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) rates.push_back( 44100 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) rates.push_back( 96000 );\r
+ }\r
+ }\r
+ else if ( inCaps.dwChannels == 1 ) {\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.nativeFormats |= RTAUDIO_SINT16;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.nativeFormats |= RTAUDIO_SINT8;\r
+\r
+ if ( info.nativeFormats & RTAUDIO_SINT16 ) {\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) rates.push_back( 11025 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) rates.push_back( 22050 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) rates.push_back( 44100 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) rates.push_back( 96000 );\r
+ }\r
+ else if ( info.nativeFormats & RTAUDIO_SINT8 ) {\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) rates.push_back( 11025 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) rates.push_back( 22050 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) rates.push_back( 44100 );\r
+ if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) rates.push_back( 96000 );\r
+ }\r
+ }\r
+ else info.inputChannels = 0; // technically, this would be an error\r
+\r
+ input->Release();\r
+\r
+ if ( info.inputChannels == 0 ) return info;\r
+\r
+ // Copy the supported rates to the info structure but avoid duplication.\r
+ bool found;\r
+ for ( unsigned int i=0; i<rates.size(); i++ ) {\r
+ found = false;\r
+ for ( unsigned int j=0; j<info.sampleRates.size(); j++ ) {\r
+ if ( rates[i] == info.sampleRates[j] ) {\r
+ found = true;\r
+ break;\r
+ }\r
+ }\r
+ if ( found == false ) info.sampleRates.push_back( rates[i] );\r
+ }\r
+ std::sort( info.sampleRates.begin(), info.sampleRates.end() );\r
+\r
+ // If device opens for both playback and capture, we determine the channels.\r
+ if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
+ info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
+\r
+ if ( device == 0 ) info.isDefaultInput = true;\r
+\r
+ // Copy name and return.\r
+ info.name = dsDevices[ device ].name;\r
+ info.probed = true;\r
+ return info;\r
+}\r
+\r
+bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
+ unsigned int firstChannel, unsigned int sampleRate,\r
+ RtAudioFormat format, unsigned int *bufferSize,\r
+ RtAudio::StreamOptions *options )\r
+{\r
+ if ( channels + firstChannel > 2 ) {\r
+ errorText_ = "RtApiDs::probeDeviceOpen: DirectSound does not support more than 2 channels per device.";\r
+ return FAILURE;\r
+ }\r
+\r
+ size_t nDevices = dsDevices.size();\r
+ if ( nDevices == 0 ) {\r
+ // This should not happen because a check is made before this function is called.\r
+ errorText_ = "RtApiDs::probeDeviceOpen: no devices found!";\r
+ return FAILURE;\r
+ }\r
+\r
+ if ( device >= nDevices ) {\r
+ // This should not happen because a check is made before this function is called.\r
+ errorText_ = "RtApiDs::probeDeviceOpen: device ID is invalid!";\r
+ return FAILURE;\r
+ }\r
+\r
+ if ( mode == OUTPUT ) {\r
+ if ( dsDevices[ device ].validId[0] == false ) {\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support output!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ }\r
+ else { // mode == INPUT\r
+ if ( dsDevices[ device ].validId[1] == false ) {\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support input!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ }\r
+\r
+ // According to a note in PortAudio, using GetDesktopWindow()\r
+ // instead of GetForegroundWindow() is supposed to avoid problems\r
+ // that occur when the application's window is not the foreground\r
+ // window. Also, if the application window closes before the\r
+ // DirectSound buffer, DirectSound can crash. In the past, I had\r
+ // problems when using GetDesktopWindow() but it seems fine now\r
+ // (January 2010). I'll leave it commented here.\r
+ // HWND hWnd = GetForegroundWindow();\r
+ HWND hWnd = GetDesktopWindow();\r
+\r
+ // Check the numberOfBuffers parameter and limit the lowest value to\r
+ // two. This is a judgement call and a value of two is probably too\r
+ // low for capture, but it should work for playback.\r
+ int nBuffers = 0;\r
+ if ( options ) nBuffers = options->numberOfBuffers;\r
+ if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) nBuffers = 2;\r
+ if ( nBuffers < 2 ) nBuffers = 3;\r
+\r
+ // Check the lower range of the user-specified buffer size and set\r
+ // (arbitrarily) to a lower bound of 32.\r
+ if ( *bufferSize < 32 ) *bufferSize = 32;\r
+\r
+ // Create the wave format structure. The data format setting will\r
+ // be determined later.\r
+ WAVEFORMATEX waveFormat;\r
+ ZeroMemory( &waveFormat, sizeof(WAVEFORMATEX) );\r
+ waveFormat.wFormatTag = WAVE_FORMAT_PCM;\r
+ waveFormat.nChannels = channels + firstChannel;\r
+ waveFormat.nSamplesPerSec = (unsigned long) sampleRate;\r
+\r
+ // Determine the device buffer size. By default, we'll use the value\r
+ // defined above (32K), but we will grow it to make allowances for\r
+ // very large software buffer sizes.\r
+ DWORD dsBufferSize = MINIMUM_DEVICE_BUFFER_SIZE;\r
+ DWORD dsPointerLeadTime = 0;\r
+\r
+ void *ohandle = 0, *bhandle = 0;\r
+ HRESULT result;\r
+ if ( mode == OUTPUT ) {\r
+\r
+ LPDIRECTSOUND output;\r
+ result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ DSCAPS outCaps;\r
+ outCaps.dwSize = sizeof( outCaps );\r
+ result = output->GetCaps( &outCaps );\r
+ if ( FAILED( result ) ) {\r
+ output->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Check channel information.\r
+ if ( channels + firstChannel == 2 && !( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ) {\r
+ errorStream_ << "RtApiDs::getDeviceInfo: the output device (" << dsDevices[ device ].name << ") does not support stereo playback.";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Check format information. Use 16-bit format unless not\r
+ // supported or user requests 8-bit.\r
+ if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT &&\r
+ !( format == RTAUDIO_SINT8 && outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) ) {\r
+ waveFormat.wBitsPerSample = 16;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
+ }\r
+ else {\r
+ waveFormat.wBitsPerSample = 8;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
+ }\r
+ stream_.userFormat = format;\r
+\r
+ // Update wave format structure and buffer information.\r
+ waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;\r
+ waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;\r
+ dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;\r
+\r
+ // If the user wants an even bigger buffer, increase the device buffer size accordingly.\r
+ while ( dsPointerLeadTime * 2U > dsBufferSize )\r
+ dsBufferSize *= 2;\r
+\r
+ // Set cooperative level to DSSCL_EXCLUSIVE ... sound stops when window focus changes.\r
+ // result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE );\r
+ // Set cooperative level to DSSCL_PRIORITY ... sound remains when window focus changes.\r
+ result = output->SetCooperativeLevel( hWnd, DSSCL_PRIORITY );\r
+ if ( FAILED( result ) ) {\r
+ output->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Even though we will write to the secondary buffer, we need to\r
+ // access the primary buffer to set the correct output format\r
+ // (since the default is 8-bit, 22 kHz!). Setup the DS primary\r
+ // buffer description.\r
+ DSBUFFERDESC bufferDescription;\r
+ ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );\r
+ bufferDescription.dwSize = sizeof( DSBUFFERDESC );\r
+ bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;\r
+\r
+ // Obtain the primary buffer\r
+ LPDIRECTSOUNDBUFFER buffer;\r
+ result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
+ if ( FAILED( result ) ) {\r
+ output->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") accessing primary buffer (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Set the primary DS buffer sound format.\r
+ result = buffer->SetFormat( &waveFormat );\r
+ if ( FAILED( result ) ) {\r
+ output->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting primary buffer format (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Setup the secondary DS buffer description.\r
+ ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );\r
+ bufferDescription.dwSize = sizeof( DSBUFFERDESC );\r
+ bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |\r
+ DSBCAPS_GLOBALFOCUS |\r
+ DSBCAPS_GETCURRENTPOSITION2 |\r
+ DSBCAPS_LOCHARDWARE ); // Force hardware mixing\r
+ bufferDescription.dwBufferBytes = dsBufferSize;\r
+ bufferDescription.lpwfxFormat = &waveFormat;\r
+\r
+ // Try to create the secondary DS buffer. If that doesn't work,\r
+ // try to use software mixing. Otherwise, there's a problem.\r
+ result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
+ if ( FAILED( result ) ) {\r
+ bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |\r
+ DSBCAPS_GLOBALFOCUS |\r
+ DSBCAPS_GETCURRENTPOSITION2 |\r
+ DSBCAPS_LOCSOFTWARE ); // Force software mixing\r
+ result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );\r
+ if ( FAILED( result ) ) {\r
+ output->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating secondary buffer (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ }\r
+\r
+ // Get the buffer size ... might be different from what we specified.\r
+ DSBCAPS dsbcaps;\r
+ dsbcaps.dwSize = sizeof( DSBCAPS );\r
+ result = buffer->GetCaps( &dsbcaps );\r
+ if ( FAILED( result ) ) {\r
+ output->Release();\r
+ buffer->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ dsBufferSize = dsbcaps.dwBufferBytes;\r
+\r
+ // Lock the DS buffer\r
+ LPVOID audioPtr;\r
+ DWORD dataLen;\r
+ result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );\r
+ if ( FAILED( result ) ) {\r
+ output->Release();\r
+ buffer->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking buffer (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Zero the DS buffer\r
+ ZeroMemory( audioPtr, dataLen );\r
+\r
+ // Unlock the DS buffer\r
+ result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
+ if ( FAILED( result ) ) {\r
+ output->Release();\r
+ buffer->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking buffer (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ ohandle = (void *) output;\r
+ bhandle = (void *) buffer;\r
+ }\r
+\r
+ if ( mode == INPUT ) {\r
+\r
+ LPDIRECTSOUNDCAPTURE input;\r
+ result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ DSCCAPS inCaps;\r
+ inCaps.dwSize = sizeof( inCaps );\r
+ result = input->GetCaps( &inCaps );\r
+ if ( FAILED( result ) ) {\r
+ input->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Check channel information.\r
+ if ( inCaps.dwChannels < channels + firstChannel ) {\r
+ errorText_ = "RtApiDs::getDeviceInfo: the input device does not support requested input channels.";\r
+ return FAILURE;\r
+ }\r
+\r
+ // Check format information. Use 16-bit format unless user\r
+ // requests 8-bit.\r
+ DWORD deviceFormats;\r
+ if ( channels + firstChannel == 2 ) {\r
+ deviceFormats = WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08;\r
+ if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {\r
+ waveFormat.wBitsPerSample = 8;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
+ }\r
+ else { // assume 16-bit is supported\r
+ waveFormat.wBitsPerSample = 16;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
+ }\r
+ }\r
+ else { // channel == 1\r
+ deviceFormats = WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08;\r
+ if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {\r
+ waveFormat.wBitsPerSample = 8;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
+ }\r
+ else { // assume 16-bit is supported\r
+ waveFormat.wBitsPerSample = 16;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
+ }\r
+ }\r
+ stream_.userFormat = format;\r
+\r
+ // Update wave format structure and buffer information.\r
+ waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;\r
+ waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;\r
+ dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;\r
+\r
+ // If the user wants an even bigger buffer, increase the device buffer size accordingly.\r
+ while ( dsPointerLeadTime * 2U > dsBufferSize )\r
+ dsBufferSize *= 2;\r
+\r
+ // Setup the secondary DS buffer description.\r
+ DSCBUFFERDESC bufferDescription;\r
+ ZeroMemory( &bufferDescription, sizeof( DSCBUFFERDESC ) );\r
+ bufferDescription.dwSize = sizeof( DSCBUFFERDESC );\r
+ bufferDescription.dwFlags = 0;\r
+ bufferDescription.dwReserved = 0;\r
+ bufferDescription.dwBufferBytes = dsBufferSize;\r
+ bufferDescription.lpwfxFormat = &waveFormat;\r
+\r
+ // Create the capture buffer.\r
+ LPDIRECTSOUNDCAPTUREBUFFER buffer;\r
+ result = input->CreateCaptureBuffer( &bufferDescription, &buffer, NULL );\r
+ if ( FAILED( result ) ) {\r
+ input->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Get the buffer size ... might be different from what we specified.\r
+ DSCBCAPS dscbcaps;\r
+ dscbcaps.dwSize = sizeof( DSCBCAPS );\r
+ result = buffer->GetCaps( &dscbcaps );\r
+ if ( FAILED( result ) ) {\r
+ input->Release();\r
+ buffer->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ dsBufferSize = dscbcaps.dwBufferBytes;\r
+\r
+ // NOTE: We could have a problem here if this is a duplex stream\r
+ // and the play and capture hardware buffer sizes are different\r
+ // (I'm actually not sure if that is a problem or not).\r
+ // Currently, we are not verifying that.\r
+\r
+ // Lock the capture buffer\r
+ LPVOID audioPtr;\r
+ DWORD dataLen;\r
+ result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );\r
+ if ( FAILED( result ) ) {\r
+ input->Release();\r
+ buffer->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking input buffer (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Zero the buffer\r
+ ZeroMemory( audioPtr, dataLen );\r
+\r
+ // Unlock the buffer\r
+ result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
+ if ( FAILED( result ) ) {\r
+ input->Release();\r
+ buffer->Release();\r
+ errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsDevices[ device ].name << ")!";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ ohandle = (void *) input;\r
+ bhandle = (void *) buffer;\r
+ }\r
+\r
+ // Set various stream parameters\r
+ DsHandle *handle = 0;\r
+ stream_.nDeviceChannels[mode] = channels + firstChannel;\r
+ stream_.nUserChannels[mode] = channels;\r
+ stream_.bufferSize = *bufferSize;\r
+ stream_.channelOffset[mode] = firstChannel;\r
+ stream_.deviceInterleaved[mode] = true;\r
+ if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
+ else stream_.userInterleaved = true;\r
+\r
+ // Set flag for buffer conversion\r
+ stream_.doConvertBuffer[mode] = false;\r
+ if (stream_.nUserChannels[mode] != stream_.nDeviceChannels[mode])\r
+ stream_.doConvertBuffer[mode] = true;\r
+ if (stream_.userFormat != stream_.deviceFormat[mode])\r
+ stream_.doConvertBuffer[mode] = true;\r
+ if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
+ stream_.nUserChannels[mode] > 1 )\r
+ stream_.doConvertBuffer[mode] = true;\r
+\r
+ // Allocate necessary internal buffers\r
+ long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
+ stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
+ if ( stream_.userBuffer[mode] == NULL ) {\r
+ errorText_ = "RtApiDs::probeDeviceOpen: error allocating user buffer memory.";\r
+ goto error;\r
+ }\r
+\r
+ if ( stream_.doConvertBuffer[mode] ) {\r
+\r
+ bool makeBuffer = true;\r
+ bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
+ if ( mode == INPUT ) {\r
+ if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
+ unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
+ if ( bufferBytes <= (long) bytesOut ) makeBuffer = false;\r
+ }\r
+ }\r
+\r
+ if ( makeBuffer ) {\r
+ bufferBytes *= *bufferSize;\r
+ if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
+ if ( stream_.deviceBuffer == NULL ) {\r
+ errorText_ = "RtApiDs::probeDeviceOpen: error allocating device buffer memory.";\r
+ goto error;\r
+ }\r
+ }\r
+ }\r
+\r
+ // Allocate our DsHandle structures for the stream.\r
+ if ( stream_.apiHandle == 0 ) {\r
+ try {\r
+ handle = new DsHandle;\r
+ }\r
+ catch ( std::bad_alloc& ) {\r
+ errorText_ = "RtApiDs::probeDeviceOpen: error allocating AsioHandle memory.";\r
+ goto error;\r
+ }\r
+\r
+ // Create a manual-reset event.\r
+ handle->condition = CreateEvent( NULL, // no security\r
+ TRUE, // manual-reset\r
+ FALSE, // non-signaled initially\r
+ NULL ); // unnamed\r
+ stream_.apiHandle = (void *) handle;\r
+ }\r
+ else\r
+ handle = (DsHandle *) stream_.apiHandle;\r
+ handle->id[mode] = ohandle;\r
+ handle->buffer[mode] = bhandle;\r
+ handle->dsBufferSize[mode] = dsBufferSize;\r
+ handle->dsPointerLeadTime[mode] = dsPointerLeadTime;\r
+\r
+ stream_.device[mode] = device;\r
+ stream_.state = STREAM_STOPPED;\r
+ if ( stream_.mode == OUTPUT && mode == INPUT )\r
+ // We had already set up an output stream.\r
+ stream_.mode = DUPLEX;\r
+ else\r
+ stream_.mode = mode;\r
+ stream_.nBuffers = nBuffers;\r
+ stream_.sampleRate = sampleRate;\r
+\r
+ // Setup the buffer conversion information structure.\r
+ if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
+\r
+ // Setup the callback thread.\r
+ if ( stream_.callbackInfo.isRunning == false ) {\r
+ unsigned threadId;\r
+ stream_.callbackInfo.isRunning = true;\r
+ stream_.callbackInfo.object = (void *) this;\r
+ stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &callbackHandler,\r
+ &stream_.callbackInfo, 0, &threadId );\r
+ if ( stream_.callbackInfo.thread == 0 ) {\r
+ errorText_ = "RtApiDs::probeDeviceOpen: error creating callback thread!";\r
+ goto error;\r
+ }\r
+\r
+ // Boost DS thread priority\r
+ SetThreadPriority( (HANDLE) stream_.callbackInfo.thread, THREAD_PRIORITY_HIGHEST );\r
+ }\r
+ return SUCCESS;\r
+\r
+ error:\r
+ if ( handle ) {\r
+ if ( handle->buffer[0] ) { // the object pointer can be NULL and valid\r
+ LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];\r
+ LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
+ if ( buffer ) buffer->Release();\r
+ object->Release();\r
+ }\r
+ if ( handle->buffer[1] ) {\r
+ LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];\r
+ LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
+ if ( buffer ) buffer->Release();\r
+ object->Release();\r
+ }\r
+ CloseHandle( handle->condition );\r
+ delete handle;\r
+ stream_.apiHandle = 0;\r
+ }\r
+\r
+ for ( int i=0; i<2; i++ ) {\r
+ if ( stream_.userBuffer[i] ) {\r
+ free( stream_.userBuffer[i] );\r
+ stream_.userBuffer[i] = 0;\r
+ }\r
+ }\r
+\r
+ if ( stream_.deviceBuffer ) {\r
+ free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = 0;\r
+ }\r
+\r
+ stream_.state = STREAM_CLOSED;\r
+ return FAILURE;\r
+}\r
+\r
+void RtApiDs :: closeStream()\r
+{\r
+ if ( stream_.state == STREAM_CLOSED ) {\r
+ errorText_ = "RtApiDs::closeStream(): no open stream to close!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ // Stop the callback thread.\r
+ stream_.callbackInfo.isRunning = false;\r
+ WaitForSingleObject( (HANDLE) stream_.callbackInfo.thread, INFINITE );\r
+ CloseHandle( (HANDLE) stream_.callbackInfo.thread );\r
+\r
+ DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
+ if ( handle ) {\r
+ if ( handle->buffer[0] ) { // the object pointer can be NULL and valid\r
+ LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];\r
+ LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
+ if ( buffer ) {\r
+ buffer->Stop();\r
+ buffer->Release();\r
+ }\r
+ object->Release();\r
+ }\r
+ if ( handle->buffer[1] ) {\r
+ LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];\r
+ LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
+ if ( buffer ) {\r
+ buffer->Stop();\r
+ buffer->Release();\r
+ }\r
+ object->Release();\r
+ }\r
+ CloseHandle( handle->condition );\r
+ delete handle;\r
+ stream_.apiHandle = 0;\r
+ }\r
+\r
+ for ( int i=0; i<2; i++ ) {\r
+ if ( stream_.userBuffer[i] ) {\r
+ free( stream_.userBuffer[i] );\r
+ stream_.userBuffer[i] = 0;\r
+ }\r
+ }\r
+\r
+ if ( stream_.deviceBuffer ) {\r
+ free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = 0;\r
+ }\r
+\r
+ stream_.mode = UNINITIALIZED;\r
+ stream_.state = STREAM_CLOSED;\r
+}\r
+\r
+void RtApiDs :: startStream()\r
+{\r
+ verifyStream();\r
+ if ( stream_.state == STREAM_RUNNING ) {\r
+ errorText_ = "RtApiDs::startStream(): the stream is already running!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
+\r
+ // Increase scheduler frequency on lesser windows (a side-effect of\r
+ // increasing timer accuracy). On greater windows (Win2K or later),\r
+ // this is already in effect.\r
+ timeBeginPeriod( 1 ); \r
+\r
+ buffersRolling = false;\r
+ duplexPrerollBytes = 0;\r
+\r
+ if ( stream_.mode == DUPLEX ) {\r
+ // 0.5 seconds of silence in DUPLEX mode while the devices spin up and synchronize.\r
+ duplexPrerollBytes = (int) ( 0.5 * stream_.sampleRate * formatBytes( stream_.deviceFormat[1] ) * stream_.nDeviceChannels[1] );\r
+ }\r
+\r
+ HRESULT result = 0;\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+\r
+ LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
+ result = buffer->Play( 0, 0, DSBPLAY_LOOPING );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting output buffer!";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+ }\r
+\r
+ if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
+\r
+ LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
+ result = buffer->Start( DSCBSTART_LOOPING );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting input buffer!";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+ }\r
+\r
+ handle->drainCounter = 0;\r
+ handle->internalDrain = false;\r
+ ResetEvent( handle->condition );\r
+ stream_.state = STREAM_RUNNING;\r
+\r
+ unlock:\r
+ if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR );\r
+}\r
+\r
+void RtApiDs :: stopStream()\r
+{\r
+ verifyStream();\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ errorText_ = "RtApiDs::stopStream(): the stream is already stopped!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ HRESULT result = 0;\r
+ LPVOID audioPtr;\r
+ DWORD dataLen;\r
+ DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+ if ( handle->drainCounter == 0 ) {\r
+ handle->drainCounter = 2;\r
+ WaitForSingleObject( handle->condition, INFINITE ); // block until signaled\r
+ }\r
+\r
+ stream_.state = STREAM_STOPPED;\r
+\r
+ MUTEX_LOCK( &stream_.mutex );\r
+\r
+ // Stop the buffer and clear memory\r
+ LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
+ result = buffer->Stop();\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping output buffer!";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+\r
+ // Lock the buffer and clear it so that if we start to play again,\r
+ // we won't have old data playing.\r
+ result = buffer->Lock( 0, handle->dsBufferSize[0], &audioPtr, &dataLen, NULL, NULL, 0 );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking output buffer!";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+\r
+ // Zero the DS buffer\r
+ ZeroMemory( audioPtr, dataLen );\r
+\r
+ // Unlock the DS buffer\r
+ result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking output buffer!";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+\r
+ // If we start playing again, we must begin at beginning of buffer.\r
+ handle->bufferPointer[0] = 0;\r
+ }\r
+\r
+ if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
+ LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
+ audioPtr = NULL;\r
+ dataLen = 0;\r
+\r
+ stream_.state = STREAM_STOPPED;\r
+\r
+ if ( stream_.mode != DUPLEX )\r
+ MUTEX_LOCK( &stream_.mutex );\r
+\r
+ result = buffer->Stop();\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping input buffer!";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+\r
+ // Lock the buffer and clear it so that if we start to play again,\r
+ // we won't have old data playing.\r
+ result = buffer->Lock( 0, handle->dsBufferSize[1], &audioPtr, &dataLen, NULL, NULL, 0 );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking input buffer!";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+\r
+ // Zero the DS buffer\r
+ ZeroMemory( audioPtr, dataLen );\r
+\r
+ // Unlock the DS buffer\r
+ result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking input buffer!";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+\r
+ // If we start recording again, we must begin at beginning of buffer.\r
+ handle->bufferPointer[1] = 0;\r
+ }\r
+\r
+ unlock:\r
+ timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows.\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+\r
+ if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR );\r
+}\r
+\r
+void RtApiDs :: abortStream()\r
+{\r
+ verifyStream();\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ errorText_ = "RtApiDs::abortStream(): the stream is already stopped!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
+ handle->drainCounter = 2;\r
+\r
+ stopStream();\r
+}\r
+\r
+void RtApiDs :: callbackEvent()\r
+{\r
+ if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) {\r
+ Sleep( 50 ); // sleep 50 milliseconds\r
+ return;\r
+ }\r
+\r
+ if ( stream_.state == STREAM_CLOSED ) {\r
+ errorText_ = "RtApiDs::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;\r
+ DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
+\r
+ // Check if we were draining the stream and signal is finished.\r
+ if ( handle->drainCounter > stream_.nBuffers + 2 ) {\r
+\r
+ stream_.state = STREAM_STOPPING;\r
+ if ( handle->internalDrain == false )\r
+ SetEvent( handle->condition );\r
+ else\r
+ stopStream();\r
+ return;\r
+ }\r
+\r
+ // Invoke user callback to get fresh output data UNLESS we are\r
+ // draining stream.\r
+ if ( handle->drainCounter == 0 ) {\r
+ RtAudioCallback callback = (RtAudioCallback) info->callback;\r
+ double streamTime = getStreamTime();\r
+ RtAudioStreamStatus status = 0;\r
+ if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
+ status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
+ handle->xrun[0] = false;\r
+ }\r
+ if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
+ status |= RTAUDIO_INPUT_OVERFLOW;\r
+ handle->xrun[1] = false;\r
+ }\r
+ int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
+ stream_.bufferSize, streamTime, status, info->userData );\r
+ if ( cbReturnValue == 2 ) {\r
+ stream_.state = STREAM_STOPPING;\r
+ handle->drainCounter = 2;\r
+ abortStream();\r
+ return;\r
+ }\r
+ else if ( cbReturnValue == 1 ) {\r
+ handle->drainCounter = 1;\r
+ handle->internalDrain = true;\r
+ }\r
+ }\r
+\r
+ HRESULT result;\r
+ DWORD currentWritePointer, safeWritePointer;\r
+ DWORD currentReadPointer, safeReadPointer;\r
+ UINT nextWritePointer;\r
+\r
+ LPVOID buffer1 = NULL;\r
+ LPVOID buffer2 = NULL;\r
+ DWORD bufferSize1 = 0;\r
+ DWORD bufferSize2 = 0;\r
+\r
+ char *buffer;\r
+ long bufferBytes;\r
+\r
+ MUTEX_LOCK( &stream_.mutex );\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ return;\r
+ }\r
+\r
+ if ( buffersRolling == false ) {\r
+ if ( stream_.mode == DUPLEX ) {\r
+ //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );\r
+\r
+ // It takes a while for the devices to get rolling. As a result,\r
+ // there's no guarantee that the capture and write device pointers\r
+ // will move in lockstep. Wait here for both devices to start\r
+ // rolling, and then set our buffer pointers accordingly.\r
+ // e.g. Crystal Drivers: the capture buffer starts up 5700 to 9600\r
+ // bytes later than the write buffer.\r
+\r
+ // Stub: a serious risk of having a pre-emptive scheduling round\r
+ // take place between the two GetCurrentPosition calls... but I'm\r
+ // really not sure how to solve the problem. Temporarily boost to\r
+ // Realtime priority, maybe; but I'm not sure what priority the\r
+ // DirectSound service threads run at. We *should* be roughly\r
+ // within a ms or so of correct.\r
+\r
+ LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
+ LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
+\r
+ DWORD startSafeWritePointer, startSafeReadPointer;\r
+\r
+ result = dsWriteBuffer->GetCurrentPosition( NULL, &startSafeWritePointer );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ result = dsCaptureBuffer->GetCurrentPosition( NULL, &startSafeReadPointer );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ while ( true ) {\r
+ result = dsWriteBuffer->GetCurrentPosition( NULL, &safeWritePointer );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ result = dsCaptureBuffer->GetCurrentPosition( NULL, &safeReadPointer );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ if ( safeWritePointer != startSafeWritePointer && safeReadPointer != startSafeReadPointer ) break;\r
+ Sleep( 1 );\r
+ }\r
+\r
+ //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );\r
+\r
+ handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];\r
+ if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];\r
+ handle->bufferPointer[1] = safeReadPointer;\r
+ }\r
+ else if ( stream_.mode == OUTPUT ) {\r
+\r
+ // Set the proper nextWritePosition after initial startup.\r
+ LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
+ result = dsWriteBuffer->GetCurrentPosition( ¤tWritePointer, &safeWritePointer );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];\r
+ if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];\r
+ }\r
+\r
+ buffersRolling = true;\r
+ }\r
+\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+ \r
+ LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
+\r
+ if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
+ bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];\r
+ bufferBytes *= formatBytes( stream_.userFormat );\r
+ memset( stream_.userBuffer[0], 0, bufferBytes );\r
+ }\r
+\r
+ // Setup parameters and do buffer conversion if necessary.\r
+ if ( stream_.doConvertBuffer[0] ) {\r
+ buffer = stream_.deviceBuffer;\r
+ convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
+ bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[0];\r
+ bufferBytes *= formatBytes( stream_.deviceFormat[0] );\r
+ }\r
+ else {\r
+ buffer = stream_.userBuffer[0];\r
+ bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];\r
+ bufferBytes *= formatBytes( stream_.userFormat );\r
+ }\r
+\r
+ // No byte swapping necessary in DirectSound implementation.\r
+\r
+ // Ahhh ... windoze. 16-bit data is signed but 8-bit data is\r
+ // unsigned. So, we need to convert our signed 8-bit data here to\r
+ // unsigned.\r
+ if ( stream_.deviceFormat[0] == RTAUDIO_SINT8 )\r
+ for ( int i=0; i<bufferBytes; i++ ) buffer[i] = (unsigned char) ( buffer[i] + 128 );\r
+\r
+ DWORD dsBufferSize = handle->dsBufferSize[0];\r
+ nextWritePointer = handle->bufferPointer[0];\r
+\r
+ DWORD endWrite, leadPointer;\r
+ while ( true ) {\r
+ // Find out where the read and "safe write" pointers are.\r
+ result = dsBuffer->GetCurrentPosition( ¤tWritePointer, &safeWritePointer );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+\r
+ // We will copy our output buffer into the region between\r
+ // safeWritePointer and leadPointer. If leadPointer is not\r
+ // beyond the next endWrite position, wait until it is.\r
+ leadPointer = safeWritePointer + handle->dsPointerLeadTime[0];\r
+ //std::cout << "safeWritePointer = " << safeWritePointer << ", leadPointer = " << leadPointer << ", nextWritePointer = " << nextWritePointer << std::endl;\r
+ if ( leadPointer > dsBufferSize ) leadPointer -= dsBufferSize;\r
+ if ( leadPointer < nextWritePointer ) leadPointer += dsBufferSize; // unwrap offset\r
+ endWrite = nextWritePointer + bufferBytes;\r
+\r
+ // Check whether the entire write region is behind the play pointer.\r
+ if ( leadPointer >= endWrite ) break;\r
+\r
+ // If we are here, then we must wait until the leadPointer advances\r
+ // beyond the end of our next write region. We use the\r
+ // Sleep() function to suspend operation until that happens.\r
+ double millis = ( endWrite - leadPointer ) * 1000.0;\r
+ millis /= ( formatBytes( stream_.deviceFormat[0]) * stream_.nDeviceChannels[0] * stream_.sampleRate);\r
+ if ( millis < 1.0 ) millis = 1.0;\r
+ Sleep( (DWORD) millis );\r
+ }\r
+\r
+ if ( dsPointerBetween( nextWritePointer, safeWritePointer, currentWritePointer, dsBufferSize )\r
+ || dsPointerBetween( endWrite, safeWritePointer, currentWritePointer, dsBufferSize ) ) { \r
+ // We've strayed into the forbidden zone ... resync the read pointer.\r
+ handle->xrun[0] = true;\r
+ nextWritePointer = safeWritePointer + handle->dsPointerLeadTime[0] - bufferBytes;\r
+ if ( nextWritePointer >= dsBufferSize ) nextWritePointer -= dsBufferSize;\r
+ handle->bufferPointer[0] = nextWritePointer;\r
+ endWrite = nextWritePointer + bufferBytes;\r
+ }\r
+\r
+ // Lock free space in the buffer\r
+ result = dsBuffer->Lock( nextWritePointer, bufferBytes, &buffer1,\r
+ &bufferSize1, &buffer2, &bufferSize2, 0 );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+\r
+ // Copy our buffer into the DS buffer\r
+ CopyMemory( buffer1, buffer, bufferSize1 );\r
+ if ( buffer2 != NULL ) CopyMemory( buffer2, buffer+bufferSize1, bufferSize2 );\r
+\r
+ // Update our buffer offset and unlock sound buffer\r
+ dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ nextWritePointer = ( nextWritePointer + bufferSize1 + bufferSize2 ) % dsBufferSize;\r
+ handle->bufferPointer[0] = nextWritePointer;\r
+ }\r
+\r
+ // Don't bother draining input\r
+ if ( handle->drainCounter ) {\r
+ handle->drainCounter++;\r
+ goto unlock;\r
+ }\r
+\r
+ if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
+\r
+ // Setup parameters.\r
+ if ( stream_.doConvertBuffer[1] ) {\r
+ buffer = stream_.deviceBuffer;\r
+ bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[1];\r
+ bufferBytes *= formatBytes( stream_.deviceFormat[1] );\r
+ }\r
+ else {\r
+ buffer = stream_.userBuffer[1];\r
+ bufferBytes = stream_.bufferSize * stream_.nUserChannels[1];\r
+ bufferBytes *= formatBytes( stream_.userFormat );\r
+ }\r
+\r
+ LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];\r
+ long nextReadPointer = handle->bufferPointer[1];\r
+ DWORD dsBufferSize = handle->dsBufferSize[1];\r
+\r
+ // Find out where the write and "safe read" pointers are.\r
+ result = dsBuffer->GetCurrentPosition( ¤tReadPointer, &safeReadPointer );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+\r
+ if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset\r
+ DWORD endRead = nextReadPointer + bufferBytes;\r
+\r
+ // Handling depends on whether we are INPUT or DUPLEX. \r
+ // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode,\r
+ // then a wait here will drag the write pointers into the forbidden zone.\r
+ // \r
+ // In DUPLEX mode, rather than wait, we will back off the read pointer until \r
+ // it's in a safe position. This causes dropouts, but it seems to be the only \r
+ // practical way to sync up the read and write pointers reliably, given the \r
+ // the very complex relationship between phase and increment of the read and write \r
+ // pointers.\r
+ //\r
+ // In order to minimize audible dropouts in DUPLEX mode, we will\r
+ // provide a pre-roll period of 0.5 seconds in which we return\r
+ // zeros from the read buffer while the pointers sync up.\r
+\r
+ if ( stream_.mode == DUPLEX ) {\r
+ if ( safeReadPointer < endRead ) {\r
+ if ( duplexPrerollBytes <= 0 ) {\r
+ // Pre-roll time over. Be more agressive.\r
+ int adjustment = endRead-safeReadPointer;\r
+\r
+ handle->xrun[1] = true;\r
+ // Two cases:\r
+ // - large adjustments: we've probably run out of CPU cycles, so just resync exactly,\r
+ // and perform fine adjustments later.\r
+ // - small adjustments: back off by twice as much.\r
+ if ( adjustment >= 2*bufferBytes )\r
+ nextReadPointer = safeReadPointer-2*bufferBytes;\r
+ else\r
+ nextReadPointer = safeReadPointer-bufferBytes-adjustment;\r
+\r
+ if ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;\r
+\r
+ }\r
+ else {\r
+ // In pre=roll time. Just do it.\r
+ nextReadPointer = safeReadPointer - bufferBytes;\r
+ while ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;\r
+ }\r
+ endRead = nextReadPointer + bufferBytes;\r
+ }\r
+ }\r
+ else { // mode == INPUT\r
+ while ( safeReadPointer < endRead && stream_.callbackInfo.isRunning ) {\r
+ // See comments for playback.\r
+ double millis = (endRead - safeReadPointer) * 1000.0;\r
+ millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate);\r
+ if ( millis < 1.0 ) millis = 1.0;\r
+ Sleep( (DWORD) millis );\r
+\r
+ // Wake up and find out where we are now.\r
+ result = dsBuffer->GetCurrentPosition( ¤tReadPointer, &safeReadPointer );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ \r
+ if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset\r
+ }\r
+ }\r
+\r
+ // Lock free space in the buffer\r
+ result = dsBuffer->Lock( nextReadPointer, bufferBytes, &buffer1,\r
+ &bufferSize1, &buffer2, &bufferSize2, 0 );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+\r
+ if ( duplexPrerollBytes <= 0 ) {\r
+ // Copy our buffer into the DS buffer\r
+ CopyMemory( buffer, buffer1, bufferSize1 );\r
+ if ( buffer2 != NULL ) CopyMemory( buffer+bufferSize1, buffer2, bufferSize2 );\r
+ }\r
+ else {\r
+ memset( buffer, 0, bufferSize1 );\r
+ if ( buffer2 != NULL ) memset( buffer + bufferSize1, 0, bufferSize2 );\r
+ duplexPrerollBytes -= bufferSize1 + bufferSize2;\r
+ }\r
+\r
+ // Update our buffer offset and unlock sound buffer\r
+ nextReadPointer = ( nextReadPointer + bufferSize1 + bufferSize2 ) % dsBufferSize;\r
+ dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );\r
+ if ( FAILED( result ) ) {\r
+ errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ handle->bufferPointer[1] = nextReadPointer;\r
+\r
+ // No byte swapping necessary in DirectSound implementation.\r
+\r
+ // If necessary, convert 8-bit data from unsigned to signed.\r
+ if ( stream_.deviceFormat[1] == RTAUDIO_SINT8 )\r
+ for ( int j=0; j<bufferBytes; j++ ) buffer[j] = (signed char) ( buffer[j] - 128 );\r
+\r
+ // Do buffer conversion if necessary.\r
+ if ( stream_.doConvertBuffer[1] )\r
+ convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
+ }\r
+\r
+ unlock:\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ RtApi::tickStreamTime();\r
+}\r
+\r
+// Definitions for utility functions and callbacks\r
+// specific to the DirectSound implementation.\r
+\r
+static unsigned __stdcall callbackHandler( void *ptr )\r
+{\r
+ CallbackInfo *info = (CallbackInfo *) ptr;\r
+ RtApiDs *object = (RtApiDs *) info->object;\r
+ bool* isRunning = &info->isRunning;\r
+\r
+ while ( *isRunning == true ) {\r
+ object->callbackEvent();\r
+ }\r
+\r
+ _endthreadex( 0 );\r
+ return 0;\r
+}\r
+\r
+#include "tchar.h"\r
+\r
+static std::string convertTChar( LPCTSTR name )\r
+{\r
+#if defined( UNICODE ) || defined( _UNICODE )\r
+ int length = WideCharToMultiByte(CP_UTF8, 0, name, -1, NULL, 0, NULL, NULL);\r
+ std::string s( length-1, '\0' );\r
+ WideCharToMultiByte(CP_UTF8, 0, name, -1, &s[0], length, NULL, NULL);\r
+#else\r
+ std::string s( name );\r
+#endif\r
+\r
+ return s;\r
+}\r
+\r
+static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,\r
+ LPCTSTR description,\r
+ LPCTSTR /*module*/,\r
+ LPVOID lpContext )\r
+{\r
+ struct DsProbeData& probeInfo = *(struct DsProbeData*) lpContext;\r
+ std::vector<struct DsDevice>& dsDevices = *probeInfo.dsDevices;\r
+\r
+ HRESULT hr;\r
+ bool validDevice = false;\r
+ if ( probeInfo.isInput == true ) {\r
+ DSCCAPS caps;\r
+ LPDIRECTSOUNDCAPTURE object;\r
+\r
+ hr = DirectSoundCaptureCreate( lpguid, &object, NULL );\r
+ if ( hr != DS_OK ) return TRUE;\r
+\r
+ caps.dwSize = sizeof(caps);\r
+ hr = object->GetCaps( &caps );\r
+ if ( hr == DS_OK ) {\r
+ if ( caps.dwChannels > 0 && caps.dwFormats > 0 )\r
+ validDevice = true;\r
+ }\r
+ object->Release();\r
+ }\r
+ else {\r
+ DSCAPS caps;\r
+ LPDIRECTSOUND object;\r
+ hr = DirectSoundCreate( lpguid, &object, NULL );\r
+ if ( hr != DS_OK ) return TRUE;\r
+\r
+ caps.dwSize = sizeof(caps);\r
+ hr = object->GetCaps( &caps );\r
+ if ( hr == DS_OK ) {\r
+ if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO )\r
+ validDevice = true;\r
+ }\r
+ object->Release();\r
+ }\r
+\r
+ // If good device, then save its name and guid.\r
+ std::string name = convertTChar( description );\r
+ //if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" )\r
+ if ( lpguid == NULL )\r
+ name = "Default Device";\r
+ if ( validDevice ) {\r
+ for ( unsigned int i=0; i<dsDevices.size(); i++ ) {\r
+ if ( dsDevices[i].name == name ) {\r
+ dsDevices[i].found = true;\r
+ if ( probeInfo.isInput ) {\r
+ dsDevices[i].id[1] = lpguid;\r
+ dsDevices[i].validId[1] = true;\r
+ }\r
+ else {\r
+ dsDevices[i].id[0] = lpguid;\r
+ dsDevices[i].validId[0] = true;\r
+ }\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ DsDevice device;\r
+ device.name = name;\r
+ device.found = true;\r
+ if ( probeInfo.isInput ) {\r
+ device.id[1] = lpguid;\r
+ device.validId[1] = true;\r
+ }\r
+ else {\r
+ device.id[0] = lpguid;\r
+ device.validId[0] = true;\r
+ }\r
+ dsDevices.push_back( device );\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+static const char* getErrorString( int code )\r
+{\r
+ switch ( code ) {\r
+\r
+ case DSERR_ALLOCATED:\r
+ return "Already allocated";\r
+\r
+ case DSERR_CONTROLUNAVAIL:\r
+ return "Control unavailable";\r
+\r
+ case DSERR_INVALIDPARAM:\r
+ return "Invalid parameter";\r
+\r
+ case DSERR_INVALIDCALL:\r
+ return "Invalid call";\r
+\r
+ case DSERR_GENERIC:\r
+ return "Generic error";\r
+\r
+ case DSERR_PRIOLEVELNEEDED:\r
+ return "Priority level needed";\r
+\r
+ case DSERR_OUTOFMEMORY:\r
+ return "Out of memory";\r
+\r
+ case DSERR_BADFORMAT:\r
+ return "The sample rate or the channel format is not supported";\r
+\r
+ case DSERR_UNSUPPORTED:\r
+ return "Not supported";\r
+\r
+ case DSERR_NODRIVER:\r
+ return "No driver";\r
+\r
+ case DSERR_ALREADYINITIALIZED:\r
+ return "Already initialized";\r
+\r
+ case DSERR_NOAGGREGATION:\r
+ return "No aggregation";\r
+\r
+ case DSERR_BUFFERLOST:\r
+ return "Buffer lost";\r
+\r
+ case DSERR_OTHERAPPHASPRIO:\r
+ return "Another application already has priority";\r
+\r
+ case DSERR_UNINITIALIZED:\r
+ return "Uninitialized";\r
+\r
+ default:\r
+ return "DirectSound unknown error";\r
+ }\r
+}\r
+//******************** End of __WINDOWS_DS__ *********************//\r
+#endif\r
+\r
+\r
+#if defined(__LINUX_ALSA__)\r
+\r
+#include <alsa/asoundlib.h>\r
+#include <unistd.h>\r
+\r
+ // A structure to hold various information related to the ALSA API\r
+ // implementation.\r
+struct AlsaHandle {\r
+ snd_pcm_t *handles[2];\r
+ bool synchronized;\r
+ bool xrun[2];\r
+ pthread_cond_t runnable_cv;\r
+ bool runnable;\r
+\r
+ AlsaHandle()\r
+ :synchronized(false), runnable(false) { xrun[0] = false; xrun[1] = false; }\r
+};\r
+\r
+static void *alsaCallbackHandler( void * ptr );\r
+\r
+RtApiAlsa :: RtApiAlsa()\r
+{\r
+ // Nothing to do here.\r
+}\r
+\r
+RtApiAlsa :: ~RtApiAlsa()\r
+{\r
+ if ( stream_.state != STREAM_CLOSED ) closeStream();\r
+}\r
+\r
+unsigned int RtApiAlsa :: getDeviceCount( void )\r
+{\r
+ unsigned nDevices = 0;\r
+ int result, subdevice, card;\r
+ char name[64];\r
+ snd_ctl_t *handle;\r
+\r
+ // Count cards and devices\r
+ card = -1;\r
+ snd_card_next( &card );\r
+ while ( card >= 0 ) {\r
+ sprintf( name, "hw:%d", card );\r
+ result = snd_ctl_open( &handle, name, 0 );\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::getDeviceCount: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ goto nextcard;\r
+ }\r
+ subdevice = -1;\r
+ while( 1 ) {\r
+ result = snd_ctl_pcm_next_device( handle, &subdevice );\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::getDeviceCount: control next device, card = " << card << ", " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ break;\r
+ }\r
+ if ( subdevice < 0 )\r
+ break;\r
+ nDevices++;\r
+ }\r
+ nextcard:\r
+ snd_ctl_close( handle );\r
+ snd_card_next( &card );\r
+ }\r
+\r
+ result = snd_ctl_open( &handle, "default", 0 );\r
+ if (result == 0) {\r
+ nDevices++;\r
+ snd_ctl_close( handle );\r
+ }\r
+\r
+ return nDevices;\r
+}\r
+\r
+RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )\r
+{\r
+ RtAudio::DeviceInfo info;\r
+ info.probed = false;\r
+\r
+ unsigned nDevices = 0;\r
+ int result, subdevice, card;\r
+ char name[64];\r
+ snd_ctl_t *chandle;\r
+\r
+ // Count cards and devices\r
+ card = -1;\r
+ snd_card_next( &card );\r
+ while ( card >= 0 ) {\r
+ sprintf( name, "hw:%d", card );\r
+ result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ goto nextcard;\r
+ }\r
+ subdevice = -1;\r
+ while( 1 ) {\r
+ result = snd_ctl_pcm_next_device( chandle, &subdevice );\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: control next device, card = " << card << ", " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ break;\r
+ }\r
+ if ( subdevice < 0 ) break;\r
+ if ( nDevices == device ) {\r
+ sprintf( name, "hw:%d,%d", card, subdevice );\r
+ goto foundDevice;\r
+ }\r
+ nDevices++;\r
+ }\r
+ nextcard:\r
+ snd_ctl_close( chandle );\r
+ snd_card_next( &card );\r
+ }\r
+\r
+ result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );\r
+ if ( result == 0 ) {\r
+ if ( nDevices == device ) {\r
+ strcpy( name, "default" );\r
+ goto foundDevice;\r
+ }\r
+ nDevices++;\r
+ }\r
+\r
+ if ( nDevices == 0 ) {\r
+ errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!";\r
+ error( RtAudioError::INVALID_USE );\r
+ return info;\r
+ }\r
+\r
+ if ( device >= nDevices ) {\r
+ errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!";\r
+ error( RtAudioError::INVALID_USE );\r
+ return info;\r
+ }\r
+\r
+ foundDevice:\r
+\r
+ // If a stream is already open, we cannot probe the stream devices.\r
+ // Thus, use the saved results.\r
+ if ( stream_.state != STREAM_CLOSED &&\r
+ ( stream_.device[0] == device || stream_.device[1] == device ) ) {\r
+ snd_ctl_close( chandle );\r
+ if ( device >= devices_.size() ) {\r
+ errorText_ = "RtApiAlsa::getDeviceInfo: device ID was not present before stream was opened.";\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+ return devices_[ device ];\r
+ }\r
+\r
+ int openMode = SND_PCM_ASYNC;\r
+ snd_pcm_stream_t stream;\r
+ snd_pcm_info_t *pcminfo;\r
+ snd_pcm_info_alloca( &pcminfo );\r
+ snd_pcm_t *phandle;\r
+ snd_pcm_hw_params_t *params;\r
+ snd_pcm_hw_params_alloca( ¶ms );\r
+\r
+ // First try for playback unless default device (which has subdev -1)\r
+ stream = SND_PCM_STREAM_PLAYBACK;\r
+ snd_pcm_info_set_stream( pcminfo, stream );\r
+ if ( subdevice != -1 ) {\r
+ snd_pcm_info_set_device( pcminfo, subdevice );\r
+ snd_pcm_info_set_subdevice( pcminfo, 0 );\r
+\r
+ result = snd_ctl_pcm_info( chandle, pcminfo );\r
+ if ( result < 0 ) {\r
+ // Device probably doesn't support playback.\r
+ goto captureProbe;\r
+ }\r
+ }\r
+\r
+ result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK );\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ goto captureProbe;\r
+ }\r
+\r
+ // The device is open ... fill the parameter structure.\r
+ result = snd_pcm_hw_params_any( phandle, params );\r
+ if ( result < 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ goto captureProbe;\r
+ }\r
+\r
+ // Get output channel information.\r
+ unsigned int value;\r
+ result = snd_pcm_hw_params_get_channels_max( params, &value );\r
+ if ( result < 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ goto captureProbe;\r
+ }\r
+ info.outputChannels = value;\r
+ snd_pcm_close( phandle );\r
+\r
+ captureProbe:\r
+ stream = SND_PCM_STREAM_CAPTURE;\r
+ snd_pcm_info_set_stream( pcminfo, stream );\r
+\r
+ // Now try for capture unless default device (with subdev = -1)\r
+ if ( subdevice != -1 ) {\r
+ result = snd_ctl_pcm_info( chandle, pcminfo );\r
+ snd_ctl_close( chandle );\r
+ if ( result < 0 ) {\r
+ // Device probably doesn't support capture.\r
+ if ( info.outputChannels == 0 ) return info;\r
+ goto probeParameters;\r
+ }\r
+ }\r
+ else\r
+ snd_ctl_close( chandle );\r
+\r
+ result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ if ( info.outputChannels == 0 ) return info;\r
+ goto probeParameters;\r
+ }\r
+\r
+ // The device is open ... fill the parameter structure.\r
+ result = snd_pcm_hw_params_any( phandle, params );\r
+ if ( result < 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ if ( info.outputChannels == 0 ) return info;\r
+ goto probeParameters;\r
+ }\r
+\r
+ result = snd_pcm_hw_params_get_channels_max( params, &value );\r
+ if ( result < 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ if ( info.outputChannels == 0 ) return info;\r
+ goto probeParameters;\r
+ }\r
+ info.inputChannels = value;\r
+ snd_pcm_close( phandle );\r
+\r
+ // If device opens for both playback and capture, we determine the channels.\r
+ if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
+ info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
+\r
+ // ALSA doesn't provide default devices so we'll use the first available one.\r
+ if ( device == 0 && info.outputChannels > 0 )\r
+ info.isDefaultOutput = true;\r
+ if ( device == 0 && info.inputChannels > 0 )\r
+ info.isDefaultInput = true;\r
+\r
+ probeParameters:\r
+ // At this point, we just need to figure out the supported data\r
+ // formats and sample rates. We'll proceed by opening the device in\r
+ // the direction with the maximum number of channels, or playback if\r
+ // they are equal. This might limit our sample rate options, but so\r
+ // be it.\r
+\r
+ if ( info.outputChannels >= info.inputChannels )\r
+ stream = SND_PCM_STREAM_PLAYBACK;\r
+ else\r
+ stream = SND_PCM_STREAM_CAPTURE;\r
+ snd_pcm_info_set_stream( pcminfo, stream );\r
+\r
+ result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ // The device is open ... fill the parameter structure.\r
+ result = snd_pcm_hw_params_any( phandle, params );\r
+ if ( result < 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ // Test our discrete set of sample rate values.\r
+ info.sampleRates.clear();\r
+ for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {\r
+ if ( snd_pcm_hw_params_test_rate( phandle, params, SAMPLE_RATES[i], 0 ) == 0 )\r
+ info.sampleRates.push_back( SAMPLE_RATES[i] );\r
+ }\r
+ if ( info.sampleRates.size() == 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: no supported sample rates found for device (" << name << ").";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ // Probe the supported data formats ... we don't care about endian-ness just yet\r
+ snd_pcm_format_t format;\r
+ info.nativeFormats = 0;\r
+ format = SND_PCM_FORMAT_S8;\r
+ if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
+ info.nativeFormats |= RTAUDIO_SINT8;\r
+ format = SND_PCM_FORMAT_S16;\r
+ if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
+ info.nativeFormats |= RTAUDIO_SINT16;\r
+ format = SND_PCM_FORMAT_S24;\r
+ if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
+ info.nativeFormats |= RTAUDIO_SINT24;\r
+ format = SND_PCM_FORMAT_S32;\r
+ if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
+ info.nativeFormats |= RTAUDIO_SINT32;\r
+ format = SND_PCM_FORMAT_FLOAT;\r
+ if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
+ info.nativeFormats |= RTAUDIO_FLOAT32;\r
+ format = SND_PCM_FORMAT_FLOAT64;\r
+ if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )\r
+ info.nativeFormats |= RTAUDIO_FLOAT64;\r
+\r
+ // Check that we have at least one supported format\r
+ if ( info.nativeFormats == 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::getDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio.";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ // Get the device name\r
+ char *cardname;\r
+ result = snd_card_get_name( card, &cardname );\r
+ if ( result >= 0 ) {\r
+ sprintf( name, "hw:%s,%d", cardname, subdevice );\r
+ free( cardname );\r
+ }\r
+ info.name = name;\r
+\r
+ // That's all ... close the device and return\r
+ snd_pcm_close( phandle );\r
+ info.probed = true;\r
+ return info;\r
+}\r
+\r
+void RtApiAlsa :: saveDeviceInfo( void )\r
+{\r
+ devices_.clear();\r
+\r
+ unsigned int nDevices = getDeviceCount();\r
+ devices_.resize( nDevices );\r
+ for ( unsigned int i=0; i<nDevices; i++ )\r
+ devices_[i] = getDeviceInfo( i );\r
+}\r
+\r
+bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
+ unsigned int firstChannel, unsigned int sampleRate,\r
+ RtAudioFormat format, unsigned int *bufferSize,\r
+ RtAudio::StreamOptions *options )\r
+\r
+{\r
+#if defined(__RTAUDIO_DEBUG__)\r
+ snd_output_t *out;\r
+ snd_output_stdio_attach(&out, stderr, 0);\r
+#endif\r
+\r
+ // I'm not using the "plug" interface ... too much inconsistent behavior.\r
+\r
+ unsigned nDevices = 0;\r
+ int result, subdevice, card;\r
+ char name[64];\r
+ snd_ctl_t *chandle;\r
+\r
+ if ( options && options->flags & RTAUDIO_ALSA_USE_DEFAULT )\r
+ snprintf(name, sizeof(name), "%s", "default");\r
+ else {\r
+ // Count cards and devices\r
+ card = -1;\r
+ snd_card_next( &card );\r
+ while ( card >= 0 ) {\r
+ sprintf( name, "hw:%d", card );\r
+ result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: control open, card = " << card << ", " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ subdevice = -1;\r
+ while( 1 ) {\r
+ result = snd_ctl_pcm_next_device( chandle, &subdevice );\r
+ if ( result < 0 ) break;\r
+ if ( subdevice < 0 ) break;\r
+ if ( nDevices == device ) {\r
+ sprintf( name, "hw:%d,%d", card, subdevice );\r
+ snd_ctl_close( chandle );\r
+ goto foundDevice;\r
+ }\r
+ nDevices++;\r
+ }\r
+ snd_ctl_close( chandle );\r
+ snd_card_next( &card );\r
+ }\r
+\r
+ result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );\r
+ if ( result == 0 ) {\r
+ if ( nDevices == device ) {\r
+ strcpy( name, "default" );\r
+ goto foundDevice;\r
+ }\r
+ nDevices++;\r
+ }\r
+\r
+ if ( nDevices == 0 ) {\r
+ // This should not happen because a check is made before this function is called.\r
+ errorText_ = "RtApiAlsa::probeDeviceOpen: no devices found!";\r
+ return FAILURE;\r
+ }\r
+\r
+ if ( device >= nDevices ) {\r
+ // This should not happen because a check is made before this function is called.\r
+ errorText_ = "RtApiAlsa::probeDeviceOpen: device ID is invalid!";\r
+ return FAILURE;\r
+ }\r
+ }\r
+\r
+ foundDevice:\r
+\r
+ // The getDeviceInfo() function will not work for a device that is\r
+ // already open. Thus, we'll probe the system before opening a\r
+ // stream and save the results for use by getDeviceInfo().\r
+ if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) // only do once\r
+ this->saveDeviceInfo();\r
+\r
+ snd_pcm_stream_t stream;\r
+ if ( mode == OUTPUT )\r
+ stream = SND_PCM_STREAM_PLAYBACK;\r
+ else\r
+ stream = SND_PCM_STREAM_CAPTURE;\r
+\r
+ snd_pcm_t *phandle;\r
+ int openMode = SND_PCM_ASYNC;\r
+ result = snd_pcm_open( &phandle, name, stream, openMode );\r
+ if ( result < 0 ) {\r
+ if ( mode == OUTPUT )\r
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for output.";\r
+ else\r
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for input.";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Fill the parameter structure.\r
+ snd_pcm_hw_params_t *hw_params;\r
+ snd_pcm_hw_params_alloca( &hw_params );\r
+ result = snd_pcm_hw_params_any( phandle, hw_params );\r
+ if ( result < 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") parameters, " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+#if defined(__RTAUDIO_DEBUG__)\r
+ fprintf( stderr, "\nRtApiAlsa: dump hardware params just after device open:\n\n" );\r
+ snd_pcm_hw_params_dump( hw_params, out );\r
+#endif\r
+\r
+ // Set access ... check user preference.\r
+ if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) {\r
+ stream_.userInterleaved = false;\r
+ result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );\r
+ if ( result < 0 ) {\r
+ result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );\r
+ stream_.deviceInterleaved[mode] = true;\r
+ }\r
+ else\r
+ stream_.deviceInterleaved[mode] = false;\r
+ }\r
+ else {\r
+ stream_.userInterleaved = true;\r
+ result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );\r
+ if ( result < 0 ) {\r
+ result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );\r
+ stream_.deviceInterleaved[mode] = false;\r
+ }\r
+ else\r
+ stream_.deviceInterleaved[mode] = true;\r
+ }\r
+\r
+ if ( result < 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") access, " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Determine how to set the device format.\r
+ stream_.userFormat = format;\r
+ snd_pcm_format_t deviceFormat = SND_PCM_FORMAT_UNKNOWN;\r
+\r
+ if ( format == RTAUDIO_SINT8 )\r
+ deviceFormat = SND_PCM_FORMAT_S8;\r
+ else if ( format == RTAUDIO_SINT16 )\r
+ deviceFormat = SND_PCM_FORMAT_S16;\r
+ else if ( format == RTAUDIO_SINT24 )\r
+ deviceFormat = SND_PCM_FORMAT_S24;\r
+ else if ( format == RTAUDIO_SINT32 )\r
+ deviceFormat = SND_PCM_FORMAT_S32;\r
+ else if ( format == RTAUDIO_FLOAT32 )\r
+ deviceFormat = SND_PCM_FORMAT_FLOAT;\r
+ else if ( format == RTAUDIO_FLOAT64 )\r
+ deviceFormat = SND_PCM_FORMAT_FLOAT64;\r
+\r
+ if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat) == 0) {\r
+ stream_.deviceFormat[mode] = format;\r
+ goto setFormat;\r
+ }\r
+\r
+ // The user requested format is not natively supported by the device.\r
+ deviceFormat = SND_PCM_FORMAT_FLOAT64;\r
+ if ( snd_pcm_hw_params_test_format( phandle, hw_params, deviceFormat ) == 0 ) {\r
+ stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;\r
+ goto setFormat;\r
+ }\r
+\r
+ deviceFormat = SND_PCM_FORMAT_FLOAT;\r
+ if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
+ stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
+ goto setFormat;\r
+ }\r
+\r
+ deviceFormat = SND_PCM_FORMAT_S32;\r
+ if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
+ goto setFormat;\r
+ }\r
+\r
+ deviceFormat = SND_PCM_FORMAT_S24;\r
+ if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
+ goto setFormat;\r
+ }\r
+\r
+ deviceFormat = SND_PCM_FORMAT_S16;\r
+ if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
+ goto setFormat;\r
+ }\r
+\r
+ deviceFormat = SND_PCM_FORMAT_S8;\r
+ if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
+ goto setFormat;\r
+ }\r
+\r
+ // If we get here, no supported format was found.\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device " << device << " data format not supported by RtAudio.";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+\r
+ setFormat:\r
+ result = snd_pcm_hw_params_set_format( phandle, hw_params, deviceFormat );\r
+ if ( result < 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") data format, " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Determine whether byte-swaping is necessary.\r
+ stream_.doByteSwap[mode] = false;\r
+ if ( deviceFormat != SND_PCM_FORMAT_S8 ) {\r
+ result = snd_pcm_format_cpu_endian( deviceFormat );\r
+ if ( result == 0 )\r
+ stream_.doByteSwap[mode] = true;\r
+ else if (result < 0) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") endian-ness, " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ }\r
+\r
+ // Set the sample rate.\r
+ result = snd_pcm_hw_params_set_rate_near( phandle, hw_params, (unsigned int*) &sampleRate, 0 );\r
+ if ( result < 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting sample rate on device (" << name << "), " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Determine the number of channels for this device. We support a possible\r
+ // minimum device channel number > than the value requested by the user.\r
+ stream_.nUserChannels[mode] = channels;\r
+ unsigned int value;\r
+ result = snd_pcm_hw_params_get_channels_max( hw_params, &value );\r
+ unsigned int deviceChannels = value;\r
+ if ( result < 0 || deviceChannels < channels + firstChannel ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: requested channel parameters not supported by device (" << name << "), " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ result = snd_pcm_hw_params_get_channels_min( hw_params, &value );\r
+ if ( result < 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting minimum channels for device (" << name << "), " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ deviceChannels = value;\r
+ if ( deviceChannels < channels + firstChannel ) deviceChannels = channels + firstChannel;\r
+ stream_.nDeviceChannels[mode] = deviceChannels;\r
+\r
+ // Set the device channels.\r
+ result = snd_pcm_hw_params_set_channels( phandle, hw_params, deviceChannels );\r
+ if ( result < 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting channels for device (" << name << "), " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Set the buffer (or period) size.\r
+ int dir = 0;\r
+ snd_pcm_uframes_t periodSize = *bufferSize;\r
+ result = snd_pcm_hw_params_set_period_size_near( phandle, hw_params, &periodSize, &dir );\r
+ if ( result < 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting period size for device (" << name << "), " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ *bufferSize = periodSize;\r
+\r
+ // Set the buffer number, which in ALSA is referred to as the "period".\r
+ unsigned int periods = 0;\r
+ if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2;\r
+ if ( options && options->numberOfBuffers > 0 ) periods = options->numberOfBuffers;\r
+ if ( periods < 2 ) periods = 4; // a fairly safe default value\r
+ result = snd_pcm_hw_params_set_periods_near( phandle, hw_params, &periods, &dir );\r
+ if ( result < 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // If attempting to setup a duplex stream, the bufferSize parameter\r
+ // MUST be the same in both directions!\r
+ if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << name << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ stream_.bufferSize = *bufferSize;\r
+\r
+ // Install the hardware configuration\r
+ result = snd_pcm_hw_params( phandle, hw_params );\r
+ if ( result < 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing hardware configuration on device (" << name << "), " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+#if defined(__RTAUDIO_DEBUG__)\r
+ fprintf(stderr, "\nRtApiAlsa: dump hardware params after installation:\n\n");\r
+ snd_pcm_hw_params_dump( hw_params, out );\r
+#endif\r
+\r
+ // Set the software configuration to fill buffers with zeros and prevent device stopping on xruns.\r
+ snd_pcm_sw_params_t *sw_params = NULL;\r
+ snd_pcm_sw_params_alloca( &sw_params );\r
+ snd_pcm_sw_params_current( phandle, sw_params );\r
+ snd_pcm_sw_params_set_start_threshold( phandle, sw_params, *bufferSize );\r
+ snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, ULONG_MAX );\r
+ snd_pcm_sw_params_set_silence_threshold( phandle, sw_params, 0 );\r
+\r
+ // The following two settings were suggested by Theo Veenker\r
+ //snd_pcm_sw_params_set_avail_min( phandle, sw_params, *bufferSize );\r
+ //snd_pcm_sw_params_set_xfer_align( phandle, sw_params, 1 );\r
+\r
+ // here are two options for a fix\r
+ //snd_pcm_sw_params_set_silence_size( phandle, sw_params, ULONG_MAX );\r
+ snd_pcm_uframes_t val;\r
+ snd_pcm_sw_params_get_boundary( sw_params, &val );\r
+ snd_pcm_sw_params_set_silence_size( phandle, sw_params, val );\r
+\r
+ result = snd_pcm_sw_params( phandle, sw_params );\r
+ if ( result < 0 ) {\r
+ snd_pcm_close( phandle );\r
+ errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing software configuration on device (" << name << "), " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+#if defined(__RTAUDIO_DEBUG__)\r
+ fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n");\r
+ snd_pcm_sw_params_dump( sw_params, out );\r
+#endif\r
+\r
+ // Set flags for buffer conversion\r
+ stream_.doConvertBuffer[mode] = false;\r
+ if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
+ stream_.doConvertBuffer[mode] = true;\r
+ if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
+ stream_.doConvertBuffer[mode] = true;\r
+ if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
+ stream_.nUserChannels[mode] > 1 )\r
+ stream_.doConvertBuffer[mode] = true;\r
+\r
+ // Allocate the ApiHandle if necessary and then save.\r
+ AlsaHandle *apiInfo = 0;\r
+ if ( stream_.apiHandle == 0 ) {\r
+ try {\r
+ apiInfo = (AlsaHandle *) new AlsaHandle;\r
+ }\r
+ catch ( std::bad_alloc& ) {\r
+ errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating AlsaHandle memory.";\r
+ goto error;\r
+ }\r
+\r
+ if ( pthread_cond_init( &apiInfo->runnable_cv, NULL ) ) {\r
+ errorText_ = "RtApiAlsa::probeDeviceOpen: error initializing pthread condition variable.";\r
+ goto error;\r
+ }\r
+\r
+ stream_.apiHandle = (void *) apiInfo;\r
+ apiInfo->handles[0] = 0;\r
+ apiInfo->handles[1] = 0;\r
+ }\r
+ else {\r
+ apiInfo = (AlsaHandle *) stream_.apiHandle;\r
+ }\r
+ apiInfo->handles[mode] = phandle;\r
+ phandle = 0;\r
+\r
+ // Allocate necessary internal buffers.\r
+ unsigned long bufferBytes;\r
+ bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
+ stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
+ if ( stream_.userBuffer[mode] == NULL ) {\r
+ errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating user buffer memory.";\r
+ goto error;\r
+ }\r
+\r
+ if ( stream_.doConvertBuffer[mode] ) {\r
+\r
+ bool makeBuffer = true;\r
+ bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
+ if ( mode == INPUT ) {\r
+ if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
+ unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
+ if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
+ }\r
+ }\r
+\r
+ if ( makeBuffer ) {\r
+ bufferBytes *= *bufferSize;\r
+ if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
+ if ( stream_.deviceBuffer == NULL ) {\r
+ errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating device buffer memory.";\r
+ goto error;\r
+ }\r
+ }\r
+ }\r
+\r
+ stream_.sampleRate = sampleRate;\r
+ stream_.nBuffers = periods;\r
+ stream_.device[mode] = device;\r
+ stream_.state = STREAM_STOPPED;\r
+\r
+ // Setup the buffer conversion information structure.\r
+ if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
+\r
+ // Setup thread if necessary.\r
+ if ( stream_.mode == OUTPUT && mode == INPUT ) {\r
+ // We had already set up an output stream.\r
+ stream_.mode = DUPLEX;\r
+ // Link the streams if possible.\r
+ apiInfo->synchronized = false;\r
+ if ( snd_pcm_link( apiInfo->handles[0], apiInfo->handles[1] ) == 0 )\r
+ apiInfo->synchronized = true;\r
+ else {\r
+ errorText_ = "RtApiAlsa::probeDeviceOpen: unable to synchronize input and output devices.";\r
+ error( RtAudioError::WARNING );\r
+ }\r
+ }\r
+ else {\r
+ stream_.mode = mode;\r
+\r
+ // Setup callback thread.\r
+ stream_.callbackInfo.object = (void *) this;\r
+\r
+ // Set the thread attributes for joinable and realtime scheduling\r
+ // priority (optional). The higher priority will only take affect\r
+ // if the program is run as root or suid. Note, under Linux\r
+ // processes with CAP_SYS_NICE privilege, a user can change\r
+ // scheduling policy and priority (thus need not be root). See\r
+ // POSIX "capabilities".\r
+ pthread_attr_t attr;\r
+ pthread_attr_init( &attr );\r
+ pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );\r
+\r
+#ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
+ if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {\r
+ // We previously attempted to increase the audio callback priority\r
+ // to SCHED_RR here via the attributes. However, while no errors\r
+ // were reported in doing so, it did not work. So, now this is\r
+ // done in the alsaCallbackHandler function.\r
+ stream_.callbackInfo.doRealtime = true;\r
+ int priority = options->priority;\r
+ int min = sched_get_priority_min( SCHED_RR );\r
+ int max = sched_get_priority_max( SCHED_RR );\r
+ if ( priority < min ) priority = min;\r
+ else if ( priority > max ) priority = max;\r
+ stream_.callbackInfo.priority = priority;\r
+ }\r
+#endif\r
+\r
+ stream_.callbackInfo.isRunning = true;\r
+ result = pthread_create( &stream_.callbackInfo.thread, &attr, alsaCallbackHandler, &stream_.callbackInfo );\r
+ pthread_attr_destroy( &attr );\r
+ if ( result ) {\r
+ stream_.callbackInfo.isRunning = false;\r
+ errorText_ = "RtApiAlsa::error creating callback thread!";\r
+ goto error;\r
+ }\r
+ }\r
+\r
+ return SUCCESS;\r
+\r
+ error:\r
+ if ( apiInfo ) {\r
+ pthread_cond_destroy( &apiInfo->runnable_cv );\r
+ if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );\r
+ if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );\r
+ delete apiInfo;\r
+ stream_.apiHandle = 0;\r
+ }\r
+\r
+ if ( phandle) snd_pcm_close( phandle );\r
+\r
+ for ( int i=0; i<2; i++ ) {\r
+ if ( stream_.userBuffer[i] ) {\r
+ free( stream_.userBuffer[i] );\r
+ stream_.userBuffer[i] = 0;\r
+ }\r
+ }\r
+\r
+ if ( stream_.deviceBuffer ) {\r
+ free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = 0;\r
+ }\r
+\r
+ stream_.state = STREAM_CLOSED;\r
+ return FAILURE;\r
+}\r
+\r
+void RtApiAlsa :: closeStream()\r
+{\r
+ if ( stream_.state == STREAM_CLOSED ) {\r
+ errorText_ = "RtApiAlsa::closeStream(): no open stream to close!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
+ stream_.callbackInfo.isRunning = false;\r
+ MUTEX_LOCK( &stream_.mutex );\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ apiInfo->runnable = true;\r
+ pthread_cond_signal( &apiInfo->runnable_cv );\r
+ }\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ pthread_join( stream_.callbackInfo.thread, NULL );\r
+\r
+ if ( stream_.state == STREAM_RUNNING ) {\r
+ stream_.state = STREAM_STOPPED;\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )\r
+ snd_pcm_drop( apiInfo->handles[0] );\r
+ if ( stream_.mode == INPUT || stream_.mode == DUPLEX )\r
+ snd_pcm_drop( apiInfo->handles[1] );\r
+ }\r
+\r
+ if ( apiInfo ) {\r
+ pthread_cond_destroy( &apiInfo->runnable_cv );\r
+ if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );\r
+ if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );\r
+ delete apiInfo;\r
+ stream_.apiHandle = 0;\r
+ }\r
+\r
+ for ( int i=0; i<2; i++ ) {\r
+ if ( stream_.userBuffer[i] ) {\r
+ free( stream_.userBuffer[i] );\r
+ stream_.userBuffer[i] = 0;\r
+ }\r
+ }\r
+\r
+ if ( stream_.deviceBuffer ) {\r
+ free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = 0;\r
+ }\r
+\r
+ stream_.mode = UNINITIALIZED;\r
+ stream_.state = STREAM_CLOSED;\r
+}\r
+\r
+void RtApiAlsa :: startStream()\r
+{\r
+ // This method calls snd_pcm_prepare if the device isn't already in that state.\r
+\r
+ verifyStream();\r
+ if ( stream_.state == STREAM_RUNNING ) {\r
+ errorText_ = "RtApiAlsa::startStream(): the stream is already running!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ MUTEX_LOCK( &stream_.mutex );\r
+\r
+ int result = 0;\r
+ snd_pcm_state_t state;\r
+ AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
+ snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+ state = snd_pcm_state( handle[0] );\r
+ if ( state != SND_PCM_STATE_PREPARED ) {\r
+ result = snd_pcm_prepare( handle[0] );\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::startStream: error preparing output pcm device, " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+ }\r
+ }\r
+\r
+ if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
+ result = snd_pcm_drop(handle[1]); // fix to remove stale data received since device has been open\r
+ state = snd_pcm_state( handle[1] );\r
+ if ( state != SND_PCM_STATE_PREPARED ) {\r
+ result = snd_pcm_prepare( handle[1] );\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::startStream: error preparing input pcm device, " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+ }\r
+ }\r
+\r
+ stream_.state = STREAM_RUNNING;\r
+\r
+ unlock:\r
+ apiInfo->runnable = true;\r
+ pthread_cond_signal( &apiInfo->runnable_cv );\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+\r
+ if ( result >= 0 ) return;\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+}\r
+\r
+void RtApiAlsa :: stopStream()\r
+{\r
+ verifyStream();\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ errorText_ = "RtApiAlsa::stopStream(): the stream is already stopped!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ stream_.state = STREAM_STOPPED;\r
+ MUTEX_LOCK( &stream_.mutex );\r
+\r
+ int result = 0;\r
+ AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
+ snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+ if ( apiInfo->synchronized ) \r
+ result = snd_pcm_drop( handle[0] );\r
+ else\r
+ result = snd_pcm_drain( handle[0] );\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::stopStream: error draining output pcm device, " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+ }\r
+\r
+ if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
+ result = snd_pcm_drop( handle[1] );\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::stopStream: error stopping input pcm device, " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+ }\r
+\r
+ unlock:\r
+ apiInfo->runnable = false; // fixes high CPU usage when stopped\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+\r
+ if ( result >= 0 ) return;\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+}\r
+\r
+void RtApiAlsa :: abortStream()\r
+{\r
+ verifyStream();\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ errorText_ = "RtApiAlsa::abortStream(): the stream is already stopped!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ stream_.state = STREAM_STOPPED;\r
+ MUTEX_LOCK( &stream_.mutex );\r
+\r
+ int result = 0;\r
+ AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
+ snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+ result = snd_pcm_drop( handle[0] );\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::abortStream: error aborting output pcm device, " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+ }\r
+\r
+ if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {\r
+ result = snd_pcm_drop( handle[1] );\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::abortStream: error aborting input pcm device, " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+ }\r
+\r
+ unlock:\r
+ apiInfo->runnable = false; // fixes high CPU usage when stopped\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+\r
+ if ( result >= 0 ) return;\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+}\r
+\r
+void RtApiAlsa :: callbackEvent()\r
+{\r
+ AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ MUTEX_LOCK( &stream_.mutex );\r
+ while ( !apiInfo->runnable )\r
+ pthread_cond_wait( &apiInfo->runnable_cv, &stream_.mutex );\r
+\r
+ if ( stream_.state != STREAM_RUNNING ) {\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ return;\r
+ }\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ }\r
+\r
+ if ( stream_.state == STREAM_CLOSED ) {\r
+ errorText_ = "RtApiAlsa::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ int doStopStream = 0;\r
+ RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
+ double streamTime = getStreamTime();\r
+ RtAudioStreamStatus status = 0;\r
+ if ( stream_.mode != INPUT && apiInfo->xrun[0] == true ) {\r
+ status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
+ apiInfo->xrun[0] = false;\r
+ }\r
+ if ( stream_.mode != OUTPUT && apiInfo->xrun[1] == true ) {\r
+ status |= RTAUDIO_INPUT_OVERFLOW;\r
+ apiInfo->xrun[1] = false;\r
+ }\r
+ doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
+ stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );\r
+\r
+ if ( doStopStream == 2 ) {\r
+ abortStream();\r
+ return;\r
+ }\r
+\r
+ MUTEX_LOCK( &stream_.mutex );\r
+\r
+ // The state might change while waiting on a mutex.\r
+ if ( stream_.state == STREAM_STOPPED ) goto unlock;\r
+\r
+ int result;\r
+ char *buffer;\r
+ int channels;\r
+ snd_pcm_t **handle;\r
+ snd_pcm_sframes_t frames;\r
+ RtAudioFormat format;\r
+ handle = (snd_pcm_t **) apiInfo->handles;\r
+\r
+ if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
+\r
+ // Setup parameters.\r
+ if ( stream_.doConvertBuffer[1] ) {\r
+ buffer = stream_.deviceBuffer;\r
+ channels = stream_.nDeviceChannels[1];\r
+ format = stream_.deviceFormat[1];\r
+ }\r
+ else {\r
+ buffer = stream_.userBuffer[1];\r
+ channels = stream_.nUserChannels[1];\r
+ format = stream_.userFormat;\r
+ }\r
+\r
+ // Read samples from device in interleaved/non-interleaved format.\r
+ if ( stream_.deviceInterleaved[1] )\r
+ result = snd_pcm_readi( handle[1], buffer, stream_.bufferSize );\r
+ else {\r
+ void *bufs[channels];\r
+ size_t offset = stream_.bufferSize * formatBytes( format );\r
+ for ( int i=0; i<channels; i++ )\r
+ bufs[i] = (void *) (buffer + (i * offset));\r
+ result = snd_pcm_readn( handle[1], bufs, stream_.bufferSize );\r
+ }\r
+\r
+ if ( result < (int) stream_.bufferSize ) {\r
+ // Either an error or overrun occured.\r
+ if ( result == -EPIPE ) {\r
+ snd_pcm_state_t state = snd_pcm_state( handle[1] );\r
+ if ( state == SND_PCM_STATE_XRUN ) {\r
+ apiInfo->xrun[1] = true;\r
+ result = snd_pcm_prepare( handle[1] );\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after overrun, " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ }\r
+ }\r
+ else {\r
+ errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ }\r
+ }\r
+ else {\r
+ errorStream_ << "RtApiAlsa::callbackEvent: audio read error, " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ }\r
+ error( RtAudioError::WARNING );\r
+ goto tryOutput;\r
+ }\r
+\r
+ // Do byte swapping if necessary.\r
+ if ( stream_.doByteSwap[1] )\r
+ byteSwapBuffer( buffer, stream_.bufferSize * channels, format );\r
+\r
+ // Do buffer conversion if necessary.\r
+ if ( stream_.doConvertBuffer[1] )\r
+ convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
+\r
+ // Check stream latency\r
+ result = snd_pcm_delay( handle[1], &frames );\r
+ if ( result == 0 && frames > 0 ) stream_.latency[1] = frames;\r
+ }\r
+\r
+ tryOutput:\r
+\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+\r
+ // Setup parameters and do buffer conversion if necessary.\r
+ if ( stream_.doConvertBuffer[0] ) {\r
+ buffer = stream_.deviceBuffer;\r
+ convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
+ channels = stream_.nDeviceChannels[0];\r
+ format = stream_.deviceFormat[0];\r
+ }\r
+ else {\r
+ buffer = stream_.userBuffer[0];\r
+ channels = stream_.nUserChannels[0];\r
+ format = stream_.userFormat;\r
+ }\r
+\r
+ // Do byte swapping if necessary.\r
+ if ( stream_.doByteSwap[0] )\r
+ byteSwapBuffer(buffer, stream_.bufferSize * channels, format);\r
+\r
+ // Write samples to device in interleaved/non-interleaved format.\r
+ if ( stream_.deviceInterleaved[0] )\r
+ result = snd_pcm_writei( handle[0], buffer, stream_.bufferSize );\r
+ else {\r
+ void *bufs[channels];\r
+ size_t offset = stream_.bufferSize * formatBytes( format );\r
+ for ( int i=0; i<channels; i++ )\r
+ bufs[i] = (void *) (buffer + (i * offset));\r
+ result = snd_pcm_writen( handle[0], bufs, stream_.bufferSize );\r
+ }\r
+\r
+ if ( result < (int) stream_.bufferSize ) {\r
+ // Either an error or underrun occured.\r
+ if ( result == -EPIPE ) {\r
+ snd_pcm_state_t state = snd_pcm_state( handle[0] );\r
+ if ( state == SND_PCM_STATE_XRUN ) {\r
+ apiInfo->xrun[0] = true;\r
+ result = snd_pcm_prepare( handle[0] );\r
+ if ( result < 0 ) {\r
+ errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ }\r
+ }\r
+ else {\r
+ errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ }\r
+ }\r
+ else {\r
+ errorStream_ << "RtApiAlsa::callbackEvent: audio write error, " << snd_strerror( result ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ }\r
+ error( RtAudioError::WARNING );\r
+ goto unlock;\r
+ }\r
+\r
+ // Check stream latency\r
+ result = snd_pcm_delay( handle[0], &frames );\r
+ if ( result == 0 && frames > 0 ) stream_.latency[0] = frames;\r
+ }\r
+\r
+ unlock:\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+\r
+ RtApi::tickStreamTime();\r
+ if ( doStopStream == 1 ) this->stopStream();\r
+}\r
+\r
+static void *alsaCallbackHandler( void *ptr )\r
+{\r
+ CallbackInfo *info = (CallbackInfo *) ptr;\r
+ RtApiAlsa *object = (RtApiAlsa *) info->object;\r
+ bool *isRunning = &info->isRunning;\r
+\r
+#ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
+ if ( &info->doRealtime ) {\r
+ pthread_t tID = pthread_self(); // ID of this thread\r
+ sched_param prio = { info->priority }; // scheduling priority of thread\r
+ pthread_setschedparam( tID, SCHED_RR, &prio );\r
+ }\r
+#endif\r
+\r
+ while ( *isRunning == true ) {\r
+ pthread_testcancel();\r
+ object->callbackEvent();\r
+ }\r
+\r
+ pthread_exit( NULL );\r
+}\r
+\r
+//******************** End of __LINUX_ALSA__ *********************//\r
+#endif\r
+\r
+#if defined(__LINUX_PULSE__)\r
+\r
+// Code written by Peter Meerwald, pmeerw@pmeerw.net\r
+// and Tristan Matthews.\r
+\r
+#include <pulse/error.h>\r
+#include <pulse/simple.h>\r
+#include <cstdio>\r
+\r
+static const unsigned int SUPPORTED_SAMPLERATES[] = { 8000, 16000, 22050, 32000,\r
+ 44100, 48000, 96000, 0};\r
+\r
+struct rtaudio_pa_format_mapping_t {\r
+ RtAudioFormat rtaudio_format;\r
+ pa_sample_format_t pa_format;\r
+};\r
+\r
+static const rtaudio_pa_format_mapping_t supported_sampleformats[] = {\r
+ {RTAUDIO_SINT16, PA_SAMPLE_S16LE},\r
+ {RTAUDIO_SINT32, PA_SAMPLE_S32LE},\r
+ {RTAUDIO_FLOAT32, PA_SAMPLE_FLOAT32LE},\r
+ {0, PA_SAMPLE_INVALID}};\r
+\r
+struct PulseAudioHandle {\r
+ pa_simple *s_play;\r
+ pa_simple *s_rec;\r
+ pthread_t thread;\r
+ pthread_cond_t runnable_cv;\r
+ bool runnable;\r
+ PulseAudioHandle() : s_play(0), s_rec(0), runnable(false) { }\r
+};\r
+\r
+RtApiPulse::~RtApiPulse()\r
+{\r
+ if ( stream_.state != STREAM_CLOSED )\r
+ closeStream();\r
+}\r
+\r
+unsigned int RtApiPulse::getDeviceCount( void )\r
+{\r
+ return 1;\r
+}\r
+\r
+RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int /*device*/ )\r
+{\r
+ RtAudio::DeviceInfo info;\r
+ info.probed = true;\r
+ info.name = "PulseAudio";\r
+ info.outputChannels = 2;\r
+ info.inputChannels = 2;\r
+ info.duplexChannels = 2;\r
+ info.isDefaultOutput = true;\r
+ info.isDefaultInput = true;\r
+\r
+ for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr )\r
+ info.sampleRates.push_back( *sr );\r
+\r
+ info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32;\r
+\r
+ return info;\r
+}\r
+\r
+static void *pulseaudio_callback( void * user )\r
+{\r
+ CallbackInfo *cbi = static_cast<CallbackInfo *>( user );\r
+ RtApiPulse *context = static_cast<RtApiPulse *>( cbi->object );\r
+ volatile bool *isRunning = &cbi->isRunning;\r
+\r
+ while ( *isRunning ) {\r
+ pthread_testcancel();\r
+ context->callbackEvent();\r
+ }\r
+\r
+ pthread_exit( NULL );\r
+}\r
+\r
+void RtApiPulse::closeStream( void )\r
+{\r
+ PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
+\r
+ stream_.callbackInfo.isRunning = false;\r
+ if ( pah ) {\r
+ MUTEX_LOCK( &stream_.mutex );\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ pah->runnable = true;\r
+ pthread_cond_signal( &pah->runnable_cv );\r
+ }\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+\r
+ pthread_join( pah->thread, 0 );\r
+ if ( pah->s_play ) {\r
+ pa_simple_flush( pah->s_play, NULL );\r
+ pa_simple_free( pah->s_play );\r
+ }\r
+ if ( pah->s_rec )\r
+ pa_simple_free( pah->s_rec );\r
+\r
+ pthread_cond_destroy( &pah->runnable_cv );\r
+ delete pah;\r
+ stream_.apiHandle = 0;\r
+ }\r
+\r
+ if ( stream_.userBuffer[0] ) {\r
+ free( stream_.userBuffer[0] );\r
+ stream_.userBuffer[0] = 0;\r
+ }\r
+ if ( stream_.userBuffer[1] ) {\r
+ free( stream_.userBuffer[1] );\r
+ stream_.userBuffer[1] = 0;\r
+ }\r
+\r
+ stream_.state = STREAM_CLOSED;\r
+ stream_.mode = UNINITIALIZED;\r
+}\r
+\r
+void RtApiPulse::callbackEvent( void )\r
+{\r
+ PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
+\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ MUTEX_LOCK( &stream_.mutex );\r
+ while ( !pah->runnable )\r
+ pthread_cond_wait( &pah->runnable_cv, &stream_.mutex );\r
+\r
+ if ( stream_.state != STREAM_RUNNING ) {\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ return;\r
+ }\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ }\r
+\r
+ if ( stream_.state == STREAM_CLOSED ) {\r
+ errorText_ = "RtApiPulse::callbackEvent(): the stream is closed ... "\r
+ "this shouldn't happen!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
+ double streamTime = getStreamTime();\r
+ RtAudioStreamStatus status = 0;\r
+ int doStopStream = callback( stream_.userBuffer[OUTPUT], stream_.userBuffer[INPUT],\r
+ stream_.bufferSize, streamTime, status,\r
+ stream_.callbackInfo.userData );\r
+\r
+ if ( doStopStream == 2 ) {\r
+ abortStream();\r
+ return;\r
+ }\r
+\r
+ MUTEX_LOCK( &stream_.mutex );\r
+ void *pulse_in = stream_.doConvertBuffer[INPUT] ? stream_.deviceBuffer : stream_.userBuffer[INPUT];\r
+ void *pulse_out = stream_.doConvertBuffer[OUTPUT] ? stream_.deviceBuffer : stream_.userBuffer[OUTPUT];\r
+\r
+ if ( stream_.state != STREAM_RUNNING )\r
+ goto unlock;\r
+\r
+ int pa_error;\r
+ size_t bytes;\r
+ if (stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+ if ( stream_.doConvertBuffer[OUTPUT] ) {\r
+ convertBuffer( stream_.deviceBuffer,\r
+ stream_.userBuffer[OUTPUT],\r
+ stream_.convertInfo[OUTPUT] );\r
+ bytes = stream_.nDeviceChannels[OUTPUT] * stream_.bufferSize *\r
+ formatBytes( stream_.deviceFormat[OUTPUT] );\r
+ } else\r
+ bytes = stream_.nUserChannels[OUTPUT] * stream_.bufferSize *\r
+ formatBytes( stream_.userFormat );\r
+\r
+ if ( pa_simple_write( pah->s_play, pulse_out, bytes, &pa_error ) < 0 ) {\r
+ errorStream_ << "RtApiPulse::callbackEvent: audio write error, " <<\r
+ pa_strerror( pa_error ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ }\r
+ }\r
+\r
+ if ( stream_.mode == INPUT || stream_.mode == DUPLEX) {\r
+ if ( stream_.doConvertBuffer[INPUT] )\r
+ bytes = stream_.nDeviceChannels[INPUT] * stream_.bufferSize *\r
+ formatBytes( stream_.deviceFormat[INPUT] );\r
+ else\r
+ bytes = stream_.nUserChannels[INPUT] * stream_.bufferSize *\r
+ formatBytes( stream_.userFormat );\r
+ \r
+ if ( pa_simple_read( pah->s_rec, pulse_in, bytes, &pa_error ) < 0 ) {\r
+ errorStream_ << "RtApiPulse::callbackEvent: audio read error, " <<\r
+ pa_strerror( pa_error ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ }\r
+ if ( stream_.doConvertBuffer[INPUT] ) {\r
+ convertBuffer( stream_.userBuffer[INPUT],\r
+ stream_.deviceBuffer,\r
+ stream_.convertInfo[INPUT] );\r
+ }\r
+ }\r
+\r
+ unlock:\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ RtApi::tickStreamTime();\r
+\r
+ if ( doStopStream == 1 )\r
+ stopStream();\r
+}\r
+\r
+void RtApiPulse::startStream( void )\r
+{\r
+ PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
+\r
+ if ( stream_.state == STREAM_CLOSED ) {\r
+ errorText_ = "RtApiPulse::startStream(): the stream is not open!";\r
+ error( RtAudioError::INVALID_USE );\r
+ return;\r
+ }\r
+ if ( stream_.state == STREAM_RUNNING ) {\r
+ errorText_ = "RtApiPulse::startStream(): the stream is already running!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ MUTEX_LOCK( &stream_.mutex );\r
+\r
+ stream_.state = STREAM_RUNNING;\r
+\r
+ pah->runnable = true;\r
+ pthread_cond_signal( &pah->runnable_cv );\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+}\r
+\r
+void RtApiPulse::stopStream( void )\r
+{\r
+ PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
+\r
+ if ( stream_.state == STREAM_CLOSED ) {\r
+ errorText_ = "RtApiPulse::stopStream(): the stream is not open!";\r
+ error( RtAudioError::INVALID_USE );\r
+ return;\r
+ }\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ errorText_ = "RtApiPulse::stopStream(): the stream is already stopped!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ stream_.state = STREAM_STOPPED;\r
+ MUTEX_LOCK( &stream_.mutex );\r
+\r
+ if ( pah && pah->s_play ) {\r
+ int pa_error;\r
+ if ( pa_simple_drain( pah->s_play, &pa_error ) < 0 ) {\r
+ errorStream_ << "RtApiPulse::stopStream: error draining output device, " <<\r
+ pa_strerror( pa_error ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ }\r
+\r
+ stream_.state = STREAM_STOPPED;\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+}\r
+\r
+void RtApiPulse::abortStream( void )\r
+{\r
+ PulseAudioHandle *pah = static_cast<PulseAudioHandle*>( stream_.apiHandle );\r
+\r
+ if ( stream_.state == STREAM_CLOSED ) {\r
+ errorText_ = "RtApiPulse::abortStream(): the stream is not open!";\r
+ error( RtAudioError::INVALID_USE );\r
+ return;\r
+ }\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ errorText_ = "RtApiPulse::abortStream(): the stream is already stopped!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ stream_.state = STREAM_STOPPED;\r
+ MUTEX_LOCK( &stream_.mutex );\r
+\r
+ if ( pah && pah->s_play ) {\r
+ int pa_error;\r
+ if ( pa_simple_flush( pah->s_play, &pa_error ) < 0 ) {\r
+ errorStream_ << "RtApiPulse::abortStream: error flushing output device, " <<\r
+ pa_strerror( pa_error ) << ".";\r
+ errorText_ = errorStream_.str();\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+ return;\r
+ }\r
+ }\r
+\r
+ stream_.state = STREAM_STOPPED;\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+}\r
+\r
+bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,\r
+ unsigned int channels, unsigned int firstChannel,\r
+ unsigned int sampleRate, RtAudioFormat format,\r
+ unsigned int *bufferSize, RtAudio::StreamOptions *options )\r
+{\r
+ PulseAudioHandle *pah = 0;\r
+ unsigned long bufferBytes = 0;\r
+ pa_sample_spec ss;\r
+\r
+ if ( device != 0 ) return false;\r
+ if ( mode != INPUT && mode != OUTPUT ) return false;\r
+ if ( channels != 1 && channels != 2 ) {\r
+ errorText_ = "RtApiPulse::probeDeviceOpen: unsupported number of channels.";\r
+ return false;\r
+ }\r
+ ss.channels = channels;\r
+\r
+ if ( firstChannel != 0 ) return false;\r
+\r
+ bool sr_found = false;\r
+ for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr ) {\r
+ if ( sampleRate == *sr ) {\r
+ sr_found = true;\r
+ stream_.sampleRate = sampleRate;\r
+ ss.rate = sampleRate;\r
+ break;\r
+ }\r
+ }\r
+ if ( !sr_found ) {\r
+ errorText_ = "RtApiPulse::probeDeviceOpen: unsupported sample rate.";\r
+ return false;\r
+ }\r
+\r
+ bool sf_found = 0;\r
+ for ( const rtaudio_pa_format_mapping_t *sf = supported_sampleformats;\r
+ sf->rtaudio_format && sf->pa_format != PA_SAMPLE_INVALID; ++sf ) {\r
+ if ( format == sf->rtaudio_format ) {\r
+ sf_found = true;\r
+ stream_.userFormat = sf->rtaudio_format;\r
+ stream_.deviceFormat[mode] = stream_.userFormat;\r
+ ss.format = sf->pa_format;\r
+ break;\r
+ }\r
+ }\r
+ if ( !sf_found ) { // Use internal data format conversion.\r
+ stream_.userFormat = format;\r
+ stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
+ ss.format = PA_SAMPLE_FLOAT32LE;\r
+ }\r
+\r
+ // Set other stream parameters.\r
+ if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
+ else stream_.userInterleaved = true;\r
+ stream_.deviceInterleaved[mode] = true;\r
+ stream_.nBuffers = 1;\r
+ stream_.doByteSwap[mode] = false;\r
+ stream_.nUserChannels[mode] = channels;\r
+ stream_.nDeviceChannels[mode] = channels + firstChannel;\r
+ stream_.channelOffset[mode] = 0;\r
+ std::string streamName = "RtAudio";\r
+\r
+ // Set flags for buffer conversion.\r
+ stream_.doConvertBuffer[mode] = false;\r
+ if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
+ stream_.doConvertBuffer[mode] = true;\r
+ if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
+ stream_.doConvertBuffer[mode] = true;\r
+\r
+ // Allocate necessary internal buffers.\r
+ bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
+ stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
+ if ( stream_.userBuffer[mode] == NULL ) {\r
+ errorText_ = "RtApiPulse::probeDeviceOpen: error allocating user buffer memory.";\r
+ goto error;\r
+ }\r
+ stream_.bufferSize = *bufferSize;\r
+\r
+ if ( stream_.doConvertBuffer[mode] ) {\r
+\r
+ bool makeBuffer = true;\r
+ bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
+ if ( mode == INPUT ) {\r
+ if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
+ unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
+ if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
+ }\r
+ }\r
+\r
+ if ( makeBuffer ) {\r
+ bufferBytes *= *bufferSize;\r
+ if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
+ if ( stream_.deviceBuffer == NULL ) {\r
+ errorText_ = "RtApiPulse::probeDeviceOpen: error allocating device buffer memory.";\r
+ goto error;\r
+ }\r
+ }\r
+ }\r
+\r
+ stream_.device[mode] = device;\r
+\r
+ // Setup the buffer conversion information structure.\r
+ if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
+\r
+ if ( !stream_.apiHandle ) {\r
+ PulseAudioHandle *pah = new PulseAudioHandle;\r
+ if ( !pah ) {\r
+ errorText_ = "RtApiPulse::probeDeviceOpen: error allocating memory for handle.";\r
+ goto error;\r
+ }\r
+\r
+ stream_.apiHandle = pah;\r
+ if ( pthread_cond_init( &pah->runnable_cv, NULL ) != 0 ) {\r
+ errorText_ = "RtApiPulse::probeDeviceOpen: error creating condition variable.";\r
+ goto error;\r
+ }\r
+ }\r
+ pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
+\r
+ int error;\r
+ if ( !options->streamName.empty() ) streamName = options->streamName;\r
+ switch ( mode ) {\r
+ case INPUT:\r
+ pa_buffer_attr buffer_attr;\r
+ buffer_attr.fragsize = bufferBytes;\r
+ buffer_attr.maxlength = -1;\r
+\r
+ pah->s_rec = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_RECORD, NULL, "Record", &ss, NULL, &buffer_attr, &error );\r
+ if ( !pah->s_rec ) {\r
+ errorText_ = "RtApiPulse::probeDeviceOpen: error connecting input to PulseAudio server.";\r
+ goto error;\r
+ }\r
+ break;\r
+ case OUTPUT:\r
+ pah->s_play = pa_simple_new( NULL, "RtAudio", PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error );\r
+ if ( !pah->s_play ) {\r
+ errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server.";\r
+ goto error;\r
+ }\r
+ break;\r
+ default:\r
+ goto error;\r
+ }\r
+\r
+ if ( stream_.mode == UNINITIALIZED )\r
+ stream_.mode = mode;\r
+ else if ( stream_.mode == mode )\r
+ goto error;\r
+ else\r
+ stream_.mode = DUPLEX;\r
+\r
+ if ( !stream_.callbackInfo.isRunning ) {\r
+ stream_.callbackInfo.object = this;\r
+ stream_.callbackInfo.isRunning = true;\r
+ if ( pthread_create( &pah->thread, NULL, pulseaudio_callback, (void *)&stream_.callbackInfo) != 0 ) {\r
+ errorText_ = "RtApiPulse::probeDeviceOpen: error creating thread.";\r
+ goto error;\r
+ }\r
+ }\r
+\r
+ stream_.state = STREAM_STOPPED;\r
+ return true;\r
+ \r
+ error:\r
+ if ( pah && stream_.callbackInfo.isRunning ) {\r
+ pthread_cond_destroy( &pah->runnable_cv );\r
+ delete pah;\r
+ stream_.apiHandle = 0;\r
+ }\r
+\r
+ for ( int i=0; i<2; i++ ) {\r
+ if ( stream_.userBuffer[i] ) {\r
+ free( stream_.userBuffer[i] );\r
+ stream_.userBuffer[i] = 0;\r
+ }\r
+ }\r
+\r
+ if ( stream_.deviceBuffer ) {\r
+ free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = 0;\r
+ }\r
+\r
+ return FAILURE;\r
+}\r
+\r
+//******************** End of __LINUX_PULSE__ *********************//\r
+#endif\r
+\r
+#if defined(__LINUX_OSS__)\r
+\r
+#include <unistd.h>\r
+#include <sys/ioctl.h>\r
+#include <unistd.h>\r
+#include <fcntl.h>\r
+#include <sys/soundcard.h>\r
+#include <errno.h>\r
+#include <math.h>\r
+\r
+static void *ossCallbackHandler(void * ptr);\r
+\r
+// A structure to hold various information related to the OSS API\r
+// implementation.\r
+struct OssHandle {\r
+ int id[2]; // device ids\r
+ bool xrun[2];\r
+ bool triggered;\r
+ pthread_cond_t runnable;\r
+\r
+ OssHandle()\r
+ :triggered(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }\r
+};\r
+\r
+RtApiOss :: RtApiOss()\r
+{\r
+ // Nothing to do here.\r
+}\r
+\r
+RtApiOss :: ~RtApiOss()\r
+{\r
+ if ( stream_.state != STREAM_CLOSED ) closeStream();\r
+}\r
+\r
+unsigned int RtApiOss :: getDeviceCount( void )\r
+{\r
+ int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
+ if ( mixerfd == -1 ) {\r
+ errorText_ = "RtApiOss::getDeviceCount: error opening '/dev/mixer'.";\r
+ error( RtAudioError::WARNING );\r
+ return 0;\r
+ }\r
+\r
+ oss_sysinfo sysinfo;\r
+ if ( ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ) == -1 ) {\r
+ close( mixerfd );\r
+ errorText_ = "RtApiOss::getDeviceCount: error getting sysinfo, OSS version >= 4.0 is required.";\r
+ error( RtAudioError::WARNING );\r
+ return 0;\r
+ }\r
+\r
+ close( mixerfd );\r
+ return sysinfo.numaudios;\r
+}\r
+\r
+RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )\r
+{\r
+ RtAudio::DeviceInfo info;\r
+ info.probed = false;\r
+\r
+ int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
+ if ( mixerfd == -1 ) {\r
+ errorText_ = "RtApiOss::getDeviceInfo: error opening '/dev/mixer'.";\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ oss_sysinfo sysinfo;\r
+ int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );\r
+ if ( result == -1 ) {\r
+ close( mixerfd );\r
+ errorText_ = "RtApiOss::getDeviceInfo: error getting sysinfo, OSS version >= 4.0 is required.";\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ unsigned nDevices = sysinfo.numaudios;\r
+ if ( nDevices == 0 ) {\r
+ close( mixerfd );\r
+ errorText_ = "RtApiOss::getDeviceInfo: no devices found!";\r
+ error( RtAudioError::INVALID_USE );\r
+ return info;\r
+ }\r
+\r
+ if ( device >= nDevices ) {\r
+ close( mixerfd );\r
+ errorText_ = "RtApiOss::getDeviceInfo: device ID is invalid!";\r
+ error( RtAudioError::INVALID_USE );\r
+ return info;\r
+ }\r
+\r
+ oss_audioinfo ainfo;\r
+ ainfo.dev = device;\r
+ result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );\r
+ close( mixerfd );\r
+ if ( result == -1 ) {\r
+ errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ // Probe channels\r
+ if ( ainfo.caps & PCM_CAP_OUTPUT ) info.outputChannels = ainfo.max_channels;\r
+ if ( ainfo.caps & PCM_CAP_INPUT ) info.inputChannels = ainfo.max_channels;\r
+ if ( ainfo.caps & PCM_CAP_DUPLEX ) {\r
+ if ( info.outputChannels > 0 && info.inputChannels > 0 && ainfo.caps & PCM_CAP_DUPLEX )\r
+ info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;\r
+ }\r
+\r
+ // Probe data formats ... do for input\r
+ unsigned long mask = ainfo.iformats;\r
+ if ( mask & AFMT_S16_LE || mask & AFMT_S16_BE )\r
+ info.nativeFormats |= RTAUDIO_SINT16;\r
+ if ( mask & AFMT_S8 )\r
+ info.nativeFormats |= RTAUDIO_SINT8;\r
+ if ( mask & AFMT_S32_LE || mask & AFMT_S32_BE )\r
+ info.nativeFormats |= RTAUDIO_SINT32;\r
+ if ( mask & AFMT_FLOAT )\r
+ info.nativeFormats |= RTAUDIO_FLOAT32;\r
+ if ( mask & AFMT_S24_LE || mask & AFMT_S24_BE )\r
+ info.nativeFormats |= RTAUDIO_SINT24;\r
+\r
+ // Check that we have at least one supported format\r
+ if ( info.nativeFormats == 0 ) {\r
+ errorStream_ << "RtApiOss::getDeviceInfo: device (" << ainfo.name << ") data format not supported by RtAudio.";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ return info;\r
+ }\r
+\r
+ // Probe the supported sample rates.\r
+ info.sampleRates.clear();\r
+ if ( ainfo.nrates ) {\r
+ for ( unsigned int i=0; i<ainfo.nrates; i++ ) {\r
+ for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
+ if ( ainfo.rates[i] == SAMPLE_RATES[k] ) {\r
+ info.sampleRates.push_back( SAMPLE_RATES[k] );\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ else {\r
+ // Check min and max rate values;\r
+ for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
+ if ( ainfo.min_rate <= (int) SAMPLE_RATES[k] && ainfo.max_rate >= (int) SAMPLE_RATES[k] )\r
+ info.sampleRates.push_back( SAMPLE_RATES[k] );\r
+ }\r
+ }\r
+\r
+ if ( info.sampleRates.size() == 0 ) {\r
+ errorStream_ << "RtApiOss::getDeviceInfo: no supported sample rates found for device (" << ainfo.name << ").";\r
+ errorText_ = errorStream_.str();\r
+ error( RtAudioError::WARNING );\r
+ }\r
+ else {\r
+ info.probed = true;\r
+ info.name = ainfo.name;\r
+ }\r
+\r
+ return info;\r
+}\r
+\r
+\r
+bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
+ unsigned int firstChannel, unsigned int sampleRate,\r
+ RtAudioFormat format, unsigned int *bufferSize,\r
+ RtAudio::StreamOptions *options )\r
+{\r
+ int mixerfd = open( "/dev/mixer", O_RDWR, 0 );\r
+ if ( mixerfd == -1 ) {\r
+ errorText_ = "RtApiOss::probeDeviceOpen: error opening '/dev/mixer'.";\r
+ return FAILURE;\r
+ }\r
+\r
+ oss_sysinfo sysinfo;\r
+ int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );\r
+ if ( result == -1 ) {\r
+ close( mixerfd );\r
+ errorText_ = "RtApiOss::probeDeviceOpen: error getting sysinfo, OSS version >= 4.0 is required.";\r
+ return FAILURE;\r
+ }\r
+\r
+ unsigned nDevices = sysinfo.numaudios;\r
+ if ( nDevices == 0 ) {\r
+ // This should not happen because a check is made before this function is called.\r
+ close( mixerfd );\r
+ errorText_ = "RtApiOss::probeDeviceOpen: no devices found!";\r
+ return FAILURE;\r
+ }\r
+\r
+ if ( device >= nDevices ) {\r
+ // This should not happen because a check is made before this function is called.\r
+ close( mixerfd );\r
+ errorText_ = "RtApiOss::probeDeviceOpen: device ID is invalid!";\r
+ return FAILURE;\r
+ }\r
+\r
+ oss_audioinfo ainfo;\r
+ ainfo.dev = device;\r
+ result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );\r
+ close( mixerfd );\r
+ if ( result == -1 ) {\r
+ errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Check if device supports input or output\r
+ if ( ( mode == OUTPUT && !( ainfo.caps & PCM_CAP_OUTPUT ) ) ||\r
+ ( mode == INPUT && !( ainfo.caps & PCM_CAP_INPUT ) ) ) {\r
+ if ( mode == OUTPUT )\r
+ errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support output.";\r
+ else\r
+ errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support input.";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ int flags = 0;\r
+ OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
+ if ( mode == OUTPUT )\r
+ flags |= O_WRONLY;\r
+ else { // mode == INPUT\r
+ if (stream_.mode == OUTPUT && stream_.device[0] == device) {\r
+ // We just set the same device for playback ... close and reopen for duplex (OSS only).\r
+ close( handle->id[0] );\r
+ handle->id[0] = 0;\r
+ if ( !( ainfo.caps & PCM_CAP_DUPLEX ) ) {\r
+ errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support duplex mode.";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ // Check that the number previously set channels is the same.\r
+ if ( stream_.nUserChannels[0] != channels ) {\r
+ errorStream_ << "RtApiOss::probeDeviceOpen: input/output channels must be equal for OSS duplex device (" << ainfo.name << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ flags |= O_RDWR;\r
+ }\r
+ else\r
+ flags |= O_RDONLY;\r
+ }\r
+\r
+ // Set exclusive access if specified.\r
+ if ( options && options->flags & RTAUDIO_HOG_DEVICE ) flags |= O_EXCL;\r
+\r
+ // Try to open the device.\r
+ int fd;\r
+ fd = open( ainfo.devnode, flags, 0 );\r
+ if ( fd == -1 ) {\r
+ if ( errno == EBUSY )\r
+ errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") is busy.";\r
+ else\r
+ errorStream_ << "RtApiOss::probeDeviceOpen: error opening device (" << ainfo.name << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // For duplex operation, specifically set this mode (this doesn't seem to work).\r
+ /*\r
+ if ( flags | O_RDWR ) {\r
+ result = ioctl( fd, SNDCTL_DSP_SETDUPLEX, NULL );\r
+ if ( result == -1) {\r
+ errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ }\r
+ */\r
+\r
+ // Check the device channel support.\r
+ stream_.nUserChannels[mode] = channels;\r
+ if ( ainfo.max_channels < (int)(channels + firstChannel) ) {\r
+ close( fd );\r
+ errorStream_ << "RtApiOss::probeDeviceOpen: the device (" << ainfo.name << ") does not support requested channel parameters.";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Set the number of channels.\r
+ int deviceChannels = channels + firstChannel;\r
+ result = ioctl( fd, SNDCTL_DSP_CHANNELS, &deviceChannels );\r
+ if ( result == -1 || deviceChannels < (int)(channels + firstChannel) ) {\r
+ close( fd );\r
+ errorStream_ << "RtApiOss::probeDeviceOpen: error setting channel parameters on device (" << ainfo.name << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ stream_.nDeviceChannels[mode] = deviceChannels;\r
+\r
+ // Get the data format mask\r
+ int mask;\r
+ result = ioctl( fd, SNDCTL_DSP_GETFMTS, &mask );\r
+ if ( result == -1 ) {\r
+ close( fd );\r
+ errorStream_ << "RtApiOss::probeDeviceOpen: error getting device (" << ainfo.name << ") data formats.";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Determine how to set the device format.\r
+ stream_.userFormat = format;\r
+ int deviceFormat = -1;\r
+ stream_.doByteSwap[mode] = false;\r
+ if ( format == RTAUDIO_SINT8 ) {\r
+ if ( mask & AFMT_S8 ) {\r
+ deviceFormat = AFMT_S8;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
+ }\r
+ }\r
+ else if ( format == RTAUDIO_SINT16 ) {\r
+ if ( mask & AFMT_S16_NE ) {\r
+ deviceFormat = AFMT_S16_NE;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
+ }\r
+ else if ( mask & AFMT_S16_OE ) {\r
+ deviceFormat = AFMT_S16_OE;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
+ stream_.doByteSwap[mode] = true;\r
+ }\r
+ }\r
+ else if ( format == RTAUDIO_SINT24 ) {\r
+ if ( mask & AFMT_S24_NE ) {\r
+ deviceFormat = AFMT_S24_NE;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
+ }\r
+ else if ( mask & AFMT_S24_OE ) {\r
+ deviceFormat = AFMT_S24_OE;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
+ stream_.doByteSwap[mode] = true;\r
+ }\r
+ }\r
+ else if ( format == RTAUDIO_SINT32 ) {\r
+ if ( mask & AFMT_S32_NE ) {\r
+ deviceFormat = AFMT_S32_NE;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
+ }\r
+ else if ( mask & AFMT_S32_OE ) {\r
+ deviceFormat = AFMT_S32_OE;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
+ stream_.doByteSwap[mode] = true;\r
+ }\r
+ }\r
+\r
+ if ( deviceFormat == -1 ) {\r
+ // The user requested format is not natively supported by the device.\r
+ if ( mask & AFMT_S16_NE ) {\r
+ deviceFormat = AFMT_S16_NE;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
+ }\r
+ else if ( mask & AFMT_S32_NE ) {\r
+ deviceFormat = AFMT_S32_NE;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
+ }\r
+ else if ( mask & AFMT_S24_NE ) {\r
+ deviceFormat = AFMT_S24_NE;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
+ }\r
+ else if ( mask & AFMT_S16_OE ) {\r
+ deviceFormat = AFMT_S16_OE;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT16;\r
+ stream_.doByteSwap[mode] = true;\r
+ }\r
+ else if ( mask & AFMT_S32_OE ) {\r
+ deviceFormat = AFMT_S32_OE;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT32;\r
+ stream_.doByteSwap[mode] = true;\r
+ }\r
+ else if ( mask & AFMT_S24_OE ) {\r
+ deviceFormat = AFMT_S24_OE;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
+ stream_.doByteSwap[mode] = true;\r
+ }\r
+ else if ( mask & AFMT_S8) {\r
+ deviceFormat = AFMT_S8;\r
+ stream_.deviceFormat[mode] = RTAUDIO_SINT8;\r
+ }\r
+ }\r
+\r
+ if ( stream_.deviceFormat[mode] == 0 ) {\r
+ // This really shouldn't happen ...\r
+ close( fd );\r
+ errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") data format not supported by RtAudio.";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Set the data format.\r
+ int temp = deviceFormat;\r
+ result = ioctl( fd, SNDCTL_DSP_SETFMT, &deviceFormat );\r
+ if ( result == -1 || deviceFormat != temp ) {\r
+ close( fd );\r
+ errorStream_ << "RtApiOss::probeDeviceOpen: error setting data format on device (" << ainfo.name << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Attempt to set the buffer size. According to OSS, the minimum\r
+ // number of buffers is two. The supposed minimum buffer size is 16\r
+ // bytes, so that will be our lower bound. The argument to this\r
+ // call is in the form 0xMMMMSSSS (hex), where the buffer size (in\r
+ // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM.\r
+ // We'll check the actual value used near the end of the setup\r
+ // procedure.\r
+ int ossBufferBytes = *bufferSize * formatBytes( stream_.deviceFormat[mode] ) * deviceChannels;\r
+ if ( ossBufferBytes < 16 ) ossBufferBytes = 16;\r
+ int buffers = 0;\r
+ if ( options ) buffers = options->numberOfBuffers;\r
+ if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) buffers = 2;\r
+ if ( buffers < 2 ) buffers = 3;\r
+ temp = ((int) buffers << 16) + (int)( log10( (double)ossBufferBytes ) / log10( 2.0 ) );\r
+ result = ioctl( fd, SNDCTL_DSP_SETFRAGMENT, &temp );\r
+ if ( result == -1 ) {\r
+ close( fd );\r
+ errorStream_ << "RtApiOss::probeDeviceOpen: error setting buffer size on device (" << ainfo.name << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ stream_.nBuffers = buffers;\r
+\r
+ // Save buffer size (in sample frames).\r
+ *bufferSize = ossBufferBytes / ( formatBytes(stream_.deviceFormat[mode]) * deviceChannels );\r
+ stream_.bufferSize = *bufferSize;\r
+\r
+ // Set the sample rate.\r
+ int srate = sampleRate;\r
+ result = ioctl( fd, SNDCTL_DSP_SPEED, &srate );\r
+ if ( result == -1 ) {\r
+ close( fd );\r
+ errorStream_ << "RtApiOss::probeDeviceOpen: error setting sample rate (" << sampleRate << ") on device (" << ainfo.name << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+\r
+ // Verify the sample rate setup worked.\r
+ if ( abs( srate - sampleRate ) > 100 ) {\r
+ close( fd );\r
+ errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support sample rate (" << sampleRate << ").";\r
+ errorText_ = errorStream_.str();\r
+ return FAILURE;\r
+ }\r
+ stream_.sampleRate = sampleRate;\r
+\r
+ if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device) {\r
+ // We're doing duplex setup here.\r
+ stream_.deviceFormat[0] = stream_.deviceFormat[1];\r
+ stream_.nDeviceChannels[0] = deviceChannels;\r
+ }\r
+\r
+ // Set interleaving parameters.\r
+ stream_.userInterleaved = true;\r
+ stream_.deviceInterleaved[mode] = true;\r
+ if ( options && options->flags & RTAUDIO_NONINTERLEAVED )\r
+ stream_.userInterleaved = false;\r
+\r
+ // Set flags for buffer conversion\r
+ stream_.doConvertBuffer[mode] = false;\r
+ if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
+ stream_.doConvertBuffer[mode] = true;\r
+ if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
+ stream_.doConvertBuffer[mode] = true;\r
+ if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
+ stream_.nUserChannels[mode] > 1 )\r
+ stream_.doConvertBuffer[mode] = true;\r
+\r
+ // Allocate the stream handles if necessary and then save.\r
+ if ( stream_.apiHandle == 0 ) {\r
+ try {\r
+ handle = new OssHandle;\r
+ }\r
+ catch ( std::bad_alloc& ) {\r
+ errorText_ = "RtApiOss::probeDeviceOpen: error allocating OssHandle memory.";\r
+ goto error;\r
+ }\r
+\r
+ if ( pthread_cond_init( &handle->runnable, NULL ) ) {\r
+ errorText_ = "RtApiOss::probeDeviceOpen: error initializing pthread condition variable.";\r
+ goto error;\r
+ }\r
+\r
+ stream_.apiHandle = (void *) handle;\r
+ }\r
+ else {\r
+ handle = (OssHandle *) stream_.apiHandle;\r
+ }\r
+ handle->id[mode] = fd;\r
+\r
+ // Allocate necessary internal buffers.\r
+ unsigned long bufferBytes;\r
+ bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
+ stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
+ if ( stream_.userBuffer[mode] == NULL ) {\r
+ errorText_ = "RtApiOss::probeDeviceOpen: error allocating user buffer memory.";\r
+ goto error;\r
+ }\r
+\r
+ if ( stream_.doConvertBuffer[mode] ) {\r
+\r
+ bool makeBuffer = true;\r
+ bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
+ if ( mode == INPUT ) {\r
+ if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
+ unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
+ if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
+ }\r
+ }\r
+\r
+ if ( makeBuffer ) {\r
+ bufferBytes *= *bufferSize;\r
+ if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
+ if ( stream_.deviceBuffer == NULL ) {\r
+ errorText_ = "RtApiOss::probeDeviceOpen: error allocating device buffer memory.";\r
+ goto error;\r
+ }\r
+ }\r
+ }\r
+\r
+ stream_.device[mode] = device;\r
+ stream_.state = STREAM_STOPPED;\r
+\r
+ // Setup the buffer conversion information structure.\r
+ if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
+\r
+ // Setup thread if necessary.\r
+ if ( stream_.mode == OUTPUT && mode == INPUT ) {\r
+ // We had already set up an output stream.\r
+ stream_.mode = DUPLEX;\r
+ if ( stream_.device[0] == device ) handle->id[0] = fd;\r
+ }\r
+ else {\r
+ stream_.mode = mode;\r
+\r
+ // Setup callback thread.\r
+ stream_.callbackInfo.object = (void *) this;\r
+\r
+ // Set the thread attributes for joinable and realtime scheduling\r
+ // priority. The higher priority will only take affect if the\r
+ // program is run as root or suid.\r
+ pthread_attr_t attr;\r
+ pthread_attr_init( &attr );\r
+ pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );\r
+#ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
+ if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {\r
+ struct sched_param param;\r
+ int priority = options->priority;\r
+ int min = sched_get_priority_min( SCHED_RR );\r
+ int max = sched_get_priority_max( SCHED_RR );\r
+ if ( priority < min ) priority = min;\r
+ else if ( priority > max ) priority = max;\r
+ param.sched_priority = priority;\r
+ pthread_attr_setschedparam( &attr, ¶m );\r
+ pthread_attr_setschedpolicy( &attr, SCHED_RR );\r
+ }\r
+ else\r
+ pthread_attr_setschedpolicy( &attr, SCHED_OTHER );\r
+#else\r
+ pthread_attr_setschedpolicy( &attr, SCHED_OTHER );\r
+#endif\r
+\r
+ stream_.callbackInfo.isRunning = true;\r
+ result = pthread_create( &stream_.callbackInfo.thread, &attr, ossCallbackHandler, &stream_.callbackInfo );\r
+ pthread_attr_destroy( &attr );\r
+ if ( result ) {\r
+ stream_.callbackInfo.isRunning = false;\r
+ errorText_ = "RtApiOss::error creating callback thread!";\r
+ goto error;\r
+ }\r
+ }\r
+\r
+ return SUCCESS;\r
+\r
+ error:\r
+ if ( handle ) {\r
+ pthread_cond_destroy( &handle->runnable );\r
+ if ( handle->id[0] ) close( handle->id[0] );\r
+ if ( handle->id[1] ) close( handle->id[1] );\r
+ delete handle;\r
+ stream_.apiHandle = 0;\r
+ }\r
+\r
+ for ( int i=0; i<2; i++ ) {\r
+ if ( stream_.userBuffer[i] ) {\r
+ free( stream_.userBuffer[i] );\r
+ stream_.userBuffer[i] = 0;\r
+ }\r
+ }\r
+\r
+ if ( stream_.deviceBuffer ) {\r
+ free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = 0;\r
+ }\r
+\r
+ return FAILURE;\r
+}\r
+\r
+void RtApiOss :: closeStream()\r
+{\r
+ if ( stream_.state == STREAM_CLOSED ) {\r
+ errorText_ = "RtApiOss::closeStream(): no open stream to close!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
+ stream_.callbackInfo.isRunning = false;\r
+ MUTEX_LOCK( &stream_.mutex );\r
+ if ( stream_.state == STREAM_STOPPED )\r
+ pthread_cond_signal( &handle->runnable );\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ pthread_join( stream_.callbackInfo.thread, NULL );\r
+\r
+ if ( stream_.state == STREAM_RUNNING ) {\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )\r
+ ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
+ else\r
+ ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
+ stream_.state = STREAM_STOPPED;\r
+ }\r
+\r
+ if ( handle ) {\r
+ pthread_cond_destroy( &handle->runnable );\r
+ if ( handle->id[0] ) close( handle->id[0] );\r
+ if ( handle->id[1] ) close( handle->id[1] );\r
+ delete handle;\r
+ stream_.apiHandle = 0;\r
+ }\r
+\r
+ for ( int i=0; i<2; i++ ) {\r
+ if ( stream_.userBuffer[i] ) {\r
+ free( stream_.userBuffer[i] );\r
+ stream_.userBuffer[i] = 0;\r
+ }\r
+ }\r
+\r
+ if ( stream_.deviceBuffer ) {\r
+ free( stream_.deviceBuffer );\r
+ stream_.deviceBuffer = 0;\r
+ }\r
+\r
+ stream_.mode = UNINITIALIZED;\r
+ stream_.state = STREAM_CLOSED;\r
+}\r
+\r
+void RtApiOss :: startStream()\r
+{\r
+ verifyStream();\r
+ if ( stream_.state == STREAM_RUNNING ) {\r
+ errorText_ = "RtApiOss::startStream(): the stream is already running!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ MUTEX_LOCK( &stream_.mutex );\r
+\r
+ stream_.state = STREAM_RUNNING;\r
+\r
+ // No need to do anything else here ... OSS automatically starts\r
+ // when fed samples.\r
+\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+\r
+ OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
+ pthread_cond_signal( &handle->runnable );\r
+}\r
+\r
+void RtApiOss :: stopStream()\r
+{\r
+ verifyStream();\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ errorText_ = "RtApiOss::stopStream(): the stream is already stopped!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ MUTEX_LOCK( &stream_.mutex );\r
+\r
+ // The state might change while waiting on a mutex.\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ return;\r
+ }\r
+\r
+ int result = 0;\r
+ OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+\r
+ // Flush the output with zeros a few times.\r
+ char *buffer;\r
+ int samples;\r
+ RtAudioFormat format;\r
+\r
+ if ( stream_.doConvertBuffer[0] ) {\r
+ buffer = stream_.deviceBuffer;\r
+ samples = stream_.bufferSize * stream_.nDeviceChannels[0];\r
+ format = stream_.deviceFormat[0];\r
+ }\r
+ else {\r
+ buffer = stream_.userBuffer[0];\r
+ samples = stream_.bufferSize * stream_.nUserChannels[0];\r
+ format = stream_.userFormat;\r
+ }\r
+\r
+ memset( buffer, 0, samples * formatBytes(format) );\r
+ for ( unsigned int i=0; i<stream_.nBuffers+1; i++ ) {\r
+ result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
+ if ( result == -1 ) {\r
+ errorText_ = "RtApiOss::stopStream: audio write error.";\r
+ error( RtAudioError::WARNING );\r
+ }\r
+ }\r
+\r
+ result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
+ if ( result == -1 ) {\r
+ errorStream_ << "RtApiOss::stopStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+ handle->triggered = false;\r
+ }\r
+\r
+ if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {\r
+ result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
+ if ( result == -1 ) {\r
+ errorStream_ << "RtApiOss::stopStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+ }\r
+\r
+ unlock:\r
+ stream_.state = STREAM_STOPPED;\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+\r
+ if ( result != -1 ) return;\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+}\r
+\r
+void RtApiOss :: abortStream()\r
+{\r
+ verifyStream();\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ errorText_ = "RtApiOss::abortStream(): the stream is already stopped!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ MUTEX_LOCK( &stream_.mutex );\r
+\r
+ // The state might change while waiting on a mutex.\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ return;\r
+ }\r
+\r
+ int result = 0;\r
+ OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+ result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );\r
+ if ( result == -1 ) {\r
+ errorStream_ << "RtApiOss::abortStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+ handle->triggered = false;\r
+ }\r
+\r
+ if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {\r
+ result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );\r
+ if ( result == -1 ) {\r
+ errorStream_ << "RtApiOss::abortStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";\r
+ errorText_ = errorStream_.str();\r
+ goto unlock;\r
+ }\r
+ }\r
+\r
+ unlock:\r
+ stream_.state = STREAM_STOPPED;\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+\r
+ if ( result != -1 ) return;\r
+ error( RtAudioError::SYSTEM_ERROR );\r
+}\r
+\r
+void RtApiOss :: callbackEvent()\r
+{\r
+ OssHandle *handle = (OssHandle *) stream_.apiHandle;\r
+ if ( stream_.state == STREAM_STOPPED ) {\r
+ MUTEX_LOCK( &stream_.mutex );\r
+ pthread_cond_wait( &handle->runnable, &stream_.mutex );\r
+ if ( stream_.state != STREAM_RUNNING ) {\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ return;\r
+ }\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+ }\r
+\r
+ if ( stream_.state == STREAM_CLOSED ) {\r
+ errorText_ = "RtApiOss::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
+ error( RtAudioError::WARNING );\r
+ return;\r
+ }\r
+\r
+ // Invoke user callback to get fresh output data.\r
+ int doStopStream = 0;\r
+ RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
+ double streamTime = getStreamTime();\r
+ RtAudioStreamStatus status = 0;\r
+ if ( stream_.mode != INPUT && handle->xrun[0] == true ) {\r
+ status |= RTAUDIO_OUTPUT_UNDERFLOW;\r
+ handle->xrun[0] = false;\r
+ }\r
+ if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {\r
+ status |= RTAUDIO_INPUT_OVERFLOW;\r
+ handle->xrun[1] = false;\r
+ }\r
+ doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
+ stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );\r
+ if ( doStopStream == 2 ) {\r
+ this->abortStream();\r
+ return;\r
+ }\r
+\r
+ MUTEX_LOCK( &stream_.mutex );\r
+\r
+ // The state might change while waiting on a mutex.\r
+ if ( stream_.state == STREAM_STOPPED ) goto unlock;\r
+\r
+ int result;\r
+ char *buffer;\r
+ int samples;\r
+ RtAudioFormat format;\r
+\r
+ if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+\r
+ // Setup parameters and do buffer conversion if necessary.\r
+ if ( stream_.doConvertBuffer[0] ) {\r
+ buffer = stream_.deviceBuffer;\r
+ convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );\r
+ samples = stream_.bufferSize * stream_.nDeviceChannels[0];\r
+ format = stream_.deviceFormat[0];\r
+ }\r
+ else {\r
+ buffer = stream_.userBuffer[0];\r
+ samples = stream_.bufferSize * stream_.nUserChannels[0];\r
+ format = stream_.userFormat;\r
+ }\r
+\r
+ // Do byte swapping if necessary.\r
+ if ( stream_.doByteSwap[0] )\r
+ byteSwapBuffer( buffer, samples, format );\r
+\r
+ if ( stream_.mode == DUPLEX && handle->triggered == false ) {\r
+ int trig = 0;\r
+ ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );\r
+ result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
+ trig = PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT;\r
+ ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );\r
+ handle->triggered = true;\r
+ }\r
+ else\r
+ // Write samples to device.\r
+ result = write( handle->id[0], buffer, samples * formatBytes(format) );\r
+\r
+ if ( result == -1 ) {\r
+ // We'll assume this is an underrun, though there isn't a\r
+ // specific means for determining that.\r
+ handle->xrun[0] = true;\r
+ errorText_ = "RtApiOss::callbackEvent: audio write error.";\r
+ error( RtAudioError::WARNING );\r
+ // Continue on to input section.\r
+ }\r
+ }\r
+\r
+ if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
+\r
+ // Setup parameters.\r
+ if ( stream_.doConvertBuffer[1] ) {\r
+ buffer = stream_.deviceBuffer;\r
+ samples = stream_.bufferSize * stream_.nDeviceChannels[1];\r
+ format = stream_.deviceFormat[1];\r
+ }\r
+ else {\r
+ buffer = stream_.userBuffer[1];\r
+ samples = stream_.bufferSize * stream_.nUserChannels[1];\r
+ format = stream_.userFormat;\r
+ }\r
+\r
+ // Read samples from device.\r
+ result = read( handle->id[1], buffer, samples * formatBytes(format) );\r
+\r
+ if ( result == -1 ) {\r
+ // We'll assume this is an overrun, though there isn't a\r
+ // specific means for determining that.\r
+ handle->xrun[1] = true;\r
+ errorText_ = "RtApiOss::callbackEvent: audio read error.";\r
+ error( RtAudioError::WARNING );\r
+ goto unlock;\r
+ }\r
+\r
+ // Do byte swapping if necessary.\r
+ if ( stream_.doByteSwap[1] )\r
+ byteSwapBuffer( buffer, samples, format );\r
+\r
+ // Do buffer conversion if necessary.\r
+ if ( stream_.doConvertBuffer[1] )\r
+ convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );\r
+ }\r
+\r
+ unlock:\r
+ MUTEX_UNLOCK( &stream_.mutex );\r
+\r
+ RtApi::tickStreamTime();\r
+ if ( doStopStream == 1 ) this->stopStream();\r
+}\r
+\r
+static void *ossCallbackHandler( void *ptr )\r
+{\r
+ CallbackInfo *info = (CallbackInfo *) ptr;\r
+ RtApiOss *object = (RtApiOss *) info->object;\r
+ bool *isRunning = &info->isRunning;\r
+\r
+ while ( *isRunning == true ) {\r
+ pthread_testcancel();\r
+ object->callbackEvent();\r
+ }\r
+\r
+ pthread_exit( NULL );\r
+}\r
+\r
+//******************** End of __LINUX_OSS__ *********************//\r
+#endif\r
+\r
+\r
+// *************************************************** //\r
+//\r
+// Protected common (OS-independent) RtAudio methods.\r
+//\r
+// *************************************************** //\r
+\r
+// This method can be modified to control the behavior of error\r
+// message printing.\r
+void RtApi :: error( RtAudioError::Type type )\r
+{\r
+ errorStream_.str(""); // clear the ostringstream\r
+\r
+ RtAudioErrorCallback errorCallback = (RtAudioErrorCallback) stream_.callbackInfo.errorCallback;\r
+ if ( errorCallback ) {\r
+ // abortStream() can generate new error messages. Ignore them. Just keep original one.\r
+\r
+ if ( firstErrorOccurred_ )\r
+ return;\r
+\r
+ firstErrorOccurred_ = true;\r
+ const std::string errorMessage = errorText_;\r
+\r
+ if ( type != RtAudioError::WARNING && stream_.state != STREAM_STOPPED) {\r
+ stream_.callbackInfo.isRunning = false; // exit from the thread\r
+ abortStream();\r
+ }\r
+\r
+ errorCallback( type, errorMessage );\r
+ firstErrorOccurred_ = false;\r
+ return;\r
+ }\r
+\r
+ if ( type == RtAudioError::WARNING && showWarnings_ == true )\r
+ std::cerr << '\n' << errorText_ << "\n\n";\r
+ else if ( type != RtAudioError::WARNING )\r
+ throw( RtAudioError( errorText_, type ) );\r
+}\r
+\r
+void RtApi :: verifyStream()\r
+{\r
+ if ( stream_.state == STREAM_CLOSED ) {\r
+ errorText_ = "RtApi:: a stream is not open!";\r
+ error( RtAudioError::INVALID_USE );\r
+ }\r
+}\r
+\r
+void RtApi :: clearStreamInfo()\r
+{\r
+ stream_.mode = UNINITIALIZED;\r
+ stream_.state = STREAM_CLOSED;\r
+ stream_.sampleRate = 0;\r
+ stream_.bufferSize = 0;\r
+ stream_.nBuffers = 0;\r
+ stream_.userFormat = 0;\r
+ stream_.userInterleaved = true;\r
+ stream_.streamTime = 0.0;\r
+ stream_.apiHandle = 0;\r
+ stream_.deviceBuffer = 0;\r
+ stream_.callbackInfo.callback = 0;\r
+ stream_.callbackInfo.userData = 0;\r
+ stream_.callbackInfo.isRunning = false;\r
+ stream_.callbackInfo.errorCallback = 0;\r
+ for ( int i=0; i<2; i++ ) {\r
+ stream_.device[i] = 11111;\r
+ stream_.doConvertBuffer[i] = false;\r
+ stream_.deviceInterleaved[i] = true;\r
+ stream_.doByteSwap[i] = false;\r
+ stream_.nUserChannels[i] = 0;\r
+ stream_.nDeviceChannels[i] = 0;\r
+ stream_.channelOffset[i] = 0;\r
+ stream_.deviceFormat[i] = 0;\r
+ stream_.latency[i] = 0;\r
+ stream_.userBuffer[i] = 0;\r
+ stream_.convertInfo[i].channels = 0;\r
+ stream_.convertInfo[i].inJump = 0;\r
+ stream_.convertInfo[i].outJump = 0;\r
+ stream_.convertInfo[i].inFormat = 0;\r
+ stream_.convertInfo[i].outFormat = 0;\r
+ stream_.convertInfo[i].inOffset.clear();\r
+ stream_.convertInfo[i].outOffset.clear();\r
+ }\r
+}\r
+\r
+unsigned int RtApi :: formatBytes( RtAudioFormat format )\r
+{\r
+ if ( format == RTAUDIO_SINT16 )\r
+ return 2;\r
+ else if ( format == RTAUDIO_SINT32 || format == RTAUDIO_FLOAT32 )\r
+ return 4;\r
+ else if ( format == RTAUDIO_FLOAT64 )\r
+ return 8;\r
+ else if ( format == RTAUDIO_SINT24 )\r
+ return 3;\r
+ else if ( format == RTAUDIO_SINT8 )\r
+ return 1;\r
+\r
+ errorText_ = "RtApi::formatBytes: undefined format.";\r
+ error( RtAudioError::WARNING );\r
+\r
+ return 0;\r
+}\r
+\r
+void RtApi :: setConvertInfo( StreamMode mode, unsigned int firstChannel )\r
+{\r
+ if ( mode == INPUT ) { // convert device to user buffer\r
+ stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1];\r
+ stream_.convertInfo[mode].outJump = stream_.nUserChannels[1];\r
+ stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1];\r
+ stream_.convertInfo[mode].outFormat = stream_.userFormat;\r
+ }\r
+ else { // convert user to device buffer\r
+ stream_.convertInfo[mode].inJump = stream_.nUserChannels[0];\r
+ stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0];\r
+ stream_.convertInfo[mode].inFormat = stream_.userFormat;\r
+ stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0];\r
+ }\r
+\r
+ if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump )\r
+ stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump;\r
+ else\r
+ stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump;\r
+\r
+ // Set up the interleave/deinterleave offsets.\r
+ if ( stream_.deviceInterleaved[mode] != stream_.userInterleaved ) {\r
+ if ( ( mode == OUTPUT && stream_.deviceInterleaved[mode] ) ||\r
+ ( mode == INPUT && stream_.userInterleaved ) ) {\r
+ for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
+ stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );\r
+ stream_.convertInfo[mode].outOffset.push_back( k );\r
+ stream_.convertInfo[mode].inJump = 1;\r
+ }\r
+ }\r
+ else {\r
+ for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
+ stream_.convertInfo[mode].inOffset.push_back( k );\r
+ stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );\r
+ stream_.convertInfo[mode].outJump = 1;\r
+ }\r
+ }\r
+ }\r
+ else { // no (de)interleaving\r
+ if ( stream_.userInterleaved ) {\r
+ for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
+ stream_.convertInfo[mode].inOffset.push_back( k );\r
+ stream_.convertInfo[mode].outOffset.push_back( k );\r
+ }\r
+ }\r
+ else {\r
+ for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {\r
+ stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );\r
+ stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );\r
+ stream_.convertInfo[mode].inJump = 1;\r
+ stream_.convertInfo[mode].outJump = 1;\r
+ }\r
+ }\r
+ }\r
+\r
+ // Add channel offset.\r
+ if ( firstChannel > 0 ) {\r
+ if ( stream_.deviceInterleaved[mode] ) {\r
+ if ( mode == OUTPUT ) {\r
+ for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
+ stream_.convertInfo[mode].outOffset[k] += firstChannel;\r
+ }\r
+ else {\r
+ for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
+ stream_.convertInfo[mode].inOffset[k] += firstChannel;\r
+ }\r
+ }\r
+ else {\r
+ if ( mode == OUTPUT ) {\r
+ for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
+ stream_.convertInfo[mode].outOffset[k] += ( firstChannel * stream_.bufferSize );\r
+ }\r
+ else {\r
+ for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )\r
+ stream_.convertInfo[mode].inOffset[k] += ( firstChannel * stream_.bufferSize );\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info )\r
+{\r
+ // This function does format conversion, input/output channel compensation, and\r
+ // data interleaving/deinterleaving. 24-bit integers are assumed to occupy\r
+ // the lower three bytes of a 32-bit integer.\r
+\r
+ // Clear our device buffer when in/out duplex device channels are different\r
+ if ( outBuffer == stream_.deviceBuffer && stream_.mode == DUPLEX &&\r
+ ( stream_.nDeviceChannels[0] < stream_.nDeviceChannels[1] ) )\r
+ memset( outBuffer, 0, stream_.bufferSize * info.outJump * formatBytes( info.outFormat ) );\r
+\r
+ int j;\r
+ if (info.outFormat == RTAUDIO_FLOAT64) {\r
+ Float64 scale;\r
+ Float64 *out = (Float64 *)outBuffer;\r
+\r
+ if (info.inFormat == RTAUDIO_SINT8) {\r
+ signed char *in = (signed char *)inBuffer;\r
+ scale = 1.0 / 127.5;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
+ out[info.outOffset[j]] += 0.5;\r
+ out[info.outOffset[j]] *= scale;\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_SINT16) {\r
+ Int16 *in = (Int16 *)inBuffer;\r
+ scale = 1.0 / 32767.5;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
+ out[info.outOffset[j]] += 0.5;\r
+ out[info.outOffset[j]] *= scale;\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_SINT24) {\r
+ Int24 *in = (Int24 *)inBuffer;\r
+ scale = 1.0 / 8388607.5;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Float64) (in[info.inOffset[j]].asInt());\r
+ out[info.outOffset[j]] += 0.5;\r
+ out[info.outOffset[j]] *= scale;\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_SINT32) {\r
+ Int32 *in = (Int32 *)inBuffer;\r
+ scale = 1.0 / 2147483647.5;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
+ out[info.outOffset[j]] += 0.5;\r
+ out[info.outOffset[j]] *= scale;\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_FLOAT32) {\r
+ Float32 *in = (Float32 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_FLOAT64) {\r
+ // Channel compensation and/or (de)interleaving only.\r
+ Float64 *in = (Float64 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = in[info.inOffset[j]];\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ }\r
+ else if (info.outFormat == RTAUDIO_FLOAT32) {\r
+ Float32 scale;\r
+ Float32 *out = (Float32 *)outBuffer;\r
+\r
+ if (info.inFormat == RTAUDIO_SINT8) {\r
+ signed char *in = (signed char *)inBuffer;\r
+ scale = (Float32) ( 1.0 / 127.5 );\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
+ out[info.outOffset[j]] += 0.5;\r
+ out[info.outOffset[j]] *= scale;\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_SINT16) {\r
+ Int16 *in = (Int16 *)inBuffer;\r
+ scale = (Float32) ( 1.0 / 32767.5 );\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
+ out[info.outOffset[j]] += 0.5;\r
+ out[info.outOffset[j]] *= scale;\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_SINT24) {\r
+ Int24 *in = (Int24 *)inBuffer;\r
+ scale = (Float32) ( 1.0 / 8388607.5 );\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Float32) (in[info.inOffset[j]].asInt());\r
+ out[info.outOffset[j]] += 0.5;\r
+ out[info.outOffset[j]] *= scale;\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_SINT32) {\r
+ Int32 *in = (Int32 *)inBuffer;\r
+ scale = (Float32) ( 1.0 / 2147483647.5 );\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
+ out[info.outOffset[j]] += 0.5;\r
+ out[info.outOffset[j]] *= scale;\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_FLOAT32) {\r
+ // Channel compensation and/or (de)interleaving only.\r
+ Float32 *in = (Float32 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = in[info.inOffset[j]];\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_FLOAT64) {\r
+ Float64 *in = (Float64 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ }\r
+ else if (info.outFormat == RTAUDIO_SINT32) {\r
+ Int32 *out = (Int32 *)outBuffer;\r
+ if (info.inFormat == RTAUDIO_SINT8) {\r
+ signed char *in = (signed char *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];\r
+ out[info.outOffset[j]] <<= 24;\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_SINT16) {\r
+ Int16 *in = (Int16 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];\r
+ out[info.outOffset[j]] <<= 16;\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_SINT24) {\r
+ Int24 *in = (Int24 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Int32) in[info.inOffset[j]].asInt();\r
+ out[info.outOffset[j]] <<= 8;\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_SINT32) {\r
+ // Channel compensation and/or (de)interleaving only.\r
+ Int32 *in = (Int32 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = in[info.inOffset[j]];\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_FLOAT32) {\r
+ Float32 *in = (Float32 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_FLOAT64) {\r
+ Float64 *in = (Float64 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ }\r
+ else if (info.outFormat == RTAUDIO_SINT24) {\r
+ Int24 *out = (Int24 *)outBuffer;\r
+ if (info.inFormat == RTAUDIO_SINT8) {\r
+ signed char *in = (signed char *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] << 16);\r
+ //out[info.outOffset[j]] <<= 16;\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_SINT16) {\r
+ Int16 *in = (Int16 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] << 8);\r
+ //out[info.outOffset[j]] <<= 8;\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_SINT24) {\r
+ // Channel compensation and/or (de)interleaving only.\r
+ Int24 *in = (Int24 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = in[info.inOffset[j]];\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_SINT32) {\r
+ Int32 *in = (Int32 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] >> 8);\r
+ //out[info.outOffset[j]] >>= 8;\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_FLOAT32) {\r
+ Float32 *in = (Float32 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_FLOAT64) {\r
+ Float64 *in = (Float64 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ }\r
+ else if (info.outFormat == RTAUDIO_SINT16) {\r
+ Int16 *out = (Int16 *)outBuffer;\r
+ if (info.inFormat == RTAUDIO_SINT8) {\r
+ signed char *in = (signed char *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Int16) in[info.inOffset[j]];\r
+ out[info.outOffset[j]] <<= 8;\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_SINT16) {\r
+ // Channel compensation and/or (de)interleaving only.\r
+ Int16 *in = (Int16 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = in[info.inOffset[j]];\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_SINT24) {\r
+ Int24 *in = (Int24 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]].asInt() >> 8);\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_SINT32) {\r
+ Int32 *in = (Int32 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 16) & 0x0000ffff);\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_FLOAT32) {\r
+ Float32 *in = (Float32 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_FLOAT64) {\r
+ Float64 *in = (Float64 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ }\r
+ else if (info.outFormat == RTAUDIO_SINT8) {\r
+ signed char *out = (signed char *)outBuffer;\r
+ if (info.inFormat == RTAUDIO_SINT8) {\r
+ // Channel compensation and/or (de)interleaving only.\r
+ signed char *in = (signed char *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = in[info.inOffset[j]];\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ if (info.inFormat == RTAUDIO_SINT16) {\r
+ Int16 *in = (Int16 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 8) & 0x00ff);\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_SINT24) {\r
+ Int24 *in = (Int24 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]].asInt() >> 16);\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_SINT32) {\r
+ Int32 *in = (Int32 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 24) & 0x000000ff);\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_FLOAT32) {\r
+ Float32 *in = (Float32 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ else if (info.inFormat == RTAUDIO_FLOAT64) {\r
+ Float64 *in = (Float64 *)inBuffer;\r
+ for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
+ for (j=0; j<info.channels; j++) {\r
+ out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);\r
+ }\r
+ in += info.inJump;\r
+ out += info.outJump;\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+//static inline uint16_t bswap_16(uint16_t x) { return (x>>8) | (x<<8); }\r
+//static inline uint32_t bswap_32(uint32_t x) { return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); }\r
+//static inline uint64_t bswap_64(uint64_t x) { return (((unsigned long long)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); }\r
+\r
+void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )\r
+{\r
+ register char val;\r
+ register char *ptr;\r
+\r
+ ptr = buffer;\r
+ if ( format == RTAUDIO_SINT16 ) {\r
+ for ( unsigned int i=0; i<samples; i++ ) {\r
+ // Swap 1st and 2nd bytes.\r
+ val = *(ptr);\r
+ *(ptr) = *(ptr+1);\r
+ *(ptr+1) = val;\r
+\r
+ // Increment 2 bytes.\r
+ ptr += 2;\r
+ }\r
+ }\r
+ else if ( format == RTAUDIO_SINT32 ||\r
+ format == RTAUDIO_FLOAT32 ) {\r
+ for ( unsigned int i=0; i<samples; i++ ) {\r
+ // Swap 1st and 4th bytes.\r
+ val = *(ptr);\r
+ *(ptr) = *(ptr+3);\r
+ *(ptr+3) = val;\r
+\r
+ // Swap 2nd and 3rd bytes.\r
+ ptr += 1;\r
+ val = *(ptr);\r
+ *(ptr) = *(ptr+1);\r
+ *(ptr+1) = val;\r
+\r
+ // Increment 3 more bytes.\r
+ ptr += 3;\r
+ }\r
+ }\r
+ else if ( format == RTAUDIO_SINT24 ) {\r
+ for ( unsigned int i=0; i<samples; i++ ) {\r
+ // Swap 1st and 3rd bytes.\r
+ val = *(ptr);\r
+ *(ptr) = *(ptr+2);\r
+ *(ptr+2) = val;\r
+\r
+ // Increment 2 more bytes.\r
+ ptr += 2;\r
+ }\r
+ }\r
+ else if ( format == RTAUDIO_FLOAT64 ) {\r
+ for ( unsigned int i=0; i<samples; i++ ) {\r
+ // Swap 1st and 8th bytes\r
+ val = *(ptr);\r
+ *(ptr) = *(ptr+7);\r
+ *(ptr+7) = val;\r
+\r
+ // Swap 2nd and 7th bytes\r
+ ptr += 1;\r
+ val = *(ptr);\r
+ *(ptr) = *(ptr+5);\r
+ *(ptr+5) = val;\r
+\r
+ // Swap 3rd and 6th bytes\r
+ ptr += 1;\r
+ val = *(ptr);\r
+ *(ptr) = *(ptr+3);\r
+ *(ptr+3) = val;\r
+\r
+ // Swap 4th and 5th bytes\r
+ ptr += 1;\r
+ val = *(ptr);\r
+ *(ptr) = *(ptr+1);\r
+ *(ptr+1) = val;\r
+\r
+ // Increment 5 more bytes.\r
+ ptr += 5;\r
+ }\r
+ }\r
+}\r
+\r
+ // Indentation settings for Vim and Emacs\r
+ //\r
+ // Local Variables:\r
+ // c-basic-offset: 2\r
+ // indent-tabs-mode: nil\r
+ // End:\r
+ //\r
+ // vim: et sts=2 sw=2\r
+\r
--- /dev/null
+/************************************************************************/
+/*! \class RtAudio
+ \brief Realtime audio i/o C++ classes.
+
+ RtAudio provides a common API (Application Programming Interface)
+ for realtime audio input/output across Linux (native ALSA, Jack,
+ and OSS), Macintosh OS X (CoreAudio and Jack), and Windows
+ (DirectSound, ASIO and WASAPI) operating systems.
+
+ RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/
+
+ RtAudio: realtime audio i/o C++ classes
+ Copyright (c) 2001-2014 Gary P. Scavone
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ Any person wishing to distribute modifications to the Software is
+ asked to send the modifications to the original developer so that
+ they can be incorporated into the canonical version. This is,
+ however, not a binding provision of this license.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+/************************************************************************/
+
+/*!
+ \file RtAudio.h
+ */
+
+#ifndef __RTAUDIO_H
+#define __RTAUDIO_H
+
+#define RTAUDIO_VERSION "4.1.1"
+
+#include <string>
+#include <vector>
+
+/* --- Monocasual hack ---------------------------------------------- */
+#if defined(__linux__)
+#include <jack/jack.h>
+#endif
+/* ------------------------------------------------------------------ */
+
+#include <exception>
+#include <iostream>
+
+/*! \typedef typedef unsigned long RtAudioFormat;
+ \brief RtAudio data format type.
+
+ Support for signed integers and floats. Audio data fed to/from an
+ RtAudio stream is assumed to ALWAYS be in host byte order. The
+ internal routines will automatically take care of any necessary
+ byte-swapping between the host format and the soundcard. Thus,
+ endian-ness is not a concern in the following format definitions.
+
+ - \e RTAUDIO_SINT8: 8-bit signed integer.
+ - \e RTAUDIO_SINT16: 16-bit signed integer.
+ - \e RTAUDIO_SINT24: 24-bit signed integer.
+ - \e RTAUDIO_SINT32: 32-bit signed integer.
+ - \e RTAUDIO_FLOAT32: Normalized between plus/minus 1.0.
+ - \e RTAUDIO_FLOAT64: Normalized between plus/minus 1.0.
+*/
+typedef unsigned long RtAudioFormat;
+static const RtAudioFormat RTAUDIO_SINT8 = 0x1; // 8-bit signed integer.
+static const RtAudioFormat RTAUDIO_SINT16 = 0x2; // 16-bit signed integer.
+static const RtAudioFormat RTAUDIO_SINT24 = 0x4; // 24-bit signed integer.
+static const RtAudioFormat RTAUDIO_SINT32 = 0x8; // 32-bit signed integer.
+static const RtAudioFormat RTAUDIO_FLOAT32 = 0x10; // Normalized between plus/minus 1.0.
+static const RtAudioFormat RTAUDIO_FLOAT64 = 0x20; // Normalized between plus/minus 1.0.
+
+/*! \typedef typedef unsigned long RtAudioStreamFlags;
+ \brief RtAudio stream option flags.
+
+ The following flags can be OR'ed together to allow a client to
+ make changes to the default stream behavior:
+
+ - \e RTAUDIO_NONINTERLEAVED: Use non-interleaved buffers (default = interleaved).
+ - \e RTAUDIO_MINIMIZE_LATENCY: Attempt to set stream parameters for lowest possible latency.
+ - \e RTAUDIO_HOG_DEVICE: Attempt grab device for exclusive use.
+ - \e RTAUDIO_ALSA_USE_DEFAULT: Use the "default" PCM device (ALSA only).
+
+ By default, RtAudio streams pass and receive audio data from the
+ client in an interleaved format. By passing the
+ RTAUDIO_NONINTERLEAVED flag to the openStream() function, audio
+ data will instead be presented in non-interleaved buffers. In
+ this case, each buffer argument in the RtAudioCallback function
+ will point to a single array of data, with \c nFrames samples for
+ each channel concatenated back-to-back. For example, the first
+ sample of data for the second channel would be located at index \c
+ nFrames (assuming the \c buffer pointer was recast to the correct
+ data type for the stream).
+
+ Certain audio APIs offer a number of parameters that influence the
+ I/O latency of a stream. By default, RtAudio will attempt to set
+ these parameters internally for robust (glitch-free) performance
+ (though some APIs, like Windows Direct Sound, make this difficult).
+ By passing the RTAUDIO_MINIMIZE_LATENCY flag to the openStream()
+ function, internal stream settings will be influenced in an attempt
+ to minimize stream latency, though possibly at the expense of stream
+ performance.
+
+ If the RTAUDIO_HOG_DEVICE flag is set, RtAudio will attempt to
+ open the input and/or output stream device(s) for exclusive use.
+ Note that this is not possible with all supported audio APIs.
+
+ If the RTAUDIO_SCHEDULE_REALTIME flag is set, RtAudio will attempt
+ to select realtime scheduling (round-robin) for the callback thread.
+
+ If the RTAUDIO_ALSA_USE_DEFAULT flag is set, RtAudio will attempt to
+ open the "default" PCM device when using the ALSA API. Note that this
+ will override any specified input or output device id.
+*/
+typedef unsigned int RtAudioStreamFlags;
+static const RtAudioStreamFlags RTAUDIO_NONINTERLEAVED = 0x1; // Use non-interleaved buffers (default = interleaved).
+static const RtAudioStreamFlags RTAUDIO_MINIMIZE_LATENCY = 0x2; // Attempt to set stream parameters for lowest possible latency.
+static const RtAudioStreamFlags RTAUDIO_HOG_DEVICE = 0x4; // Attempt grab device and prevent use by others.
+static const RtAudioStreamFlags RTAUDIO_SCHEDULE_REALTIME = 0x8; // Try to select realtime scheduling for callback thread.
+static const RtAudioStreamFlags RTAUDIO_ALSA_USE_DEFAULT = 0x10; // Use the "default" PCM device (ALSA only).
+
+/*! \typedef typedef unsigned long RtAudioStreamStatus;
+ \brief RtAudio stream status (over- or underflow) flags.
+
+ Notification of a stream over- or underflow is indicated by a
+ non-zero stream \c status argument in the RtAudioCallback function.
+ The stream status can be one of the following two options,
+ depending on whether the stream is open for output and/or input:
+
+ - \e RTAUDIO_INPUT_OVERFLOW: Input data was discarded because of an overflow condition at the driver.
+ - \e RTAUDIO_OUTPUT_UNDERFLOW: The output buffer ran low, likely producing a break in the output sound.
+*/
+typedef unsigned int RtAudioStreamStatus;
+static const RtAudioStreamStatus RTAUDIO_INPUT_OVERFLOW = 0x1; // Input data was discarded because of an overflow condition at the driver.
+static const RtAudioStreamStatus RTAUDIO_OUTPUT_UNDERFLOW = 0x2; // The output buffer ran low, likely causing a gap in the output sound.
+
+//! RtAudio callback function prototype.
+/*!
+ All RtAudio clients must create a function of type RtAudioCallback
+ to read and/or write data from/to the audio stream. When the
+ underlying audio system is ready for new input or output data, this
+ function will be invoked.
+
+ \param outputBuffer For output (or duplex) streams, the client
+ should write \c nFrames of audio sample frames into this
+ buffer. This argument should be recast to the datatype
+ specified when the stream was opened. For input-only
+ streams, this argument will be NULL.
+
+ \param inputBuffer For input (or duplex) streams, this buffer will
+ hold \c nFrames of input audio sample frames. This
+ argument should be recast to the datatype specified when the
+ stream was opened. For output-only streams, this argument
+ will be NULL.
+
+ \param nFrames The number of sample frames of input or output
+ data in the buffers. The actual buffer size in bytes is
+ dependent on the data type and number of channels in use.
+
+ \param streamTime The number of seconds that have elapsed since the
+ stream was started.
+
+ \param status If non-zero, this argument indicates a data overflow
+ or underflow condition for the stream. The particular
+ condition can be determined by comparison with the
+ RtAudioStreamStatus flags.
+
+ \param userData A pointer to optional data provided by the client
+ when opening the stream (default = NULL).
+
+ To continue normal stream operation, the RtAudioCallback function
+ should return a value of zero. To stop the stream and drain the
+ output buffer, the function should return a value of one. To abort
+ the stream immediately, the client should return a value of two.
+ */
+typedef int (*RtAudioCallback)( void *outputBuffer, void *inputBuffer,
+ unsigned int nFrames,
+ double streamTime,
+ RtAudioStreamStatus status,
+ void *userData );
+
+/************************************************************************/
+/*! \class RtAudioError
+ \brief Exception handling class for RtAudio.
+
+ The RtAudioError class is quite simple but it does allow errors to be
+ "caught" by RtAudioError::Type. See the RtAudio documentation to know
+ which methods can throw an RtAudioError.
+*/
+/************************************************************************/
+
+class RtAudioError : public std::exception
+{
+ public:
+ //! Defined RtAudioError types.
+ enum Type {
+ WARNING, /*!< A non-critical error. */
+ DEBUG_WARNING, /*!< A non-critical error which might be useful for debugging. */
+ UNSPECIFIED, /*!< The default, unspecified error type. */
+ NO_DEVICES_FOUND, /*!< No devices found on system. */
+ INVALID_DEVICE, /*!< An invalid device ID was specified. */
+ MEMORY_ERROR, /*!< An error occured during memory allocation. */
+ INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */
+ INVALID_USE, /*!< The function was called incorrectly. */
+ DRIVER_ERROR, /*!< A system driver error occured. */
+ SYSTEM_ERROR, /*!< A system error occured. */
+ THREAD_ERROR /*!< A thread error occured. */
+ };
+
+ //! The constructor.
+ RtAudioError( const std::string& message, Type type = RtAudioError::UNSPECIFIED ) throw() : message_(message), type_(type) {}
+
+ //! The destructor.
+ virtual ~RtAudioError( void ) throw() {}
+
+ //! Prints thrown error message to stderr.
+ virtual void printMessage( void ) const throw() { std::cerr << '\n' << message_ << "\n\n"; }
+
+ //! Returns the thrown error message type.
+ virtual const Type& getType(void) const throw() { return type_; }
+
+ //! Returns the thrown error message string.
+ virtual const std::string& getMessage(void) const throw() { return message_; }
+
+ //! Returns the thrown error message as a c-style string.
+ virtual const char* what( void ) const throw() { return message_.c_str(); }
+
+ protected:
+ std::string message_;
+ Type type_;
+};
+
+//! RtAudio error callback function prototype.
+/*!
+ \param type Type of error.
+ \param errorText Error description.
+ */
+typedef void (*RtAudioErrorCallback)( RtAudioError::Type type, const std::string &errorText );
+
+// **************************************************************** //
+//
+// RtAudio class declaration.
+//
+// RtAudio is a "controller" used to select an available audio i/o
+// interface. It presents a common API for the user to call but all
+// functionality is implemented by the class RtApi and its
+// subclasses. RtAudio creates an instance of an RtApi subclass
+// based on the user's API choice. If no choice is made, RtAudio
+// attempts to make a "logical" API selection.
+//
+// **************************************************************** //
+
+class RtApi;
+
+class RtAudio
+{
+ public:
+
+ //! Audio API specifier arguments.
+ enum Api {
+ UNSPECIFIED, /*!< Search for a working compiled API. */
+ LINUX_ALSA, /*!< The Advanced Linux Sound Architecture API. */
+ LINUX_PULSE, /*!< The Linux PulseAudio API. */
+ LINUX_OSS, /*!< The Linux Open Sound System API. */
+ UNIX_JACK, /*!< The Jack Low-Latency Audio Server API. */
+ MACOSX_CORE, /*!< Macintosh OS-X Core Audio API. */
+ WINDOWS_WASAPI, /*!< The Microsoft WASAPI API. */
+ WINDOWS_ASIO, /*!< The Steinberg Audio Stream I/O API. */
+ WINDOWS_DS, /*!< The Microsoft Direct Sound API. */
+ RTAUDIO_DUMMY /*!< A compilable but non-functional API. */
+ };
+
+ //! The public device information structure for returning queried values.
+ struct DeviceInfo {
+ bool probed; /*!< true if the device capabilities were successfully probed. */
+ std::string name; /*!< Character string device identifier. */
+ unsigned int outputChannels; /*!< Maximum output channels supported by device. */
+ unsigned int inputChannels; /*!< Maximum input channels supported by device. */
+ unsigned int duplexChannels; /*!< Maximum simultaneous input/output channels supported by device. */
+ bool isDefaultOutput; /*!< true if this is the default output device. */
+ bool isDefaultInput; /*!< true if this is the default input device. */
+ std::vector<unsigned int> sampleRates; /*!< Supported sample rates (queried from list of standard rates). */
+ RtAudioFormat nativeFormats; /*!< Bit mask of supported data formats. */
+
+ // Default constructor.
+ DeviceInfo()
+ :probed(false), outputChannels(0), inputChannels(0), duplexChannels(0),
+ isDefaultOutput(false), isDefaultInput(false), nativeFormats(0) {}
+ };
+
+ //! The structure for specifying input or ouput stream parameters.
+ struct StreamParameters {
+ unsigned int deviceId; /*!< Device index (0 to getDeviceCount() - 1). */
+ unsigned int nChannels; /*!< Number of channels. */
+ unsigned int firstChannel; /*!< First channel index on device (default = 0). */
+
+ // Default constructor.
+ StreamParameters()
+ : deviceId(0), nChannels(0), firstChannel(0) {}
+ };
+
+ //! The structure for specifying stream options.
+ /*!
+ The following flags can be OR'ed together to allow a client to
+ make changes to the default stream behavior:
+
+ - \e RTAUDIO_NONINTERLEAVED: Use non-interleaved buffers (default = interleaved).
+ - \e RTAUDIO_MINIMIZE_LATENCY: Attempt to set stream parameters for lowest possible latency.
+ - \e RTAUDIO_HOG_DEVICE: Attempt grab device for exclusive use.
+ - \e RTAUDIO_SCHEDULE_REALTIME: Attempt to select realtime scheduling for callback thread.
+ - \e RTAUDIO_ALSA_USE_DEFAULT: Use the "default" PCM device (ALSA only).
+
+ By default, RtAudio streams pass and receive audio data from the
+ client in an interleaved format. By passing the
+ RTAUDIO_NONINTERLEAVED flag to the openStream() function, audio
+ data will instead be presented in non-interleaved buffers. In
+ this case, each buffer argument in the RtAudioCallback function
+ will point to a single array of data, with \c nFrames samples for
+ each channel concatenated back-to-back. For example, the first
+ sample of data for the second channel would be located at index \c
+ nFrames (assuming the \c buffer pointer was recast to the correct
+ data type for the stream).
+
+ Certain audio APIs offer a number of parameters that influence the
+ I/O latency of a stream. By default, RtAudio will attempt to set
+ these parameters internally for robust (glitch-free) performance
+ (though some APIs, like Windows Direct Sound, make this difficult).
+ By passing the RTAUDIO_MINIMIZE_LATENCY flag to the openStream()
+ function, internal stream settings will be influenced in an attempt
+ to minimize stream latency, though possibly at the expense of stream
+ performance.
+
+ If the RTAUDIO_HOG_DEVICE flag is set, RtAudio will attempt to
+ open the input and/or output stream device(s) for exclusive use.
+ Note that this is not possible with all supported audio APIs.
+
+ If the RTAUDIO_SCHEDULE_REALTIME flag is set, RtAudio will attempt
+ to select realtime scheduling (round-robin) for the callback thread.
+ The \c priority parameter will only be used if the RTAUDIO_SCHEDULE_REALTIME
+ flag is set. It defines the thread's realtime priority.
+
+ If the RTAUDIO_ALSA_USE_DEFAULT flag is set, RtAudio will attempt to
+ open the "default" PCM device when using the ALSA API. Note that this
+ will override any specified input or output device id.
+
+ The \c numberOfBuffers parameter can be used to control stream
+ latency in the Windows DirectSound, Linux OSS, and Linux Alsa APIs
+ only. A value of two is usually the smallest allowed. Larger
+ numbers can potentially result in more robust stream performance,
+ though likely at the cost of stream latency. The value set by the
+ user is replaced during execution of the RtAudio::openStream()
+ function by the value actually used by the system.
+
+ The \c streamName parameter can be used to set the client name
+ when using the Jack API. By default, the client name is set to
+ RtApiJack. However, if you wish to create multiple instances of
+ RtAudio with Jack, each instance must have a unique client name.
+ */
+ struct StreamOptions {
+ RtAudioStreamFlags flags; /*!< A bit-mask of stream flags (RTAUDIO_NONINTERLEAVED, RTAUDIO_MINIMIZE_LATENCY, RTAUDIO_HOG_DEVICE, RTAUDIO_ALSA_USE_DEFAULT). */
+ unsigned int numberOfBuffers; /*!< Number of stream buffers. */
+ std::string streamName; /*!< A stream name (currently used only in Jack). */
+ int priority; /*!< Scheduling priority of callback thread (only used with flag RTAUDIO_SCHEDULE_REALTIME). */
+
+ // Default constructor.
+ StreamOptions()
+ : flags(0), numberOfBuffers(0), priority(0) {}
+ };
+
+ //! A static function to determine the current RtAudio version.
+ static std::string getVersion( void ) throw();
+
+ //! A static function to determine the available compiled audio APIs.
+ /*!
+ The values returned in the std::vector can be compared against
+ the enumerated list values. Note that there can be more than one
+ API compiled for certain operating systems.
+ */
+ static void getCompiledApi( std::vector<RtAudio::Api> &apis ) throw();
+
+ //! The class constructor.
+ /*!
+ The constructor performs minor initialization tasks. An exception
+ can be thrown if no API support is compiled.
+
+ If no API argument is specified and multiple API support has been
+ compiled, the default order of use is JACK, ALSA, OSS (Linux
+ systems) and ASIO, DS (Windows systems).
+ */
+ RtAudio( RtAudio::Api api=UNSPECIFIED );
+
+ //! The destructor.
+ /*!
+ If a stream is running or open, it will be stopped and closed
+ automatically.
+ */
+ ~RtAudio() throw();
+
+ //! Returns the audio API specifier for the current instance of RtAudio.
+ RtAudio::Api getCurrentApi( void ) throw();
+
+ //! A public function that queries for the number of audio devices available.
+ /*!
+ This function performs a system query of available devices each time it
+ is called, thus supporting devices connected \e after instantiation. If
+ a system error occurs during processing, a warning will be issued.
+ */
+ unsigned int getDeviceCount( void ) throw();
+
+ //! Return an RtAudio::DeviceInfo structure for a specified device number.
+ /*!
+
+ Any device integer between 0 and getDeviceCount() - 1 is valid.
+ If an invalid argument is provided, an RtAudioError (type = INVALID_USE)
+ will be thrown. If a device is busy or otherwise unavailable, the
+ structure member "probed" will have a value of "false" and all
+ other members are undefined. If the specified device is the
+ current default input or output device, the corresponding
+ "isDefault" member will have a value of "true".
+ */
+ RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
+
+ //! A function that returns the index of the default output device.
+ /*!
+ If the underlying audio API does not provide a "default
+ device", or if no devices are available, the return value will be
+ 0. Note that this is a valid device identifier and it is the
+ client's responsibility to verify that a device is available
+ before attempting to open a stream.
+ */
+ unsigned int getDefaultOutputDevice( void ) throw();
+
+ //! A function that returns the index of the default input device.
+ /*!
+ If the underlying audio API does not provide a "default
+ device", or if no devices are available, the return value will be
+ 0. Note that this is a valid device identifier and it is the
+ client's responsibility to verify that a device is available
+ before attempting to open a stream.
+ */
+ unsigned int getDefaultInputDevice( void ) throw();
+
+ //! A public function for opening a stream with the specified parameters.
+ /*!
+ An RtAudioError (type = SYSTEM_ERROR) is thrown if a stream cannot be
+ opened with the specified parameters or an error occurs during
+ processing. An RtAudioError (type = INVALID_USE) is thrown if any
+ invalid device ID or channel number parameters are specified.
+
+ \param outputParameters Specifies output stream parameters to use
+ when opening a stream, including a device ID, number of channels,
+ and starting channel number. For input-only streams, this
+ argument should be NULL. The device ID is an index value between
+ 0 and getDeviceCount() - 1.
+ \param inputParameters Specifies input stream parameters to use
+ when opening a stream, including a device ID, number of channels,
+ and starting channel number. For output-only streams, this
+ argument should be NULL. The device ID is an index value between
+ 0 and getDeviceCount() - 1.
+ \param format An RtAudioFormat specifying the desired sample data format.
+ \param sampleRate The desired sample rate (sample frames per second).
+ \param *bufferFrames A pointer to a value indicating the desired
+ internal buffer size in sample frames. The actual value
+ used by the device is returned via the same pointer. A
+ value of zero can be specified, in which case the lowest
+ allowable value is determined.
+ \param callback A client-defined function that will be invoked
+ when input data is available and/or output data is needed.
+ \param userData An optional pointer to data that can be accessed
+ from within the callback function.
+ \param options An optional pointer to a structure containing various
+ global stream options, including a list of OR'ed RtAudioStreamFlags
+ and a suggested number of stream buffers that can be used to
+ control stream latency. More buffers typically result in more
+ robust performance, though at a cost of greater latency. If a
+ value of zero is specified, a system-specific median value is
+ chosen. If the RTAUDIO_MINIMIZE_LATENCY flag bit is set, the
+ lowest allowable value is used. The actual value used is
+ returned via the structure argument. The parameter is API dependent.
+ \param errorCallback A client-defined function that will be invoked
+ when an error has occured.
+ */
+ void openStream( RtAudio::StreamParameters *outputParameters,
+ RtAudio::StreamParameters *inputParameters,
+ RtAudioFormat format, unsigned int sampleRate,
+ unsigned int *bufferFrames, RtAudioCallback callback,
+ void *userData = NULL, RtAudio::StreamOptions *options = NULL, RtAudioErrorCallback errorCallback = NULL );
+
+ //! A function that closes a stream and frees any associated stream memory.
+ /*!
+ If a stream is not open, this function issues a warning and
+ returns (no exception is thrown).
+ */
+ void closeStream( void ) throw();
+
+ //! A function that starts a stream.
+ /*!
+ An RtAudioError (type = SYSTEM_ERROR) is thrown if an error occurs
+ during processing. An RtAudioError (type = INVALID_USE) is thrown if a
+ stream is not open. A warning is issued if the stream is already
+ running.
+ */
+ void startStream( void );
+
+ //! Stop a stream, allowing any samples remaining in the output queue to be played.
+ /*!
+ An RtAudioError (type = SYSTEM_ERROR) is thrown if an error occurs
+ during processing. An RtAudioError (type = INVALID_USE) is thrown if a
+ stream is not open. A warning is issued if the stream is already
+ stopped.
+ */
+ void stopStream( void );
+
+ //! Stop a stream, discarding any samples remaining in the input/output queue.
+ /*!
+ An RtAudioError (type = SYSTEM_ERROR) is thrown if an error occurs
+ during processing. An RtAudioError (type = INVALID_USE) is thrown if a
+ stream is not open. A warning is issued if the stream is already
+ stopped.
+ */
+ void abortStream( void );
+
+ //! Returns true if a stream is open and false if not.
+ bool isStreamOpen( void ) const throw();
+
+ //! Returns true if the stream is running and false if it is stopped or not open.
+ bool isStreamRunning( void ) const throw();
+
+ //! Returns the number of elapsed seconds since the stream was started.
+ /*!
+ If a stream is not open, an RtAudioError (type = INVALID_USE) will be thrown.
+ */
+ double getStreamTime( void );
+
+ //! Set the stream time to a time in seconds greater than or equal to 0.0.
+ /*!
+ If a stream is not open, an RtAudioError (type = INVALID_USE) will be thrown.
+ */
+ void setStreamTime( double time );
+
+ //! Returns the internal stream latency in sample frames.
+ /*!
+ The stream latency refers to delay in audio input and/or output
+ caused by internal buffering by the audio system and/or hardware.
+ For duplex streams, the returned value will represent the sum of
+ the input and output latencies. If a stream is not open, an
+ RtAudioError (type = INVALID_USE) will be thrown. If the API does not
+ report latency, the return value will be zero.
+ */
+ long getStreamLatency( void );
+
+ //! Returns actual sample rate in use by the stream.
+ /*!
+ On some systems, the sample rate used may be slightly different
+ than that specified in the stream parameters. If a stream is not
+ open, an RtAudioError (type = INVALID_USE) will be thrown.
+ */
+ unsigned int getStreamSampleRate( void );
+
+ //! Specify whether warning messages should be printed to stderr.
+ void showWarnings( bool value = true ) throw();
+
+/* --- Monocasual hack ---------------------------------------------- */
+ //protected:
+/* ------------------------------------------------------------------ */
+
+ void openRtApi( RtAudio::Api api );
+ RtApi *rtapi_;
+};
+
+// Operating system dependent thread functionality.
+#if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) || defined(__WINDOWS_WASAPI__)
+
+ #ifndef NOMINMAX
+ #define NOMINMAX
+ #endif
+ #include <windows.h>
+ #include <process.h>
+
+ typedef uintptr_t ThreadHandle;
+ typedef CRITICAL_SECTION StreamMutex;
+
+#elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)
+ // Using pthread library for various flavors of unix.
+ #include <pthread.h>
+
+ typedef pthread_t ThreadHandle;
+ typedef pthread_mutex_t StreamMutex;
+
+#else // Setup for "dummy" behavior
+
+ #define __RTAUDIO_DUMMY__
+ typedef int ThreadHandle;
+ typedef int StreamMutex;
+
+#endif
+
+// This global structure type is used to pass callback information
+// between the private RtAudio stream structure and global callback
+// handling functions.
+struct CallbackInfo {
+ void *object; // Used as a "this" pointer.
+ ThreadHandle thread;
+ void *callback;
+ void *userData;
+ void *errorCallback;
+ void *apiInfo; // void pointer for API specific callback information
+ bool isRunning;
+ bool doRealtime;
+ int priority;
+
+ // Default constructor.
+ CallbackInfo()
+ :object(0), callback(0), userData(0), errorCallback(0), apiInfo(0), isRunning(false), doRealtime(false) {}
+};
+
+// **************************************************************** //
+//
+// RtApi class declaration.
+//
+// Subclasses of RtApi contain all API- and OS-specific code necessary
+// to fully implement the RtAudio API.
+//
+// Note that RtApi is an abstract base class and cannot be
+// explicitly instantiated. The class RtAudio will create an
+// instance of an RtApi subclass (RtApiOss, RtApiAlsa,
+// RtApiJack, RtApiCore, RtApiDs, or RtApiAsio).
+//
+// **************************************************************** //
+
+#pragma pack(push, 1)
+class S24 {
+
+ protected:
+ unsigned char c3[3];
+
+ public:
+ S24() {}
+
+ S24& operator = ( const int& i ) {
+ c3[0] = (i & 0x000000ff);
+ c3[1] = (i & 0x0000ff00) >> 8;
+ c3[2] = (i & 0x00ff0000) >> 16;
+ return *this;
+ }
+
+ S24( const S24& v ) { *this = v; }
+ S24( const double& d ) { *this = (int) d; }
+ S24( const float& f ) { *this = (int) f; }
+ S24( const signed short& s ) { *this = (int) s; }
+ S24( const char& c ) { *this = (int) c; }
+
+ int asInt() {
+ int i = c3[0] | (c3[1] << 8) | (c3[2] << 16);
+ if (i & 0x800000) i |= ~0xffffff;
+ return i;
+ }
+};
+#pragma pack(pop)
+
+#if defined( HAVE_GETTIMEOFDAY )
+ #include <sys/time.h>
+#endif
+
+#include <sstream>
+
+class RtApi
+{
+public:
+
+/* --- Monocasual hack ---------------------------------------------- */
+#ifdef __linux__
+ void *__HACK__getJackClient();
+#endif
+/* ------------------------------------------------------------------ */
+
+ RtApi();
+ virtual ~RtApi();
+ virtual RtAudio::Api getCurrentApi( void ) = 0;
+ virtual unsigned int getDeviceCount( void ) = 0;
+ virtual RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) = 0;
+ virtual unsigned int getDefaultInputDevice( void );
+ virtual unsigned int getDefaultOutputDevice( void );
+ void openStream( RtAudio::StreamParameters *outputParameters,
+ RtAudio::StreamParameters *inputParameters,
+ RtAudioFormat format, unsigned int sampleRate,
+ unsigned int *bufferFrames, RtAudioCallback callback,
+ void *userData, RtAudio::StreamOptions *options,
+ RtAudioErrorCallback errorCallback );
+ virtual void closeStream( void );
+ virtual void startStream( void ) = 0;
+ virtual void stopStream( void ) = 0;
+ virtual void abortStream( void ) = 0;
+ long getStreamLatency( void );
+ unsigned int getStreamSampleRate( void );
+ virtual double getStreamTime( void );
+ virtual void setStreamTime( double time );
+ bool isStreamOpen( void ) const { return stream_.state != STREAM_CLOSED; }
+ bool isStreamRunning( void ) const { return stream_.state == STREAM_RUNNING; }
+ void showWarnings( bool value ) { showWarnings_ = value; }
+
+
+protected:
+
+ static const unsigned int MAX_SAMPLE_RATES;
+ static const unsigned int SAMPLE_RATES[];
+
+ enum { FAILURE, SUCCESS };
+
+ enum StreamState {
+ STREAM_STOPPED,
+ STREAM_STOPPING,
+ STREAM_RUNNING,
+ STREAM_CLOSED = -50
+ };
+
+ enum StreamMode {
+ OUTPUT,
+ INPUT,
+ DUPLEX,
+ UNINITIALIZED = -75
+ };
+
+ // A protected structure used for buffer conversion.
+ struct ConvertInfo {
+ int channels;
+ int inJump, outJump;
+ RtAudioFormat inFormat, outFormat;
+ std::vector<int> inOffset;
+ std::vector<int> outOffset;
+ };
+
+ // A protected structure for audio streams.
+ struct RtApiStream {
+ unsigned int device[2]; // Playback and record, respectively.
+ void *apiHandle; // void pointer for API specific stream handle information
+ StreamMode mode; // OUTPUT, INPUT, or DUPLEX.
+ StreamState state; // STOPPED, RUNNING, or CLOSED
+ char *userBuffer[2]; // Playback and record, respectively.
+ char *deviceBuffer;
+ bool doConvertBuffer[2]; // Playback and record, respectively.
+ bool userInterleaved;
+ bool deviceInterleaved[2]; // Playback and record, respectively.
+ bool doByteSwap[2]; // Playback and record, respectively.
+ unsigned int sampleRate;
+ unsigned int bufferSize;
+ unsigned int nBuffers;
+ unsigned int nUserChannels[2]; // Playback and record, respectively.
+ unsigned int nDeviceChannels[2]; // Playback and record channels, respectively.
+ unsigned int channelOffset[2]; // Playback and record, respectively.
+ unsigned long latency[2]; // Playback and record, respectively.
+ RtAudioFormat userFormat;
+ RtAudioFormat deviceFormat[2]; // Playback and record, respectively.
+ StreamMutex mutex;
+ CallbackInfo callbackInfo;
+ ConvertInfo convertInfo[2];
+ double streamTime; // Number of elapsed seconds since the stream started.
+
+#if defined(HAVE_GETTIMEOFDAY)
+ struct timeval lastTickTimestamp;
+#endif
+
+ RtApiStream()
+ :apiHandle(0), deviceBuffer(0) { device[0] = 11111; device[1] = 11111; }
+ };
+
+ typedef S24 Int24;
+ typedef signed short Int16;
+ typedef signed int Int32;
+ typedef float Float32;
+ typedef double Float64;
+
+ std::ostringstream errorStream_;
+ std::string errorText_;
+ bool showWarnings_;
+ RtApiStream stream_;
+ bool firstErrorOccurred_;
+
+ /*!
+ Protected, api-specific method that attempts to open a device
+ with the given parameters. This function MUST be implemented by
+ all subclasses. If an error is encountered during the probe, a
+ "warning" message is reported and FAILURE is returned. A
+ successful probe is indicated by a return value of SUCCESS.
+ */
+ virtual bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+ unsigned int firstChannel, unsigned int sampleRate,
+ RtAudioFormat format, unsigned int *bufferSize,
+ RtAudio::StreamOptions *options );
+
+ //! A protected function used to increment the stream time.
+ void tickStreamTime( void );
+
+ //! Protected common method to clear an RtApiStream structure.
+ void clearStreamInfo();
+
+ /*!
+ Protected common method that throws an RtAudioError (type =
+ INVALID_USE) if a stream is not open.
+ */
+ void verifyStream( void );
+
+ //! Protected common error method to allow global control over error handling.
+ void error( RtAudioError::Type type );
+
+ /*!
+ Protected method used to perform format, channel number, and/or interleaving
+ conversions between the user and device buffers.
+ */
+ void convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info );
+
+ //! Protected common method used to perform byte-swapping on buffers.
+ void byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format );
+
+ //! Protected common method that returns the number of bytes for a given format.
+ unsigned int formatBytes( RtAudioFormat format );
+
+ //! Protected common method that sets up the parameters for buffer conversion.
+ void setConvertInfo( StreamMode mode, unsigned int firstChannel );
+};
+
+// **************************************************************** //
+//
+// Inline RtAudio definitions.
+//
+// **************************************************************** //
+
+inline RtAudio::Api RtAudio :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); }
+inline unsigned int RtAudio :: getDeviceCount( void ) throw() { return rtapi_->getDeviceCount(); }
+inline RtAudio::DeviceInfo RtAudio :: getDeviceInfo( unsigned int device ) { return rtapi_->getDeviceInfo( device ); }
+inline unsigned int RtAudio :: getDefaultInputDevice( void ) throw() { return rtapi_->getDefaultInputDevice(); }
+inline unsigned int RtAudio :: getDefaultOutputDevice( void ) throw() { return rtapi_->getDefaultOutputDevice(); }
+inline void RtAudio :: closeStream( void ) throw() { return rtapi_->closeStream(); }
+inline void RtAudio :: startStream( void ) { return rtapi_->startStream(); }
+inline void RtAudio :: stopStream( void ) { return rtapi_->stopStream(); }
+inline void RtAudio :: abortStream( void ) { return rtapi_->abortStream(); }
+inline bool RtAudio :: isStreamOpen( void ) const throw() { return rtapi_->isStreamOpen(); }
+inline bool RtAudio :: isStreamRunning( void ) const throw() { return rtapi_->isStreamRunning(); }
+inline long RtAudio :: getStreamLatency( void ) { return rtapi_->getStreamLatency(); }
+inline unsigned int RtAudio :: getStreamSampleRate( void ) { return rtapi_->getStreamSampleRate(); }
+inline double RtAudio :: getStreamTime( void ) { return rtapi_->getStreamTime(); }
+inline void RtAudio :: setStreamTime( double time ) { return rtapi_->setStreamTime( time ); }
+inline void RtAudio :: showWarnings( bool value ) throw() { rtapi_->showWarnings( value ); }
+
+// RtApi Subclass prototypes.
+
+#if defined(__MACOSX_CORE__)
+
+#include <CoreAudio/AudioHardware.h>
+
+class RtApiCore: public RtApi
+{
+public:
+
+ RtApiCore();
+ ~RtApiCore();
+ RtAudio::Api getCurrentApi( void ) { return RtAudio::MACOSX_CORE; }
+ unsigned int getDeviceCount( void );
+ RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
+ unsigned int getDefaultOutputDevice( void );
+ unsigned int getDefaultInputDevice( void );
+ void closeStream( void );
+ void startStream( void );
+ void stopStream( void );
+ void abortStream( void );
+ long getStreamLatency( void );
+
+ // This function is intended for internal use only. It must be
+ // public because it is called by the internal callback handler,
+ // which is not a member of RtAudio. External use of this function
+ // will most likely produce highly undesireable results!
+ bool callbackEvent( AudioDeviceID deviceId,
+ const AudioBufferList *inBufferList,
+ const AudioBufferList *outBufferList );
+
+ private:
+
+ bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+ unsigned int firstChannel, unsigned int sampleRate,
+ RtAudioFormat format, unsigned int *bufferSize,
+ RtAudio::StreamOptions *options );
+ static const char* getErrorCode( OSStatus code );
+};
+
+#endif
+
+#if defined(__UNIX_JACK__)
+
+class RtApiJack: public RtApi
+{
+public:
+
+ RtApiJack();
+ ~RtApiJack();
+ RtAudio::Api getCurrentApi( void ) { return RtAudio::UNIX_JACK; }
+ unsigned int getDeviceCount( void );
+ RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
+ void closeStream( void );
+ void startStream( void );
+ void stopStream( void );
+ void abortStream( void );
+ long getStreamLatency( void );
+
+ // This function is intended for internal use only. It must be
+ // public because it is called by the internal callback handler,
+ // which is not a member of RtAudio. External use of this function
+ // will most likely produce highly undesireable results!
+ bool callbackEvent( unsigned long nframes );
+
+ private:
+
+ bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+ unsigned int firstChannel, unsigned int sampleRate,
+ RtAudioFormat format, unsigned int *bufferSize,
+ RtAudio::StreamOptions *options );
+};
+
+#endif
+
+#if defined(__WINDOWS_ASIO__)
+
+class RtApiAsio: public RtApi
+{
+public:
+
+ RtApiAsio();
+ ~RtApiAsio();
+ RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_ASIO; }
+ unsigned int getDeviceCount( void );
+ RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
+ void closeStream( void );
+ void startStream( void );
+ void stopStream( void );
+ void abortStream( void );
+ long getStreamLatency( void );
+
+ // This function is intended for internal use only. It must be
+ // public because it is called by the internal callback handler,
+ // which is not a member of RtAudio. External use of this function
+ // will most likely produce highly undesireable results!
+ bool callbackEvent( long bufferIndex );
+
+ private:
+
+ std::vector<RtAudio::DeviceInfo> devices_;
+ void saveDeviceInfo( void );
+ bool coInitialized_;
+ bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+ unsigned int firstChannel, unsigned int sampleRate,
+ RtAudioFormat format, unsigned int *bufferSize,
+ RtAudio::StreamOptions *options );
+};
+
+#endif
+
+#if defined(__WINDOWS_DS__)
+
+class RtApiDs: public RtApi
+{
+public:
+
+ RtApiDs();
+ ~RtApiDs();
+ RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_DS; }
+ unsigned int getDeviceCount( void );
+ unsigned int getDefaultOutputDevice( void );
+ unsigned int getDefaultInputDevice( void );
+ RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
+ void closeStream( void );
+ void startStream( void );
+ void stopStream( void );
+ void abortStream( void );
+ long getStreamLatency( void );
+
+ // This function is intended for internal use only. It must be
+ // public because it is called by the internal callback handler,
+ // which is not a member of RtAudio. External use of this function
+ // will most likely produce highly undesireable results!
+ void callbackEvent( void );
+
+ private:
+
+ bool coInitialized_;
+ bool buffersRolling;
+ long duplexPrerollBytes;
+ std::vector<struct DsDevice> dsDevices;
+ bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+ unsigned int firstChannel, unsigned int sampleRate,
+ RtAudioFormat format, unsigned int *bufferSize,
+ RtAudio::StreamOptions *options );
+};
+
+#endif
+
+#if defined(__WINDOWS_WASAPI__)
+
+struct IMMDeviceEnumerator;
+
+class RtApiWasapi : public RtApi
+{
+public:
+ RtApiWasapi();
+ ~RtApiWasapi();
+
+ RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_WASAPI; }
+ unsigned int getDeviceCount( void );
+ RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
+ unsigned int getDefaultOutputDevice( void );
+ unsigned int getDefaultInputDevice( void );
+ void closeStream( void );
+ void startStream( void );
+ void stopStream( void );
+ void abortStream( void );
+
+private:
+ bool coInitialized_;
+ IMMDeviceEnumerator* deviceEnumerator_;
+
+ bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+ unsigned int firstChannel, unsigned int sampleRate,
+ RtAudioFormat format, unsigned int* bufferSize,
+ RtAudio::StreamOptions* options );
+
+ static DWORD WINAPI runWasapiThread( void* wasapiPtr );
+ static DWORD WINAPI stopWasapiThread( void* wasapiPtr );
+ static DWORD WINAPI abortWasapiThread( void* wasapiPtr );
+ void wasapiThread();
+};
+
+#endif
+
+#if defined(__LINUX_ALSA__)
+
+class RtApiAlsa: public RtApi
+{
+public:
+
+ RtApiAlsa();
+ ~RtApiAlsa();
+ RtAudio::Api getCurrentApi() { return RtAudio::LINUX_ALSA; }
+ unsigned int getDeviceCount( void );
+ RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
+ void closeStream( void );
+ void startStream( void );
+ void stopStream( void );
+ void abortStream( void );
+
+ // This function is intended for internal use only. It must be
+ // public because it is called by the internal callback handler,
+ // which is not a member of RtAudio. External use of this function
+ // will most likely produce highly undesireable results!
+ void callbackEvent( void );
+
+ private:
+
+ std::vector<RtAudio::DeviceInfo> devices_;
+ void saveDeviceInfo( void );
+ bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+ unsigned int firstChannel, unsigned int sampleRate,
+ RtAudioFormat format, unsigned int *bufferSize,
+ RtAudio::StreamOptions *options );
+};
+
+#endif
+
+#if defined(__LINUX_PULSE__)
+
+class RtApiPulse: public RtApi
+{
+public:
+ ~RtApiPulse();
+ RtAudio::Api getCurrentApi() { return RtAudio::LINUX_PULSE; }
+ unsigned int getDeviceCount( void );
+ RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
+ void closeStream( void );
+ void startStream( void );
+ void stopStream( void );
+ void abortStream( void );
+
+ // This function is intended for internal use only. It must be
+ // public because it is called by the internal callback handler,
+ // which is not a member of RtAudio. External use of this function
+ // will most likely produce highly undesireable results!
+ void callbackEvent( void );
+
+ private:
+
+ std::vector<RtAudio::DeviceInfo> devices_;
+ void saveDeviceInfo( void );
+ bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+ unsigned int firstChannel, unsigned int sampleRate,
+ RtAudioFormat format, unsigned int *bufferSize,
+ RtAudio::StreamOptions *options );
+};
+
+#endif
+
+#if defined(__LINUX_OSS__)
+
+class RtApiOss: public RtApi
+{
+public:
+
+ RtApiOss();
+ ~RtApiOss();
+ RtAudio::Api getCurrentApi() { return RtAudio::LINUX_OSS; }
+ unsigned int getDeviceCount( void );
+ RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
+ void closeStream( void );
+ void startStream( void );
+ void stopStream( void );
+ void abortStream( void );
+
+ // This function is intended for internal use only. It must be
+ // public because it is called by the internal callback handler,
+ // which is not a member of RtAudio. External use of this function
+ // will most likely produce highly undesireable results!
+ void callbackEvent( void );
+
+ private:
+
+ bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+ unsigned int firstChannel, unsigned int sampleRate,
+ RtAudioFormat format, unsigned int *bufferSize,
+ RtAudio::StreamOptions *options );
+};
+
+#endif
+
+#if defined(__RTAUDIO_DUMMY__)
+
+class RtApiDummy: public RtApi
+{
+public:
+
+ RtApiDummy() { errorText_ = "RtApiDummy: This class provides no functionality."; error( RtAudioError::WARNING ); }
+ RtAudio::Api getCurrentApi( void ) { return RtAudio::RTAUDIO_DUMMY; }
+ unsigned int getDeviceCount( void ) { return 0; }
+ RtAudio::DeviceInfo getDeviceInfo( unsigned int /*device*/ ) { RtAudio::DeviceInfo info; return info; }
+ void closeStream( void ) {}
+ void startStream( void ) {}
+ void stopStream( void ) {}
+ void abortStream( void ) {}
+
+ private:
+
+ bool probeDeviceOpen( unsigned int /*device*/, StreamMode /*mode*/, unsigned int /*channels*/,
+ unsigned int /*firstChannel*/, unsigned int /*sampleRate*/,
+ RtAudioFormat /*format*/, unsigned int * /*bufferSize*/,
+ RtAudio::StreamOptions * /*options*/ ) { return false; }
+};
+
+#endif
+
+#endif
+
+// Indentation settings for Vim and Emacs
+//
+// Local Variables:
+// c-basic-offset: 2
+// indent-tabs-mode: nil
+// End:
+//
+// vim: et sts=2 sw=2
--- /dev/null
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+# Free Software Foundation, Inc.
+
+timestamp='2004-02-26'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Written by Per Bothner <bothner@cygnus.com>.
+# Please send patches to <config-patches@gnu.org>.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 93, 94, 95, 96, 97, 98, 99, 2000
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit 0 ;;
+ --version | -v )
+ echo "$version" ; exit 0 ;;
+ --help | --h* | -h )
+ echo "$usage"; exit 0 ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+
+dummy=dummy-$$
+trap 'rm -f $dummy.c $dummy.o $dummy.rel $dummy; exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script.
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,) echo "int dummy(){}" > $dummy.c
+ for c in cc gcc c89 ; do
+ ($c $dummy.c -c -o $dummy.o) >/dev/null 2>&1
+ if test $? = 0 ; then
+ CC_FOR_BUILD="$c"; break
+ fi
+ done
+ rm -f $dummy.c $dummy.o $dummy.rel
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+esac
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 8/24/94.)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ *:NetBSD:*:*)
+ # Netbsd (nbsd) targets should (where applicable) match one or
+ # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ # Determine the machine/vendor (is the vendor relevant).
+ case "${UNAME_MACHINE}" in
+ amiga) machine=m68k-unknown ;;
+ arm32) machine=arm-unknown ;;
+ atari*) machine=m68k-atari ;;
+ sun3*) machine=m68k-sun ;;
+ mac68k) machine=m68k-apple ;;
+ macppc) machine=powerpc-apple ;;
+ hp3[0-9][05]) machine=m68k-hp ;;
+ ibmrt|romp-ibm) machine=romp-ibm ;;
+ *) machine=${UNAME_MACHINE}-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently, or will in the future.
+ case "${UNAME_MACHINE}" in
+ i386|sparc|amiga|arm*|hp300|mvme68k|vax|atari|luna68k|mac68k|news68k|next68k|pc532|sun3*|x68k)
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep __ELF__ >/dev/null
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # The OS release
+ release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit 0 ;;
+ alpha:OSF1:*:*)
+ if test $UNAME_RELEASE = "V4.0"; then
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ fi
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ cat <<EOF >$dummy.s
+ .data
+\$Lformat:
+ .byte 37,100,45,37,120,10,0 # "%d-%x\n"
+
+ .text
+ .globl main
+ .align 4
+ .ent main
+main:
+ .frame \$30,16,\$26,0
+ ldgp \$29,0(\$27)
+ .prologue 1
+ .long 0x47e03d80 # implver \$0
+ lda \$2,-1
+ .long 0x47e20c21 # amask \$2,\$1
+ lda \$16,\$Lformat
+ mov \$0,\$17
+ not \$1,\$18
+ jsr \$26,printf
+ ldgp \$29,0(\$26)
+ mov 0,\$16
+ jsr \$26,exit
+ .end main
+EOF
+ $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null
+ if test "$?" = 0 ; then
+ case `./$dummy` in
+ 0-0)
+ UNAME_MACHINE="alpha"
+ ;;
+ 1-0)
+ UNAME_MACHINE="alphaev5"
+ ;;
+ 1-1)
+ UNAME_MACHINE="alphaev56"
+ ;;
+ 1-101)
+ UNAME_MACHINE="alphapca56"
+ ;;
+ 2-303)
+ UNAME_MACHINE="alphaev6"
+ ;;
+ 2-307)
+ UNAME_MACHINE="alphaev67"
+ ;;
+ esac
+ fi
+ rm -f $dummy.s $dummy
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit 0 ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit 0 ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit 0 ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit 0;;
+ amiga:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit 0 ;;
+ arc64:OpenBSD:*:*)
+ echo mips64el-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ hkmips:OpenBSD:*:*)
+ echo mips-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ pmax:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sgi:OpenBSD:*:*)
+ echo mips-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ wgrisc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit 0 ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit 0;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit 0;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit 0 ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit 0 ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ i86pc:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit 0 ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit 0 ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit 0 ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit 0 ;;
+ sun3*:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme88k:OpenBSD:*:*)
+ echo m88k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit 0 ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit 0 ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit 0 ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD $dummy.c -o $dummy \
+ && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+ && rm -f $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit 0 ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit 0 ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit 0 ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit 0 ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit 0 ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit 0 ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit 0 ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit 0 ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit 0 ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit 0 ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ echo rs6000-ibm-aix3.2.5
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit 0 ;;
+ *:AIX:*:[45])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit 0 ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit 0 ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit 0 ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit 0 ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit 0 ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit 0 ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit 0 ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ case "${HPUX_REV}" in
+ 11.[0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "${sc_cpu_version}" in
+ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "${sc_kernel_bits}" in
+ 32) HP_ARCH="hppa2.0n" ;;
+ 64) HP_ARCH="hppa2.0w" ;;
+ esac ;;
+ esac
+ fi ;;
+ esac
+ if [ "${HP_ARCH}" = "" ]; then
+ sed 's/^ //' << EOF >$dummy.c
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS= $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy`
+ if test -z "$HP_ARCH"; then HP_ARCH=hppa; fi
+ rm -f $dummy.c $dummy
+ fi ;;
+ esac
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ 3050*:HI-UX:*:*)
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ echo unknown-hitachi-hiuxwe2
+ exit 0 ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit 0 ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit 0 ;;
+ *9??*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit 0 ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit 0 ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit 0 ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit 0 ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit 0 ;;
+ hppa*:OpenBSD:*:*)
+ echo hppa-unknown-openbsd
+ exit 0 ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ CRAY*X-MP:*:*:*)
+ echo xmp-cray-unicos
+ exit 0 ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE}
+ exit 0 ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
+ exit 0 ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*T3D:*:*:*)
+ echo alpha-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY-2:*:*:*)
+ echo cray2-cray-unicos
+ exit 0 ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit 0 ;;
+ hp300:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:FreeBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit 0 ;;
+ *:OpenBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ exit 0 ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit 0 ;;
+ i*:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit 0 ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit 0 ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i386-pc-interix
+ exit 0 ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit 0 ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit 0 ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ *:GNU:*:*)
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit 0 ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit 0 ;;
+ arm*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux
+ exit 0 ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ mips:Linux:*:*)
+ cat >$dummy.c <<EOF
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+int main (int argc, char *argv[]) {
+#else
+int main (argc, argv) int argc; char *argv[]; {
+#endif
+#ifdef __MIPSEB__
+ printf ("%s-unknown-linux-gnu\n", argv[1]);
+#endif
+#ifdef __MIPSEL__
+ printf ("%sel-unknown-linux-gnu\n", argv[1]);
+#endif
+ return 0;
+}
+EOF
+ $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm -f $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ ;;
+ ppc:Linux:*:*)
+ # Determine Lib Version
+ cat >$dummy.c <<EOF
+#include <features.h>
+#if defined(__GLIBC__)
+extern char __libc_version[];
+extern char __libc_release[];
+#endif
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+#if defined(__GLIBC__)
+ printf("%s %s\n", __libc_version, __libc_release);
+#else
+ printf("unknown\n");
+#endif
+ return 0;
+}
+EOF
+ LIBC=""
+ $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null
+ if test "$?" = 0 ; then
+ ./$dummy | grep 1\.99 > /dev/null
+ if test "$?" = 0 ; then LIBC="libc1" ; fi
+ fi
+ rm -f $dummy.c $dummy
+ echo powerpc-unknown-linux-gnu${LIBC}
+ exit 0 ;;
+ alpha:Linux:*:*)
+ cat <<EOF >$dummy.s
+ .data
+ \$Lformat:
+ .byte 37,100,45,37,120,10,0 # "%d-%x\n"
+ .text
+ .globl main
+ .align 4
+ .ent main
+ main:
+ .frame \$30,16,\$26,0
+ ldgp \$29,0(\$27)
+ .prologue 1
+ .long 0x47e03d80 # implver \$0
+ lda \$2,-1
+ .long 0x47e20c21 # amask \$2,\$1
+ lda \$16,\$Lformat
+ mov \$0,\$17
+ not \$1,\$18
+ jsr \$26,printf
+ ldgp \$29,0(\$26)
+ mov 0,\$16
+ jsr \$26,exit
+ .end main
+EOF
+ LIBC=""
+ $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null
+ if test "$?" = 0 ; then
+ case `./$dummy` in
+ 0-0) UNAME_MACHINE="alpha" ;;
+ 1-0) UNAME_MACHINE="alphaev5" ;;
+ 1-1) UNAME_MACHINE="alphaev56" ;;
+ 1-101) UNAME_MACHINE="alphapca56" ;;
+ 2-303) UNAME_MACHINE="alphaev6" ;;
+ 2-307) UNAME_MACHINE="alphaev67" ;;
+ esac
+ objdump --private-headers $dummy | \
+ grep ld.so.1 > /dev/null
+ if test "$?" = 0 ; then
+ LIBC="libc1"
+ fi
+ fi
+ rm -f $dummy.s $dummy
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit 0 ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-gnu ;;
+ PA8*) echo hppa2.0-unknown-linux-gnu ;;
+ *) echo hppa-unknown-linux-gnu ;;
+ esac
+ exit 0 ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
+ exit 0 ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit 0 ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ x86_64:Linux:*:*)
+ echo x86_64-unknown-linux-gnu
+ exit 0 ;;
+ i*86:Linux:*:*)
+ # The BFD linker knows what the default object file format is, so
+ # first see if it will tell us. cd to the root directory to prevent
+ # problems with other programs or directories called `ld' in the path.
+ ld_supported_emulations=`cd /; ld --help 2>&1 \
+ | sed -ne '/supported emulations:/!d
+ s/[ ][ ]*/ /g
+ s/.*supported emulations: *//
+ s/ .*//
+ p'`
+ case "$ld_supported_emulations" in
+ i*86linux)
+ echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+ exit 0
+ ;;
+ elf_i*86)
+ TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+ ;;
+ i*86coff)
+ echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+ exit 0
+ ;;
+ esac
+ # Either a pre-BFD a.out linker (linux-gnuoldld)
+ # or one that does not give us useful --help.
+ # GCC wants to distinguish between linux-gnuoldld and linux-gnuaout.
+ # If ld does not provide *any* "supported emulations:"
+ # that means it is gnuoldld.
+ test -z "$ld_supported_emulations" && echo "${UNAME_MACHINE}-pc-linux-gnuoldld" && exit 0
+ case "${UNAME_MACHINE}" in
+ i*86)
+ VENDOR=pc;
+ ;;
+ *)
+ VENDOR=unknown;
+ ;;
+ esac
+ # Determine whether the default compiler is a.out or elf
+ cat >$dummy.c <<EOF
+#include <features.h>
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+#ifdef __ELF__
+# ifdef __GLIBC__
+# if __GLIBC__ >= 2
+ printf ("%s-${VENDOR}-linux-gnu\n", argv[1]);
+# else
+ printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]);
+# endif
+# else
+ printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]);
+# endif
+#else
+ printf ("%s-${VENDOR}-linux-gnuaout\n", argv[1]);
+#endif
+ return 0;
+}
+EOF
+ $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm -f $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
+ ;;
+# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. earlier versions
+# are messed up and put the nodename in both sysname and nodename.
+ i*86:DYNIX/ptx:4*:*)
+ echo i386-sequent-sysv4
+ exit 0 ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit 0 ;;
+ i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit 0 ;;
+ i*86:*:5:7*)
+ # Fixed at (any) Pentium or better
+ UNAME_MACHINE=i586
+ if [ ${UNAME_SYSTEM} = "UnixWare" ] ; then
+ echo ${UNAME_MACHINE}-sco-sysv${UNAME_RELEASE}uw${UNAME_VERSION}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit 0 ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit 0 ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i386.
+ echo i386-pc-msdosdjgpp
+ exit 0 ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit 0 ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit 0 ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit 0 ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit 0 ;;
+ M68*:*:R3V[567]*:*)
+ test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+ 3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4 && exit 0 ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit 0 ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+ echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit 0 ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit 0 ;;
+ PENTIUM:CPunix:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit 0 ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit 0 ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit 0 ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit 0 ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit 0 ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit 0 ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit 0 ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit 0 ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Darwin:*:*)
+ echo `uname -p`-apple-darwin${UNAME_RELEASE}
+ exit 0 ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ if test "${UNAME_MACHINE}" = "x86pc"; then
+ UNAME_MACHINE=pc
+ fi
+ echo `uname -p`-${UNAME_MACHINE}-nto-qnx
+ exit 0 ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit 0 ;;
+ NSR-[KW]:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk${UNAME_RELEASE}
+ exit 0 ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit 0 ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit 0 ;;
+ DS/*:UNIX_System_V:*:*)
+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ exit 0 ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "$cputype" = "386"; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo ${UNAME_MACHINE}-unknown-plan9
+ exit 0 ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo ${UNAME_MACHINE}-pc-os2-emx
+ exit 0 ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit 0 ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit 0 ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit 0 ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit 0 ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit 0 ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+# include <sys/param.h>
+# if defined (BSD)
+# if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+# else
+# if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# endif
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# else
+ printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm -f $dummy.c $dummy && exit 0
+rm -f $dummy.c $dummy
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ c34*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ c38*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ c4*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+ ftp://ftp.gnu.org/pub/gnu/config/
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
--- /dev/null
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+# Free Software Foundation, Inc.
+
+timestamp='2012-11-19'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+ $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit 0 ;;
+ --version | -v )
+ echo "$version" ; exit 0 ;;
+ --help | --h* | -h )
+ echo "$usage"; exit 0 ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help"
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo $1
+ exit 0;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ nto-qnx* | linux-gnu* | storm-chaos* | os2-emx*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple | -axis)
+ os=
+ basic_machine=$1
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ tahoe | i860 | ia64 | m32r | m68k | m68000 | m88k | ns32k | arc \
+ | arm | arme[lb] | arm[bl]e | armv[2345] | armv[345][lb] | strongarm | xscale \
+ | pyramid | mn10200 | mn10300 | tron | a29k \
+ | 580 | i960 | h8300 \
+ | x86 | ppcbe | mipsbe | mipsle | shbe | shle \
+ | hppa | hppa1.0 | hppa1.1 | hppa2.0 | hppa2.0w | hppa2.0n \
+ | hppa64 \
+ | alpha | alphaev[4-8] | alphaev56 | alphapca5[67] \
+ | alphaev6[78] \
+ | we32k | ns16k | clipper | i370 | sh | sh[34] \
+ | powerpc | powerpc64 | powerpcle \
+ | 1750a | dsp16xx | pdp10 | pdp11 \
+ | mips16 | mips64 | mipsel | mips64el \
+ | mips64orion | mips64orionel | mipstx39 | mipstx39el \
+ | mips64vr4300 | mips64vr4300el | mips64vr4100 | mips64vr4100el \
+ | mips64vr5000 | miprs64vr5000el | mcore | s390 | s390x \
+ | sparc | sparclet | sparclite | sparc64 | sparcv9 | sparcv9b \
+ | v850 | c4x \
+ | thumb | d10v | d30v | fr30 | avr | openrisc | tic80 \
+ | pj | pjl | h8500)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12)
+ # Motorola 68HC11/12.
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | z8k | v70 | w65)
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ # FIXME: clean up the formatting here.
+ vax-* | tahoe-* | i*86-* | i860-* | ia64-* | m32r-* | m68k-* | m68000-* \
+ | m88k-* | sparc-* | ns32k-* | fx80-* | arc-* | c[123]* \
+ | arm-* | armbe-* | armle-* | armv*-* | strongarm-* | xscale-* \
+ | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* \
+ | power-* | none-* | 580-* | cray2-* | h8300-* | h8500-* | i960-* \
+ | xmp-* | ymp-* \
+ | x86-* | ppcbe-* | mipsbe-* | mipsle-* | shbe-* | shle-* \
+ | hppa-* | hppa1.0-* | hppa1.1-* | hppa2.0-* | hppa2.0w-* \
+ | hppa2.0n-* | hppa64-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphapca5[67]-* \
+ | alphaev6[78]-* \
+ | we32k-* | cydra-* | ns16k-* | pn-* | np1-* | xps100-* \
+ | clipper-* | orion-* \
+ | sparclite-* | pdp10-* | pdp11-* | sh-* | powerpc-* | powerpc64-* | powerpcle-* \
+ | sparc64-* | sparcv9-* | sparcv9b-* | sparc86x-* \
+ | mips16-* | mips64-* | mipsel-* \
+ | mips64el-* | mips64orion-* | mips64orionel-* \
+ | mips64vr4100-* | mips64vr4100el-* | mips64vr4300-* | mips64vr4300el-* \
+ | mipstx39-* | mipstx39el-* | mcore-* \
+ | f30[01]-* | f700-* | s390-* | s390x-* | sv1-* | t3e-* \
+ | [cjt]90-* \
+ | m88110-* | m680[01234]0-* | m683?2-* | m68360-* | z8k-* | d10v-* \
+ | thumb-* | v850-* | d30v-* | tic30-* | tic80-* | c30-* | fr30-* \
+ | bs2000-* | tic54x-* | c54x-* | x86_64-* | pj-* | pjl-*)
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 386bsd)
+ basic_machine=i386-unknown
+ os=-bsd
+ ;;
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-unknown
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ cray2)
+ basic_machine=cray2-cray
+ os=-unicos
+ ;;
+ [cjt]90)
+ basic_machine=${basic_machine}-cray
+ os=-unicos
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=-go32
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i*86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i*86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i*86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i*86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ i386-vsta | vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ mingw32)
+ basic_machine=i386-pc
+ os=-mingw32
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mipsel*-linux*)
+ basic_machine=mipsel-unknown
+ os=-linux-gnu
+ ;;
+ mips*-linux*)
+ basic_machine=mips-unknown
+ os=-linux-gnu
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ mmix*)
+ basic_machine=mmix-knuth
+ os=-mmixware
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=-msdos
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=-nonstopux
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ nsr-tandem)
+ basic_machine=nsr-tandem
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pentium | p5 | k5 | k6 | nexgen)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | 6x86 | athlon)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2)
+ basic_machine=i686-pc
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=power-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64) basic_machine=powerpc64-unknown
+ ;;
+ ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=-pw32
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sparclite-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=-unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=t3e-cray
+ os=-unicos
+ ;;
+ tic54x | c54x*)
+ basic_machine=tic54x-unknown
+ os=-coff
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ xmp)
+ basic_machine=xmp-cray
+ os=-unicos
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ mips)
+ if [ x$os = x-linux-gnu ]; then
+ basic_machine=mips-unknown
+ else
+ basic_machine=mips-mips
+ fi
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp10)
+ # there are many clones, so DEC is not a safe bet
+ basic_machine=pdp10-unknown
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sh3 | sh4)
+ basic_machine=sh-unknown
+ ;;
+ sparc | sparcv9 | sparcv9b)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ c4x*)
+ basic_machine=c4x-none
+ os=-coff
+ ;;
+ *-unknown)
+ # Make sure to match an already-canonicalized machine name.
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
+ | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \
+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* | -os2*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+ case $basic_machine in
+ x86-* | i*86-*)
+ ;;
+ *)
+ os=-nto$os
+ ;;
+ esac
+ ;;
+ -nto*)
+ os=-nto-qnx
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -wince*)
+ os=-wince
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ -nsk*)
+ os=-nsk
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -es1800*)
+ os=-ose
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ os=-mint
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ pdp10-*)
+ os=-tops20
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -vxsim* | -vxworks*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ vendor=atari
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
--- /dev/null
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.69 for RtAudio 4.1.
+#
+# Report bugs to <gary@music.mcgill.ca>.
+#
+#
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in #(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Use a proper internal environment variable to ensure we don't fall
+ # into an infinite loop, continuously re-executing ourselves.
+ if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+ _as_can_reexec=no; export _as_can_reexec;
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+ fi
+ # We don't want this to propagate to other subprocesses.
+ { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+ as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '\${1+\"\$@\"}'='\"\$@\"'
+ setopt NO_GLOB_SUBST
+else
+ case \`(set -o) 2>/dev/null\` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+"
+ as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+ exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
+ as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+ as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+ eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+ test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1"
+ if (eval "$as_required") 2>/dev/null; then :
+ as_have_required=yes
+else
+ as_have_required=no
+fi
+ if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ as_found=:
+ case $as_dir in #(
+ /*)
+ for as_base in sh bash ksh sh5; do
+ # Try only shells that exist, to save several forks.
+ as_shell=$as_dir/$as_base
+ if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ CONFIG_SHELL=$as_shell as_have_required=yes
+ if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ break 2
+fi
+fi
+ done;;
+ esac
+ as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+ CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+ if test "x$CONFIG_SHELL" != x; then :
+ export CONFIG_SHELL
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+ if test x$as_have_required = xno; then :
+ $as_echo "$0: This script requires a shell more modern than all"
+ $as_echo "$0: the shells that I found on your system."
+ if test x${ZSH_VERSION+set} = xset ; then
+ $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+ $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+ else
+ $as_echo "$0: Please tell bug-autoconf@gnu.org and
+$0: gary@music.mcgill.ca about your system, including any
+$0: error possibly output before this message. Then install
+$0: a modern shell, or manually run the script under such a
+$0: shell if you do have one."
+ fi
+ exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} # as_fn_exit
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+ as_lineno_1=$LINENO as_lineno_1a=$LINENO
+ as_lineno_2=$LINENO as_lineno_2a=$LINENO
+ eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+ test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+ # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+ # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+ # already done that, so ensure we don't try to do so again and fall
+ # in an infinite loop. This has already happened in practice.
+ _as_can_reexec=no; export _as_can_reexec
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -pR'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -pR'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -pR'
+ fi
+else
+ as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='RtAudio'
+PACKAGE_TARNAME='rtaudio'
+PACKAGE_VERSION='4.1'
+PACKAGE_STRING='RtAudio 4.1'
+PACKAGE_BUGREPORT='gary@music.mcgill.ca'
+PACKAGE_URL=''
+
+ac_unique_file="RtAudio.cpp"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='LTLIBOBJS
+LIBOBJS
+objects
+PULSE_LIBS
+PULSE_CFLAGS
+req
+api
+libflags
+sharedname
+sharedlib
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+object_path
+cxxflag
+cppflag
+EGREP
+GREP
+CPP
+ac_ct_CC
+CFLAGS
+CC
+AR
+RANLIB
+OBJEXT
+EXEEXT
+ac_ct_CXX
+CPPFLAGS
+LDFLAGS
+CXXFLAGS
+CXX
+PKG_CONFIG_LIBDIR
+PKG_CONFIG_PATH
+PKG_CONFIG
+GXX
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+enable_debug
+with_jack
+with_alsa
+with_pulse
+with_oss
+with_core
+with_asio
+with_ds
+with_wasapi
+'
+ ac_precious_vars='build_alias
+host_alias
+target_alias
+PKG_CONFIG
+PKG_CONFIG_PATH
+PKG_CONFIG_LIBDIR
+CXX
+CXXFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CCC
+CC
+CFLAGS
+CPP
+PULSE_CFLAGS
+PULSE_LIBS'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval $ac_prev=\$ac_option
+ ac_prev=
+ continue
+ fi
+
+ case $ac_option in
+ *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+ *=) ac_optarg= ;;
+ *) ac_optarg=yes ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_dashdash$ac_option in
+ --)
+ ac_dashdash=yes ;;
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=*)
+ datadir=$ac_optarg ;;
+
+ -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+ | --dataroo | --dataro | --datar)
+ ac_prev=datarootdir ;;
+ -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+ datarootdir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=no ;;
+
+ -docdir | --docdir | --docdi | --doc | --do)
+ ac_prev=docdir ;;
+ -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+ docdir=$ac_optarg ;;
+
+ -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+ ac_prev=dvidir ;;
+ -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+ dvidir=$ac_optarg ;;
+
+ -enable-* | --enable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=\$ac_optarg ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+ ac_prev=htmldir ;;
+ -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+ | --ht=*)
+ htmldir=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localedir | --localedir | --localedi | --localed | --locale)
+ ac_prev=localedir ;;
+ -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+ localedir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst | --locals)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+ ac_prev=pdfdir ;;
+ -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+ pdfdir=$ac_optarg ;;
+
+ -psdir | --psdir | --psdi | --psd | --ps)
+ ac_prev=psdir ;;
+ -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+ psdir=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=\$ac_optarg ;;
+
+ -without-* | --without-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=no ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ case $ac_envvar in #(
+ '' | [0-9]* | *[!_$as_cr_alnum]* )
+ as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+ esac
+ eval $ac_envvar=\$ac_optarg
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+ case $enable_option_checking in
+ no) ;;
+ fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+ *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+ esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
+ datadir sysconfdir sharedstatedir localstatedir includedir \
+ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+ libdir localedir mandir
+do
+ eval ac_val=\$$ac_var
+ # Remove trailing slashes.
+ case $ac_val in
+ */ )
+ ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+ eval $ac_var=\$ac_val;;
+ esac
+ # Be sure to have absolute directory names.
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) continue;;
+ NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+ esac
+ as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+ as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+ as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then the parent directory.
+ ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_myself" : 'X\(//\)[^/]' \| \
+ X"$as_myself" : 'X\(//\)$' \| \
+ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r "$srcdir/$ac_unique_file"; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+ test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+ as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+ cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+ pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+ srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+ eval ac_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_env_${ac_var}_value=\$${ac_var}
+ eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures RtAudio 4.1 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking ...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
+ --infodir=DIR info documentation [DATAROOTDIR/info]
+ --localedir=DIR locale-dependent data [DATAROOTDIR/locale]
+ --mandir=DIR man documentation [DATAROOTDIR/man]
+ --docdir=DIR documentation root [DATAROOTDIR/doc/rtaudio]
+ --htmldir=DIR html documentation [DOCDIR]
+ --dvidir=DIR dvi documentation [DOCDIR]
+ --pdfdir=DIR pdf documentation [DOCDIR]
+ --psdir=DIR ps documentation [DOCDIR]
+_ACEOF
+
+ cat <<\_ACEOF
+
+System types:
+ --build=BUILD configure for building on BUILD [guessed]
+ --host=HOST cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+ case $ac_init_help in
+ short | recursive ) echo "Configuration of RtAudio 4.1:";;
+ esac
+ cat <<\_ACEOF
+
+Optional Features:
+ --disable-option-checking ignore unrecognized --enable/--with options
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --enable-debug = enable various debug output
+
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-jack = choose JACK server support (mac and linux only)
+ --with-alsa = choose native ALSA API support (linux only)
+ --with-pulse = choose PulseAudio API support (linux only)
+ --with-oss = choose OSS API support (linux only)
+ --with-jack = choose JACK server support (unix only)
+ --with-core = choose CoreAudio API support (mac only)
+ --with-asio = choose ASIO API support (windoze only)
+ --with-ds = choose DirectSound API support (windoze only)
+ --with-wasapi = choose Windows Audio Session API support (windoze only)
+
+Some influential environment variables:
+ PKG_CONFIG path to pkg-config utility
+ PKG_CONFIG_PATH
+ directories to add to pkg-config's search path
+ PKG_CONFIG_LIBDIR
+ path overriding pkg-config's built-in search path
+ CXX C++ compiler command
+ CXXFLAGS C++ compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ LIBS libraries to pass to the linker, e.g. -l<library>
+ CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+ you have headers in a nonstandard directory <include dir>
+ CC C compiler command
+ CFLAGS C compiler flags
+ CPP C preprocessor
+ PULSE_CFLAGS
+ C compiler flags for PULSE, overriding pkg-config
+ PULSE_LIBS linker flags for PULSE, overriding pkg-config
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <gary@music.mcgill.ca>.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d "$ac_dir" ||
+ { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+ continue
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+ cd "$ac_dir" || { ac_status=$?; continue; }
+ # Check for guested configure.
+ if test -f "$ac_srcdir/configure.gnu"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+ elif test -f "$ac_srcdir/configure"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure" --help=recursive
+ else
+ $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi || ac_status=$?
+ cd "$ac_pwd" || { ac_status=$?; break; }
+ done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+ cat <<\_ACEOF
+RtAudio configure 4.1
+generated by GNU Autoconf 2.69
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_cxx_try_compile LINENO
+# ----------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext
+ if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_cxx_try_compile
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext
+ if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+
+# ac_fn_c_try_cpp LINENO
+# ----------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } > conftest.i && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_cpp
+
+# ac_fn_c_try_run LINENO
+# ----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
+# that executables *can* be run.
+ac_fn_c_try_run ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=$ac_status
+fi
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_run
+
+# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists, giving a warning if it cannot be compiled using
+# the include files in INCLUDES and setting the cache variable VAR
+# accordingly.
+ac_fn_c_check_header_mongrel ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if eval \${$3+:} false; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_header_compiler=yes
+else
+ ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <$2>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ ac_header_preproc=yes
+else
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
+ yes:no: )
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+( $as_echo "## ----------------------------------- ##
+## Report this to gary@music.mcgill.ca ##
+## ----------------------------------- ##"
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_mongrel
+
+# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists and can be compiled using the include files in
+# INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_c_check_header_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$3=yes"
+else
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_compile
+
+# ac_fn_c_try_link LINENO
+# -----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext conftest$ac_exeext
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ test -x conftest$ac_exeext
+ }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+ # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+ # interfere with the next link command; also delete a directory that is
+ # left behind by Apple's compiler. We do this before executing the actions.
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_link
+
+# ac_fn_c_check_func LINENO FUNC VAR
+# ----------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $2 (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$3=yes"
+else
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_func
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by RtAudio $as_me 4.1, which was
+generated by GNU Autoconf 2.69. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ $as_echo "PATH: $as_dir"
+ done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *\'*)
+ ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+ 2)
+ as_fn_append ac_configure_args1 " '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ as_fn_append ac_configure_args " '$ac_arg'"
+ ;;
+ esac
+ done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+(
+ for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+ (set) 2>&1 |
+ case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ sed -n \
+ "s/'\''/'\''\\\\'\'''\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+ ;; #(
+ *)
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+)
+ echo
+
+ $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+ echo
+ cat confdefs.h
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ $as_echo "$as_me: caught signal $ac_signal"
+ $as_echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core core.conftest.* &&
+ rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+ # We do not want a PATH search for config.site.
+ case $CONFIG_SITE in #((
+ -*) ac_site_file1=./$CONFIG_SITE;;
+ */*) ac_site_file1=$CONFIG_SITE;;
+ *) ac_site_file1=./$CONFIG_SITE;;
+ esac
+elif test "x$prefix" != xNONE; then
+ ac_site_file1=$prefix/share/config.site
+ ac_site_file2=$prefix/etc/config.site
+else
+ ac_site_file1=$ac_default_prefix/share/config.site
+ ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+ test "x$ac_site_file" = xNONE && continue
+ if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file" \
+ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special files
+ # actually), so we avoid doing that. DJGPP emulates it as a regular file.
+ if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . "$cache_file";;
+ *) . "./$cache_file";;
+ esac
+ fi
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val=\$ac_cv_env_${ac_var}_value
+ eval ac_new_val=\$ac_env_${ac_var}_value
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ # differences in whitespace do not lead to failure.
+ ac_old_val_w=`echo x $ac_old_val`
+ ac_new_val_w=`echo x $ac_new_val`
+ if test "$ac_old_val_w" != "$ac_new_val_w"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ ac_cache_corrupted=:
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+ eval $ac_var=\$ac_old_val
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5
+$as_echo "$as_me: former value: \`$ac_old_val'" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5
+$as_echo "$as_me: current value: \`$ac_new_val'" >&2;}
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+ac_aux_dir=
+for ac_dir in config "$srcdir"/config; do
+ if test -f "$ac_dir/install-sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f "$ac_dir/install.sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f "$ac_dir/shtool"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ as_fn_error $? "cannot find install-sh, install.sh, or shtool in config \"$srcdir\"/config" "$LINENO" 5
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
+
+
+
+ac_config_files="$ac_config_files rtaudio-config librtaudio.pc Makefile tests/Makefile"
+
+
+# Fill GXX with something before test.
+GXX="no"
+
+
+
+
+
+
+
+
+
+
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PKG_CONFIG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $PKG_CONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+PKG_CONFIG=$ac_cv_path_PKG_CONFIG
+if test -n "$PKG_CONFIG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
+$as_echo "$PKG_CONFIG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_PKG_CONFIG"; then
+ ac_pt_PKG_CONFIG=$PKG_CONFIG
+ # Extract the first word of "pkg-config", so it can be a program name with args.
+set dummy pkg-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $ac_pt_PKG_CONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG
+if test -n "$ac_pt_PKG_CONFIG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5
+$as_echo "$ac_pt_PKG_CONFIG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_pt_PKG_CONFIG" = x; then
+ PKG_CONFIG=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ PKG_CONFIG=$ac_pt_PKG_CONFIG
+ fi
+else
+ PKG_CONFIG="$ac_cv_path_PKG_CONFIG"
+fi
+
+fi
+if test -n "$PKG_CONFIG"; then
+ _pkg_min_version=0.9.0
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5
+$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; }
+ if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ PKG_CONFIG=""
+ fi
+fi
+
+
+
+# Checks for programs.
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test -z "$CXX"; then
+ if test -n "$CCC"; then
+ CXX=$CCC
+ else
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in g++ CC c++ cxx
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CXX+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CXX"; then
+ ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5
+$as_echo "$CXX" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$CXX" && break
+ done
+fi
+if test -z "$CXX"; then
+ ac_ct_CXX=$CXX
+ for ac_prog in g++ CC c++ cxx
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CXX+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CXX"; then
+ ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CXX="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5
+$as_echo "$ac_ct_CXX" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CXX" && break
+done
+
+ if test "x$ac_ct_CXX" = x; then
+ CXX="g++"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CXX=$ac_ct_CXX
+ fi
+fi
+
+ fi
+fi
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+ { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ sed '10a\
+... rest of stderr output deleted ...
+ 10q' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ fi
+ rm -f conftest.er1 conftest.err
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler works" >&5
+$as_echo_n "checking whether the C++ compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+ esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link_default") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile. We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+ then :; else
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ fi
+ # We set ac_cv_exeext here because the later test for it is not
+ # safe: cross compilers may not add the suffix if given an `-o'
+ # argument, so we may need to know it at that point already.
+ # Even if this section looks crufty: it has the advantage of
+ # actually working.
+ break;;
+ * )
+ break;;
+ esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+ ac_file=''
+fi
+if test -z "$ac_file"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C++ compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler default output file name" >&5
+$as_echo_n "checking for C++ compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+ { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ if { ac_try='./conftest$ac_cv_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C++ compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ for ac_file in conftest.o conftest.obj conftest.*; do
+ test -f "$ac_file" || continue;
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5
+$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; }
+if ${ac_cv_cxx_compiler_gnu+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+ ac_compiler_gnu=yes
+else
+ ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5
+$as_echo "$ac_cv_cxx_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+ GXX=yes
+else
+ GXX=
+fi
+ac_test_CXXFLAGS=${CXXFLAGS+set}
+ac_save_CXXFLAGS=$CXXFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5
+$as_echo_n "checking whether $CXX accepts -g... " >&6; }
+if ${ac_cv_prog_cxx_g+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_save_cxx_werror_flag=$ac_cxx_werror_flag
+ ac_cxx_werror_flag=yes
+ ac_cv_prog_cxx_g=no
+ CXXFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+ ac_cv_prog_cxx_g=yes
+else
+ CXXFLAGS=""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+
+else
+ ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+ CXXFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+ ac_cv_prog_cxx_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5
+$as_echo "$ac_cv_prog_cxx_g" >&6; }
+if test "$ac_test_CXXFLAGS" = set; then
+ CXXFLAGS=$ac_save_CXXFLAGS
+elif test $ac_cv_prog_cxx_g = yes; then
+ if test "$GXX" = yes; then
+ CXXFLAGS="-g -O2"
+ else
+ CXXFLAGS="-g"
+ fi
+else
+ if test "$GXX" = yes; then
+ CXXFLAGS="-O2"
+ else
+ CXXFLAGS=
+ fi
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+ ac_ct_RANLIB=$RANLIB
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_RANLIB"; then
+ ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_RANLIB" = x; then
+ RANLIB=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ RANLIB=$ac_ct_RANLIB
+ fi
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+# Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $AR in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_AR="$AR" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_AR="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_AR" && ac_cv_path_AR="no"
+ ;;
+esac
+fi
+AR=$ac_cv_path_AR
+if test -n "$AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+if [ $AR = "no" ] ; then
+ as_fn_error $? "\"Could not find ar - needed to create a library\"" "$LINENO" 5;
+fi
+
+# Checks for header files.
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+ { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ sed '10a\
+... rest of stderr output deleted ...
+ 10q' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ fi
+ rm -f conftest.er1 conftest.err
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_compiler_gnu=yes
+else
+ ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+else
+ CFLAGS=""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if ${ac_cv_prog_CPP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # Double quotes because CPP needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if ${ac_cv_path_GREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$GREP"; then
+ ac_path_GREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in grep ggrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_GREP" || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+ # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'GREP' >> "conftest.nl"
+ "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_GREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_GREP="$ac_path_GREP"
+ ac_path_GREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_GREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_GREP"; then
+ as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if ${ac_cv_path_EGREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+ then ac_cv_path_EGREP="$GREP -E"
+ else
+ if test -z "$EGREP"; then
+ ac_path_EGREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in egrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_EGREP" || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+ # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'EGREP' >> "conftest.nl"
+ "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_EGREP="$ac_path_EGREP"
+ ac_path_EGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_EGREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_EGREP"; then
+ as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_EGREP=$EGREP
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_stdc=yes
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then :
+ :
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+ inttypes.h stdint.h unistd.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in sys/ioctl.h unistd.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+# Check for debug
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to compile debug version" >&5
+$as_echo_n "checking whether to compile debug version... " >&6; }
+# Check whether --enable-debug was given.
+if test "${enable_debug+set}" = set; then :
+ enableval=$enable_debug; cppflag=-D__RTAUDIO_DEBUG__
+ cxxflag=-g
+ object_path=Debug
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ cppflag=
+ cxxflag=-O2
+ object_path=Release
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+# Checks for functions
+ac_fn_c_check_func "$LINENO" "gettimeofday" "ac_cv_func_gettimeofday"
+if test "x$ac_cv_func_gettimeofday" = xyes; then :
+ cppflag="$cppflag -DHAVE_GETTIMEOFDAY"
+fi
+
+
+# Set paths if prefix is defined
+if test "x$prefix" != "x" && test "x$prefix" != "xNONE"; then
+ LIBS="$LIBS -L$prefix/lib"
+ CPPFLAGS="$CPPFLAGS -I$prefix/include"
+fi
+
+# For -I and -D flags
+CPPFLAGS="$CPPFLAGS $cppflag"
+
+# For debugging and optimization ... overwrite default because it has both -g and -O2
+#CXXFLAGS="$CXXFLAGS $cxxflag"
+CXXFLAGS="$cxxflag"
+
+# Check compiler and use -Wall if gnu.
+if test $GXX = "yes" ; then
+ cxxflag="-Wall -Wextra"
+
+fi
+
+CXXFLAGS="$CXXFLAGS $cxxflag"
+
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+ as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+$as_echo_n "checking build system type... " >&6; }
+if ${ac_cv_build+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+ ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+ as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+$as_echo "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
+$as_echo_n "checking host system type... " >&6; }
+if ${ac_cv_host+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "x$host_alias" = x; then
+ ac_cv_host=$ac_cv_build
+else
+ ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
+$as_echo "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+
+sharedlib="librtaudio.so"
+
+sharedname="librtaudio.so.\$(RELEASE)"
+
+libflags="-shared -Wl,-soname,\$(SHARED).\$(MAJOR) -o \$(SHARED).\$(RELEASE)"
+
+case $host in
+ *-apple*)
+ sharedlib="librtaudio.dylib"
+
+ sharedname="librtaudio.\$(RELEASE).dylib"
+
+ libflags="-dynamiclib -o librtaudio.\$(RELEASE).dylib"
+
+esac
+
+# Checks for package options and external software
+api=""
+
+req=""
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for audio API" >&5
+$as_echo_n "checking for audio API... " >&6; }
+case $host in
+ *-*-netbsd*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: using OSS" >&5
+$as_echo "using OSS" >&6; }
+ api="$api -D__LINUX_OSS__"
+ LIBS="$LIBS -lossaudio"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5
+$as_echo_n "checking for pthread_create in -lpthread... " >&6; }
+if ${ac_cv_lib_pthread_pthread_create+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpthread $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_create ();
+int
+main ()
+{
+return pthread_create ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_pthread_pthread_create=yes
+else
+ ac_cv_lib_pthread_pthread_create=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5
+$as_echo "$ac_cv_lib_pthread_pthread_create" >&6; }
+if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBPTHREAD 1
+_ACEOF
+
+ LIBS="-lpthread $LIBS"
+
+else
+ as_fn_error $? "RtAudio requires the pthread library!" "$LINENO" 5
+fi
+
+ ;;
+
+ *-*-linux*)
+
+# Check whether --with-jack was given.
+if test "${with_jack+set}" = set; then :
+ withval=$with_jack;
+ api="$api -D__UNIX_JACK__"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: using JACK" >&5
+$as_echo "using JACK" >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for jack_client_open in -ljack" >&5
+$as_echo_n "checking for jack_client_open in -ljack... " >&6; }
+if ${ac_cv_lib_jack_jack_client_open+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ljack $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char jack_client_open ();
+int
+main ()
+{
+return jack_client_open ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_jack_jack_client_open=yes
+else
+ ac_cv_lib_jack_jack_client_open=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_jack_jack_client_open" >&5
+$as_echo "$ac_cv_lib_jack_jack_client_open" >&6; }
+if test "x$ac_cv_lib_jack_jack_client_open" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBJACK 1
+_ACEOF
+
+ LIBS="-ljack $LIBS"
+
+else
+ as_fn_error $? "JACK support requires the jack library!" "$LINENO" 5
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for snd_pcm_open in -lasound" >&5
+$as_echo_n "checking for snd_pcm_open in -lasound... " >&6; }
+if ${ac_cv_lib_asound_snd_pcm_open+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lasound $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char snd_pcm_open ();
+int
+main ()
+{
+return snd_pcm_open ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_asound_snd_pcm_open=yes
+else
+ ac_cv_lib_asound_snd_pcm_open=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_asound_snd_pcm_open" >&5
+$as_echo "$ac_cv_lib_asound_snd_pcm_open" >&6; }
+if test "x$ac_cv_lib_asound_snd_pcm_open" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBASOUND 1
+_ACEOF
+
+ LIBS="-lasound $LIBS"
+
+else
+ as_fn_error $? "Jack support also requires the asound library!" "$LINENO" 5
+fi
+
+fi
+
+
+ # Look for ALSA flag
+
+# Check whether --with-alsa was given.
+if test "${with_alsa+set}" = set; then :
+ withval=$with_alsa;
+ api="$api -D__LINUX_ALSA__"
+ req="$req alsa"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: using ALSA" >&5
+$as_echo "using ALSA" >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for snd_pcm_open in -lasound" >&5
+$as_echo_n "checking for snd_pcm_open in -lasound... " >&6; }
+if ${ac_cv_lib_asound_snd_pcm_open+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lasound $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char snd_pcm_open ();
+int
+main ()
+{
+return snd_pcm_open ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_asound_snd_pcm_open=yes
+else
+ ac_cv_lib_asound_snd_pcm_open=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_asound_snd_pcm_open" >&5
+$as_echo "$ac_cv_lib_asound_snd_pcm_open" >&6; }
+if test "x$ac_cv_lib_asound_snd_pcm_open" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBASOUND 1
+_ACEOF
+
+ LIBS="-lasound $LIBS"
+
+else
+ as_fn_error $? "ALSA support requires the asound library!" "$LINENO" 5
+fi
+
+fi
+
+
+ # Look for PULSE flag
+
+# Check whether --with-pulse was given.
+if test "${with_pulse+set}" = set; then :
+ withval=$with_pulse;
+ api="$api -D__LINUX_PULSE__"
+ req="$req libpulse-simple"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: using PulseAudio" >&5
+$as_echo "using PulseAudio" >&6; }
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for PULSE" >&5
+$as_echo_n "checking for PULSE... " >&6; }
+
+if test -n "$PULSE_CFLAGS"; then
+ pkg_cv_PULSE_CFLAGS="$PULSE_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libpulse-simple\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libpulse-simple") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_PULSE_CFLAGS=`$PKG_CONFIG --cflags "libpulse-simple" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$PULSE_LIBS"; then
+ pkg_cv_PULSE_LIBS="$PULSE_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libpulse-simple\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libpulse-simple") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_PULSE_LIBS=`$PKG_CONFIG --libs "libpulse-simple" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ PULSE_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libpulse-simple" 2>&1`
+ else
+ PULSE_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libpulse-simple" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$PULSE_PKG_ERRORS" >&5
+
+ as_fn_error $? "PulseAudio support requires the pulse-simple library!" "$LINENO" 5
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ as_fn_error $? "PulseAudio support requires the pulse-simple library!" "$LINENO" 5
+else
+ PULSE_CFLAGS=$pkg_cv_PULSE_CFLAGS
+ PULSE_LIBS=$pkg_cv_PULSE_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+fi
+ LIBS="$LIBS `pkg-config --libs libpulse-simple`"
+fi
+
+
+ # Look for OSS flag
+
+# Check whether --with-oss was given.
+if test "${with_oss+set}" = set; then :
+ withval=$with_oss;
+ api="$api -D__LINUX_OSS__"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: using OSS" >&5
+$as_echo "using OSS" >&6; }
+fi
+
+
+ # If no audio api flags specified, use ALSA
+ if test "$api" == ""; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: using ALSA" >&5
+$as_echo "using ALSA" >&6; }
+ api=-D__LINUX_ALSA__
+
+ req="$req alsa"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for snd_pcm_open in -lasound" >&5
+$as_echo_n "checking for snd_pcm_open in -lasound... " >&6; }
+if ${ac_cv_lib_asound_snd_pcm_open+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lasound $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char snd_pcm_open ();
+int
+main ()
+{
+return snd_pcm_open ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_asound_snd_pcm_open=yes
+else
+ ac_cv_lib_asound_snd_pcm_open=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_asound_snd_pcm_open" >&5
+$as_echo "$ac_cv_lib_asound_snd_pcm_open" >&6; }
+if test "x$ac_cv_lib_asound_snd_pcm_open" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBASOUND 1
+_ACEOF
+
+ LIBS="-lasound $LIBS"
+
+else
+ as_fn_error $? "ALSA support requires the asound library!" "$LINENO" 5
+fi
+
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5
+$as_echo_n "checking for pthread_create in -lpthread... " >&6; }
+if ${ac_cv_lib_pthread_pthread_create+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpthread $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_create ();
+int
+main ()
+{
+return pthread_create ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_pthread_pthread_create=yes
+else
+ ac_cv_lib_pthread_pthread_create=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5
+$as_echo "$ac_cv_lib_pthread_pthread_create" >&6; }
+if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBPTHREAD 1
+_ACEOF
+
+ LIBS="-lpthread $LIBS"
+
+else
+ as_fn_error $? "RtAudio requires the pthread library!" "$LINENO" 5
+fi
+
+ ;;
+
+ *-apple*)
+
+# Check whether --with-jack was given.
+if test "${with_jack+set}" = set; then :
+ withval=$with_jack;
+ api="$api -D__UNIX_JACK__"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: using JACK" >&5
+$as_echo "using JACK" >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for jack_client_open in -ljack" >&5
+$as_echo_n "checking for jack_client_open in -ljack... " >&6; }
+if ${ac_cv_lib_jack_jack_client_open+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ljack $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char jack_client_open ();
+int
+main ()
+{
+return jack_client_open ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_jack_jack_client_open=yes
+else
+ ac_cv_lib_jack_jack_client_open=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_jack_jack_client_open" >&5
+$as_echo "$ac_cv_lib_jack_jack_client_open" >&6; }
+if test "x$ac_cv_lib_jack_jack_client_open" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBJACK 1
+_ACEOF
+
+ LIBS="-ljack $LIBS"
+
+else
+ as_fn_error $? "JACK support requires the jack library!" "$LINENO" 5
+fi
+
+fi
+
+
+# AC_CHECK_HEADER(jack/jack.h, [], [AC_MSG_ERROR(Jack header file not found!)] )
+# LIBS="$LIBS -framework jackmp" ], )
+
+
+ # Look for Core flag
+
+# Check whether --with-core was given.
+if test "${with_core+set}" = set; then :
+ withval=$with_core;
+ api="$api -D__MACOSX_CORE__"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: using CoreAudio" >&5
+$as_echo "using CoreAudio" >&6; }
+ ac_fn_c_check_header_mongrel "$LINENO" "CoreAudio/CoreAudio.h" "ac_cv_header_CoreAudio_CoreAudio_h" "$ac_includes_default"
+if test "x$ac_cv_header_CoreAudio_CoreAudio_h" = xyes; then :
+
+else
+ as_fn_error $? "CoreAudio header files not found!" "$LINENO" 5
+fi
+
+
+ LIBS="$LIBS -framework CoreAudio -framework CoreFoundation"
+fi
+
+
+ # If no audio api flags specified, use CoreAudio
+ if test "$api" == ""; then
+ api=-D__MACOSX_CORE__
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: using CoreAudio" >&5
+$as_echo "using CoreAudio" >&6; }
+ ac_fn_c_check_header_mongrel "$LINENO" "CoreAudio/CoreAudio.h" "ac_cv_header_CoreAudio_CoreAudio_h" "$ac_includes_default"
+if test "x$ac_cv_header_CoreAudio_CoreAudio_h" = xyes; then :
+
+else
+ as_fn_error $? "CoreAudio header files not found!" "$LINENO" 5
+fi
+
+
+ LIBS="-framework CoreAudio -framework CoreFoundation"
+
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5
+$as_echo_n "checking for pthread_create in -lpthread... " >&6; }
+if ${ac_cv_lib_pthread_pthread_create+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpthread $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_create ();
+int
+main ()
+{
+return pthread_create ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_pthread_pthread_create=yes
+else
+ ac_cv_lib_pthread_pthread_create=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5
+$as_echo "$ac_cv_lib_pthread_pthread_create" >&6; }
+if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBPTHREAD 1
+_ACEOF
+
+ LIBS="-lpthread $LIBS"
+
+else
+ as_fn_error $? "RtAudio requires the pthread library!" "$LINENO" 5
+fi
+
+ ;;
+
+ *-mingw32*)
+
+# Check whether --with-asio was given.
+if test "${with_asio+set}" = set; then :
+ withval=$with_asio;
+ api="$api -D__WINDOWS_ASIO__"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: using ASIO" >&5
+$as_echo "using ASIO" >&6; }
+ objects="asio.o asiodrivers.o asiolist.o iasiothiscallresolver.o"
+
+fi
+
+
+ # Look for DirectSound flag
+
+# Check whether --with-ds was given.
+if test "${with_ds+set}" = set; then :
+ withval=$with_ds;
+ api="$api -D__WINDOWS_DS__"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: using DirectSound" >&5
+$as_echo "using DirectSound" >&6; }
+ LIBS="-ldsound -lwinmm $LIBS"
+fi
+
+
+ # Look for WASAPI flag
+
+# Check whether --with-wasapi was given.
+if test "${with_wasapi+set}" = set; then :
+ withval=$with_wasapi;
+ api="$api -D__WINDOWS_WASAPI__"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: using WASAPI" >&5
+$as_echo "using WASAPI" >&6; }
+ LIBS="-lwinmm -luuid -lksuser $LIBS"
+fi
+
+
+ # If no audio api flags specified, use DS
+ if test "$api" == ""; then
+ api=-D__WINDOWS_DS__
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: using DirectSound" >&5
+$as_echo "using DirectSound" >&6; }
+ LIBS="-ldsound -lwinmm $LIBS"
+ fi
+
+ LIBS="-lole32 $LIBS"
+ ;;
+
+ *)
+ # Default case for unknown realtime systems.
+ as_fn_error $? "Unknown system type for realtime support!" "$LINENO" 5
+ ;;
+esac
+
+CPPFLAGS="$CPPFLAGS $api"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+ for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+
+ (set) 2>&1 |
+ case $as_nl`(ac_space=' '; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ # `set' does not quote correctly, so add quotes: double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \.
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;; #(
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+) |
+ sed '
+ /^ac_cv_env_/b end
+ t clear
+ :clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+ if test -w "$cache_file"; then
+ if test "x$cache_file" != "x/dev/null"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+ if test ! -f "$cache_file" || test -h "$cache_file"; then
+ cat confcache >"$cache_file"
+ else
+ case $cache_file in #(
+ */* | ?:*)
+ mv -f confcache "$cache_file"$$ &&
+ mv -f "$cache_file"$$ "$cache_file" ;; #(
+ *)
+ mv -f confcache "$cache_file" ;;
+ esac
+ fi
+ fi
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+#
+# If the first sed substitution is executed (which looks for macros that
+# take arguments), then branch to the quote section. Otherwise,
+# look for a macro that doesn't take arguments.
+ac_script='
+:mline
+/\\$/{
+ N
+ s,\\\n,,
+ b mline
+}
+t clear
+:clear
+s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g
+t quote
+s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g
+t quote
+b any
+:quote
+s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g
+s/\[/\\&/g
+s/\]/\\&/g
+s/\$/$$/g
+H
+:any
+${
+ g
+ s/^\n//
+ s/\n/ /g
+ p
+}
+'
+DEFS=`sed -n "$ac_script" confdefs.h`
+
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+ ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+ # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
+ # will be set to the directory where LIBOBJS objects are built.
+ as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+ as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in #(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -pR'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -pR'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -pR'
+ fi
+else
+ as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by RtAudio $as_me 4.1, which was
+generated by GNU Autoconf 2.69. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration. Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number and configuration settings, then exit
+ --config print configuration, then exit
+ -q, --quiet, --silent
+ do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+
+Configuration files:
+$config_files
+
+Report bugs to <gary@music.mcgill.ca>."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+RtAudio config.status 4.1
+configured by $0, generated by GNU Autoconf 2.69,
+ with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=?*)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ --*=)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=
+ ac_shift=:
+ ;;
+ *)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+ $as_echo "$ac_cs_version"; exit ;;
+ --config | --confi | --conf | --con | --co | --c )
+ $as_echo "$ac_cs_config"; exit ;;
+ --debug | --debu | --deb | --de | --d | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ '') as_fn_error $? "missing file argument" ;;
+ esac
+ as_fn_append CONFIG_FILES " '$ac_optarg'"
+ ac_need_defaults=false;;
+ --he | --h | --help | --hel | -h )
+ $as_echo "$ac_cs_usage"; exit ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+ *) as_fn_append ac_config_targets " $1"
+ ac_need_defaults=false ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+ set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+ shift
+ \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+ CONFIG_SHELL='$SHELL'
+ export CONFIG_SHELL
+ exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+ $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+ case $ac_config_target in
+ "rtaudio-config") CONFIG_FILES="$CONFIG_FILES rtaudio-config" ;;
+ "librtaudio.pc") CONFIG_FILES="$CONFIG_FILES librtaudio.pc" ;;
+ "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+ "tests/Makefile") CONFIG_FILES="$CONFIG_FILES tests/Makefile" ;;
+
+ *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+ esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+ tmp= ac_tmp=
+ trap 'exit_status=$?
+ : "${ac_tmp:=$tmp}"
+ { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+ trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+ test -d "$tmp"
+} ||
+{
+ tmp=./conf$$-$RANDOM
+ (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+ eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+ ac_cs_awk_cr='\\r'
+else
+ ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+ echo "cat >conf$$subs.awk <<_ACEOF" &&
+ echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+ echo "_ACEOF"
+} >conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+ . ./conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+ ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+ if test $ac_delim_n = $ac_delim_num; then
+ break
+ elif $ac_last_try; then
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+ N
+ s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+ for (key in S) S_is_set[key] = 1
+ FS = "\a"
+
+}
+{
+ line = $ 0
+ nfields = split(line, field, "@")
+ substed = 0
+ len = length(field[1])
+ for (i = 2; i < nfields; i++) {
+ key = field[i]
+ keylen = length(key)
+ if (S_is_set[key]) {
+ value = S[key]
+ line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+ len += length(value) + length(field[++i])
+ substed = 1
+ } else
+ len += 1 + keylen
+ }
+
+ print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+ sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+ cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{
+h
+s///
+s/^/:/
+s/[ ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[ ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[ ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+
+eval set X " :F $CONFIG_FILES "
+shift
+for ac_tag
+do
+ case $ac_tag in
+ :[FHLC]) ac_mode=$ac_tag; continue;;
+ esac
+ case $ac_mode$ac_tag in
+ :[FHL]*:*);;
+ :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+ :[FH]-) ac_tag=-:-;;
+ :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+ esac
+ ac_save_IFS=$IFS
+ IFS=:
+ set x $ac_tag
+ IFS=$ac_save_IFS
+ shift
+ ac_file=$1
+ shift
+
+ case $ac_mode in
+ :L) ac_source=$1;;
+ :[FH])
+ ac_file_inputs=
+ for ac_f
+ do
+ case $ac_f in
+ -) ac_f="$ac_tmp/stdin";;
+ *) # Look for the file first in the build tree, then in the source tree
+ # (if the path is not absolute). The absolute path cannot be DOS-style,
+ # because $ac_f cannot contain `:'.
+ test -f "$ac_f" ||
+ case $ac_f in
+ [\\/$]*) false;;
+ *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+ esac ||
+ as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+ esac
+ case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+ as_fn_append ac_file_inputs " '$ac_f'"
+ done
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ configure_input='Generated from '`
+ $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+ `' by configure.'
+ if test x"$ac_file" != x-; then
+ configure_input="$ac_file. $configure_input"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+ fi
+ # Neutralize special characters interpreted by sed in replacement strings.
+ case $configure_input in #(
+ *\&* | *\|* | *\\* )
+ ac_sed_conf_input=`$as_echo "$configure_input" |
+ sed 's/[\\\\&|]/\\\\&/g'`;; #(
+ *) ac_sed_conf_input=$configure_input;;
+ esac
+
+ case $ac_tag in
+ *:-:* | *:-) cat >"$ac_tmp/stdin" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+ esac
+ ;;
+ esac
+
+ ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ as_dir="$ac_dir"; as_fn_mkdir_p
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+ case $ac_mode in
+ :F)
+ #
+ # CONFIG_FILE
+ #
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+ p
+ q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ ac_datarootdir_hack='
+ s&@datadir@&$datadir&g
+ s&@docdir@&$docdir&g
+ s&@infodir@&$infodir&g
+ s&@localedir@&$localedir&g
+ s&@mandir@&$mandir&g
+ s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+ { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \
+ "$ac_tmp/out"`; test -z "$ac_out"; } &&
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&2;}
+
+ rm -f "$ac_tmp/stdin"
+ case $ac_file in
+ -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+ *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+ esac \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+
+
+
+ esac
+
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+ as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
+
+chmod oug+x rtaudio-config
--- /dev/null
+# Process this file with autoconf to produce a configure script.
+AC_INIT(RtAudio, 4.1, gary@music.mcgill.ca, rtaudio)
+AC_CONFIG_AUX_DIR(config)
+AC_CONFIG_SRCDIR(RtAudio.cpp)
+AC_CONFIG_FILES([rtaudio-config librtaudio.pc Makefile tests/Makefile])
+
+# Fill GXX with something before test.
+AC_SUBST( GXX, ["no"] )
+
+dnl Check for pkg-config program, used for configuring some libraries.
+m4_define_default([PKG_PROG_PKG_CONFIG],
+[AC_MSG_CHECKING([pkg-config])
+AC_MSG_RESULT([no])])
+
+PKG_PROG_PKG_CONFIG
+
+dnl If the pkg-config autoconf support isn't installed, define its
+dnl autoconf macro to disable any packages depending on it.
+m4_define_default([PKG_CHECK_MODULES],
+[AC_MSG_CHECKING([$1])
+AC_MSG_RESULT([no])
+$4])
+
+# Checks for programs.
+AC_PROG_CXX(g++ CC c++ cxx)
+AC_PROG_RANLIB
+AC_PATH_PROG(AR, ar, no)
+if [[ $AR = "no" ]] ; then
+ AC_MSG_ERROR("Could not find ar - needed to create a library");
+fi
+
+# Checks for header files.
+AC_HEADER_STDC
+AC_CHECK_HEADERS(sys/ioctl.h unistd.h)
+
+# Check for debug
+AC_MSG_CHECKING(whether to compile debug version)
+AC_ARG_ENABLE(debug,
+ [ --enable-debug = enable various debug output],
+ [AC_SUBST( cppflag, [-D__RTAUDIO_DEBUG__] ) AC_SUBST( cxxflag, [-g] ) AC_SUBST( object_path, [Debug] ) AC_MSG_RESULT(yes)],
+ [AC_SUBST( cppflag, [] ) AC_SUBST( cxxflag, [-O2] ) AC_SUBST( object_path, [Release] ) AC_MSG_RESULT(no)])
+
+
+# Checks for functions
+AC_CHECK_FUNC(gettimeofday, [cppflag="$cppflag -DHAVE_GETTIMEOFDAY"], )
+
+# Set paths if prefix is defined
+if test "x$prefix" != "x" && test "x$prefix" != "xNONE"; then
+ LIBS="$LIBS -L$prefix/lib"
+ CPPFLAGS="$CPPFLAGS -I$prefix/include"
+fi
+
+# For -I and -D flags
+CPPFLAGS="$CPPFLAGS $cppflag"
+
+# For debugging and optimization ... overwrite default because it has both -g and -O2
+#CXXFLAGS="$CXXFLAGS $cxxflag"
+CXXFLAGS="$cxxflag"
+
+# Check compiler and use -Wall if gnu.
+if [test $GXX = "yes" ;] then
+ AC_SUBST( cxxflag, ["-Wall -Wextra"] )
+fi
+
+CXXFLAGS="$CXXFLAGS $cxxflag"
+
+AC_CANONICAL_HOST
+
+AC_SUBST( sharedlib, ["librtaudio.so"] )
+AC_SUBST( sharedname, ["librtaudio.so.\$(RELEASE)"] )
+AC_SUBST( libflags, ["-shared -Wl,-soname,\$(SHARED).\$(MAJOR) -o \$(SHARED).\$(RELEASE)"] )
+case $host in
+ *-apple*)
+ AC_SUBST( sharedlib, ["librtaudio.dylib"] )
+ AC_SUBST( sharedname, ["librtaudio.\$(RELEASE).dylib"] )
+ AC_SUBST( libflags, ["-dynamiclib -o librtaudio.\$(RELEASE).dylib"] )
+esac
+
+# Checks for package options and external software
+AC_SUBST( api, [""] )
+AC_SUBST( req, [""] )
+AC_MSG_CHECKING(for audio API)
+case $host in
+ *-*-netbsd*)
+ AC_MSG_RESULT(using OSS)
+ api="$api -D__LINUX_OSS__"
+ LIBS="$LIBS -lossaudio"
+ AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR(RtAudio requires the pthread library!))
+ ;;
+
+ *-*-linux*)
+ AC_ARG_WITH(jack, [ --with-jack = choose JACK server support (mac and linux only)], [
+ api="$api -D__UNIX_JACK__"
+ AC_MSG_RESULT(using JACK)
+ AC_CHECK_LIB(jack, jack_client_open, , AC_MSG_ERROR(JACK support requires the jack library!))
+ AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR(Jack support also requires the asound library!))], )
+
+ # Look for ALSA flag
+ AC_ARG_WITH(alsa, [ --with-alsa = choose native ALSA API support (linux only)], [
+ api="$api -D__LINUX_ALSA__"
+ req="$req alsa"
+ AC_MSG_RESULT(using ALSA)
+ AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR(ALSA support requires the asound library!))], )
+
+ # Look for PULSE flag
+ AC_ARG_WITH(pulse, [ --with-pulse = choose PulseAudio API support (linux only)], [
+ api="$api -D__LINUX_PULSE__"
+ req="$req libpulse-simple"
+ AC_MSG_RESULT(using PulseAudio)
+ PKG_CHECK_MODULES([PULSE], [libpulse-simple], , AC_MSG_ERROR(PulseAudio support requires the pulse-simple library!))
+ LIBS="$LIBS `pkg-config --libs libpulse-simple`" ], )
+
+ # Look for OSS flag
+ AC_ARG_WITH(oss, [ --with-oss = choose OSS API support (linux only)], [
+ api="$api -D__LINUX_OSS__"
+ AC_MSG_RESULT(using OSS)], )
+
+ # If no audio api flags specified, use ALSA
+ if [test "$api" == "";] then
+ AC_MSG_RESULT(using ALSA)
+ AC_SUBST( api, [-D__LINUX_ALSA__] )
+ req="$req alsa"
+ AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR(ALSA support requires the asound library!))
+ fi
+
+ AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR(RtAudio requires the pthread library!))
+ ;;
+
+ *-apple*)
+ AC_ARG_WITH(jack, [ --with-jack = choose JACK server support (unix only)], [
+ api="$api -D__UNIX_JACK__"
+ AC_MSG_RESULT(using JACK)
+ AC_CHECK_LIB(jack, jack_client_open, , AC_MSG_ERROR(JACK support requires the jack library!))], )
+
+# AC_CHECK_HEADER(jack/jack.h, [], [AC_MSG_ERROR(Jack header file not found!)] )
+# LIBS="$LIBS -framework jackmp" ], )
+
+
+ # Look for Core flag
+ AC_ARG_WITH(core, [ --with-core = choose CoreAudio API support (mac only)], [
+ api="$api -D__MACOSX_CORE__"
+ AC_MSG_RESULT(using CoreAudio)
+ AC_CHECK_HEADER(CoreAudio/CoreAudio.h, [], [AC_MSG_ERROR(CoreAudio header files not found!)] )
+ LIBS="$LIBS -framework CoreAudio -framework CoreFoundation" ], )
+
+ # If no audio api flags specified, use CoreAudio
+ if [test "$api" == ""; ] then
+ AC_SUBST( api, [-D__MACOSX_CORE__] )
+ AC_MSG_RESULT(using CoreAudio)
+ AC_CHECK_HEADER(CoreAudio/CoreAudio.h,
+ [],
+ [AC_MSG_ERROR(CoreAudio header files not found!)] )
+ AC_SUBST( LIBS, ["-framework CoreAudio -framework CoreFoundation"] )
+ fi
+
+ AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR(RtAudio requires the pthread library!))
+ ;;
+
+ *-mingw32*)
+ AC_ARG_WITH(asio, [ --with-asio = choose ASIO API support (windoze only)], [
+ api="$api -D__WINDOWS_ASIO__"
+ AC_MSG_RESULT(using ASIO)
+ AC_SUBST( objects, ["asio.o asiodrivers.o asiolist.o iasiothiscallresolver.o"] ) ], )
+
+ # Look for DirectSound flag
+ AC_ARG_WITH(ds, [ --with-ds = choose DirectSound API support (windoze only)], [
+ api="$api -D__WINDOWS_DS__"
+ AC_MSG_RESULT(using DirectSound)
+ LIBS="-ldsound -lwinmm $LIBS" ], )
+
+ # Look for WASAPI flag
+ AC_ARG_WITH(wasapi, [ --with-wasapi = choose Windows Audio Session API support (windoze only)], [
+ api="$api -D__WINDOWS_WASAPI__"
+ AC_MSG_RESULT(using WASAPI)
+ LIBS="-lwinmm -luuid -lksuser $LIBS" ], )
+
+ # If no audio api flags specified, use DS
+ if [test "$api" == "";] then
+ AC_SUBST( api, [-D__WINDOWS_DS__] )
+ AC_MSG_RESULT(using DirectSound)
+ LIBS="-ldsound -lwinmm $LIBS"
+ fi
+
+ LIBS="-lole32 $LIBS"
+ ;;
+
+ *)
+ # Default case for unknown realtime systems.
+ AC_MSG_ERROR(Unknown system type for realtime support!)
+ ;;
+esac
+
+CPPFLAGS="$CPPFLAGS $api"
+
+AC_OUTPUT
+
+chmod oug+x rtaudio-config
--- /dev/null
+prefix=@prefix@
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include
+
+Name: librtaudio
+Description: RtAudio - a set of C++ classes that provide a common API for realtime audio input/output
+Version: 4.1.1
+Requires: @req@
+Libs: -L${libdir} -lrtaudio
+Libs.private: -lpthread
+Cflags: -pthread -I${includedir} @CPPFLAGS@
\ No newline at end of file
--- /dev/null
+#! /bin/sh
+if (test "x$#" != "x1") ; then
+ echo "Usage: $0 [--libs | --cxxflags | --cppflags]"
+ exit;
+fi
+
+LIBRARY="@LIBS@"
+CXXFLAGS="@CXXFLAGS@"
+CPPFLAGS="@CPPFLAGS@"
+
+if (test "x$1" = "x--libs") ; then
+ echo "$LIBRARY -lrtaudio"
+elif (test "x$1" = "x--cxxflags") ; then
+ echo "$CXXFLAGS"
+elif (test "x$1" = "x--cppflags") ; then
+ echo "$CPPFLAGS"
+else
+ echo "Unknown option: $1"
+fi
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * channel
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include <math.h>
+#include "sampleChannel.h"
+#include "patch.h"
+#include "conf.h"
+#include "wave.h"
+#include "pluginHost.h"
+#include "waveFx.h"
+#include "mixerHandler.h"
+#include "log.h"
+
+
+extern Patch G_Patch;
+extern Mixer G_Mixer;
+extern Conf G_Conf;
+#ifdef WITH_VST
+extern PluginHost G_PluginHost;
+#endif
+
+
+SampleChannel::SampleChannel(int bufferSize)
+ : Channel (CHANNEL_SAMPLE, STATUS_EMPTY, bufferSize),
+ frameRewind (-1),
+ wave (NULL),
+ tracker (0),
+ begin (0),
+ end (0),
+ pitch (gDEFAULT_PITCH),
+ boost (1.0f),
+ mode (DEFAULT_CHANMODE),
+ qWait (false),
+ fadeinOn (false),
+ fadeinVol (1.0f),
+ fadeoutOn (false),
+ fadeoutVol (1.0f),
+ fadeoutTracker (0),
+ fadeoutStep (DEFAULT_FADEOUT_STEP),
+ readActions (true),
+ midiInReadActions(0x0),
+ midiInPitch (0x0)
+{
+ rsmp_state = src_new(SRC_LINEAR, 2, NULL);
+ pChan = (float *) malloc(kernelAudio::realBufsize * 2 * sizeof(float));
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+SampleChannel::~SampleChannel() {
+ if (wave)
+ delete wave;
+ src_delete(rsmp_state);
+ free(pChan);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::clear() {
+
+ /** TODO - these memsets can be done only if status PLAY (if below),
+ * but it would require extra clearPChan calls when samples stop */
+
+ memset(vChan, 0, sizeof(float) * bufferSize);
+ memset(pChan, 0, sizeof(float) * bufferSize);
+
+ if (status & (STATUS_PLAY | STATUS_ENDING)) {
+ tracker = fillChan(vChan, tracker, 0);
+ if (fadeoutOn && fadeoutType == XFADE) {
+ gLog("[clear] filling pChan fadeoutTracker=%d\n", fadeoutTracker);
+ fadeoutTracker = fillChan(pChan, fadeoutTracker, 0);
+ }
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::calcVolumeEnv(int frame) {
+
+ /* method: check this frame && next frame, then calculate delta */
+
+ recorder::action *a0 = NULL;
+ recorder::action *a1 = NULL;
+ int res;
+
+ /* get this action on frame 'frame'. It's unlikely that the action
+ * is not found. */
+
+ res = recorder::getAction(index, ACTION_VOLUME, frame, &a0);
+ if (res == 0)
+ return;
+
+ /* get the action next to this one.
+ * res == -1: a1 not found, this is the last one. Rewind the search
+ * and use action at frame number 0 (actions[0]).
+ * res == -2 ACTION_VOLUME not found. This should never happen */
+
+ res = recorder::getNextAction(index, ACTION_VOLUME, frame, &a1);
+
+ if (res == -1)
+ res = recorder::getAction(index, ACTION_VOLUME, 0, &a1);
+
+ volume_i = a0->fValue;
+ volume_d = ((a1->fValue - a0->fValue) / ((a1->frame - a0->frame) / 2)) * 1.003f;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::hardStop(int frame) {
+ if (frame != 0) // clear data in range [frame, bufferSize-1]
+ clearChan(vChan, frame);
+ status = STATUS_OFF;
+ reset(frame);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::onBar(int frame) {
+ ///if (mode == LOOP_REPEAT && status == STATUS_PLAY)
+ /// //setXFade(frame);
+ /// reset(frame);
+
+ if (mode == LOOP_REPEAT) {
+ if (status == STATUS_PLAY)
+ //setXFade(frame);
+ reset(frame);
+ }
+ else
+ if (mode == LOOP_ONCE_BAR) {
+ if (status == STATUS_WAIT) {
+ status = STATUS_PLAY;
+ tracker = fillChan(vChan, tracker, frame);
+ }
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int SampleChannel::save(const char *path) {
+ return wave->writeData(path);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::setBegin(unsigned v) {
+ begin = v;
+ tracker = begin;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::setEnd(unsigned v) {
+ end = v;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::setPitch(float v) {
+
+ pitch = v;
+ rsmp_data.src_ratio = 1/pitch;
+
+ /* if status is off don't slide between frequencies */
+
+ if (status & (STATUS_OFF | STATUS_WAIT))
+ src_set_ratio(rsmp_state, 1/pitch);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::rewind() {
+
+ /* rewind LOOP_ANY or SINGLE_ANY only if it's in read-record-mode */
+
+ if (wave != NULL) {
+ if ((mode & LOOP_ANY) || (recStatus == REC_READING && (mode & SINGLE_ANY)))
+ reset(0); // rewind is user-generated events, always on frame 0
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::parseAction(recorder::action *a, int localFrame, int globalFrame) {
+
+ if (readActions == false)
+ return;
+
+ switch (a->type) {
+ case ACTION_KEYPRESS:
+ if (mode & SINGLE_ANY)
+ start(localFrame, false);
+ break;
+ case ACTION_KEYREL:
+ if (mode & SINGLE_ANY)
+ stop();
+ break;
+ case ACTION_KILLCHAN:
+ if (mode & SINGLE_ANY)
+ kill(localFrame);
+ break;
+ case ACTION_MUTEON:
+ setMute(true); // internal mute
+ break;
+ case ACTION_MUTEOFF:
+ unsetMute(true); // internal mute
+ break;
+ case ACTION_VOLUME:
+ calcVolumeEnv(globalFrame);
+ break;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::sum(int frame, bool running) {
+
+ if (wave == NULL || status & ~(STATUS_PLAY | STATUS_ENDING))
+ return;
+
+ if (frame != frameRewind) {
+
+ /* volume envelope, only if seq is running */
+
+ if (running) {
+ volume_i += volume_d;
+ if (volume_i < 0.0f)
+ volume_i = 0.0f;
+ else
+ if (volume_i > 1.0f)
+ volume_i = 1.0f;
+ }
+
+ /* fadein or fadeout processes. If mute, delete any signal. */
+
+ /** TODO - big issue: fade[in/out]Vol * internal_volume might be a
+ * bad choice: it causes glitches when muting on and off during a
+ * volume envelope. */
+
+ if (mute || mute_i) {
+ vChan[frame] = 0.0f;
+ vChan[frame+1] = 0.0f;
+ }
+ else
+ if (fadeinOn) {
+ if (fadeinVol < 1.0f) {
+ vChan[frame] *= fadeinVol * volume_i;
+ vChan[frame+1] *= fadeinVol * volume_i;
+ fadeinVol += 0.01f;
+ }
+ else {
+ fadeinOn = false;
+ fadeinVol = 0.0f;
+ }
+ }
+ else
+ if (fadeoutOn) {
+ if (fadeoutVol > 0.0f) { // fadeout ongoing
+ if (fadeoutType == XFADE) {
+ vChan[frame] *= volume_i;
+ vChan[frame+1] *= volume_i;
+ vChan[frame] = pChan[frame] * fadeoutVol * volume_i;
+ vChan[frame+1] = pChan[frame+1] * fadeoutVol * volume_i;
+ }
+ else {
+ vChan[frame] *= fadeoutVol * volume_i;
+ vChan[frame+1] *= fadeoutVol * volume_i;
+ }
+ fadeoutVol -= fadeoutStep;
+ }
+ else { // fadeout end
+ fadeoutOn = false;
+ fadeoutVol = 1.0f;
+
+ /* QWait ends with the end of the xfade */
+
+ if (fadeoutType == XFADE) {
+ qWait = false;
+ }
+ else {
+ if (fadeoutEnd == DO_MUTE)
+ mute = true;
+ else
+ if (fadeoutEnd == DO_MUTE_I)
+ mute_i = true;
+ else // DO_STOP
+ hardStop(frame);
+ }
+ }
+ }
+ else {
+ vChan[frame] *= volume_i;
+ vChan[frame+1] *= volume_i;
+ }
+ }
+ else {
+
+ if (mode & (SINGLE_BASIC | SINGLE_PRESS | SINGLE_RETRIG) ||
+ (mode == SINGLE_ENDLESS && status == STATUS_ENDING) ||
+ (mode & LOOP_ANY && !running)) // stop loops when the seq is off
+ {
+ status = STATUS_OFF;
+ }
+
+ /* temporary stop LOOP_ONCE not in ENDING status, otherwise they
+ * would return in wait, losing the ENDING status */
+
+ //if (mode == LOOP_ONCE && status != STATUS_ENDING)
+ if ((mode & (LOOP_ONCE | LOOP_ONCE_BAR)) && status != STATUS_ENDING)
+ status = STATUS_WAIT;
+
+ /* check for end of samples. SINGLE_ENDLESS runs forever unless
+ * it's in ENDING mode. */
+
+ reset(frame);
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::onZero(int frame) {
+
+ if (wave == NULL)
+ return;
+
+ if (mode & LOOP_ANY) {
+
+ /* do a crossfade if the sample is playing. Regular chanReset
+ * instead if it's muted, otherwise a click occurs */
+
+ if (status == STATUS_PLAY) {
+ /*
+ if (mute || mute_i)
+ reset(frame);
+ else
+ setXFade(frame);
+ */
+ reset(frame);
+ }
+ else
+ if (status == STATUS_ENDING)
+ hardStop(frame);
+ }
+
+ if (status == STATUS_WAIT) { /// FIXME - should be inside previous if!
+ status = STATUS_PLAY;
+ tracker = fillChan(vChan, tracker, frame);
+ }
+
+ if (recStatus == REC_ENDING) {
+ recStatus = REC_STOPPED;
+ setReadActions(false); // rec stop
+ }
+ else
+ if (recStatus == REC_WAITING) {
+ recStatus = REC_READING;
+ setReadActions(true); // rec start
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::quantize(int index, int localFrame, int globalFrame) {
+
+ /* skip if LOOP_ANY or not in quantizer-wait mode */
+
+ if ((mode & LOOP_ANY) || !qWait)
+ return;
+
+ /* no fadeout if the sample starts for the first time (from a
+ * STATUS_OFF), it would be meaningless. */
+
+ if (status == STATUS_OFF) {
+ status = STATUS_PLAY;
+ qWait = false;
+ tracker = fillChan(vChan, tracker, localFrame); /// FIXME: ???
+ }
+ else
+ //setXFade(localFrame);
+ reset(localFrame);
+
+ /* this is the moment in which we record the keypress, if the
+ * quantizer is on. SINGLE_PRESS needs overdub */
+
+ if (recorder::canRec(this)) {
+ if (mode == SINGLE_PRESS)
+ recorder::startOverdub(index, ACTION_KEYS, globalFrame);
+ else
+ recorder::rec(index, ACTION_KEYPRESS, globalFrame);
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int SampleChannel::getPosition() {
+ if (status & ~(STATUS_EMPTY | STATUS_MISSING | STATUS_OFF)) // if is not (...)
+ return tracker - begin;
+ else
+ return -1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::setMute(bool internal) {
+
+ if (internal) {
+
+ /* global mute is on? don't waste time with fadeout, just mute it
+ * internally */
+
+ if (mute)
+ mute_i = true;
+ else {
+ if (isPlaying())
+ setFadeOut(DO_MUTE_I);
+ else
+ mute_i = true;
+ }
+ }
+ else {
+
+ /* internal mute is on? don't waste time with fadeout, just mute it
+ * globally */
+
+ if (mute_i)
+ mute = true;
+ else {
+
+ /* sample in play? fadeout needed. Else, just mute it globally */
+
+ if (isPlaying())
+ setFadeOut(DO_MUTE);
+ else
+ mute = true;
+ }
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::unsetMute(bool internal) {
+ if (internal) {
+ if (mute)
+ mute_i = false;
+ else {
+ if (isPlaying())
+ setFadeIn(internal);
+ else
+ mute_i = false;
+ }
+ }
+ else {
+ if (mute_i)
+ mute = false;
+ else {
+ if (isPlaying())
+ setFadeIn(internal);
+ else
+ mute = false;
+ }
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::calcFadeoutStep() {
+ if (end - tracker < (1 / DEFAULT_FADEOUT_STEP) * 2)
+ fadeoutStep = ceil((end - tracker) / volume) * 2; /// or volume_i ???
+ else
+ fadeoutStep = DEFAULT_FADEOUT_STEP;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::setReadActions(bool v) {
+ if (v)
+ readActions = true;
+ else {
+ readActions = false;
+ if (G_Conf.recsStopOnChanHalt)
+ kill(0); /// FIXME - wrong frame value
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::setFadeIn(bool internal) {
+ if (internal) mute_i = false; // remove mute before fading in
+ else mute = false;
+ fadeinOn = true;
+ fadeinVol = 0.0f;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::setFadeOut(int actionPostFadeout) {
+ calcFadeoutStep();
+ fadeoutOn = true;
+ fadeoutVol = 1.0f;
+ fadeoutType = FADEOUT;
+ fadeoutEnd = actionPostFadeout;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::setXFade(int frame) {
+
+ gLog("[xFade] frame=%d tracker=%d\n", frame, tracker);
+
+ calcFadeoutStep();
+ fadeoutOn = true;
+ fadeoutVol = 1.0f;
+ fadeoutType = XFADE;
+ fadeoutTracker = fillChan(pChan, tracker, 0, false);
+ reset(frame);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+/* on reset, if frame > 0 and in play, fill again pChan to create
+ * something like this:
+ *
+ * |abcdefabcdefab*abcdefabcde|
+ * [old data-----]*[new data--]
+ *
+ * */
+
+void SampleChannel::reset(int frame) {
+ //fadeoutTracker = tracker; // store old frame number for xfade
+ tracker = begin;
+ mute_i = false;
+ if (frame > 0 && status & (STATUS_PLAY | STATUS_ENDING))
+ tracker = fillChan(vChan, tracker, frame);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::empty() {
+ status = STATUS_OFF;
+ if (wave) {
+ delete wave;
+ wave = NULL;
+ }
+ status = STATUS_EMPTY;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::pushWave(Wave *w) {
+ wave = w;
+ status = STATUS_OFF;
+ begin = 0;
+ end = wave->size;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool SampleChannel::allocEmpty(int frames, int takeId) {
+
+ Wave *w = new Wave();
+ if (!w->allocEmpty(frames))
+ return false;
+
+ char wname[32];
+ sprintf(wname, "TAKE-%d", takeId);
+
+ w->pathfile = gGetCurrentPath()+"/"+wname; // FIXME - use gGetSlash() in utils.h
+ w->name = wname;
+ wave = w;
+ status = STATUS_OFF;
+ begin = 0;
+ end = wave->size;
+
+ return true;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::process(float *buffer) {
+
+#ifdef WITH_VST
+ G_PluginHost.processStack(vChan, PluginHost::CHANNEL, this);
+#endif
+
+ for (int j=0; j<bufferSize; j+=2) {
+ buffer[j] += vChan[j] * volume * panLeft * boost;
+ buffer[j+1] += vChan[j+1] * volume * panRight * boost;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::kill(int frame) {
+ if (wave != NULL && status != STATUS_OFF) {
+ if (mute || mute_i || (status == STATUS_WAIT && mode & LOOP_ANY))
+ hardStop(frame);
+ else
+ setFadeOut(DO_STOP);
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::stopBySeq() {
+
+ /* kill loop channels and recs if "samplesStopOnSeqHalt" == true,
+ * else do nothing and return. Always kill at frame=0, this is a
+ * user-generated event. */
+
+ if (!G_Conf.chansStopOnSeqHalt)
+ return;
+
+ if (mode & (LOOP_BASIC | LOOP_ONCE | LOOP_REPEAT))
+ kill(0);
+
+ /** FIXME - merge these */
+
+ /* when a channel has recs in play?
+ * Recorder has events for that channel
+ * G_Mixer has at least one sample in play
+ * Recorder's channel is active (altrimenti può capitare che
+ * si stoppino i sample suonati manualmente in un canale con rec
+ * disattivate) */
+
+ if (hasActions && readActions && status == STATUS_PLAY)
+ kill(0);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::stop() {
+ if (mode == SINGLE_PRESS && status == STATUS_PLAY) {
+ if (mute || mute_i)
+ hardStop(0); /// FIXME - wrong frame value
+ else
+ setFadeOut(DO_STOP);
+ }
+ else // stop a SINGLE_PRESS immediately, if the quantizer is on
+ if (mode == SINGLE_PRESS && qWait == true)
+ qWait = false;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int SampleChannel::load(const char *file) {
+
+ if (strcmp(file, "") == 0 || gIsDir(file)) {
+ gLog("[SampleChannel] file not specified\n");
+ return SAMPLE_LEFT_EMPTY;
+ }
+
+ if (strlen(file) > FILENAME_MAX)
+ return SAMPLE_PATH_TOO_LONG;
+
+ Wave *w = new Wave();
+
+ if (!w->open(file)) {
+ gLog("[SampleChannel] %s: read error\n", file);
+ delete w;
+ return SAMPLE_READ_ERROR;
+ }
+
+ if (w->channels() > 2) {
+ gLog("[SampleChannel] %s: unsupported multichannel wave\n", file);
+ delete w;
+ return SAMPLE_MULTICHANNEL;
+ }
+
+ if (!w->readData()) {
+ delete w;
+ return SAMPLE_READ_ERROR;
+ }
+
+ if (w->channels() == 1) /** FIXME: error checking */
+ wfx_monoToStereo(w);
+
+ if (w->rate() != G_Conf.samplerate) {
+ gLog("[SampleChannel] input rate (%d) != system rate (%d), conversion needed\n",
+ w->rate(), G_Conf.samplerate);
+ w->resample(G_Conf.rsmpQuality, G_Conf.samplerate);
+ }
+
+ pushWave(w);
+
+ /* sample name must be unique. Start from k = 1, zero is too nerdy */
+
+ std::string oldName = wave->name;
+ int k = 1;
+ while (!mh_uniqueSamplename(this, wave->name.c_str())) {
+ wave->updateName((oldName + "-" + gItoa(k)).c_str());
+ k++;
+ }
+
+ gLog("[SampleChannel] %s loaded in channel %d\n", file, index);
+ return SAMPLE_LOADED_OK;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int SampleChannel::loadByPatch(const char *f, int i) {
+
+ int res = load(f);
+
+ volume = G_Patch.getVol(i);
+ key = G_Patch.getKey(i);
+ index = G_Patch.getIndex(i);
+ mode = G_Patch.getMode(i);
+ mute = G_Patch.getMute(i);
+ mute_s = G_Patch.getMute_s(i);
+ solo = G_Patch.getSolo(i);
+ boost = G_Patch.getBoost(i);
+ panLeft = G_Patch.getPanLeft(i);
+ panRight = G_Patch.getPanRight(i);
+ readActions = G_Patch.getRecActive(i);
+ recStatus = readActions ? REC_READING : REC_STOPPED;
+
+ readPatchMidiIn(i);
+ midiInReadActions = G_Patch.getMidiValue(i, "InReadActions");
+ midiInPitch = G_Patch.getMidiValue(i, "InPitch");
+
+ if (res == SAMPLE_LOADED_OK) {
+ setBegin(G_Patch.getBegin(i));
+ setEnd (G_Patch.getEnd(i, wave->size));
+ setPitch(G_Patch.getPitch(i));
+ }
+ else {
+ // volume = DEFAULT_VOL;
+ // mode = DEFAULT_CHANMODE;
+ // status = STATUS_WRONG;
+ // key = 0;
+
+ if (res == SAMPLE_LEFT_EMPTY)
+ status = STATUS_EMPTY;
+ else
+ if (res == SAMPLE_READ_ERROR)
+ status = STATUS_MISSING;
+ }
+
+ return res;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool SampleChannel::canInputRec() {
+ return wave == NULL;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::start(int frame, bool doQuantize) {
+
+ switch (status) {
+ case STATUS_EMPTY:
+ case STATUS_MISSING:
+ case STATUS_WRONG:
+ {
+ return;
+ }
+
+ case STATUS_OFF:
+ {
+ if (mode & LOOP_ANY) {
+ status = STATUS_WAIT;
+ }
+ else {
+ if (G_Mixer.quantize > 0 && G_Mixer.running && doQuantize)
+ qWait = true;
+ else {
+
+ /* fillChan only if frame != 0. If you call fillChan on frame == 0
+ * a duplicate call to fillChan occurs with loss of data. */
+
+ status = STATUS_PLAY;
+ if (frame != 0)
+ tracker = fillChan(vChan, tracker, frame);
+ }
+ }
+ break;
+ }
+
+ case STATUS_PLAY:
+ {
+ if (mode == SINGLE_BASIC)
+ setFadeOut(DO_STOP);
+ else
+ if (mode == SINGLE_RETRIG) {
+ if (G_Mixer.quantize > 0 && G_Mixer.running && doQuantize)
+ qWait = true;
+ else
+ reset(frame);
+ }
+ else
+ if (mode & (LOOP_ANY | SINGLE_ENDLESS))
+ status = STATUS_ENDING;
+ break;
+ }
+
+ case STATUS_WAIT:
+ {
+ status = STATUS_OFF;
+ break;
+ }
+
+ case STATUS_ENDING:
+ {
+ status = STATUS_PLAY;
+ break;
+ }
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::writePatch(FILE *fp, int i, bool isProject) {
+
+ Channel::writePatch(fp, i, isProject);
+
+ const char *path = "";
+ if (wave != NULL) {
+ path = wave->pathfile.c_str();
+ if (isProject)
+ path = gBasename(path).c_str(); // make it portable
+ }
+
+ fprintf(fp, "samplepath%d=%s\n", i, path);
+ fprintf(fp, "chanKey%d=%d\n", i, key);
+ //fprintf(fp, "columnIndex%d=%d\n", i, index);
+ fprintf(fp, "chanmode%d=%d\n", i, mode);
+ fprintf(fp, "chanBegin%d=%d\n", i, begin);
+ fprintf(fp, "chanend%d=%d\n", i, end);
+ fprintf(fp, "chanBoost%d=%f\n", i, boost);
+ fprintf(fp, "chanRecActive%d=%d\n", i, readActions);
+ fprintf(fp, "chanPitch%d=%f\n", i, pitch);
+
+ fprintf(fp, "chanMidiInReadActions%d=%u\n", i, midiInReadActions);
+ fprintf(fp, "chanMidiInPitch%d=%u\n", i, midiInPitch);
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void SampleChannel::clearChan(float *dest, int start) {
+ memset(dest+start, 0, sizeof(float)*(bufferSize-start));
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int SampleChannel::fillChan(float *dest, int start, int offset, bool rewind) {
+
+ int position; // return value: the new position
+
+ if (pitch == 1.0f) {
+
+ /* case 1: 'dest' lies within the original sample boundaries (start-
+ * end) */
+
+ if (start+bufferSize-offset <= end) {
+ memcpy(dest+offset, wave->data+start, (bufferSize-offset)*sizeof(float));
+ position = start+bufferSize-offset;
+ if (rewind)
+ frameRewind = -1;
+ }
+
+ /* case2: 'dest' lies outside the end of the sample, OR the sample
+ * is smaller than 'dest' */
+
+ else {
+ memcpy(dest+offset, wave->data+start, (end-start)*sizeof(float));
+ position = end;
+ if (rewind)
+ frameRewind = end-start+offset;
+ }
+ }
+ else {
+
+ rsmp_data.data_in = wave->data+start; // source data
+ rsmp_data.input_frames = (end-start)/2; // how many readable bytes
+ rsmp_data.data_out = dest+offset; // destination (processed data)
+ rsmp_data.output_frames = (bufferSize-offset)/2; // how many bytes to process
+ rsmp_data.end_of_input = false;
+
+ src_process(rsmp_state, &rsmp_data);
+ int gen = rsmp_data.output_frames_gen*2; // frames generated by this call
+
+ position = start + rsmp_data.input_frames_used*2; // position goes forward of frames_used (i.e. read from wave)
+
+ if (rewind) {
+ if (gen == bufferSize-offset)
+ frameRewind = -1;
+ else
+ frameRewind = gen+offset;
+ }
+ }
+ return position;
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * sampleChannel
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef SAMPLE_CHANNEL_H
+#define SAMPLE_CHANNEL_H
+
+
+#include <samplerate.h>
+#include "channel.h"
+
+
+class SampleChannel : public Channel {
+
+private:
+
+ /* rsmp_state, rsmp_data
+ * structs from libsamplerate */
+
+ SRC_STATE *rsmp_state;
+ SRC_DATA rsmp_data;
+
+ /* pChan
+ * extra virtual channel for processing resampled data. */
+
+ float *pChan;
+
+ /* frameRewind
+ * exact frame in which a rewind occurs */
+
+ int frameRewind;
+
+ /* fillChan
+ * copy from wave to *dest and resample data from wave, if necessary.
+ * Start to fill pChan from byte 'offset'. If rewind=false don't
+ * rewind internal tracker. Returns new sample position, in frames */
+
+ int fillChan(float *dest, int start, int offset, bool rewind=true);
+
+ /* clearChan
+ * set data to zero from start to bufferSize-1. */
+
+ void clearChan(float *dest, int start);
+
+ /* calcFadeoutStep
+ * how many frames are left before the end of the sample? Is there
+ * enough room for a complete fadeout? Should we shorten it? */
+
+ void calcFadeoutStep();
+
+ /* calcVolumeEnv
+ * compute any changes in volume done via envelope tool */
+
+ void calcVolumeEnv(int frame);
+
+public:
+
+ SampleChannel(int bufferSize);
+ ~SampleChannel();
+
+ void clear ();
+ void process (float *buffer);
+ void start (int frame, bool doQuantize);
+ void kill (int frame);
+ void empty ();
+ void stopBySeq ();
+ void stop ();
+ void rewind ();
+ void setMute (bool internal);
+ void unsetMute (bool internal);
+ void reset (int frame);
+ int load (const char *file);
+ int loadByPatch(const char *file, int i);
+ void writePatch (FILE *fp, int i, bool isProject);
+ void quantize (int index, int localFrame, int globalFrame);
+ void onZero (int frame);
+ void onBar (int frame);
+ void parseAction(recorder::action *a, int localFrame, int globalFrame);
+
+ /* fade methods
+ * prepare channel for fade, mixer will take care of the process
+ * during master play. */
+
+ void setFadeIn (bool internal);
+ void setFadeOut (int actionPostFadeout);
+ void setXFade (int frame);
+
+ /* pushWave
+ * add a new wave to an existing channel. */
+
+ void pushWave(class Wave *w);
+
+ /* getPosition
+ * returns the position of an active sample. If EMPTY o MISSING
+ * returns -1. */
+
+ int getPosition();
+
+ /* sum
+ * add sample frames to virtual channel. Frame = processed frame in
+ * Mixer. Running = is Mixer in play? */
+
+ void sum(int frame, bool running);
+
+ /* setPitch
+ * updates the pitch value and chanStart+chanEnd accordingly. */
+
+ void setPitch(float v);
+
+ /* setStart/end
+ * change begin/end read points in sample. */
+
+ void setBegin(unsigned v);
+ void setEnd (unsigned v);
+
+ /* save
+ * save sample to file. */
+
+ int save(const char *path);
+
+ /* hardStop
+ * stop the channel immediately, no further checks. */
+
+ void hardStop(int frame);
+
+ /* allocEmpty
+ * alloc an empty wave used in input recordings. */
+
+ bool allocEmpty(int frames, int takeId);
+
+ /* canInputRec
+ * true if channel can host a new wave from input recording. */
+
+ bool canInputRec();
+
+ /* setReadActions
+ * if enabled, recorder will read actions from this channel */
+
+ void setReadActions(bool v);
+
+ /* ---------------------------------------------------------------- */
+
+ class Wave *wave;
+ int tracker; // chan position
+ int begin;
+ int end;
+ float pitch;
+ float boost;
+ int mode; // mode: see const.h
+ bool qWait; // quantizer wait
+ bool fadeinOn;
+ float fadeinVol;
+ bool fadeoutOn;
+ float fadeoutVol; // fadeout volume
+ int fadeoutTracker; // tracker fadeout, xfade only
+ float fadeoutStep; // fadeout decrease
+ int fadeoutType; // xfade or fadeout
+ int fadeoutEnd; // what to do when fadeout ends
+
+ /* recorder:: stuff */
+
+ bool readActions; // read actions or not
+
+ /* midi stuff */
+
+ uint32_t midiInReadActions;
+ uint32_t midiInPitch;
+
+ /* const - what to do when a fadeout ends */
+
+ enum {
+ DO_STOP = 0x01,
+ DO_MUTE = 0x02,
+ DO_MUTE_I = 0x04
+ };
+
+ /* const - fade types */
+
+ enum {
+ FADEOUT = 0x01,
+ XFADE = 0x02
+ };
+};
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * utils
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "utils.h"
+#if defined(_WIN32) // getcwd (unix) or __getcwd (win)
+ #include <direct.h>
+ #include <windows.h>
+#else
+ #include <unistd.h>
+#endif
+
+#include <cstdarg>
+#include <sys/stat.h> // stat (gDirExists)
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string>
+#include <string.h>
+#include <sstream>
+#include <limits.h>
+#if defined(__APPLE__)
+ #include <libgen.h> // basename unix
+ #include <pwd.h> // getpwuid
+#endif
+
+
+
+bool gFileExists(const char *filename) {
+ FILE *fh = fopen(filename, "rb");
+ if (!fh) {
+ return 0;
+ }
+ else {
+ fclose(fh);
+ return 1;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool gIsDir(const char *path)
+{
+ bool ret;
+
+#if defined(__linux__)
+
+ struct stat s1;
+ stat(path, &s1);
+ ret = S_ISDIR(s1.st_mode);
+
+#elif defined(__APPLE__)
+
+ if (strcmp(path, "")==0)
+ ret = false;
+ else {
+ struct stat s1;
+ stat(path, &s1);
+ ret = S_ISDIR(s1.st_mode);
+
+ /* check if ret is a bundle, a special OS X folder which must be
+ * shown as a regular file (VST).
+ * FIXME - consider native functions CFBundle... */
+
+ if (ret) {
+ std::string tmp = path;
+ tmp += "/Contents/Info.plist";
+ if (gFileExists(tmp.c_str()))
+ ret = false;
+ }
+ }
+
+#elif defined(__WIN32)
+
+ unsigned dwAttrib = GetFileAttributes(path);
+ ret = (dwAttrib != INVALID_FILE_ATTRIBUTES &&
+ (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
+#endif
+
+ return ret & !gIsProject(path);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool gDirExists(const char *path)
+{
+ struct stat st;
+ if (stat(path, &st) != 0 && errno == ENOENT)
+ return false;
+ return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool gMkdir(const char *path)
+{
+#if defined(__linux__) || defined(__APPLE__)
+ if (mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0)
+#else
+ if (_mkdir(path) == 0)
+#endif
+ return true;
+ return false;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::string gBasename(const char *path)
+{
+ std::string out = path;
+ out.erase(0, out.find_last_of(gGetSlash().c_str())+1);
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::string gDirname(const char *path)
+{
+ std::string out = path;
+ out.erase(out.find_last_of(gGetSlash().c_str()));
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::string gGetCurrentPath()
+{
+ char buf[PATH_MAX];
+#if defined(__WIN32)
+ if (_getcwd(buf, PATH_MAX) != NULL)
+#else
+ if (getcwd(buf, PATH_MAX) != NULL)
+#endif
+ return buf;
+ else
+ return "";
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::string gGetExt(const char *file)
+{
+ int len = strlen(file);
+ int pos = len;
+ while (pos>0) {
+ if (file[pos] == '.')
+ break;
+ pos--;
+ }
+ if (pos==0)
+ return "";
+ std::string out = file;
+ return out.substr(pos+1, len);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::string gStripExt(const char *file)
+{
+ int len = strlen(file);
+ int pos = -1;
+ for (int i=0; i<len; i++)
+ if (file[i] == '.') {
+ pos = i;
+ break;
+ }
+ std::string out = file;
+ return pos == -1 ? out : out.substr(0, pos);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool gIsProject(const char *path)
+{
+ /** FIXME - checks too weak */
+
+ if (gGetExt(path) == "gprj" && gDirExists(path))
+ return 1;
+ return 0;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool gIsPatch(const char *path)
+{
+ if (gGetExt(path) == "gptc")
+ return 1;
+ return 0;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::string gGetProjectName(const char *path)
+{
+ std::string out;
+ out = gStripExt(path);
+
+ int i = out.size();
+ while (i>=0) { /// TODO - use gGetSlash()
+#if defined(__linux__) || defined(__APPLE__)
+ if (out[i] == '/')
+#elif defined(_WIN32)
+ if (out[i] == '\\')
+#endif
+ break;
+ i--;
+ }
+
+ out.erase(0, i+1); // includes the '/' (or '\' on windows)
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::string gGetSlash() // TODO - create SLASH macro or constant
+{
+#if defined(_WIN32)
+ return "\\";
+#else
+ return "/";
+#endif
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::string gItoa(int i)
+{
+ std::stringstream out;
+ out << i;
+ return out.str();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::string gTrim(const char *f)
+{
+ std::string out = f;
+ return gTrim(out);
+}
+
+
+std::string gTrim(const std::string &s)
+{
+ std::size_t first = s.find_first_not_of(" \n\t");
+ std::size_t last = s.find_last_not_of(" \n\t");
+ return s.substr(first, last-first+1);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::string gReplace(std::string in, const std::string& search, const std::string& replace)
+{
+ size_t pos = 0;
+ while ((pos = in.find(search, pos)) != std::string::npos) {
+ in.replace(pos, search.length(), replace);
+ pos += replace.length();
+ }
+ return in;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::string gStripFileUrl(const char *f)
+{
+ std::string out = f;
+ out = gReplace(out, "file://", "");
+ out = gReplace(out, "%20", " ");
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::string gGetHomePath()
+{
+ char path[PATH_MAX];
+
+#if defined(__linux__)
+
+ snprintf(path, PATH_MAX, "%s/.giada", getenv("HOME"));
+
+#elif defined(_WIN32)
+
+ snprintf(path, PATH_MAX, ".");
+
+#elif defined(__APPLE__)
+
+ struct passwd *p = getpwuid(getuid());
+ if (p == NULL) {
+ gLog("[gGetHomePath] unable to fetch user infos\n");
+ return "";
+ }
+ else {
+ const char *home = p->pw_dir;
+ snprintf(path, PATH_MAX, "%s/Library/Application Support/Giada", home);
+ }
+
+#endif
+
+ return std::string(path);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gSplit(std::string in, std::string sep, gVector<std::string> *v)
+{
+ std::string full = in;
+ std::string token = "";
+ size_t curr = 0;
+ size_t next = -1;
+ do {
+ curr = next + 1;
+ next = full.find_first_of(sep, curr);
+ token = full.substr(curr, next - curr);
+ if (token != "")
+ v->add(token);
+ }
+ while (next != std::string::npos);
+}
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * utils
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef UTILS_H
+#define UTILS_H
+
+
+#include <string>
+#include <cstdio>
+#include "log.h"
+
+
+/* gVector
+ * lightweight template class. */
+
+template <class T> class gVector
+{
+public:
+
+ /* gVector()
+ * default constructor, no parameters */
+
+ gVector() : size(0), s(NULL) {}
+
+ /* gVector(const &)
+ * copy-constructor, when gVector a = b (where b is gVector).
+ * Default constructor doesn't copy referenced ojbects, so we need
+ * to re-allocate the internal stack for the copied object */
+
+ gVector(const gVector &other)
+ {
+ s = new T[other.size];
+ for (unsigned i=0; i<other.size; i++)
+ s[i] = other.s[i];
+ size = other.size;
+ }
+
+
+ ~gVector()
+ {
+ /// FIXME empty s with clear()?!?
+ }
+
+
+ void add(const T &item)
+ {
+ T *tmp = new T[size+1]; /// TODO: chunk increment (size+N), N ~= 16
+ for (unsigned i=0; i<size; i++)
+ tmp[i] = s[i];
+ tmp[size] = item;
+ delete[] s;
+ s = tmp;
+ size++;
+ }
+
+
+ int del(const T &item)
+ {
+ for (unsigned i=0; i<size; i++)
+ if (s[i] == item)
+ return del(i);
+ return -1;
+ }
+
+
+ int del(unsigned p)
+ {
+ if (p > size-1) gLog("[vector] del() outside! requested=%d, size=%d\n", p, size);
+ T *tmp = new T[size-1];
+ unsigned i=0;
+ unsigned j=0;
+ while (i<size) {
+ if (i != p) {
+ tmp[j] = s[i];
+ j++;
+ }
+ i++;
+ }
+ delete[] s;
+ s = tmp;
+ size -= 1;
+ return size;
+ }
+
+
+ void clear()
+ {
+ if (size > 0) {
+ delete [] s;
+ s = NULL;
+ size = 0;
+ }
+ }
+
+
+ void swap(unsigned x, unsigned y)
+ {
+ T tmp = s[x];
+ s[x] = s[y];
+ s[y] = tmp;
+ }
+
+
+ T &at(unsigned p)
+ {
+ if (p > size-1) gLog("[vector] at() outside! requested=%d, size=%d\n", p, size);
+ return s[p];
+ }
+
+
+ T &last()
+ {
+ return s[size-1];
+ }
+
+
+ unsigned size;
+ T *s; // stack (array of T)
+};
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool gFileExists(const char *path);
+
+bool gDirExists(const char *path);
+
+bool gIsDir(const char *path);
+
+bool gIsProject(const char *path);
+
+bool gIsPatch(const char *path);
+
+bool gMkdir(const char *path);
+
+std::string gBasename(const char *path);
+
+std::string gReplace(std::string in, const std::string& search, const std::string& replace);
+
+std::string gDirname(const char *path);
+
+std::string gTrim(const char *path);
+std::string gTrim(const std::string &s);
+
+std::string gGetCurrentPath();
+
+std::string gGetHomePath();
+
+std::string gStripFileUrl(const char *path);
+
+std::string gGetExt(const char *path);
+
+std::string gStripExt(const char *path);
+
+std::string gGetProjectName(const char *path);
+
+std::string gGetSlash();
+
+std::string gItoa(int i);
+
+void gSplit(std::string in, std::string sep, gVector<std::string> *v);
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * wave
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h> // memcpy
+#include <math.h>
+#include "wave.h"
+#include "utils.h"
+#include "conf.h"
+#include "init.h"
+#include "log.h"
+
+
+extern Conf G_Conf;
+
+
+Wave::Wave()
+ : data (NULL),
+ size (0),
+ isLogical(0),
+ isEdited (0) {}
+
+
+/* ------------------------------------------------------------------ */
+
+
+Wave::~Wave() {
+ clear();
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Wave::open(const char *f) {
+
+ pathfile = f;
+ name = gStripExt(gBasename(f).c_str());
+ fileIn = sf_open(f, SFM_READ, &inHeader);
+
+ if (fileIn == NULL) {
+ gLog("[wave] unable to read %s. %s\n", f, sf_strerror(fileIn));
+ pathfile = "";
+ name = "";
+ return 0;
+ }
+
+ isLogical = false;
+ isEdited = false;
+
+ return 1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+/* how to read and write with libsndfile:
+ *
+ * a frame consists of all items (samples) that belong to the same
+ * point in time. So in each frame there are as many items as there
+ * are channels.
+ *
+ * Quindi:
+ * frame = [item, item, ...]
+ * In pratica:
+ * frame1 = [itemLeft, itemRight]
+ * frame2 = [itemLeft, itemRight]
+ * ...
+ */
+
+int Wave::readData() {
+ size = inHeader.frames * inHeader.channels;
+ data = (float *) malloc(size * sizeof(float));
+ if (data == NULL) {
+ gLog("[wave] unable to allocate memory\n");
+ return 0;
+ }
+
+ if (sf_read_float(fileIn, data, size) != size)
+ gLog("[wave] warning: incomplete read!\n");
+
+ sf_close(fileIn);
+ return 1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Wave::writeData(const char *f) {
+
+ /* prepare the header for output file */
+
+ outHeader.samplerate = inHeader.samplerate;
+ outHeader.channels = inHeader.channels;
+ outHeader.format = inHeader.format;
+
+ fileOut = sf_open(f, SFM_WRITE, &outHeader);
+ if (fileOut == NULL) {
+ gLog("[wave] unable to open %s for exporting\n", f);
+ return 0;
+ }
+
+ int out = sf_write_float(fileOut, data, size);
+ if (out != (int) size) {
+ gLog("[wave] error while exporting %s! %s\n", f, sf_strerror(fileOut));
+ return 0;
+ }
+
+ isLogical = false;
+ isEdited = false;
+ sf_close(fileOut);
+ return 1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Wave::clear() {
+ if (data != NULL) {
+ free(data);
+ data = NULL;
+ pathfile = "";
+ size = 0;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Wave::allocEmpty(unsigned __size) {
+
+ /* the caller must pass a __size for stereo values */
+
+ /// FIXME - this way if malloc fails size becomes wrong
+ size = __size;
+ data = (float *) malloc(size * sizeof(float));
+ if (data == NULL) {
+ gLog("[wave] unable to allocate memory\n");
+ return 0;
+ }
+
+ memset(data, 0, sizeof(float) * size); /// FIXME - is it useful?
+
+ inHeader.samplerate = G_Conf.samplerate;
+ inHeader.channels = 2;
+ inHeader.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT; // wave only
+
+ isLogical = true;
+ return 1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int Wave::resample(int quality, int newRate) {
+
+ float ratio = newRate / (float) inHeader.samplerate;
+ int newSize = ceil(size * ratio);
+ if (newSize % 2 != 0) // libsndfile goes crazy with odd size in case of saving
+ newSize++;
+
+ float *tmp = (float *) malloc(newSize * sizeof(float));
+ if (!tmp) {
+ gLog("[wave] unable to allocate memory for resampling\n");
+ return -1;
+ }
+
+ SRC_DATA src_data;
+ src_data.data_in = data;
+ src_data.input_frames = size/2; // in frames, i.e. /2 (stereo)
+ src_data.data_out = tmp;
+ src_data.output_frames = newSize/2; // in frames, i.e. /2 (stereo)
+ src_data.src_ratio = ratio;
+
+ gLog("[wave] resampling: new size=%d (%d frames)\n", newSize, newSize/2);
+
+ int ret = src_simple(&src_data, quality, 2);
+ if (ret != 0) {
+ gLog("[wave] resampling error: %s\n", src_strerror(ret));
+ return 0;
+ }
+
+ free(data);
+ data = tmp;
+ size = newSize;
+ inHeader.samplerate = newRate;
+ return 1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+std::string Wave::basename() {
+ return gStripExt(gBasename(pathfile.c_str()).c_str());
+}
+
+std::string Wave::extension() {
+ return gGetExt(pathfile.c_str());
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void Wave::updateName(const char *n) {
+ std::string ext = gGetExt(pathfile.c_str());
+ name = gStripExt(gBasename(n).c_str());
+ pathfile = gDirname(pathfile.c_str()) + gGetSlash() + name + "." + ext;
+ isLogical = true;
+
+ /* a wave with updated name must become logical, since the underlying
+ * file does not exist yet. */
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * wave
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef WAVE_H
+#define WAVE_H
+
+
+#include <samplerate.h>
+#include <sndfile.h>
+#include <string>
+
+
+class Wave {
+
+private:
+
+ SNDFILE *fileIn;
+ SNDFILE *fileOut;
+ SF_INFO inHeader;
+ SF_INFO outHeader;
+
+public:
+
+ Wave();
+ ~Wave();
+
+ std::string pathfile; // full path + sample name
+ std::string name; // sample name (changeable)
+
+ float *data;
+ int size; // wave size (size in stereo: size / 2)
+ bool isLogical; // memory only (a take)
+ bool isEdited; // edited via editor
+
+ inline int rate () { return inHeader.samplerate; }
+ inline int channels() { return inHeader.channels; }
+ inline int frames () { return inHeader.frames; }
+ inline void rate (int v) { inHeader.samplerate = v; }
+ inline void channels(int v) { inHeader.channels = v; }
+ inline void frames (int v) { inHeader.frames = v; }
+
+ std::string basename ();
+ std::string extension();
+
+ void updateName(const char *n);
+ int open (const char *f);
+ int readData ();
+ int writeData (const char *f);
+ void clear ();
+
+ /* allocEmpty
+ * alloc an empty waveform. */
+
+ int allocEmpty(unsigned size);
+
+ /* resample
+ * simple algorithm for one-shot resampling. */
+
+ int resample(int quality, int newRate);
+};
+
+#endif
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * waveFx
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#include <math.h>
+#include "waveFx.h"
+#include "channel.h"
+#include "mixer.h"
+#include "wave.h"
+#include "log.h"
+
+
+extern Mixer G_Mixer;
+
+
+float wfx_normalizeSoft(Wave *w) {
+ float peak = 0.0f;
+ float abs = 0.0f;
+ for (int i=0; i<w->size; i++) { // i++: both L and R samples
+ abs = fabs(w->data[i]);
+ if (abs > peak)
+ peak = abs;
+ }
+
+ /* peak == 0.0f: don't normalize the silence
+ * peak > 1.0f: don't reduce the amplitude, just leave it alone */
+
+ if (peak == 0.0f || peak > 1.0f)
+ return 1.0f;
+
+ return 1.0f / peak;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+bool wfx_monoToStereo(Wave *w) {
+
+ unsigned newSize = w->size * 2;
+ float *dataNew = (float *) malloc(newSize * sizeof(float));
+ if (dataNew == NULL) {
+ gLog("[wfx] unable to allocate memory for mono>stereo conversion\n");
+ return 0;
+ }
+
+ for (int i=0, j=0; i<w->size; i++) {
+ dataNew[j] = w->data[i];
+ dataNew[j+1] = w->data[i];
+ j+=2;
+ }
+
+ free(w->data);
+ w->data = dataNew;
+ w->size = newSize;
+ w->frames(w->frames()*2);
+ w->channels(2);
+
+ return 1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void wfx_silence(Wave *w, int a, int b) {
+
+ /* stereo values */
+ a = a * 2;
+ b = b * 2;
+
+ gLog("[wfx] silencing from %d to %d\n", a, b);
+
+ for (int i=a; i<b; i+=2) {
+ w->data[i] = 0.0f;
+ w->data[i+1] = 0.0f;
+ }
+
+ w->isEdited = true;
+
+ return;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int wfx_cut(Wave *w, int a, int b) {
+ a = a * 2;
+ b = b * 2;
+
+ if (a < 0) a = 0;
+ if (b > w->size) b = w->size;
+
+ /* create a new temp wave and copy there the original one, skipping
+ * the a-b range */
+
+ unsigned newSize = w->size-(b-a);
+ float *temp = (float *) malloc(newSize * sizeof(float));
+ if (temp == NULL) {
+ gLog("[wfx] unable to allocate memory for cutting\n");
+ return 0;
+ }
+
+ gLog("[wfx] cutting from %d to %d, new size=%d (video=%d)\n", a, b, newSize, newSize/2);
+
+ for (int i=0, k=0; i<w->size; i++) {
+ if (i < a || i >= b) { // left margin always included, in order to keep
+ temp[k] = w->data[i]; // the stereo pair
+ k++;
+ }
+ }
+
+ free(w->data);
+ w->data = temp;
+ w->size = newSize;
+ //w->inHeader.frames -= b-a;
+ w->frames(w->frames() - b - a);
+ w->isEdited = true;
+
+ gLog("[wfx] cutting done\n");
+
+ return 1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+int wfx_trim(Wave *w, int a, int b) {
+ a = a * 2;
+ b = b * 2;
+
+ if (a < 0) a = 0;
+ if (b > w->size) b = w->size;
+
+ int newSize = b - a;
+ float *temp = (float *) malloc(newSize * sizeof(float));
+ if (temp == NULL) {
+ gLog("[wfx] unable to allocate memory for trimming\n");
+ return 0;
+ }
+
+ gLog("[wfx] trimming from %d to %d (area = %d)\n", a, b, b-a);
+
+ for (int i=a, k=0; i<b; i++, k++)
+ temp[k] = w->data[i];
+
+ free(w->data);
+ w->data = temp;
+ w->size = newSize;
+ //w->inHeader.frames = b-a;
+ w->frames(b - a);
+ w->isEdited = true;
+
+ return 1;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void wfx_fade(Wave *w, int a, int b, int type) {
+
+ float m = type == 0 ? 0.0f : 1.0f;
+ float d = 1.0f/(float)(b-a);
+ if (type == 1)
+ d = -d;
+
+ a *= 2;
+ b *= 2;
+
+ for (int i=a; i<b; i+=2) {
+ w->data[i] *= m;
+ w->data[i+1] *= m;
+ m += d;
+ }
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+void wfx_smooth(Wave *w, int a, int b) {
+
+ int d = 32; // 64 if stereo data
+
+ /* do nothing if fade edges (both of 32 samples) are > than selected
+ * portion of wave. d*2 => count both edges, (b-a)*2 => stereo
+ * values. */
+
+ if (d*2 > (b-a)*2) {
+ gLog("[WFX] selection is too small, nothing to do\n");
+ return;
+ }
+
+ wfx_fade(w, a, a+d, 0);
+ wfx_fade(w, b-d, b, 1);
+}
--- /dev/null
+/* ---------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * waveFx
+ *
+ * ---------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2015 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine 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.
+ *
+ * Giada - Your Hardcore Loopmachine 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 Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ------------------------------------------------------------------ */
+
+
+#ifndef WAVEFX_H
+#define WAVEFX_H
+
+
+/* normalizeSoft
+ * normalize the wave by returning the dB value for the boost volume. It
+ * doesn't deal with data in memory. */
+
+float wfx_normalizeSoft(class Wave *w);
+
+bool wfx_monoToStereo(class Wave *w);
+
+void wfx_silence(class Wave *w, int a, int b);
+
+int wfx_cut(class Wave *w, int a, int b);
+
+int wfx_trim(class Wave *w, int a, int b);
+
+/* fade
+ * fade in or fade out selection. Fade In = type 0, Fade Out = type 1 */
+
+void wfx_fade(class Wave *w, int a, int b, int type);
+
+/* smooth
+ * smooth edges of selection. */
+
+void wfx_smooth(class Wave *w, int a, int b);
+
+
+#endif