Imported Upstream version 0.9.6~dfsg1
authorJaromír Mikeš <mira.mikes@seznam.cz>
Tue, 9 Jun 2015 18:43:28 +0000 (20:43 +0200)
committerJaromír Mikeš <mira.mikes@seznam.cz>
Tue, 9 Jun 2015 18:43:28 +0000 (20:43 +0200)
126 files changed:
COPYING [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
README.md [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
configure.ac [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/channel.cpp [new file with mode: 0644]
src/channel.h [new file with mode: 0644]
src/conf.cpp [new file with mode: 0644]
src/conf.h [new file with mode: 0644]
src/const.h [new file with mode: 0644]
src/dataStorage.cpp [new file with mode: 0644]
src/dataStorage.h [new file with mode: 0644]
src/gd_about.cpp [new file with mode: 0644]
src/gd_about.h [new file with mode: 0644]
src/gd_actionEditor.cpp [new file with mode: 0644]
src/gd_actionEditor.h [new file with mode: 0644]
src/gd_beatsInput.cpp [new file with mode: 0644]
src/gd_beatsInput.h [new file with mode: 0644]
src/gd_bpmInput.cpp [new file with mode: 0644]
src/gd_bpmInput.h [new file with mode: 0644]
src/gd_browser.cpp [new file with mode: 0644]
src/gd_browser.h [new file with mode: 0644]
src/gd_config.cpp [new file with mode: 0644]
src/gd_config.h [new file with mode: 0644]
src/gd_devInfo.cpp [new file with mode: 0644]
src/gd_devInfo.h [new file with mode: 0644]
src/gd_editor.cpp [new file with mode: 0644]
src/gd_editor.h [new file with mode: 0644]
src/gd_keyGrabber.cpp [new file with mode: 0644]
src/gd_keyGrabber.h [new file with mode: 0644]
src/gd_mainWindow.cpp [new file with mode: 0644]
src/gd_mainWindow.h [new file with mode: 0644]
src/gd_midiGrabber.cpp [new file with mode: 0644]
src/gd_midiGrabber.h [new file with mode: 0644]
src/gd_midiOutputSetup.cpp [new file with mode: 0644]
src/gd_midiOutputSetup.h [new file with mode: 0644]
src/gd_pluginList.cpp [new file with mode: 0644]
src/gd_pluginList.h [new file with mode: 0644]
src/gd_pluginWindow.cpp [new file with mode: 0644]
src/gd_pluginWindow.h [new file with mode: 0644]
src/gd_pluginWindowGUI.cpp [new file with mode: 0644]
src/gd_pluginWindowGUI.h [new file with mode: 0644]
src/gd_warnings.cpp [new file with mode: 0644]
src/gd_warnings.h [new file with mode: 0644]
src/ge_actionChannel.cpp [new file with mode: 0644]
src/ge_actionChannel.h [new file with mode: 0644]
src/ge_actionWidget.cpp [new file with mode: 0644]
src/ge_actionWidget.h [new file with mode: 0644]
src/ge_browser.cpp [new file with mode: 0644]
src/ge_browser.h [new file with mode: 0644]
src/ge_channel.cpp [new file with mode: 0644]
src/ge_channel.h [new file with mode: 0644]
src/ge_column.cpp [new file with mode: 0644]
src/ge_column.h [new file with mode: 0644]
src/ge_envelopeChannel.cpp [new file with mode: 0644]
src/ge_envelopeChannel.h [new file with mode: 0644]
src/ge_midiChannel.cpp [new file with mode: 0644]
src/ge_midiChannel.h [new file with mode: 0644]
src/ge_mixed.cpp [new file with mode: 0644]
src/ge_mixed.h [new file with mode: 0644]
src/ge_muteChannel.cpp [new file with mode: 0644]
src/ge_muteChannel.h [new file with mode: 0644]
src/ge_pianoRoll.cpp [new file with mode: 0644]
src/ge_pianoRoll.h [new file with mode: 0644]
src/ge_sampleChannel.cpp [new file with mode: 0644]
src/ge_sampleChannel.h [new file with mode: 0644]
src/ge_waveform.cpp [new file with mode: 0644]
src/ge_waveform.h [new file with mode: 0644]
src/ge_window.cpp [new file with mode: 0644]
src/ge_window.h [new file with mode: 0644]
src/gg_keyboard.cpp [new file with mode: 0644]
src/gg_keyboard.h [new file with mode: 0644]
src/gg_waveTools.cpp [new file with mode: 0644]
src/gg_waveTools.h [new file with mode: 0644]
src/giada.ico [new file with mode: 0644]
src/glue.cpp [new file with mode: 0644]
src/glue.h [new file with mode: 0644]
src/graphics.cpp [new file with mode: 0644]
src/graphics.h [new file with mode: 0644]
src/gui_utils.cpp [new file with mode: 0644]
src/gui_utils.h [new file with mode: 0644]
src/init.cpp [new file with mode: 0644]
src/init.h [new file with mode: 0644]
src/kernelAudio.cpp [new file with mode: 0644]
src/kernelAudio.h [new file with mode: 0644]
src/kernelMidi.cpp [new file with mode: 0644]
src/kernelMidi.h [new file with mode: 0644]
src/log.cpp [new file with mode: 0644]
src/log.h [new file with mode: 0644]
src/main.cpp [new file with mode: 0644]
src/midiChannel.cpp [new file with mode: 0644]
src/midiChannel.h [new file with mode: 0644]
src/mixer.cpp [new file with mode: 0644]
src/mixer.h [new file with mode: 0644]
src/mixerHandler.cpp [new file with mode: 0644]
src/mixerHandler.h [new file with mode: 0644]
src/patch.cpp [new file with mode: 0644]
src/patch.h [new file with mode: 0644]
src/plugin.cpp [new file with mode: 0644]
src/plugin.h [new file with mode: 0644]
src/pluginHost.cpp [new file with mode: 0644]
src/pluginHost.h [new file with mode: 0644]
src/recorder.cpp [new file with mode: 0644]
src/recorder.h [new file with mode: 0644]
src/resource.h [new file with mode: 0644]
src/resource.rc [new file with mode: 0644]
src/rtaudio-mod/Makefile.in [new file with mode: 0644]
src/rtaudio-mod/RtAudio.cpp [new file with mode: 0644]
src/rtaudio-mod/RtAudio.h [new file with mode: 0644]
src/rtaudio-mod/config/config.guess [new file with mode: 0644]
src/rtaudio-mod/config/config.sub [new file with mode: 0755]
src/rtaudio-mod/config/install.sh [new file with mode: 0755]
src/rtaudio-mod/configure [new file with mode: 0755]
src/rtaudio-mod/configure.ac [new file with mode: 0644]
src/rtaudio-mod/librtaudio.pc.in [new file with mode: 0644]
src/rtaudio-mod/rtaudio-config.in [new file with mode: 0644]
src/sampleChannel.cpp [new file with mode: 0644]
src/sampleChannel.h [new file with mode: 0644]
src/utils.cpp [new file with mode: 0644]
src/utils.h [new file with mode: 0644]
src/wave.cpp [new file with mode: 0644]
src/wave.h [new file with mode: 0644]
src/waveFx.cpp [new file with mode: 0644]
src/waveFx.h [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..94a9ed0
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..712ae50
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,813 @@
+
+--------------------------------------------------------------------------------
+
+
+  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)
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..e1c45f3
--- /dev/null
@@ -0,0 +1 @@
+SUBDIRS=src
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..ba0e0fb
--- /dev/null
+++ b/README.md
@@ -0,0 +1,85 @@
+
+
+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.
+
+![Giada Loop Machine screenshot](http://giadamusic.com/public/img/screenshots/giada-loop-machine-screenshot-14-carousel.jpg)
+
+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/>.
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..d1fea40
--- /dev/null
@@ -0,0 +1,1579 @@
+#!/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
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..6ddcf0b
--- /dev/null
@@ -0,0 +1,179 @@
+#                                               -*- 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
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..644c35d
--- /dev/null
@@ -0,0 +1,115 @@
+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
diff --git a/src/channel.cpp b/src/channel.cpp
new file mode 100644 (file)
index 0000000..f9a3b7c
--- /dev/null
@@ -0,0 +1,141 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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);
+}
diff --git a/src/channel.h b/src/channel.h
new file mode 100644 (file)
index 0000000..6540fab
--- /dev/null
@@ -0,0 +1,195 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/conf.cpp b/src/conf.cpp
new file mode 100644 (file)
index 0000000..af1a4fe
--- /dev/null
@@ -0,0 +1,473 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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';
+}
diff --git a/src/conf.h b/src/conf.h
new file mode 100644 (file)
index 0000000..69ea870
--- /dev/null
@@ -0,0 +1,122 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/const.h b/src/const.h
new file mode 100644 (file)
index 0000000..a0b5152
--- /dev/null
@@ -0,0 +1,291 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/dataStorage.cpp b/src/dataStorage.cpp
new file mode 100644 (file)
index 0000000..4704afc
--- /dev/null
@@ -0,0 +1,72 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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;
+}
+
+
+
+
+
diff --git a/src/dataStorage.h b/src/dataStorage.h
new file mode 100644 (file)
index 0000000..4232b68
--- /dev/null
@@ -0,0 +1,49 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gd_about.cpp b/src/gd_about.cpp
new file mode 100644 (file)
index 0000000..8caa7a0
--- /dev/null
@@ -0,0 +1,126 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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();
+}
diff --git a/src/gd_about.h b/src/gd_about.h
new file mode 100644 (file)
index 0000000..695a91e
--- /dev/null
@@ -0,0 +1,58 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gd_actionEditor.cpp b/src/gd_actionEditor.cpp
new file mode 100644 (file)
index 0000000..9737e9a
--- /dev/null
@@ -0,0 +1,472 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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();
+}
diff --git a/src/gd_actionEditor.h b/src/gd_actionEditor.h
new file mode 100644 (file)
index 0000000..71c37fd
--- /dev/null
@@ -0,0 +1,131 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gd_beatsInput.cpp b/src/gd_beatsInput.cpp
new file mode 100644 (file)
index 0000000..0c381c9
--- /dev/null
@@ -0,0 +1,102 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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();
+}
diff --git a/src/gd_beatsInput.h b/src/gd_beatsInput.h
new file mode 100644 (file)
index 0000000..8a5ccca
--- /dev/null
@@ -0,0 +1,56 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gd_bpmInput.cpp b/src/gd_bpmInput.cpp
new file mode 100644 (file)
index 0000000..048fe0a
--- /dev/null
@@ -0,0 +1,105 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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();
+}
diff --git a/src/gd_bpmInput.h b/src/gd_bpmInput.h
new file mode 100644 (file)
index 0000000..e3bc211
--- /dev/null
@@ -0,0 +1,51 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gd_browser.cpp b/src/gd_browser.cpp
new file mode 100644 (file)
index 0000000..188a6c0
--- /dev/null
@@ -0,0 +1,395 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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();
+}
diff --git a/src/gd_browser.h b/src/gd_browser.h
new file mode 100644 (file)
index 0000000..b5e8734
--- /dev/null
@@ -0,0 +1,98 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gd_config.cpp b/src/gd_config.cpp
new file mode 100644 (file)
index 0000000..54f2627
--- /dev/null
@@ -0,0 +1,841 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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();
+}
diff --git a/src/gd_config.h b/src/gd_config.h
new file mode 100644 (file)
index 0000000..1cfc202
--- /dev/null
@@ -0,0 +1,167 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gd_devInfo.cpp b/src/gd_devInfo.cpp
new file mode 100644 (file)
index 0000000..b7c5e40
--- /dev/null
@@ -0,0 +1,107 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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() {}
diff --git a/src/gd_devInfo.h b/src/gd_devInfo.h
new file mode 100644 (file)
index 0000000..c5a06ca
--- /dev/null
@@ -0,0 +1,47 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gd_editor.cpp b/src/gd_editor.cpp
new file mode 100644 (file)
index 0000000..3e96a1b
--- /dev/null
@@ -0,0 +1,491 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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()));
+}
diff --git a/src/gd_editor.h b/src/gd_editor.h
new file mode 100644 (file)
index 0000000..9ae3266
--- /dev/null
@@ -0,0 +1,116 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gd_keyGrabber.cpp b/src/gd_keyGrabber.cpp
new file mode 100644 (file)
index 0000000..469adf0
--- /dev/null
@@ -0,0 +1,144 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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);
+}
diff --git a/src/gd_keyGrabber.h b/src/gd_keyGrabber.h
new file mode 100644 (file)
index 0000000..4e4b2a3
--- /dev/null
@@ -0,0 +1,63 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gd_mainWindow.cpp b/src/gd_mainWindow.cpp
new file mode 100644 (file)
index 0000000..45ebccb
--- /dev/null
@@ -0,0 +1,672 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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);
+}
diff --git a/src/gd_mainWindow.h b/src/gd_mainWindow.h
new file mode 100644 (file)
index 0000000..0361d5d
--- /dev/null
@@ -0,0 +1,210 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gd_midiGrabber.cpp b/src/gd_midiGrabber.cpp
new file mode 100644 (file)
index 0000000..18a9c3c
--- /dev/null
@@ -0,0 +1,261 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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();
+}
+
diff --git a/src/gd_midiGrabber.h b/src/gd_midiGrabber.h
new file mode 100644 (file)
index 0000000..4da9e72
--- /dev/null
@@ -0,0 +1,150 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gd_midiOutputSetup.cpp b/src/gd_midiOutputSetup.cpp
new file mode 100644 (file)
index 0000000..6cc0024
--- /dev/null
@@ -0,0 +1,127 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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);
+}
diff --git a/src/gd_midiOutputSetup.h b/src/gd_midiOutputSetup.h
new file mode 100644 (file)
index 0000000..9acf603
--- /dev/null
@@ -0,0 +1,63 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gd_pluginList.cpp b/src/gd_pluginList.cpp
new file mode 100644 (file)
index 0000000..ade532d
--- /dev/null
@@ -0,0 +1,393 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gd_pluginList.h b/src/gd_pluginList.h
new file mode 100644 (file)
index 0000000..33ac4a1
--- /dev/null
@@ -0,0 +1,107 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gd_pluginWindow.cpp b/src/gd_pluginWindow.cpp
new file mode 100644 (file)
index 0000000..f9119c0
--- /dev/null
@@ -0,0 +1,137 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gd_pluginWindow.h b/src/gd_pluginWindow.h
new file mode 100644 (file)
index 0000000..03d49bf
--- /dev/null
@@ -0,0 +1,76 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gd_pluginWindowGUI.cpp b/src/gd_pluginWindowGUI.cpp
new file mode 100644 (file)
index 0000000..6fd0a07
--- /dev/null
@@ -0,0 +1,196 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gd_pluginWindowGUI.h b/src/gd_pluginWindowGUI.h
new file mode 100644 (file)
index 0000000..1675e73
--- /dev/null
@@ -0,0 +1,84 @@
+
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gd_warnings.cpp b/src/gd_warnings.cpp
new file mode 100644 (file)
index 0000000..31a5dbc
--- /dev/null
@@ -0,0 +1,78 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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;
+}
diff --git a/src/gd_warnings.h b/src/gd_warnings.h
new file mode 100644 (file)
index 0000000..ca9c633
--- /dev/null
@@ -0,0 +1,43 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/ge_actionChannel.cpp b/src/ge_actionChannel.cpp
new file mode 100644 (file)
index 0000000..a98d394
--- /dev/null
@@ -0,0 +1,676 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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;
+}
diff --git a/src/ge_actionChannel.h b/src/ge_actionChannel.h
new file mode 100644 (file)
index 0000000..bd0fe24
--- /dev/null
@@ -0,0 +1,135 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/ge_actionWidget.cpp b/src/ge_actionWidget.cpp
new file mode 100644 (file)
index 0000000..a06eab9
--- /dev/null
@@ -0,0 +1,101 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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);
+}
diff --git a/src/ge_actionWidget.h b/src/ge_actionWidget.h
new file mode 100644 (file)
index 0000000..554f569
--- /dev/null
@@ -0,0 +1,53 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/ge_browser.cpp b/src/ge_browser.cpp
new file mode 100644 (file)
index 0000000..15f6836
--- /dev/null
@@ -0,0 +1,307 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/ge_browser.h b/src/ge_browser.h
new file mode 100644 (file)
index 0000000..8445319
--- /dev/null
@@ -0,0 +1,68 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/ge_channel.cpp b/src/ge_channel.cpp
new file mode 100644 (file)
index 0000000..d244ad6
--- /dev/null
@@ -0,0 +1,287 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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) {}
diff --git a/src/ge_channel.h b/src/ge_channel.h
new file mode 100644 (file)
index 0000000..113f537
--- /dev/null
@@ -0,0 +1,162 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/ge_column.cpp b/src/ge_column.cpp
new file mode 100644 (file)
index 0000000..9acabd6
--- /dev/null
@@ -0,0 +1,304 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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));
+               }
+       }
+}
diff --git a/src/ge_column.h b/src/ge_column.h
new file mode 100644 (file)
index 0000000..84ebc8a
--- /dev/null
@@ -0,0 +1,98 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/ge_envelopeChannel.cpp b/src/ge_envelopeChannel.cpp
new file mode 100644 (file)
index 0000000..442f6ba
--- /dev/null
@@ -0,0 +1,399 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
+                       }
+               }
+
+}
diff --git a/src/ge_envelopeChannel.h b/src/ge_envelopeChannel.h
new file mode 100644 (file)
index 0000000..7a1aa24
--- /dev/null
@@ -0,0 +1,115 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/ge_midiChannel.cpp b/src/ge_midiChannel.cpp
new file mode 100644 (file)
index 0000000..d9dfbf4
--- /dev/null
@@ -0,0 +1,342 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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);
+}
diff --git a/src/ge_midiChannel.h b/src/ge_midiChannel.h
new file mode 100644 (file)
index 0000000..af07fb3
--- /dev/null
@@ -0,0 +1,89 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/ge_mixed.cpp b/src/ge_mixed.cpp
new file mode 100644 (file)
index 0000000..81200c8
--- /dev/null
@@ -0,0 +1,666 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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);
+}
diff --git a/src/ge_mixed.h b/src/ge_mixed.h
new file mode 100644 (file)
index 0000000..26942d5
--- /dev/null
@@ -0,0 +1,355 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/ge_muteChannel.cpp b/src/ge_muteChannel.cpp
new file mode 100644 (file)
index 0000000..01d1391
--- /dev/null
@@ -0,0 +1,411 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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;
+}
diff --git a/src/ge_muteChannel.h b/src/ge_muteChannel.h
new file mode 100644 (file)
index 0000000..5cb0ca1
--- /dev/null
@@ -0,0 +1,103 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/ge_pianoRoll.cpp b/src/ge_pianoRoll.cpp
new file mode 100644 (file)
index 0000000..51e8b95
--- /dev/null
@@ -0,0 +1,730 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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;
+}
diff --git a/src/ge_pianoRoll.h b/src/ge_pianoRoll.h
new file mode 100644 (file)
index 0000000..30c04d2
--- /dev/null
@@ -0,0 +1,183 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/ge_sampleChannel.cpp b/src/ge_sampleChannel.cpp
new file mode 100644 (file)
index 0000000..11bc005
--- /dev/null
@@ -0,0 +1,606 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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;
+}
diff --git a/src/ge_sampleChannel.h b/src/ge_sampleChannel.h
new file mode 100644 (file)
index 0000000..09238d7
--- /dev/null
@@ -0,0 +1,103 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/ge_waveform.cpp b/src/ge_waveform.cpp
new file mode 100644 (file)
index 0000000..5dcbdad
--- /dev/null
@@ -0,0 +1,838 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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();
+}
diff --git a/src/ge_waveform.h b/src/ge_waveform.h
new file mode 100644 (file)
index 0000000..65c1e40
--- /dev/null
@@ -0,0 +1,193 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/ge_window.cpp b/src/ge_window.cpp
new file mode 100644 (file)
index 0000000..aba6fc0
--- /dev/null
@@ -0,0 +1,183 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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;
+}
diff --git a/src/ge_window.h b/src/ge_window.h
new file mode 100644 (file)
index 0000000..79d772e
--- /dev/null
@@ -0,0 +1,73 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gg_keyboard.cpp b/src/gg_keyboard.cpp
new file mode 100644 (file)
index 0000000..8ea9be8
--- /dev/null
@@ -0,0 +1,366 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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());
+}
diff --git a/src/gg_keyboard.h b/src/gg_keyboard.h
new file mode 100644 (file)
index 0000000..6059824
--- /dev/null
@@ -0,0 +1,145 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gg_waveTools.cpp b/src/gg_waveTools.cpp
new file mode 100644 (file)
index 0000000..d8462dd
--- /dev/null
@@ -0,0 +1,103 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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;
+}
+
diff --git a/src/gg_waveTools.h b/src/gg_waveTools.h
new file mode 100644 (file)
index 0000000..8f81ba1
--- /dev/null
@@ -0,0 +1,49 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/giada.ico b/src/giada.ico
new file mode 100644 (file)
index 0000000..e0a90bd
Binary files /dev/null and b/src/giada.ico differ
diff --git a/src/glue.cpp b/src/glue.cpp
new file mode 100644 (file)
index 0000000..7844175
--- /dev/null
@@ -0,0 +1,1187 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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);
+}
diff --git a/src/glue.h b/src/glue.h
new file mode 100644 (file)
index 0000000..d018a6c
--- /dev/null
@@ -0,0 +1,169 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/graphics.cpp b/src/graphics.cpp
new file mode 100644 (file)
index 0000000..82ca2bc
--- /dev/null
@@ -0,0 +1,1631 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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",
+"             ",
+"             ",
+"             ",
+"   .#   #.   ",
+"   #-& &-#   ",
+"    &-=-&    ",
+"     =;=     ",
+"    %-*-&    ",
+"   #-% %-#   ",
+"   .#   #.   ",
+"             ",
+"             ",
+"             "};
diff --git a/src/graphics.h b/src/graphics.h
new file mode 100644 (file)
index 0000000..c76da0f
--- /dev/null
@@ -0,0 +1,102 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gui_utils.cpp b/src/gui_utils.cpp
new file mode 100644 (file)
index 0000000..4c3ffa7
--- /dev/null
@@ -0,0 +1,210 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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);
+}
diff --git a/src/gui_utils.h b/src/gui_utils.h
new file mode 100644 (file)
index 0000000..80c873c
--- /dev/null
@@ -0,0 +1,93 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/init.cpp b/src/init.cpp
new file mode 100644 (file)
index 0000000..51915b7
--- /dev/null
@@ -0,0 +1,188 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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();
+}
diff --git a/src/init.h b/src/init.h
new file mode 100644 (file)
index 0000000..42a1102
--- /dev/null
@@ -0,0 +1,49 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/kernelAudio.cpp b/src/kernelAudio.cpp
new file mode 100644 (file)
index 0000000..094422c
--- /dev/null
@@ -0,0 +1,467 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
+
+}
+
+
diff --git a/src/kernelAudio.h b/src/kernelAudio.h
new file mode 100644 (file)
index 0000000..0d566e8
--- /dev/null
@@ -0,0 +1,96 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/kernelMidi.cpp b/src/kernelMidi.cpp
new file mode 100644 (file)
index 0000000..3a42424
--- /dev/null
@@ -0,0 +1,385 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/kernelMidi.h b/src/kernelMidi.h
new file mode 100644 (file)
index 0000000..b17bbeb
--- /dev/null
@@ -0,0 +1,104 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/log.cpp b/src/log.cpp
new file mode 100644 (file)
index 0000000..0f5be34
--- /dev/null
@@ -0,0 +1,80 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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);
+}
diff --git a/src/log.h b/src/log.h
new file mode 100644 (file)
index 0000000..c53f9c7
--- /dev/null
+++ b/src/log.h
@@ -0,0 +1,45 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644 (file)
index 0000000..687fb05
--- /dev/null
@@ -0,0 +1,98 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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;
+}
diff --git a/src/midiChannel.cpp b/src/midiChannel.cpp
new file mode 100644 (file)
index 0000000..f1e43b7
--- /dev/null
@@ -0,0 +1,323 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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);
+}
diff --git a/src/midiChannel.h b/src/midiChannel.h
new file mode 100644 (file)
index 0000000..e6e1a09
--- /dev/null
@@ -0,0 +1,136 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/mixer.cpp b/src/mixer.cpp
new file mode 100644 (file)
index 0000000..4c25065
--- /dev/null
@@ -0,0 +1,684 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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;
+       }
+}
diff --git a/src/mixer.h b/src/mixer.h
new file mode 100644 (file)
index 0000000..586bacc
--- /dev/null
@@ -0,0 +1,209 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/mixerHandler.cpp b/src/mixerHandler.cpp
new file mode 100644 (file)
index 0000000..4bd520f
--- /dev/null
@@ -0,0 +1,238 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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;
+}
diff --git a/src/mixerHandler.h b/src/mixerHandler.h
new file mode 100644 (file)
index 0000000..f842004
--- /dev/null
@@ -0,0 +1,76 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/patch.cpp b/src/patch.cpp
new file mode 100644 (file)
index 0000000..005c691
--- /dev/null
@@ -0,0 +1,744 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/patch.h b/src/patch.h
new file mode 100644 (file)
index 0000000..e431b85
--- /dev/null
@@ -0,0 +1,95 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/plugin.cpp b/src/plugin.cpp
new file mode 100644 (file)
index 0000000..17cace3
--- /dev/null
@@ -0,0 +1,520 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/plugin.h b/src/plugin.h
new file mode 100644 (file)
index 0000000..0dbaef7
--- /dev/null
@@ -0,0 +1,168 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/pluginHost.cpp b/src/pluginHost.cpp
new file mode 100644 (file)
index 0000000..f28f5aa
--- /dev/null
@@ -0,0 +1,679 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/pluginHost.h b/src/pluginHost.h
new file mode 100644 (file)
index 0000000..9b4db08
--- /dev/null
@@ -0,0 +1,132 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/recorder.cpp b/src/recorder.cpp
new file mode 100644 (file)
index 0000000..a0c8f0d
--- /dev/null
@@ -0,0 +1,698 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/recorder.h b/src/recorder.h
new file mode 100644 (file)
index 0000000..0fd378d
--- /dev/null
@@ -0,0 +1,192 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/resource.h b/src/resource.h
new file mode 100644 (file)
index 0000000..d771ba8
--- /dev/null
@@ -0,0 +1 @@
+#define IDI_ICON1 101
\ No newline at end of file
diff --git a/src/resource.rc b/src/resource.rc
new file mode 100644 (file)
index 0000000..fb0be40
--- /dev/null
@@ -0,0 +1,2 @@
+#include "resource.h"
+IDI_ICON1 ICON DISCARDABLE "giada.ico"
\ No newline at end of file
diff --git a/src/rtaudio-mod/Makefile.in b/src/rtaudio-mod/Makefile.in
new file mode 100644 (file)
index 0000000..89cacdc
--- /dev/null
@@ -0,0 +1,75 @@
+### 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
diff --git a/src/rtaudio-mod/RtAudio.cpp b/src/rtaudio-mod/RtAudio.cpp
new file mode 100644 (file)
index 0000000..5716c59
--- /dev/null
@@ -0,0 +1,10145 @@
+/************************************************************************/\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( &currentRate );\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( &currentWritePointer, &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( &currentWritePointer, &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( &currentReadPointer, &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( &currentReadPointer, &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( &params );\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, &param );\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
diff --git a/src/rtaudio-mod/RtAudio.h b/src/rtaudio-mod/RtAudio.h
new file mode 100644 (file)
index 0000000..a3534ad
--- /dev/null
@@ -0,0 +1,1177 @@
+/************************************************************************/
+/*! \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
diff --git a/src/rtaudio-mod/config/config.guess b/src/rtaudio-mod/config/config.guess
new file mode 100644 (file)
index 0000000..313be34
--- /dev/null
@@ -0,0 +1,1371 @@
+#! /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:
diff --git a/src/rtaudio-mod/config/config.sub b/src/rtaudio-mod/config/config.sub
new file mode 100755 (executable)
index 0000000..9a7d59a
--- /dev/null
@@ -0,0 +1,1366 @@
+#! /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:
diff --git a/src/rtaudio-mod/config/install.sh b/src/rtaudio-mod/config/install.sh
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/src/rtaudio-mod/configure b/src/rtaudio-mod/configure
new file mode 100755 (executable)
index 0000000..9f3d3f8
--- /dev/null
@@ -0,0 +1,5888 @@
+#! /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
diff --git a/src/rtaudio-mod/configure.ac b/src/rtaudio-mod/configure.ac
new file mode 100644 (file)
index 0000000..fa45967
--- /dev/null
@@ -0,0 +1,197 @@
+# 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
diff --git a/src/rtaudio-mod/librtaudio.pc.in b/src/rtaudio-mod/librtaudio.pc.in
new file mode 100644 (file)
index 0000000..45c3f4e
--- /dev/null
@@ -0,0 +1,12 @@
+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
diff --git a/src/rtaudio-mod/rtaudio-config.in b/src/rtaudio-mod/rtaudio-config.in
new file mode 100644 (file)
index 0000000..5f83d51
--- /dev/null
@@ -0,0 +1,19 @@
+#! /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
diff --git a/src/sampleChannel.cpp b/src/sampleChannel.cpp
new file mode 100644 (file)
index 0000000..300db28
--- /dev/null
@@ -0,0 +1,993 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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;
+}
diff --git a/src/sampleChannel.h b/src/sampleChannel.h
new file mode 100644 (file)
index 0000000..736063a
--- /dev/null
@@ -0,0 +1,210 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/utils.cpp b/src/utils.cpp
new file mode 100644 (file)
index 0000000..e7b2e8c
--- /dev/null
@@ -0,0 +1,379 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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);
+}
diff --git a/src/utils.h b/src/utils.h
new file mode 100644 (file)
index 0000000..0c1a2d0
--- /dev/null
@@ -0,0 +1,190 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/wave.cpp b/src/wave.cpp
new file mode 100644 (file)
index 0000000..edaef7b
--- /dev/null
@@ -0,0 +1,245 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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. */
+}
diff --git a/src/wave.h b/src/wave.h
new file mode 100644 (file)
index 0000000..9ce81df
--- /dev/null
@@ -0,0 +1,88 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/waveFx.cpp b/src/waveFx.cpp
new file mode 100644 (file)
index 0000000..25e0812
--- /dev/null
@@ -0,0 +1,224 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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);
+}
diff --git a/src/waveFx.h b/src/waveFx.h
new file mode 100644 (file)
index 0000000..ebdef4a
--- /dev/null
@@ -0,0 +1,59 @@
+/* ---------------------------------------------------------------------
+ *
+ * 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