diff --git a/COPYING.LIB b/COPYING.LIB new file mode 100644 index 0000000..732811e --- /dev/null +++ b/COPYING.LIB @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/Makefile b/Makefile index 59b3aae..b96a120 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,6 @@ ### Generated by Winemaker -# Check (with pkgconfig) for existance of these packages -PACKAGES = jack - -# Usually '/usr' or '/usr/local' PREFIX = /usr -LIBDIR = $(subst $(PREFIX)/,,$(firstword $(wildcard $(addsuffix /wine/, $(PREFIX)/lib32 $(PREFIX)/lib)))) SRCDIR = . SUBDIRS = DLLS = wineasio.dll @@ -32,17 +27,16 @@ wineasio_dll_C_SRCS = asio.c \ regsvr.c wineasio_dll_CXX_SRCS = wineasio_dll_RC_SRCS = -wineasio_dll_LDFLAGS = -m32 -shared \ +wineasio_dll_LDFLAGS = -shared \ + -m32 \ $(wineasio_dll_MODULE:%=%.spec) \ - -mnocygwin + -mnocygwin \ + -L/usr/lib32/wine \ + -L/usr/lib32 wineasio_dll_DLL_PATH = wineasio_dll_DLLS = odbc32 \ ole32 \ - oleaut32 \ - winspool \ - winmm \ - psapi \ - pthread + winmm wineasio_dll_LIBRARY_PATH= wineasio_dll_LIBRARIES= uuid @@ -69,14 +63,11 @@ RC = wrc ### Generic targets -all: asio.h $(PACKAGES) $(SUBDIRS) $(DLLS:%=%.so) $(EXES:%=%.so) - -$(PACKAGES): dummy - pkg-config --exists $@ +all: asio.h $(SUBDIRS) $(DLLS:%=%.so) $(EXES:%=%.so) ### Build rules -.PHONY: all clean dummy $(PACKAGES) +.PHONY: all clean dummy $(SUBDIRS): dummy @cd $@ && $(MAKE) @@ -123,4 +114,4 @@ $(wineasio_dll_MODULE).so: $(wineasio_dll_OBJS) $(WINECC) $(wineasio_dll_LDFLAGS) -o $@ $(wineasio_dll_OBJS) $(wineasio_dll_LIBRARY_PATH) $(DEFLIB) $(wineasio_dll_DLLS:%=-l%) $(wineasio_dll_LIBRARIES:%=-l%) install: - cp wineasio.dll.so $(PREFIX)/$(LIBDIR) + if [ -d /usr/lib32/wine ]; then cp wineasio.dll.so /usr/lib32/wine; else cp wineasio.dll.so /usr/lib/wine; fi diff --git a/Makefile.JackWASIO b/Makefile.JackWASIO deleted file mode 100644 index e60a0b7..0000000 --- a/Makefile.JackWASIO +++ /dev/null @@ -1,127 +0,0 @@ -### Generated by Winemaker - -# Check (with pkgconfig) for existance of these packages -PACKAGES = jack - -# Usually '/usr' or '/usr/local' -PREFIX = /usr -LIBDIR = $(subst $(PREFIX)/,,$(firstword $(wildcard $(addsuffix /wine/, $(PREFIX)/lib32 $(PREFIX)/lib)))) -SRCDIR = . -SUBDIRS = -DLLS = wineasio.dll -EXES = - - - -### Common settings - -CEXTRA = -m32 -g -O2 -DJackWASIO -D__WINESRC__ -D_REENTRANT -fPIC -Wall -pipe -fno-strict-aliasing -Wdeclaration-after-statement -Wwrite-strings -Wpointer-arith -CXXEXTRA = -m32 -DJackWASIO -D__WINESRC__ -D_REENTRANT -fPIC -Wall -pipe -fno-strict-aliasing -Wdeclaration-after-statement -Wwrite-strings -Wpointer-arith -RCEXTRA = -INCLUDE_PATH = -I. -I/usr/include -I$(PREFIX)/include -I$(PREFIX)/include/wine -I$(PREFIX)/include/wine/windows -DLL_PATH = -LIBRARY_PATH = -LIBRARIES = -ljack - - -### wineasio.dll sources and settings - -wineasio_dll_MODULE = wineasio.dll -wineasio_dll_C_SRCS = asio.c \ - main.c \ - regsvr.c -wineasio_dll_CXX_SRCS = -wineasio_dll_RC_SRCS = -wineasio_dll_LDFLAGS = -m32 -shared \ - $(wineasio_dll_MODULE:%=%.spec) \ - -mnocygwin \ - -framework CoreAudio -wineasio_dll_DLL_PATH = -wineasio_dll_DLLS = odbc32 \ - ole32 \ - oleaut32 \ - winspool \ - winmm \ - psapi \ - pthread -wineasio_dll_LIBRARY_PATH= -wineasio_dll_LIBRARIES= uuid - -wineasio_dll_OBJS = $(wineasio_dll_C_SRCS:.c=.o) \ - $(wineasio_dll_CXX_SRCS:.cpp=.o) \ - $(wineasio_dll_RC_SRCS:.rc=.res) - - - -### Global source lists - -C_SRCS = $(wineasio_dll_C_SRCS) -CXX_SRCS = $(wineasio_dll_CXX_SRCS) -RC_SRCS = $(wineasio_dll_RC_SRCS) - - -### Tools - -CC = gcc -CXX = g++ -WINECC = winegcc -RC = wrc - - -### Generic targets - -all: asio.h $(PACKAGES) $(SUBDIRS) $(DLLS:%=%.so) $(EXES:%=%.so) - -$(PACKAGES): dummy - pkg-config --exists $@ - -### Build rules - -.PHONY: all clean dummy $(PACKAGES) - -$(SUBDIRS): dummy - @cd $@ && $(MAKE) - -# Implicit rules - -.SUFFIXES: .cpp .rc .res -DEFINCL = $(INCLUDE_PATH) $(DEFINES) $(OPTIONS) - -.c.o: - $(CC) -c $(DEFINCL) $(CFLAGS) $(CEXTRA) -o $@ $< - -.cpp.o: - $(CXX) -c $(CXXFLAGS) $(CXXEXTRA) $(DEFINCL) -o $@ $< - -.cxx.o: - $(CXX) -c $(CXXFLAGS) $(CXXEXTRA) $(DEFINCL) -o $@ $< - -.rc.res: - $(RC) $(RCFLAGS) $(RCEXTRA) $(DEFINCL) -fo$@ $< - -# Rules for cleaning - -CLEAN_FILES = y.tab.c y.tab.h lex.yy.c core *.orig *.rej \ - \\\#*\\\# *~ *% .\\\#* - -clean:: $(SUBDIRS:%=%/__clean__) $(EXTRASUBDIRS:%=%/__clean__) - $(RM) $(CLEAN_FILES) $(RC_SRCS:.rc=.res) $(C_SRCS:.c=.o) $(CXX_SRCS:.cpp=.o) - $(RM) $(DLLS:%=%.so) $(EXES:%=%.so) $(EXES:%.exe=%) - -$(SUBDIRS:%=%/__clean__): dummy - cd `dirname $@` && $(MAKE) clean - -$(EXTRASUBDIRS:%=%/__clean__): dummy - -cd `dirname $@` && $(RM) $(CLEAN_FILES) - -distclean:: clean - $(RM) asio.h - -### Target specific build rules -DEFLIB = $(LIBRARY_PATH) $(LIBRARIES) $(DLL_PATH) - -$(wineasio_dll_MODULE).so: $(wineasio_dll_OBJS) - $(WINECC) $(wineasio_dll_LDFLAGS) -o $@ $(wineasio_dll_OBJS) $(wineasio_dll_LIBRARY_PATH) $(DEFLIB) $(wineasio_dll_DLLS:%=-l%) $(wineasio_dll_LIBRARIES:%=-l%) - -install: - cp wineasio.dll.so $(PREFIX)/$(LIBDIR) diff --git a/README b/README new file mode 100644 index 0000000..65caa5d --- /dev/null +++ b/README @@ -0,0 +1,180 @@ +CONTENTS +======== +1. Installation +2. General Information +3. Change Log +4. Legal Stuff + +1. INSTALLATION +--------------- + +Copy the file asio.h from Steinberg's ASIO SDK to the wineasio source +directory. + +Due to a bug in winegcc, and the confusion regarding where 32bit wine dlls go +on the various 64bit linux distributions, I've hardcoded support for +/usr/lib/wine and /usr/lib32/wine into the makefile. If your distro uses +another path edit the makefile and replace /usr/lib32/wine with the correct +path. Look at the last line of the makefile. This wineasio should compile and +install 32 bit wineasio on all 64 bit linux'. + +To build, type (as a normal user): make +To install, type (as root): make install +To register the wineasio object, type (as a normal user): regsvr32 wineasio.dll + +The last command registers the ASIO COM object in the default prefix "~/.wine". +If you use another prefix you will have to explicitly specify it, +like "env WINEPREFIX=~/asioapp regsvr32 wineasio.dll" + +2. GENERAL INFORMATION +---------------------- + +The wineasio driver has been almost completely refactored, so the changes are +far too many to explain in detail. These are some of the more obvious changes: + +Wineasio takes advantage of a new function in the jack api, which lets jack +create the wine callback thread. This results in a much cleaner callback +without having to wait on semaphores, requires jack1 (0.117.0) or jack2 (1.9.4) +or later. + +The default sample type has been changed to use ASIOSTFloat32LSB, which is the +native format of jack, and allows the removal of the sample rate conversion +from the processing callback. + +Wineasio can now slave to the jack transport. + +Support has been added for jack's xrun_callback() and bufsize_callback(), the +latter means that asio apps get notified if the jack buffersize changes. + +CreateBuffers() can now change jack's buffersize if so desired. Must be +enabled in the registry, see below. + +The old configuration file has been removed and been replaced by a registry key +(HKEY_CURRENT_USER\Software\Wine\WineASIO). All these options can be overriden +by an environment variable. The key is automatically created with default +values if it doesn't exist when the driver initializes. It contains the +following values: + +[Number of inputs] & [Number of outputs] +These two settings control the number of jack ports that wineasio will try to +open. Defaults are 16 in and 16 out. Environment variables are +WINEASIO_NUMBER_INPUTS and WINEASIO_NUMBER_OUTPUTS + +[Autostart server] +Defaults to off (0), setting it to 1 enables wineasio to launch the jack +server. See the jack documentation for further details. The environment +variable is WINEASIO_AUTOSTART_SERVER, and it can be set to on or off. + +[Connect to hardware] +Defaults to on (1), makes wineasio try to connect the asio channels to the +physical I/O ports on your hardware. Setting it to 0 disables it. The +environment variable is WINEASIO_CONNECT_TO_HARDWARE, and it can be set to on +or off. + +[Fixed buffersize] +Defaults to on (1) and is the old behaviour where the buffer size is controlled +by jack and wineasio has no say in the matter. When set to 0, an asio app will +be able to change the jack buffer size when calling CreateBuffers(). The +environment variable is WINEASIO_FIXED_BUFFERSIZE and it can be set to on or +off. + +[Preferred buffersize] +Defaults to 1024, and is one of the sizes returned by GetBufferSize(), see the +ASIO documentation for details. Must be a power of 2. The other values +returned by the driver are hardcoded in the source, see ASIO_MINIMUM_BUFFERSIZE +which is set at 16, and ASIO_MAXIMUM_BUFFERSIZE which is set to 8192. The +environment variable is WINEASIO_PREFERRED_BUFFERSIZE. Be careful, if you set +a size that isn't supported by the backend, the jack server will most likely +shut down, might be a good idea to change ASIO_MINIMUM_BUFFERSIZE and +ASIO_MAXIMUM_BUFFERSIZE to values you know work on your system before building. + +[Sample type] +Defaults to 19 (decimal) which is ASIOSTFloat32LSB, setting it to 18 (dec) +makes wineasio use ASIOSTIntt32LSB (the old default). Needed for some apps +that don't properly implement the ASIO specification. The environment variable +is WINEASIO_SAMPLE_TYPE. + +In addition there is a WINEASIO_CLIENT_NAME environment variable, that +overrides the JACK client name derrived from the program name. + +3. CHANGE LOG +------------- + +0.9.0 +19-FEB-2011: Nearly complete refactoring of the wineasio codebase (asio.c) (JH) + +0.8.1: +05-OCT-2010: Code from Win32 callback thread moved to JACK process callback, except for bufferSwitch() call. +05-OCT-2010: Switch from int to float for samples. + +0.8.0: +08-AUG-2010: Forward port JackWASIO changes... needs testing hard. (PLJ) + +0.7.6: +27-DEC-2009: Fixes for compilation on 64bit platform. (PLJ) + +0.7.5: +29-Oct-2009: Added fork with call to qjackctl from ASIOControlPanel(). (JH) +29-Oct-2009: Changed the SCHED_FIFO priority of the win32 callback thread. (JH) +28-Oct-2009: Fixed wrongly reported output latency. (JH) + +0.7.4: +08-APR-2008: Updates to the README.TXT (PLJ) +02-APR-2008: Move update to "toggle" to hopefully better place (PLJ) +24-MCH-2008: Don't trace in win32_callback. Set patch-level to 4. (PLJ) +09-JAN-2008: Nedko Arnaudov supplied a fix for Nuendo under WINE. + +0.7.3: +27-DEC-2007: Make slaving to jack transport work, correct port allocation bug. (RB) + +0.7: +01-DEC-2007: In a fit of insanity, I merged JackLab and Robert Reif code bases. (PLJ) + +0.6: +21-NOV-2007: add dynamic client naming (PLJ) + +0.0.3: +17-NOV-2007: Unique port name code (RR) + +0.5: +03-SEP-2007: port mapping and config file (PLJ) + +0.3: +30-APR-2007: corrected connection of in/outputs (RB) + +0.1: +???????????: Initial RB release (RB) + +0.0.2: +12-SEP-2006: Fix thread bug, tidy up code (RR) + +0.0.1: +31-AUG-2006: Initial version (RR) + +4. LEGAL STUFF +-------------- + +Copyright (C) 2006 Robert Reif +Portions copyright (C) 2007 Ralf Beck +Portions copyright (C) 2007 Johnny Petrantoni +Portions copyright (C) 2007 Stephane Letz +Portions copyright (C) 2008 William Steidtmann +Portions copyright (C) 2010 Peter L Jones +Portions copyright (C) 2010 Torben Hohn +Portions copyright (C) 2010 Nedko Arnaudov +Portions copyright (C) 2010 Joakim Hernberg + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + diff --git a/README.TXT b/README.TXT deleted file mode 100644 index addb1d7..0000000 --- a/README.TXT +++ /dev/null @@ -1,179 +0,0 @@ -CONTENTS -======== -1. Installation -2. User Instruction -3. Credits -4. Change Log - -1. INSTALLATION ---------------- - -Before installation edit the prefix path in the Makefile -PREFIX = - -usually this will either be - -PREFIX = /usr -or -PREFIX = /usr/local - -wineasio is currently 32bit only and must live in the lib32 library on -64bit systems. The Makefile tries to autodetect this. But if it fails, -set LIBDIR. - -Copy the file asio.h from Steinberg's asio-sdk to the wineasio directory - -then execute: make -and as root: make install - -then, again as normal user: regsvr32 wineasio.dll - -Notes: -The asio.c file uses 32 bit integer buffers, which is supported by most asio -applications. - - -2. USER INSTRUCTIONS --------------------- - -The driver can be configured in two ways: either using environment variables or -using a configuration file. - -The configuration file can be set per user in ".wineasiocfg". As a fallback, a -site-wide file can be provided in "/etc/default/wineasiocfg" if desired. - -The format for the configuration file is simply "var=val". - -If using the shell, either include the assignment on the command line: - ASIO_INPUTS=0 ~/bin/reaper.exe -or ensure the variable has been exported: - export ASIO_INPUTS=0 - ~/bin/reaper.exe - -The available variables are as follows: -ASIO_INPUTS -ASIO_OUTPUTS -ASIO_INPORTNAME -ASIO_OUTPORTNAME -ASIO_INPORT -ASIO_OUTPORT -ASIO_AUTOCONNECT - - -The last entry allows you to change the client name from the default, which is -constructed from the program name prefixed by "ASIO_". For example, - ASIO_reaper=REAPER -All of the entries beginning ASIO_ can also have entries specific to a client, -using the assigned client name. For example, - ASIO_reaper_INPUTS=0 -or (if the client name has been re-assigned as above), - REAPER_INPUTS=0 - -INPUTS and OUTPUTS ------------------- -These let you limit the number of JACK ports allocated to this client. The -default value for both is 2. - -INPORTNAME and OUTPORTNAME --------------------------- -These allow you to rename the input and output ports for the client. The -default names are "input_" and "output_". For example, - REAPER_OUTPORTNAME0=left - REAPER_OUTPORTNAME1=right - -INPORT and OUTPORT ------------------- -These allow you to connect the client to JACK ports of your choice. The -default is to connect JACK's "hardware" inputs to your client's inputs and your -client's outputs to JACK's "hardware" outputs. You might be running some other -application, e.g. an icecast server, and want to send output to that. For -example, - ASIO_OUTPORT0=idjc-mx:aux_lt - ASIO_OUTPORT1=idjc-mx:aux_rt - -AUTOCONNECT ------------ -This enables you to control whether a client automatically connects to JACK or -whether you will do the wiring up yourself. The default is to connect. To turn -it off, use, for example, - ASIO_reaper_AUTOCONNECT=false -(where "false" is anything other than "true" (case independent)). - -3. CREDITS ----------- - -original code: Robert Reif posted to the wine mailinglist - see also http://bugs.winehq.org/show_bug.cgi?id=2161 - -modified by: Ralf Beck (musical_snake@gmx.de) - -port mapping, config file, dynamic client naming, bringing back in line with -Robert Reif's code, merging JackWASIO: - Peter L Jones (pljones@users.sf.net) - -fix for windows-style path handling: William Steidtmann - -todo: -- make timecode sync to jack transport -- are we leaving memory allocated? - - -4. CHANGE LOG -------------- -0.8.1.0: -05-OCT-2010: Code from Win32 callback thread moved to JACK process callback, except for bufferSwitch() call. -05-OCT-2010: Switch from int to float for samples. - -0.8: -08-AUG-2010: Forward port JackWASIO changes... needs testing hard. (PLJ) - -0.7.6: -27-DEC-2009: Fixes for compilation on 64bit platform. (PLJ) - -0.7.5: -29-Oct-2009: Added fork with call to qjackctl from ASIOControlPanel(). (JH) -29-Oct-2009: Changed the SCHED_FIFO priority of the win32 callback thread. (JH) -28-Oct-2009: Fixed wrongly reported output latency. (JH) - -0.7.4: -08-APR-2008: Updates to the README.TXT (PLJ) -02-APR-2008: Move update to "toggle" to hopefully better place (PLJ) -24-MCH-2008: Don't trace in win32_callback. Set patch-level to 4. (PLJ) -09-JAN-2008: Nedko Arnaudov supplied a fix for Nuendo under WINE. - -0.7.3: -27-DEC-2007: Make slaving to jack transport work, correct port allocation bug. (RB) - -0.7: -01-DEC-2007: In a fit of insanity, I merged JackLab and Robert Reif code bases. (PLJ) - -0.6: -21-NOV-2007: add dynamic client naming (PLJ) - -0.0.3: -17-NOV-2007: Unique port name code (RR) - -JackWASIO (branched from 0.5-ish): -02-OCT-2007: Johnny Petrantoni - added more Linux compatibility code, it should compile now on a linux box (still need edit makefile) -30-SEP-2007: Johnny Petrantoni - added JackPilot preferences for virtual-ports and auto connect, added some macros for linux compilation, - removed prepath from libjack. -29-SEP-2007: Johnny Petrantoni - added mutex and condition code to synch IT IS WORKING NOW!!! tested down to 256 buffersize using reaper. -28-SEP-2007: Johnny Petrantoni - Added pThreadUtilities.h code... but the cracks are still there.. -28-SEP-2007: Johnny Petrantoni - Minor fixes, added TODO and this changes log. -28-SEP-2007: Johnny Petrantoni - Added Stephane GetEXEName() and code cleanup. -28-SEP-2007: Johnny Petrantoni - First Import. - -0.5: -03-SEP-2007: port mapping and config file (PLJ) - -0.3: -30-APR-2007: corrected connection of in/outputs (RB) - -0.1: -???????????: Initial RB release (RB) - -0.0.2: -12-SEP-2006: Fix thread bug, tidy up code (RR) - -0.0.1: -31-AUG-2006: Initial version (RR) diff --git a/asio.c b/asio.c index 4e77d6b..478380f 100644 --- a/asio.c +++ b/asio.c @@ -1,10 +1,13 @@ /* * Copyright (C) 2006 Robert Reif - * Copyright (C) 2007 Ralf Beck - * Copyright (C) 2007 Johnny Petrantoni - * Copyright (C) 2007 Stephane Letz - * Copyright (C) 2009 Joakim Hernberg - * Copyright (C) 2010 Peter L Jones + * Portions copyright (C) 2007 Ralf Beck + * Portions copyright (C) 2007 Johnny Petrantoni + * Portions copyright (C) 2007 Stephane Letz + * Portions copyright (C) 2008 William Steidtmann + * Portions copyright (C) 2010 Peter L Jones + * Portions copyright (C) 2010 Torben Hohn + * Portions copyright (C) 2010 Nedko Arnaudov + * Portions copyright (C) 2010 Joakim Hernberg * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,1360 +24,1708 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -#ifndef JackWASIO -#include "config.h" -#include "settings.h" -#else -#include "config.h.JackWASIO" -static const char* ENVVAR_INPORTNAMEPREFIX = "INPORTNAME"; -static const char* ENVVAR_OUTPORTNAMEPREFIX = "OUTPORTNAME"; -static const char* ENVVAR_INMAP = "INPORT"; -static const char* ENVVAR_OUTMAP = "OUTPORT"; -static const char* DEFAULT_PREFIX = "ASIO"; -static const char* DEFAULT_INPORT = "Input"; -static const char* DEFAULT_OUTPORT = "Output"; -#endif -#include "port.h" +/* + * Todo (maybe): + * create a freely distributable asio.h replacement + * add ASIOOutputReady() support + */ #include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include +#include +#include +#include #include -#include -#include "wine/library.h" #include "wine/debug.h" +#include "objbase.h" +#include "mmsystem.h" +#include "winreg.h" +#include "wine/unicode.h" -#ifndef JackWASIO #include #include -#include -#else -#include -#include -#include -#include "pThreadUtilities.h" -#endif #define IEEE754_64FLOAT 1 #include "asio.h" WINE_DEFAULT_DEBUG_CHANNEL(asio); -/* JACK callback function */ -static int jack_process(jack_nframes_t nframes, void * arg); - -/* WIN32 callback function */ -static DWORD CALLBACK win32_callback(LPVOID arg); - -/* {48D0C522-BFCC-45cc-8B84-17F25F33E6E8} */ -static GUID const CLSID_WineASIO = { -0x48d0c522, 0xbfcc, 0x45cc, { 0x8b, 0x84, 0x17, 0xf2, 0x5f, 0x33, 0xe6, 0xe8 } }; - -#define twoRaisedTo32 4294967296.0 -#define twoRaisedTo32Reciprocal (1.0 / twoRaisedTo32) +#define MAX_ENVIRONMENT_SIZE 6 +#define ASIO_MAX_NAME_LENGTH 32 +#define ASIO_MINIMUM_BUFFERSIZE 16 +#define ASIO_MAXIMUM_BUFFERSIZE 8192 +#define ASIO_PREFERRED_BUFFERSIZE 1024 -/* ASIO drivers use the thiscall calling convention which only Microsoft compilers - * produce. These macros add an extra layer to fixup the registers properly for - * this calling convention. +/* ASIO drivers (breaking the COM specification) use the Microsoft variety of + * thiscall calling convention which gcc is unable to produce. These macros + * add an extra layer to fixup the registers. Borrowed from config.h and the + * wine source code. */ -#ifdef __i386__ /* thiscall functions are i386-specific */ +/* From config.h */ +#define __ASM_DEFINE_FUNC(name,suffix,code) asm(".text\n\t.align 4\n\t.globl " #name suffix "\n\t.type " #name suffix ",@function\n" #name suffix ":\n\t.cfi_startproc\n\t" code "\n\t.cfi_endproc\n\t.previous"); +#define __ASM_GLOBAL_FUNC(name,code) __ASM_DEFINE_FUNC(name,"",code) +#define __ASM_NAME(name) name +#define __ASM_STDCALL(args) "" -#ifdef __GNUC__ -/* GCC erroneously warns that the newly wrapped function - * isn't used, let us help it out of its thinking - */ -#define SUPPRESS_NOTUSED __attribute__((used)) -#else -#define SUPPRESS_NOTUSED -#endif /* __GNUC__ */ +/* From wine source */ +#ifdef __i386__ /* thiscall functions are i386-specific */ -#define WRAP_THISCALL(type, func, parm) \ - extern type func parm; \ - __ASM_GLOBAL_FUNC( func, \ +#define THISCALL(func) __thiscall_ ## func +#define THISCALL_NAME(func) __ASM_NAME("__thiscall_" #func) +#define __thiscall __stdcall +#define DEFINE_THISCALL_WRAPPER(func,args) \ + extern void THISCALL(func)(void); \ + __ASM_GLOBAL_FUNC(__thiscall_ ## func, \ "popl %eax\n\t" \ "pushl %ecx\n\t" \ "pushl %eax\n\t" \ - "jmp " __ASM_NAME("__wrapped_" #func) ); \ - SUPPRESS_NOTUSED static type __wrapped_ ## func parm -#else -#define WRAP_THISCALL(functype, function, param) \ - functype function param -#endif + "jmp " __ASM_NAME(#func) __ASM_STDCALL(args) ) +#else /* __i386__ */ + +#define THISCALL(func) func +#define THISCALL_NAME(func) __ASM_NAME(#func) +#define __thiscall __cdecl +#define DEFINE_THISCALL_WRAPPER(func,args) /* nothing */ + +#endif /* __i386__ */ + +/* Hide ELF symbols for the COM members - No need to to export them */ +#define HIDDEN __attribute__ ((visibility("hidden"))) /***************************************************************************** * IWineAsio interface */ + #define INTERFACE IWineASIO DECLARE_INTERFACE_(IWineASIO,IUnknown) { - STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE; - STDMETHOD_(ULONG,AddRef)(THIS) PURE; - STDMETHOD_(ULONG,Release)(THIS) PURE; - STDMETHOD_(ASIOBool,init)(THIS_ void *sysHandle) PURE; - STDMETHOD_(void,getDriverName)(THIS_ char *name) PURE; - STDMETHOD_(long,getDriverVersion)(THIS) PURE; - STDMETHOD_(void,getErrorMessage)(THIS_ char *string) PURE; - STDMETHOD_(ASIOError,start)(THIS) PURE; - STDMETHOD_(ASIOError,stop)(THIS) PURE; - STDMETHOD_(ASIOError,getChannels)(THIS_ long *numInputChannels, long *numOutputChannels) PURE; - STDMETHOD_(ASIOError,getLatencies)(THIS_ long *inputLatency, long *outputLatency) PURE; - STDMETHOD_(ASIOError,getBufferSize)(THIS_ long *minSize, long *maxSize, long *preferredSize, long *granularity) PURE; - STDMETHOD_(ASIOError,canSampleRate)(THIS_ ASIOSampleRate sampleRate) PURE; - STDMETHOD_(ASIOError,getSampleRate)(THIS_ ASIOSampleRate *sampleRate) PURE; - STDMETHOD_(ASIOError,setSampleRate)(THIS_ ASIOSampleRate sampleRate) PURE; - STDMETHOD_(ASIOError,getClockSources)(THIS_ ASIOClockSource *clocks, long *numSources) PURE; - STDMETHOD_(ASIOError,setClockSource)(THIS_ long reference) PURE; - STDMETHOD_(ASIOError,getSamplePosition)(THIS_ ASIOSamples *sPos, ASIOTimeStamp *tStamp) PURE; - STDMETHOD_(ASIOError,getChannelInfo)(THIS_ ASIOChannelInfo *info) PURE; - STDMETHOD_(ASIOError,createBuffers)(THIS_ ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks) PURE; - STDMETHOD_(ASIOError,disposeBuffers)(THIS) PURE; - STDMETHOD_(ASIOError,controlPanel)(THIS) PURE; - STDMETHOD_(ASIOError,future)(THIS_ long selector,void *opt) PURE; - STDMETHOD_(ASIOError,outputReady)(THIS) PURE; + STDMETHOD_(HRESULT, QueryInterface) (THIS_ IID riid, void** ppvObject) PURE; + STDMETHOD_(ULONG, AddRef) (THIS) PURE; + STDMETHOD_(ULONG, Release) (THIS) PURE; + STDMETHOD_(ASIOBool, Init) (THIS_ void *sysRef) PURE; + STDMETHOD_(void, GetDriverName) (THIS_ char *name) PURE; + STDMETHOD_(LONG, GetDriverVersion) (THIS) PURE; + STDMETHOD_(void, GetErrorMessage) (THIS_ char *string) PURE; + STDMETHOD_(ASIOError, Start) (THIS) PURE; + STDMETHOD_(ASIOError, Stop) (THIS) PURE; + STDMETHOD_(ASIOError, GetChannels) (THIS_ LONG *numInputChannels, LONG *numOutputChannels) PURE; + STDMETHOD_(ASIOError, GetLatencies) (THIS_ LONG *inputLatency, LONG *outputLatency) PURE; + STDMETHOD_(ASIOError, GetBufferSize) (THIS_ LONG *minSize, LONG *maxSize, LONG *preferredSize, LONG *granularity) PURE; + STDMETHOD_(ASIOError, CanSampleRate) (THIS_ ASIOSampleRate sampleRate) PURE; + STDMETHOD_(ASIOError, GetSampleRate) (THIS_ ASIOSampleRate *sampleRate) PURE; + STDMETHOD_(ASIOError, SetSampleRate) (THIS_ ASIOSampleRate sampleRate) PURE; + STDMETHOD_(ASIOError, GetClockSources) (THIS_ ASIOClockSource *clocks, LONG *numSources) PURE; + STDMETHOD_(ASIOError, SetClockSource) (THIS_ LONG index) PURE; + STDMETHOD_(ASIOError, GetSamplePosition) (THIS_ ASIOSamples *sPos, ASIOTimeStamp *tStamp) PURE; + STDMETHOD_(ASIOError, GetChannelInfo) (THIS_ ASIOChannelInfo *info) PURE; + STDMETHOD_(ASIOError, CreateBuffers) (THIS_ ASIOBufferInfo *bufferInfo, LONG numChannels, LONG bufferSize, ASIOCallbacks *asioCallbacks) PURE; + STDMETHOD_(ASIOError, DisposeBuffers) (THIS) PURE; + STDMETHOD_(ASIOError, ControlPanel) (THIS) PURE; + STDMETHOD_(ASIOError, Future) (THIS_ LONG selector,void *opt) PURE; + STDMETHOD_(ASIOError, OutputReady) (THIS) PURE; }; #undef INTERFACE -typedef struct IWineASIO *LPWINEASIO, **LPLPWINEASIO; +typedef struct IWineASIO *LPWINEASIO; -enum +typedef struct IOChannel { - Init, - Run, - Exit -}; - -typedef struct _Channel { - ASIOBool active; - char* buffer; - jack_ringbuffer_t *ring; - const char *port_name; - jack_port_t *port; -} Channel; + ASIOBool active; + jack_default_audio_sample_t *audio_buffer; + char port_name[ASIO_MAX_NAME_LENGTH]; + jack_port_t *port; +} IOChannel; -typedef struct sched_param SCHED_PARAM; - -struct IWineASIOImpl +typedef struct IWineASIOImpl { /* COM stuff */ - const IWineASIOVtbl *lpVtbl; - LONG ref; + const IWineASIOVtbl *lpVtbl; + LONG ref; + + /* The app's main window handle on windows, 0 on OS/X */ + HWND sys_ref; /* ASIO stuff */ - HWND hwnd; - ASIOSampleRate sample_rate; - long input_latency; - long output_latency; - long block_frames; - ASIOTime asio_time; - long miliseconds; - ASIOTimeStamp system_time; - double sample_position; - ASIOBufferInfo *bufferInfos; - ASIOCallbacks *callbacks; - char error_message[256]; - long active_inputs; - long active_outputs; - BOOL time_info_mode; - BOOL tc_read; - long state; + LONG asio_active_inputs; + LONG asio_active_outputs; + BOOL asio_buffer_index; + ASIOCallbacks *asio_callbacks; + BOOL asio_can_time_code; + LONG asio_current_buffersize; + INT asio_driver_state; + ASIOSamples asio_sample_position; + ASIOSampleRate asio_sample_rate; + ASIOTime asio_time; + BOOL asio_time_info_mode; + ASIOTimeStamp asio_time_stamp; + LONG asio_version; + + /* WineASIO configuration options */ + LONG wineasio_number_inputs; + LONG wineasio_number_outputs; + BOOL wineasio_autostart_server; + BOOL wineasio_connect_to_hardware; + LONG wineasio_fixed_buffersize; + LONG wineasio_preferred_buffersize; /* JACK stuff */ - char *client_name; - unsigned int num_inputs; - unsigned int num_outputs; - jack_client_t *client; - long toggle; - SCHED_PARAM jack_client_priority; - - /* callback stuff */ - HANDLE thread; - HANDLE start_event; - HANDLE stop_event; - DWORD thread_id; - sem_t semaphore1; - sem_t semaphore2; - BOOL terminate; - - Channel *input; - Channel *output; -}; + jack_client_t *jack_client; + char jack_client_name[ASIO_MAX_NAME_LENGTH]; + int jack_num_input_ports; + int jack_num_output_ports; + const char **jack_input_ports; + const char **jack_output_ports; + + /* process callback buffers */ + jack_default_audio_sample_t *callback_audio_buffer; + IOChannel *input_channel; + IOChannel *output_channel; +} IWineASIOImpl; + +enum { Loaded, Initialized, Prepared, Running }; + +/**************************************************************************** + * Interface Methods + */ -typedef struct IWineASIOImpl IWineASIOImpl; +/* + * as seen from the WineASIO source + */ -static ULONG WINAPI IWineASIOImpl_AddRef(LPWINEASIO iface) -{ - IWineASIOImpl *This = (IWineASIOImpl *)iface; - ULONG ref = InterlockedIncrement(&(This->ref)); - TRACE("(%p)\n", iface); - TRACE("(%p) ref was %d\n", This, ref - 1); +HIDDEN HRESULT STDMETHODCALLTYPE QueryInterface(LPWINEASIO iface, REFIID riid, void **ppvObject); +HIDDEN ULONG STDMETHODCALLTYPE AddRef(LPWINEASIO iface); +HIDDEN ULONG STDMETHODCALLTYPE Release(LPWINEASIO iface); +HIDDEN ASIOBool STDMETHODCALLTYPE Init(LPWINEASIO iface, void *sysRef); +HIDDEN void STDMETHODCALLTYPE GetDriverName(LPWINEASIO iface, char *name); +HIDDEN LONG STDMETHODCALLTYPE GetDriverVersion(LPWINEASIO iface); +HIDDEN void STDMETHODCALLTYPE GetErrorMessage(LPWINEASIO iface, char *string); +HIDDEN ASIOError STDMETHODCALLTYPE Start(LPWINEASIO iface); +HIDDEN ASIOError STDMETHODCALLTYPE Stop(LPWINEASIO iface); +HIDDEN ASIOError STDMETHODCALLTYPE GetChannels (LPWINEASIO iface, LONG *numInputChannels, LONG *numOutputChannels); +HIDDEN ASIOError STDMETHODCALLTYPE GetLatencies(LPWINEASIO iface, LONG *inputLatency, LONG *outputLatency); +HIDDEN ASIOError STDMETHODCALLTYPE GetBufferSize(LPWINEASIO iface, LONG *minSize, LONG *maxSize, LONG *preferredSize, LONG *granularity); +HIDDEN ASIOError STDMETHODCALLTYPE CanSampleRate(LPWINEASIO iface, ASIOSampleRate sampleRate); +HIDDEN ASIOError STDMETHODCALLTYPE GetSampleRate(LPWINEASIO iface, ASIOSampleRate *sampleRate); +HIDDEN ASIOError STDMETHODCALLTYPE SetSampleRate(LPWINEASIO iface, ASIOSampleRate sampleRate); +HIDDEN ASIOError STDMETHODCALLTYPE GetClockSources(LPWINEASIO iface, ASIOClockSource *clocks, LONG *numSources); +HIDDEN ASIOError STDMETHODCALLTYPE SetClockSource(LPWINEASIO iface, LONG index); +HIDDEN ASIOError STDMETHODCALLTYPE GetSamplePosition(LPWINEASIO iface, ASIOSamples *sPos, ASIOTimeStamp *tStamp); +HIDDEN ASIOError STDMETHODCALLTYPE GetChannelInfo(LPWINEASIO iface, ASIOChannelInfo *info); +HIDDEN ASIOError STDMETHODCALLTYPE CreateBuffers(LPWINEASIO iface, ASIOBufferInfo *bufferInfo, LONG numChannels, LONG bufferSize, ASIOCallbacks *asioCallbacks); +HIDDEN ASIOError STDMETHODCALLTYPE DisposeBuffers(LPWINEASIO iface); +HIDDEN ASIOError STDMETHODCALLTYPE ControlPanel(LPWINEASIO iface); +HIDDEN ASIOError STDMETHODCALLTYPE Future(LPWINEASIO iface, LONG selector, void *opt); +HIDDEN ASIOError STDMETHODCALLTYPE OutputReady(LPWINEASIO iface); - return ref; -} +/* + * thiscall wrappers for the vtbl (as seen from app side 32bit) + */ -static ULONG WINAPI IWineASIOImpl_Release(LPWINEASIO iface) -{ - IWineASIOImpl *This = (IWineASIOImpl *)iface; - ULONG ref = InterlockedDecrement(&(This->ref)); - int i; - TRACE("(%p)\n", iface); - TRACE("(%p) ref was %d\n", This, ref + 1); +HIDDEN void __thiscall_Init(void); +HIDDEN void __thiscall_GetDriverName(void); +HIDDEN void __thiscall_GetDriverVersion(void); +HIDDEN void __thiscall_GetErrorMessage(void); +HIDDEN void __thiscall_Start(void); +HIDDEN void __thiscall_Stop(void); +HIDDEN void __thiscall_GetChannels(void); +HIDDEN void __thiscall_GetLatencies(void); +HIDDEN void __thiscall_GetBufferSize(void); +HIDDEN void __thiscall_CanSampleRate(void); +HIDDEN void __thiscall_GetSampleRate(void); +HIDDEN void __thiscall_SetSampleRate(void); +HIDDEN void __thiscall_GetClockSources(void); +HIDDEN void __thiscall_SetClockSource(void); +HIDDEN void __thiscall_GetSamplePosition(void); +HIDDEN void __thiscall_GetChannelInfo(void); +HIDDEN void __thiscall_CreateBuffers(void); +HIDDEN void __thiscall_DisposeBuffers(void); +HIDDEN void __thiscall_ControlPanel(void); +HIDDEN void __thiscall_Future(void); +HIDDEN void __thiscall_OutputReady(void); - if (!ref) { - TRACE("Entering state = Exit\n"); - This->state = Exit; +/* + * Jack callbacks + */ - jack_client_close(This->client); - TRACE("JACK client closed\n"); +static int bufsize_callback (jack_nframes_t nframes, void *arg); +static int process_callback (jack_nframes_t nframes, void *arg); +static int srate_callback (jack_nframes_t nframes, void *arg); +static int xrun_callback(void *arg); - TRACE("Setting Win32 thread termination to TRUE\n"); - This->terminate = TRUE; - sem_post(&This->semaphore1); +/* + * Support functions + */ - WaitForSingleObject(This->stop_event, INFINITE); - TRACE("Win32 thread terminated\n"); - CloseHandle(This->stop_event); - This->stop_event = INVALID_HANDLE_VALUE; +HRESULT WINAPI WineASIOCreateInstance(REFIID riid, LPVOID *ppobj); +static BOOL configure_driver(IWineASIOImpl *This); - sem_destroy(&This->semaphore1); - sem_destroy(&This->semaphore2); +#ifdef __WINESRC__ +static DWORD WINAPI jack_thread_creator_helper(LPVOID arg); +static int jack_thread_creator(pthread_t* thread_id, const pthread_attr_t* attr, void *(*function)(void*), void* arg); +#endif - for (i = 0; i < This->num_inputs; i++) - { - jack_ringbuffer_free(This->input[i].ring); - This->input[i].ring = NULL; - } - for (i = 0; i < This->num_outputs; i++) - { - jack_ringbuffer_free(This->output[i].ring); - This->output[i].ring = NULL; - } - HeapFree(GetProcessHeap(),0,This); - TRACE("(%p) released\n", This); - } - return ref; -} +/* {48D0C522-BFCC-45cc-8B84-17F25F33E6E8} */ +static GUID const CLSID_WineASIO = { +0x48d0c522, 0xbfcc, 0x45cc, { 0x8b, 0x84, 0x17, 0xf2, 0x5f, 0x33, 0xe6, 0xe8 } }; + +static const IWineASIOVtbl WineASIO_Vtbl = +{ + (void *) QueryInterface, + (void *) AddRef, + (void *) Release, + + (void *) THISCALL(Init), + (void *) THISCALL(GetDriverName), + (void *) THISCALL(GetDriverVersion), + (void *) THISCALL(GetErrorMessage), + (void *) THISCALL(Start), + (void *) THISCALL(Stop), + (void *) THISCALL(GetChannels), + (void *) THISCALL(GetLatencies), + (void *) THISCALL(GetBufferSize), + (void *) THISCALL(CanSampleRate), + (void *) THISCALL(GetSampleRate), + (void *) THISCALL(SetSampleRate), + (void *) THISCALL(GetClockSources), + (void *) THISCALL(SetClockSource), + (void *) THISCALL(GetSamplePosition), + (void *) THISCALL(GetChannelInfo), + (void *) THISCALL(CreateBuffers), + (void *) THISCALL(DisposeBuffers), + (void *) THISCALL(ControlPanel), + (void *) THISCALL(Future), + (void *) THISCALL(OutputReady) +}; + +#ifdef __WINESRC__ +/* structure needed to create the JACK callback thread in the wine process context */ +struct { + void *(*jack_callback_thread) (void*); + void *arg; + pthread_t jack_callback_pthread_id; + HANDLE jack_callback_thread_created; +} jack_thread_creator_privates; +#endif + +/***************************************************************************** + * Interface method definitions + */ -static HRESULT WINAPI IWineASIOImpl_QueryInterface(LPWINEASIO iface, REFIID riid, void** ppvObject) + +HIDDEN HRESULT STDMETHODCALLTYPE QueryInterface(LPWINEASIO iface, REFIID riid, void **ppvObject) { - IWineASIOImpl *This = (IWineASIOImpl *)iface; - TRACE("(%p, %s, %p)\n", iface, debugstr_guid(riid), ppvObject); + IWineASIOImpl *This = (IWineASIOImpl *)iface; + + TRACE("iface: %p, riid: %s, ppvObject: %p)\n", iface, debugstr_guid(riid), ppvObject); if (ppvObject == NULL) return E_INVALIDARG; if (IsEqualIID(&CLSID_WineASIO, riid)) { - IWineASIOImpl_AddRef(iface); - + AddRef(iface); *ppvObject = This; - return S_OK; } return E_NOINTERFACE; } -#ifndef JackWASIO -static void read_config(IWineASIOImpl* This) +/* + * ULONG STDMETHODCALLTYPE AddRef(LPWINEASIO iface); + * Function: Increment the reference count on the object + * Returns: Ref count + */ + +HIDDEN ULONG STDMETHODCALLTYPE AddRef(LPWINEASIO iface) { - char *usercfg = NULL; - FILE *cfg; + IWineASIOImpl *This = (IWineASIOImpl *)iface; + ULONG ref = InterlockedIncrement(&(This->ref)); - asprintf(&usercfg, "%s/%s", getenv("HOME"), USERCFG); - cfg = fopen(usercfg, "r"); - if (cfg) - TRACE("Config: %s\n", usercfg); - else - { - cfg = fopen(SITECFG, "r"); - if (cfg) - TRACE("Config: %s\n", SITECFG); - } - free(usercfg); + TRACE("iface: %p, ref count is %d\n", iface, ref); + return ref; +} - if (cfg) - { - char *line = NULL; - size_t len = 0; - ssize_t read; +/* + * ULONG Release (LPWINEASIO iface); + * Function: Destroy the interface + * Returns: Ref count + * Implies: ASIOStop() and ASIODisposeBuffers() + */ + +HIDDEN ULONG STDMETHODCALLTYPE Release(LPWINEASIO iface) +{ + IWineASIOImpl *This = (IWineASIOImpl *)iface; + ULONG ref = InterlockedDecrement(&This->ref); + int i; + + TRACE("iface: %p, ref count is %d\n", iface, ref); - line = NULL; - len = 0; - while( (read = getline(&line, &len, cfg)) != -1) + if (This->asio_driver_state == Running) + Stop(iface); + if (This->asio_driver_state == Prepared) + DisposeBuffers(iface); + + if (This->asio_driver_state == Initialized) + { + /* just for good measure we deinitialize IOChannel structures and unregister JACK ports */ + for (i = 0; i < This->wineasio_number_inputs; i++) { - while (isspace(line[--read])) line[read]='\0'; - if ((strstr(line, ENVVAR_INPUTS) - || strstr(line, ENVVAR_OUTPUTS) - || strstr(line, ENVVAR_INPORTNAMEPREFIX) - || strstr(line, ENVVAR_OUTPORTNAMEPREFIX) - || strstr(line, ENVVAR_INMAP) - || strstr(line, ENVVAR_OUTMAP) - || strstr(line, ENVVAR_AUTOCONNECT) - || strstr(line, This->client_name) == line - ) && strchr(line, '=')) - { - TRACE("(%p) env: '%s'\n", This, line); - putenv(line); - } - else - { - free(line); - } - line = NULL; - len = 0; + if(jack_port_unregister (This->jack_client, This->input_channel[i].port)) + MESSAGE("Error trying to unregister port %s\n", This->input_channel[i].port_name); + This->input_channel[i].active = ASIOFalse; + This->input_channel[i].port = NULL; + } + for (i = 0; i < This->wineasio_number_outputs; i++) + { + if(jack_port_unregister (This->jack_client, This->output_channel[i].port)) + MESSAGE("Error trying to unregister port %s\n", This->output_channel[i].port_name); + This->output_channel[i].active = ASIOFalse; + This->output_channel[i].port = NULL; } + This->asio_active_inputs = This->asio_active_outputs = 0; + TRACE("%i IOChannel structures released\n", This->wineasio_number_inputs + This->wineasio_number_outputs); - fclose(cfg); - } + if (This->jack_output_ports) + jack_free (This->jack_output_ports); + if (This->jack_input_ports) + jack_free (This->jack_input_ports); - usercfg = getenv(This->client_name); - if (usercfg != NULL) { - free(This->client_name); - This->client_name = strdup(usercfg); - } -} + if (This->jack_client) + if (jack_client_close(This->jack_client)) + MESSAGE("Error trying to close JACK client\n"); -static int get_numChannels(IWineASIOImpl *This, const char* inout, int defval) -{ - int i = defval; - char *envv = NULL, *envi; - - asprintf(&envv, "%s%s", This->client_name, inout); - envi = getenv(envv); - free(envv); - if (envi == NULL) { - asprintf(&envv, "%s%s", DEFAULT_PREFIX, inout); - envi = getenv(envv); - free(envv); + if (This->input_channel) + HeapFree(GetProcessHeap(), 0, This->input_channel); } - if (envi != NULL) i = atoi(envi); - - return i; + TRACE("WineASIO terminated\n\n"); + if (ref == 0) + HeapFree(GetProcessHeap(), 0, This); + return ref; } -static BOOL get_autoconnect(IWineASIOImpl* This) +/* + * ASIOBool Init (void *sysRef); + * Function: Initialize the driver + * Parameters: Pointer to "This" + * sysHanle is 0 on OS/X and on windows it contains the applications main window handle + * Returns: ASIOFalse on error, and ASIOTrue on success + */ + +DEFINE_THISCALL_WRAPPER(Init,8) +HIDDEN ASIOBool STDMETHODCALLTYPE Init(LPWINEASIO iface, void *sysRef) { - char* envv = NULL, *envi; - - asprintf(&envv, "%s%s", This->client_name, ENVVAR_AUTOCONNECT); - envi = getenv(envv); - free(envv); - if (envi == NULL) { - asprintf(&envv, "%s%s", DEFAULT_PREFIX, ENVVAR_AUTOCONNECT); - envi = getenv(envv); - free(envv); - } + IWineASIOImpl *This = (IWineASIOImpl *)iface; + jack_status_t jack_status; + jack_options_t jack_options = JackNullOption; + int i; - return (envi == NULL) ? DEFAULT_AUTOCONNECT : (strcasecmp(envi, "true") == 0); -} -#else -static int GetEXEName(DWORD dwProcessID, char* name) { - DWORD aProcesses [1024], cbNeeded, cProcesses; - unsigned int i; - char szEXEName[MAX_PATH]; - - /* Enumerate all processes */ - if(!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded)) return FALSE; - - Calculate how many process identifiers were returned. - cProcesses = cbNeeded / sizeof(DWORD); - - /* Loop through all process to find the one that matches - the one we are looking for */ - - for (i = 0; i < cProcesses; i++) { - if (aProcesses [i] == dwProcessID) { - /* Get a handle to the process */ - HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | - PROCESS_VM_READ, FALSE, dwProcessID); - - /* Get the process name */ - if (NULL != hProcess) { - HMODULE hMod; - DWORD cbNeeded; - - if(EnumProcessModules(hProcess, &hMod,sizeof(hMod), &cbNeeded)) { - int len; - /* Get the name of the exe file */ - GetModuleBaseNameA(hProcess,hMod,szEXEName,sizeof(szEXEName)/sizeof(char)); - len = strlen((char*)szEXEName) - 3; /* remove ".exe" */ - lstrcpynA(name,(char*)szEXEName,len); - name[len] = '\0'; - return TRUE; - } - } - } - } + TRACE("iface: %p, sysRef: %p\n", iface, sysRef); - return FALSE; -} + This->sys_ref = sysRef; -static int MAX_INPUTS = 2; -static int MAX_OUTPUTS = 2; -static int gAUTO_CONNECT = FALSE; -static void ReadJPPrefs() { - char *envar; - char path[256]; - FILE *prefFile; - - envar = getenv("HOME"); - - sprintf(path, "%s/Library/Preferences/JAS.jpil", envar); - if ((prefFile = fopen(path, "rt"))) { - int nullo; - int input, output, autoconnect, debug, default_input, default_output, default_system; - fscanf( - prefFile, "\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d", - &input, - &nullo, - &output, - &nullo, - &autoconnect, - &nullo, - &default_input, - &nullo, - &default_output, - &nullo, - &default_system, - &nullo, - &debug, - &nullo, - &nullo - ); - - fclose(prefFile); - - MAX_INPUTS = input; - MAX_OUTPUTS = output; - gAUTO_CONNECT = autoconnect; - } -} +#ifdef __WINESRC__ + mlockall(MCL_FUTURE); #endif -static char* get_targetname(IWineASIOImpl* This, const char* inout, int i) -{ - char* envv = NULL, *envi; - -#ifndef JackWASIO - asprintf(&envv, "%s%s%d", This->client_name, inout, i); -#else - asprintf(&envv, "%s%d", inout, i); -#endif - envi = getenv(envv); - free(envv); -#ifndef JackWASIO - if (envi == NULL) { - asprintf(&envv, "%s%s%d", DEFAULT_PREFIX, inout, i); - envi = getenv(envv); - free(envv); + if (!configure_driver(This)) + { + WARN("Unable to configure WineASIO\n"); + return ASIOFalse; } -#endif - return envi; -} + if (!This->wineasio_autostart_server) + jack_options |= JackNoStartServer; + This->jack_client = jack_client_open(This->jack_client_name, jack_options, &jack_status); -static void set_clientname(IWineASIOImpl *This) -{ -#ifndef JackWASIO - FILE *cmd; - char *cmdline = NULL; - char *ptr, *line = NULL; - size_t len = 0; - - asprintf(&cmdline, "/proc/%d/cmdline", getpid()); - cmd = fopen(cmdline, "r"); - free(cmdline); - - getline(&line, &len, cmd); - fclose(cmd); - - ptr = line; - while(strchr(ptr, '/') || strchr(ptr, '\\')) ++ptr; - line = ptr; - ptr = strcasestr(line, ".exe"); - if (ptr) { - while (strcasestr(ptr, ".exe")) ++ptr; - *(--ptr) = '\0'; + if (This->jack_client == NULL) + { + WARN("Unable to open a JACK client as: %s\n", This->jack_client_name); + return ASIOFalse; } + TRACE("JACK client opened as: '%s'\n", jack_get_client_name(This->jack_client)); - asprintf(&This->client_name, "%s_%s", DEFAULT_PREFIX, line); -#else - char proc_name[256]; - memset(&proc_name[0],0x0,sizeof(char)*256); /* size of char is redundant.. but dunno i like to write it:) */ - - if(!GetEXEName(GetCurrentProcessId(),&proc_name[0])) { - strcpy(&proc_name[0],"Wine_ASIO"); + if (!(This->asio_sample_rate = jack_get_sample_rate(This->jack_client))) + { + WARN("Unable to get samplerate from JACK\n"); + return ASIOFalse; } - This->client_name = strdup(proc_name); -#endif -} - -static void set_portname(IWineASIOImpl *This, const char* inout, const char* defname, int i, Channel c[]) -{ - char *envv = NULL, *envi; - asprintf(&envv, "%s_%s%d", This->client_name, inout, i); - envi = getenv(envv); - free(envv); - if (envi == NULL) + if (!(This->asio_current_buffersize = jack_get_buffer_size(This->jack_client))) { - envv = NULL; - asprintf(&envv, "%s_%s%d", DEFAULT_PREFIX, inout, i); - envi = getenv(envv); - free(envv); + WARN("Unable to get buffer size from JACK\n"); + return ASIOFalse; } - if (envi == NULL) asprintf(&envv, "%s%d", defname, i+1); - else asprintf(&envv, "%s", envi); - c[i].port_name = strdup(envv); - free(envv); -} - -WRAP_THISCALL( ASIOBool __stdcall, IWineASIOImpl_init, (LPWINEASIO iface, void *sysHandle)) -{ - IWineASIOImpl *This = (IWineASIOImpl *)iface; - jack_status_t status; - int i; - TRACE("(%p, %p)\n", iface, sysHandle); - - This->sample_rate = 48000.0; - This->block_frames = 1024; - This->input_latency = This->block_frames; - This->output_latency = This->block_frames; - This->miliseconds = (long)((double)(This->block_frames * 1000) / This->sample_rate); - This->sample_position = 0; - strcpy(This->error_message, "No Error"); - This->num_inputs = 0; - This->num_outputs = 0; - This->active_inputs = 0; - This->active_outputs = 0; - This->toggle = 0; - This->callbacks = NULL; - This->time_info_mode = FALSE; - This->tc_read = FALSE; - This->state = Init; - - set_clientname(This); - -#ifndef JackWASIO - /* uses This->client_name */ - read_config(This); -#else - ReadJPPrefs(); -#endif - This->client = jack_client_open(This->client_name, JackNullOption, &status, NULL); - if (This->client == NULL) + /* Allocate IOChannel structures */ + This->input_channel = HeapAlloc(GetProcessHeap(), 0, (This->wineasio_number_inputs + This->wineasio_number_outputs) * sizeof(IOChannel)); + if (!This->input_channel) { - WARN("(%p) failed to open jack server\n", This); + jack_client_close(This->jack_client); + ERR("Unable to allocate IOChannel structures for %i channels\n", This->wineasio_number_inputs); return ASIOFalse; } - - TRACE("JACK client opened, client name: '%s'; sample rate: %f\n", jack_get_client_name(This->client), This->sample_rate); - - if (status & JackServerStarted) - TRACE("(%p) JACK server started\n", This); - - /* Thread initialisation */ - sem_init(&This->semaphore1, 0, 0); - sem_init(&This->semaphore2, 0, 0); - - This->terminate = FALSE; - This->jack_client_priority.sched_priority = -1; - This->start_event = CreateEventW(NULL, FALSE, FALSE, NULL); - This->stop_event = CreateEventW(NULL, FALSE, FALSE, NULL); - This->thread = CreateThread(NULL, 0, win32_callback, (LPVOID)This, 0, &This->thread_id); - if (This->thread) + This->output_channel = This->input_channel + This->wineasio_number_inputs; + TRACE("%i IOChannel structures allocated\n", This->wineasio_number_inputs + This->wineasio_number_outputs); + + /* Get and count physical JACK ports */ + This->jack_input_ports = jack_get_ports(This->jack_client, NULL, NULL, JackPortIsPhysical | JackPortIsOutput); + for (This->jack_num_input_ports = 0; This->jack_input_ports && This->jack_input_ports[This->jack_num_input_ports]; This->jack_num_input_ports++) + ; + This->jack_output_ports = jack_get_ports(This->jack_client, NULL, NULL, JackPortIsPhysical | JackPortIsInput); + for (This->jack_num_output_ports = 0; This->jack_output_ports && This->jack_output_ports[This->jack_num_output_ports]; This->jack_num_output_ports++) + ; + + /* Initialize IOChannel structures */ + for (i = 0; i < This->wineasio_number_inputs; i++) { - TRACE("Wait for Win32 thread to start\n", This); - WaitForSingleObject(This->start_event, INFINITE); - TRACE("Win32 thread started\n", This); - CloseHandle(This->start_event); - This->start_event = INVALID_HANDLE_VALUE; + This->input_channel[i].active = ASIOFalse; + This->input_channel[i].port = NULL; + snprintf(This->input_channel[i].port_name, ASIO_MAX_NAME_LENGTH, "in_%i", i + 1); + This->input_channel[i].port = jack_port_register(This->jack_client, + This->input_channel[i].port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, i); + /* TRACE("IOChannel structure initialized for input %d: '%s'\n", i, This->input_channel[i].port_name); */ } - else + for (i = 0; i < This->wineasio_number_outputs; i++) { - WARN("(%p) Couldn't create thread\n", This); - return ASIOFalse; + This->output_channel[i].active = ASIOFalse; + This->output_channel[i].port = NULL; + snprintf(This->output_channel[i].port_name, ASIO_MAX_NAME_LENGTH, "out_%i", i + 1); + This->output_channel[i].port = jack_port_register(This->jack_client, + This->output_channel[i].port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, i); + /* TRACE("IOChannel structure initialized for output %d: '%s'\n", i, This->output_channel[i].port_name); */ } + TRACE("%i IOChannel structures initialized\n", This->wineasio_number_inputs + This->wineasio_number_outputs); - jack_set_process_callback(This->client, jack_process, This); - - This->sample_rate = jack_get_sample_rate(This->client); - This->block_frames = jack_get_buffer_size(This->client); - - This->miliseconds = (long)((double)(This->block_frames * 1000) / This->sample_rate); - This->input_latency = This->block_frames; - This->output_latency = This->block_frames; - - This->active_inputs = 0; -#ifndef JackWASIO - This->num_inputs = get_numChannels(This, ENVVAR_INPUTS, DEFAULT_NUMINPUTS); -#else - This->num_inputs = MAX_INPUTS; +#ifdef __WINESRC__ + jack_set_thread_creator(jack_thread_creator); #endif - This->input = HeapAlloc(GetProcessHeap(), 0, sizeof(Channel) * This->num_inputs); - if (!This->input) + + if (jack_set_process_callback(This->jack_client, process_callback, This)) { - MESSAGE("(%p) Not enough memory for %d input channels\n", This, This->num_inputs); + jack_client_close(This->jack_client); + HeapFree(GetProcessHeap(), 0, This->input_channel); + ERR("Unable to register JACK process callback\n"); return ASIOFalse; } - TRACE("(%p) Max inputs: %d\n", This, This->num_inputs); - for (i = 0; i < This->num_inputs; i++) + + if (jack_set_buffer_size_callback(This->jack_client, bufsize_callback, This)) { - This->input[i].active = ASIOFalse; - This->input[i].buffer = NULL; - set_portname(This, ENVVAR_INPORTNAMEPREFIX, DEFAULT_INPORT, i, This->input); - TRACE("(%p) input %d: '%s'\n", This, i, This->input[i].port_name); - - This->input[i].port = jack_port_register(This->client, - This->input[i].port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); - if (This->input[i].port) - TRACE("(%p) Registered input port %i: '%s' (%p)\n", This, i, This->input[i].port_name, This->input[i].port); - else { - MESSAGE("(%p) Failed to register input port %i ('%s')\n", This, i, This->input[i].port_name); - return ASE_NotPresent; - } - This->input[i].ring = NULL; - This->input[i].ring = jack_ringbuffer_create(4 * This->block_frames * sizeof(float)); - if (!This->input[i].ring) - { - WARN("no input ringbuffer memory\n"); - return ASE_NotPresent; - } + jack_client_close(This->jack_client); + HeapFree(GetProcessHeap(), 0, This->input_channel); + ERR("Unable to register JACK buffersize change callback\n"); + return ASIOFalse; } - This->active_outputs = 0; -#ifndef JackWASIO - This->num_outputs = get_numChannels(This, ENVVAR_OUTPUTS, DEFAULT_NUMOUTPUTS); -#else - This->num_outputs = MAX_OUTPUTS; -#endif - This->output = HeapAlloc(GetProcessHeap(), 0, sizeof(Channel) * This->num_outputs); - if (!This->output) + if (jack_set_sample_rate_callback (This->jack_client, srate_callback, This)) { - MESSAGE("(%p) Not enough memory for %d output channels\n", This, This->num_outputs); + jack_client_close(This->jack_client); + HeapFree(GetProcessHeap(), 0, This->input_channel); + ERR("Unable to register JACK samplerate change callback\n"); return ASIOFalse; } - TRACE("(%p) Max outputs: %d\n", This, This->num_outputs); - for (i = 0; i < This->num_outputs; i++) + + + if (jack_set_xrun_callback(This->jack_client, xrun_callback, This)) { - This->output[i].active = ASIOFalse; - This->output[i].buffer = NULL; - set_portname(This, ENVVAR_OUTPORTNAMEPREFIX, DEFAULT_OUTPORT, i, This->output); - TRACE("(%p) output %d: '%s'\n", This, i, This->output[i].port_name); - - This->output[i].port = jack_port_register(This->client, - This->output[i].port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); - if (This->output[i].port) - TRACE("(%p) Registered output port %i: '%s' (%p)\n", This, i, This->output[i].port_name, This->output[i].port); - else { - MESSAGE("(%p) Failed to register output port %i ('%s')\n", This, i, This->output[i].port_name); - return ASE_NotPresent; - } - This->output[i].ring = NULL; - This->output[i].ring = jack_ringbuffer_create(4 * This->block_frames * sizeof(float)); - if (!This->output[i].ring) - { - WARN("no output ringbuffer memory\n"); - return ASE_NotPresent; - } + jack_client_close(This->jack_client); + HeapFree(GetProcessHeap(), 0, This->input_channel); + ERR("Unable to register JACK xrun callback\n"); + return ASIOFalse; } + This->asio_driver_state = Initialized; + TRACE("WineASIO 0.%.1f initialized\n",(float) This->asio_version / 10); return ASIOTrue; } -WRAP_THISCALL( void __stdcall, IWineASIOImpl_getDriverName, (LPWINEASIO iface, char *name)) +/* + * void GetDriverName(char *name); + * Function: Returns the driver name in name + */ + +DEFINE_THISCALL_WRAPPER(GetDriverName,8) +HIDDEN void STDMETHODCALLTYPE GetDriverName(LPWINEASIO iface, char *name) { - TRACE("(%p, %p)\n", iface, name); - strcpy(name, "Wine ASIO"); + TRACE("iface: %p, name: %p\n", iface, name); + strcpy(name, "WineASIO"); + return; } -WRAP_THISCALL( long __stdcall, IWineASIOImpl_getDriverVersion, (LPWINEASIO iface)) +/* + * LONG GetDriverVersion (void); + * Function: Returns the driver version number + */ + +DEFINE_THISCALL_WRAPPER(GetDriverVersion,4) +HIDDEN LONG STDMETHODCALLTYPE GetDriverVersion(LPWINEASIO iface) { - TRACE("(%p)\n", iface); - return 81; /* 0.8.1.x */ + IWineASIOImpl *This = (IWineASIOImpl*)iface; + + TRACE("iface: %p\n", iface); + return This->asio_version; } -WRAP_THISCALL( void __stdcall, IWineASIOImpl_getErrorMessage, (LPWINEASIO iface, char *string)) +/* + * void GetErrorMessage(char *string); + * Function: Returns an error message for the last occured error in string + */ + +DEFINE_THISCALL_WRAPPER(GetErrorMessage,8) +HIDDEN void STDMETHODCALLTYPE GetErrorMessage(LPWINEASIO iface, char *string) { - IWineASIOImpl * This = (IWineASIOImpl*)iface; - TRACE("(%p, %p)\n", iface, string); - strcpy(string, This->error_message); + TRACE("iface: %p, string: %p)\n", iface, string); + strcpy(string, "WineASIO does not return error messages\n"); + return; } -WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_start, (LPWINEASIO iface)) +/* + * ASIOError Start(void); + * Function: Start JACK IO processing and reset the sample counter to zero + * Returns: ASE_NotPresent if IO is missing + * ASE_HWMalfunction if JACK fails to start + */ + +DEFINE_THISCALL_WRAPPER(Start,4) +HIDDEN ASIOError STDMETHODCALLTYPE Start(LPWINEASIO iface) { - IWineASIOImpl * This = (IWineASIOImpl*)iface; -#ifndef JackWASIO - BOOL autoconnect = get_autoconnect(This); -#else - BOOL autoconnect = gAUTO_CONNECT; -#endif - char *envi; - const char ** ports; - int numports; - int i, j; - TRACE("(%p)\n", iface); + IWineASIOImpl *This = (IWineASIOImpl*)iface; + int i; - if (This->callbacks) - { - This->sample_position = 0; - This->system_time.lo = 0; - This->system_time.hi = 0; +#ifndef _WIN64 + DWORD temp_time; +#endif - if (jack_activate(This->client)) - { - WARN("couldn't activate client\n"); - return ASE_NotPresent; - } + TRACE("iface: %p\n", iface); - /* get maximum reccomended client priority from JACK */ - This->jack_client_priority.sched_priority = jack_client_real_time_priority(This->client); + if (This->asio_driver_state != Prepared) + { + ERR("Unable to start WineASIO\n"); + return ASE_NotPresent; + } - /* Win32 thread should now be able to query jack for priority */ - sem_post(&This->semaphore1); + /* Zero the audio buffer */ + for (i = 0; i < (This->wineasio_number_inputs + This->wineasio_number_outputs) * 2 * This->asio_current_buffersize; i++) + This->callback_audio_buffer[i] = 0; - /* get list of port names */ - ports = jack_get_ports(This->client, NULL, NULL, JackPortIsPhysical | JackPortIsOutput); - for(numports = 0; ports && ports[numports]; numports++); - TRACE("(%p) inputs desired: %d; JACK outputs: %d\n", This, This->num_inputs, numports); + /* prime the callback */ + This->asio_buffer_index = 0; + if (This->asio_callbacks) + { +#ifdef _WIN64 + This->asio_sample_position = 0; + This->asio_time_stamp = timeGetTime() * 1000000; +#else + This->asio_sample_position.hi = This->asio_sample_position.lo = 0; + temp_time = timeGetTime(); + This->asio_time_stamp.lo = temp_time * 1000000; + This->asio_time_stamp.hi = ((unsigned long long) temp_time * 1000000) >> 32; +#endif + This->asio_time_info_mode = FALSE; + This->asio_can_time_code = FALSE; - for (i = j = 0; i < This->num_inputs; i++) + if (This->asio_callbacks->asioMessage(kAsioSupportsTimeInfo, 0, 0, 0)) { - if (This->input[i].active != ASIOTrue) - continue; - - if (autoconnect) { - /* Get the desired JACK output (source) name, if there is one, for this ASIO input */ - envi = get_targetname(This, ENVVAR_INMAP, i); - envi = envi ? envi : j < numports ? ports[j++] : NULL; - if (!envi) continue; - - TRACE("(%p) %d: Connect JACK output '%s' to my input '%s'\n", This, i - ,envi - ,jack_port_name(This->input[i].port) - ); - if (jack_connect(This->client - ,envi - ,jack_port_name(This->input[i].port) - )) - { - MESSAGE("(%p) Connect failed\n", This); - } + TRACE("TimeInfo mode enabled\n"); + This->asio_time_info_mode = TRUE; +#ifdef _WIN64 + This->asio_time.timeInfo.systemTime = This->asio_time_stamp; + This->asio_time.timeInfo.samplePosition = 0; +#else + This->asio_time.timeInfo.systemTime.hi = This->asio_time_stamp.hi; + This->asio_time.timeInfo.systemTime.lo = This->asio_time_stamp.lo; + This->asio_time.timeInfo.samplePosition.hi = This->asio_time.timeInfo.samplePosition.lo = 0; +#endif + This->asio_time.timeCode.speed = 0; + This->asio_time.timeInfo.sampleRate = This->asio_sample_rate; + This->asio_time.timeInfo.flags = kSystemTimeValid | kSamplePositionValid | kSampleRateValid; + if (This->asio_callbacks->asioMessage(kAsioSupportsTimeCode, 0, 0, 0)) + { + TRACE("TimeCode supported\n"); + This->asio_can_time_code = TRUE; +#ifdef _WIN64 + This->asio_time.timeCode.timeCodeSamples = This->asio_time_stamp; +#else + This->asio_time.timeCode.timeCodeSamples.hi = This->asio_time_stamp.hi; + This->asio_time.timeCode.timeCodeSamples.lo = This->asio_time_stamp.lo; +#endif + This->asio_time.timeCode.flags = ~(kTcValid | kTcRunning); } + This->asio_callbacks->bufferSwitchTimeInfo(&This->asio_time, This->asio_buffer_index, ASIOTrue); } - if (ports) - free(ports); - - /* get list of port names */ - ports = jack_get_ports(This->client, NULL, NULL, JackPortIsPhysical | JackPortIsInput); - for(numports = 0; ports && ports[numports]; numports++); - TRACE("(%p) JACK inputs: %d; outputs desired: %d\n", This, numports, This->num_outputs); - - for (i = j = 0; i < This->num_outputs; i++) + else { - if (This->output[i].active != ASIOTrue) - continue; - - if (autoconnect) { - /* Get the desired JACK input (target) name, if there is one, for this ASIO output */ - envi = get_targetname(This, ENVVAR_OUTMAP, i); - envi = envi ? envi : j < numports ? ports[j++] : NULL; - - TRACE("(%p) %d: Connect my output '%s' to JACK input '%s'\n", This, i - ,jack_port_name(This->output[i].port) - ,envi - ); - if (jack_connect(This->client - ,jack_port_name(This->output[i].port) - ,envi - )) - { - MESSAGE("(%p) Connect failed\n", This); - } - } + TRACE("Runnning simple BufferSwitch() callback\n"); + This->asio_callbacks->bufferSwitch(This->asio_buffer_index, ASIOTrue); } - if (ports) - free(ports); + This->asio_buffer_index = This->asio_buffer_index ? 0 : 1; + } + else + { + WARN("The ASIO host supplied no callback structure\n"); + return ASE_NotPresent; + } - TRACE("Entering state = Run\n"); - This->state = Run; + if (jack_activate(This->jack_client)) + { + ERR("Unable to activate JACK client\n"); + return ASE_NotPresent; + } - return ASE_OK; + /* connect to the hardware io */ + if (This->wineasio_connect_to_hardware) + { + for (i = 0; i < This->jack_num_input_ports && i < This->wineasio_number_inputs; i++) + { + /* TRACE("Connecting JACK port: %s to asio: %s\n", This->jack_input_ports[i], jack_port_name(This->input_channel[i].port)); */ + if (strstr(jack_port_type(jack_port_by_name(This->jack_client, This->jack_input_ports[i])), "audio")) + if (jack_connect(This->jack_client, This->jack_input_ports[i], jack_port_name(This->input_channel[i].port))) + WARN("Unable to connect %s to %s\n", This->jack_input_ports[i], jack_port_name(This->input_channel[i].port)); + } + for (i = 0; i < This->jack_num_output_ports && i < This->wineasio_number_outputs; i++) + { + /* TRACE("Connecting asio: %s to jack port: %s\n", jack_port_name(This->output_channel[i].port), This->jack_output_ports[i]); */ + if (strstr(jack_port_type(jack_port_by_name(This->jack_client, This->jack_output_ports[i])), "audio")) + if (jack_connect(This->jack_client, jack_port_name(This->output_channel[i].port), This->jack_output_ports[i])) + WARN("Unable to connect to %s\n", jack_port_name(This->output_channel[i].port)); + } } - return ASE_NotPresent; + This->asio_driver_state = Running; + TRACE("WineASIO successfully loaded\n"); + return ASE_OK; } -WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_stop, (LPWINEASIO iface)) +/* + * ASIOError Stop(void); + * Function: Stop JACK IO processing + * Returns: ASE_NotPresent on missing IO + * Note: BufferSwitch() must not called after returning + */ + +DEFINE_THISCALL_WRAPPER(Stop,4) +HIDDEN ASIOError STDMETHODCALLTYPE Stop(LPWINEASIO iface) { - IWineASIOImpl * This = (IWineASIOImpl*)iface; - TRACE("(%p)\n", iface); + IWineASIOImpl *This = (IWineASIOImpl*)iface; + + TRACE("iface: %p\n", iface); - if (jack_deactivate(This->client)) + if (This->asio_driver_state != Running) { - WARN("couldn't deactivate client\n"); + WARN("Unable to stop WineASIO, not running\n"); return ASE_NotPresent; } + This->asio_driver_state = Prepared; + + if (jack_deactivate(This->jack_client)) + { + ERR("Unable to deactivate JACK client\n"); + return ASE_NotPresent; + } return ASE_OK; } -WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_getChannels, (LPWINEASIO iface, long *numInputChannels, long *numOutputChannels)) -{ - IWineASIOImpl * This = (IWineASIOImpl*)iface; - TRACE("(%p, %p, %p)\n", iface, numInputChannels, numOutputChannels); - - if (numInputChannels) - *numInputChannels = This->num_inputs; +/* + * ASIOError GetChannels(LONG *numInputChannels, LONG *numOutputChannels); + * Function: Report number of IO channels + * Parameters: numInputChannels and numOutputChannels will hold number of channels on returning + * Returns: ASE_NotPresent if no channels are available, otherwise AES_OK + */ - if (numOutputChannels) - *numOutputChannels = This->num_outputs; +DEFINE_THISCALL_WRAPPER(GetChannels,12) +HIDDEN ASIOError STDMETHODCALLTYPE GetChannels (LPWINEASIO iface, LONG *numInputChannels, LONG *numOutputChannels) +{ + IWineASIOImpl *This = (IWineASIOImpl*)iface; - TRACE("inputs: %d outputs: %d\n", This->num_inputs, This->num_outputs); + if (!numInputChannels && !numOutputChannels) + { + WARN("Nullpointer argument\n"); + return ASE_InvalidParameter; + } + *numInputChannels = This->wineasio_number_inputs; + *numOutputChannels = This->wineasio_number_outputs; + TRACE("iface: %p, inputs: %i, outputs: %i\n", iface, This->wineasio_number_inputs, This->wineasio_number_outputs); return ASE_OK; } -WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_getLatencies, (LPWINEASIO iface, long *inputLatency, long *outputLatency)) -{ - IWineASIOImpl * This = (IWineASIOImpl*)iface; - TRACE("(%p, %p, %p)\n", iface, inputLatency, outputLatency); +/* + * ASIOError GetLatencies(LONG *inputLatency, LONG *outputLatency); + * Function: Return latency in frames + * Returns: ASE_NotPresent if no IO is available, otherwise AES_OK + */ - if (inputLatency) - *inputLatency = This->input_latency; +DEFINE_THISCALL_WRAPPER(GetLatencies,12) +HIDDEN ASIOError STDMETHODCALLTYPE GetLatencies(LPWINEASIO iface, LONG *inputLatency, LONG *outputLatency) +{ + IWineASIOImpl *This = (IWineASIOImpl*)iface; - if (outputLatency) - *outputLatency = This->output_latency; + if (!inputLatency && !outputLatency) + { + WARN("Nullpointer argument\n"); + return ASE_InvalidParameter; + } + *inputLatency = *outputLatency = This->asio_current_buffersize; + TRACE("iface: %p Latency = %i frames\n", iface, This->asio_current_buffersize); return ASE_OK; } -WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_getBufferSize, (LPWINEASIO iface, long *minSize, long *maxSize, long *preferredSize, long *granularity)) -{ - IWineASIOImpl * This = (IWineASIOImpl*)iface; - TRACE("(%p, %p, %p, %p, %p)\n", iface, minSize, maxSize, preferredSize, granularity); +/* + * ASIOError GetBufferSize(LONG *minSize, LONG *maxSize, LONG *preferredSize, LONG *granularity); + * Function: Return minimum, maximum, preferred buffer sizes, and granularity + * At the moment return all the same, and granularity 0 + * Returns: ASE_NotPresent on missing IO + */ - if (minSize) - *minSize = This->block_frames; +DEFINE_THISCALL_WRAPPER(GetBufferSize,20) +HIDDEN ASIOError STDMETHODCALLTYPE GetBufferSize(LPWINEASIO iface, LONG *minSize, LONG *maxSize, LONG *preferredSize, LONG *granularity) +{ + IWineASIOImpl *This = (IWineASIOImpl*)iface; - if (maxSize) - *maxSize = This->block_frames; + TRACE("iface: %p, minSize: %p, maxSize: %p, preferredSize: %p, granularity: %p\n", iface, minSize, maxSize, preferredSize, granularity); - if (preferredSize) - *preferredSize = This->block_frames; + if (!minSize && !maxSize && !preferredSize && !granularity) + { + WARN("Nullpointer argument\n"); + return ASE_InvalidParameter; + } - if (granularity) + if (This->wineasio_fixed_buffersize) + { + *minSize = *maxSize = *preferredSize = This->asio_current_buffersize; *granularity = 0; + TRACE("Buffersize fixed at %i\n", This->asio_current_buffersize); + return ASE_OK; + } - TRACE("min: %ld max: %ld preferred: %ld granularity: 0\n", This->block_frames, This->block_frames, This->block_frames); - + *minSize = ASIO_MINIMUM_BUFFERSIZE; + *maxSize = ASIO_MAXIMUM_BUFFERSIZE; + *preferredSize = This->wineasio_preferred_buffersize; + *granularity = -1; + TRACE("The ASIO host can control buffersize\nMinimum: %i, maximum: %i, preferred: %i, granularity: %i, current: %i\n", + *minSize, *maxSize, *preferredSize, *granularity, This->asio_current_buffersize); return ASE_OK; } -WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_canSampleRate, (LPWINEASIO iface, ASIOSampleRate sampleRate)) +/* + * ASIOError CanSampleRate(ASIOSampleRate sampleRate); + * Function: Ask if specific SR is available + * Returns: ASE_NoClock if SR isn't available, ASE_NotPresent on missing IO + */ + +DEFINE_THISCALL_WRAPPER(CanSampleRate,12) +HIDDEN ASIOError STDMETHODCALLTYPE CanSampleRate(LPWINEASIO iface, ASIOSampleRate sampleRate) { - IWineASIOImpl * This = (IWineASIOImpl*)iface; - TRACE("(%p, %f)\n", iface, sampleRate); + IWineASIOImpl *This = (IWineASIOImpl*)iface; - if (sampleRate == This->sample_rate) - return ASE_OK; + TRACE("iface: %p, Samplerate = %li, requested samplerate = %li\n", iface, (long) This->asio_sample_rate, (long) sampleRate); - return ASE_NoClock; + if (sampleRate != This->asio_sample_rate) + return ASE_NoClock; + return ASE_OK; } -WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_getSampleRate, (LPWINEASIO iface, ASIOSampleRate *sampleRate)) -{ - IWineASIOImpl * This = (IWineASIOImpl*)iface; - TRACE("(%p, %p)\n", iface, sampleRate); +/* + * ASIOError GetSampleRate(ASIOSampleRate *currentRate); + * Function: Return current SR + * Parameters: currentRate will hold SR on return, 0 if unknown + * Returns: ASE_NoClock if SR is unknown, ASE_NotPresent on missing IO + */ - if (sampleRate) - *sampleRate = This->sample_rate; +DEFINE_THISCALL_WRAPPER(GetSampleRate,8) +HIDDEN ASIOError STDMETHODCALLTYPE GetSampleRate(LPWINEASIO iface, ASIOSampleRate *sampleRate) +{ + IWineASIOImpl *This = (IWineASIOImpl*)iface; - TRACE("rate: %f\n", This->sample_rate); + TRACE("iface: %p, Sample rate is %i\n", iface, (int) This->asio_sample_rate); + if (!sampleRate) + { + WARN("Nullpointer argument\n"); + return ASE_InvalidParameter; + } + *sampleRate = This->asio_sample_rate; return ASE_OK; } -WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_setSampleRate, (LPWINEASIO iface, ASIOSampleRate sampleRate)) +/* + * ASIOError SetSampleRate(ASIOSampleRate sampleRate); + * Function: Set requested SR, enable external sync if SR == 0 + * Returns: ASE_NoClock if unknown SR + * ASE_InvalidMode if current clock is external and SR != 0 + * ASE_NotPresent on missing IO + */ + +DEFINE_THISCALL_WRAPPER(SetSampleRate,12) +HIDDEN ASIOError STDMETHODCALLTYPE SetSampleRate(LPWINEASIO iface, ASIOSampleRate sampleRate) { - IWineASIOImpl * This = (IWineASIOImpl*)iface; - TRACE("(%p, %f)\n", iface, sampleRate); + IWineASIOImpl *This = (IWineASIOImpl*)iface; - if (sampleRate != This->sample_rate) - return ASE_NoClock; + TRACE("iface: %p, Sample rate %f requested\n", iface, sampleRate); + if (sampleRate != This->asio_sample_rate) + return ASE_NoClock; return ASE_OK; } -WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_getClockSources, (LPWINEASIO iface, ASIOClockSource *clocks, long *numSources)) +/* + * ASIOError GetClockSources(ASIOClockSource *clocks, LONG *numSources); + * Function: Return available clock sources + * Parameters: clocks - a pointer to an array of ASIOClockSource structures. + * numSources - when called: number of allocated members + * - on return: number of clock sources, the minimum is 1 - the internal clock + * Returns: ASE_NotPresent on missing IO + */ + +DEFINE_THISCALL_WRAPPER(GetClockSources,12) +HIDDEN ASIOError STDMETHODCALLTYPE GetClockSources(LPWINEASIO iface, ASIOClockSource *clocks, LONG *numSources) { - TRACE("(%p, %p, %p)\n", iface, clocks, numSources); + TRACE("iface: %p, clocks: %p, numSources: %p\n", iface, clocks, numSources); - if (clocks && numSources) + if (!clocks && !numSources) { - clocks->index = 0; - clocks->associatedChannel = -1; - clocks->associatedGroup = -1; - clocks->isCurrentSource = ASIOTrue; - strcpy(clocks->name, "Internal"); - - *numSources = 1; - return ASE_OK; + WARN("Nullpointer argument\n"); + return ASE_InvalidParameter; } - - return ASE_InvalidParameter; + clocks->index = 0; + clocks->associatedChannel = -1; + clocks->associatedGroup = -1; + clocks->isCurrentSource = ASIOTrue; + strcpy(clocks->name, "Internal"); + *numSources = 1; + return ASE_OK; } -WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_setClockSource, (LPWINEASIO iface, long reference)) -{ - IWineASIOImpl * This = (IWineASIOImpl*)iface; - TRACE("(%p, %ld)\n", iface, reference); - - if (reference == 0) - { - This->asio_time.timeInfo.flags |= kClockSourceChanged; +/* + * ASIOError SetClockSource(LONG index); + * Function: Set clock source + * Parameters: index returned by ASIOGetClockSources() - See asio.h for more details + * Returns: ASE_NotPresent on missing IO + * ASE_InvalidMode may be returned if a clock can't be selected + * ASE_NoClock should not be returned + */ - return ASE_OK; - } +DEFINE_THISCALL_WRAPPER(SetClockSource,8) +HIDDEN ASIOError STDMETHODCALLTYPE SetClockSource(LPWINEASIO iface, LONG index) +{ + TRACE("iface: %p, index: %i\n", iface, index); - return ASE_NotPresent; + if (index != 0) + return ASE_NotPresent; + return ASE_OK; } -WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_getSamplePosition, (LPWINEASIO iface, ASIOSamples *sPos, ASIOTimeStamp *tStamp)) +/* + * ASIOError GetSamplePosition (ASIOSamples *sPos, ASIOTimeStamp *tStamp); + * Function: Return sample position and timestamp + * Parameters: sPos holds the position on return, reset to 0 on ASIOStart() + * tStamp holds the system time of sPos + * Return: ASE_NotPresent on missing IO + * ASE_SPNotAdvancing on missing clock + */ + +DEFINE_THISCALL_WRAPPER(GetSamplePosition,12) +HIDDEN ASIOError STDMETHODCALLTYPE GetSamplePosition(LPWINEASIO iface, ASIOSamples *sPos, ASIOTimeStamp *tStamp) { - IWineASIOImpl * This = (IWineASIOImpl*)iface; - TRACE("(%p, %p, %p)\n", iface, sPos, tStamp); + IWineASIOImpl *This = (IWineASIOImpl*)iface; - tStamp->lo = This->system_time.lo; - tStamp->hi = This->system_time.hi; + TRACE("iface: %p, sPos: %p, tStamp: %p\n", iface, sPos, tStamp); - if (This->sample_position >= twoRaisedTo32) - { - sPos->hi = (unsigned long)(This->sample_position * twoRaisedTo32Reciprocal); - sPos->lo = (unsigned long)(This->sample_position - (sPos->hi * twoRaisedTo32)); - } - else + if (!sPos && !tStamp) { - sPos->hi = 0; - sPos->lo = (unsigned long)This->sample_position; + WARN("Nullpointer argument\n"); + return ASE_InvalidParameter; } - +#ifdef _WIN64 + *tStamp = This->asio_time_stamp; + *sPos = This->asio_sample_position; +#else + tStamp->lo = This->asio_time_stamp.lo; + tStamp->hi = This->asio_time_stamp.hi; + sPos->lo = This->asio_sample_position.lo; + sPos->hi = 0; +#endif return ASE_OK; } -WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_getChannelInfo, (LPWINEASIO iface, ASIOChannelInfo *info)) +/* + * ASIOError GetChannelInfo (ASIOChannelInfo *info); + * Function: Retrive channel info. - See asio.h for more detail + * Returns: ASE_NotPresent on missing IO + */ + +DEFINE_THISCALL_WRAPPER(GetChannelInfo,8) +HIDDEN ASIOError STDMETHODCALLTYPE GetChannelInfo(LPWINEASIO iface, ASIOChannelInfo *info) { - IWineASIOImpl * This = (IWineASIOImpl*)iface; - TRACE("(%p, %p)\n", iface, info); + IWineASIOImpl *This = (IWineASIOImpl*)iface; - if (info->channel < 0 || (info->isInput ? info->channel >= This->num_inputs : info->channel >= This->num_outputs)) + /* TRACE("(iface: %p, info: %p\n", iface, info); */ + + if (info->channel < 0 || (info->isInput ? info->channel >= This->wineasio_number_inputs : info->channel >= This->wineasio_number_outputs)) + { + TRACE("Invalid Parameter\n"); return ASE_InvalidParameter; + } - info->type = ASIOSTFloat32LSB; /* info->type = This->sample_type; */ info->channelGroup = 0; - if (info->isInput) - { - info->isActive = This->input[info->channel].active; -#ifndef JackWASIO - strcpy(info->name, This->input[info->channel].port_name); +#ifdef ASIOST32INT + info->type = ASIOSTInt32LSB; #else - asprintf(&info->name, "Input %ld", info->channel); + info->type = ASIOSTFloat32LSB; #endif + + if (info->isInput) + { + info->isActive = This->input_channel[info->channel].active; + memcpy(info->name, This->input_channel[info->channel].port_name, ASIO_MAX_NAME_LENGTH); } else { - info->isActive = This->output[info->channel].active; -#ifndef JackWASIO - strcpy(info->name, This->output[info->channel].port_name); -#else - asprintf(&info->name, "Output %ld", info->channel); -#endif + info->isActive = This->output_channel[info->channel].active; + memcpy(info->name, This->output_channel[info->channel].port_name, ASIO_MAX_NAME_LENGTH); } - return ASE_OK; } -WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_disposeBuffers, (LPWINEASIO iface)) +/* + * ASIOError CreateBuffers(ASIOBufferInfo *bufferInfo, LONG numChannels, LONG bufferSize, ASIOCallbacks *asioCallbacks); + * Function: Allocate buffers for IO channels + * Parameters: bufferInfo - pointer to an array of ASIOBufferInfo structures + * numChannels - the total number of IO channels to be allocated + * bufferSize - one of the buffer sizes retrieved with ASIOGetBufferSize() + * asioCallbacks - pointer to an ASIOCallbacks structure + * See asio.h for more detail + * Returns: ASE_NoMemory if impossible to allocate enough memory + * ASE_InvalidMode on unsupported bufferSize or invalid bufferInfo data + * ASE_NotPresent on missing IO + */ + +DEFINE_THISCALL_WRAPPER(CreateBuffers,20) +HIDDEN ASIOError STDMETHODCALLTYPE CreateBuffers(LPWINEASIO iface, ASIOBufferInfo *bufferInfo, LONG numChannels, LONG bufferSize, ASIOCallbacks *asioCallbacks) { - int i; - IWineASIOImpl * This = (IWineASIOImpl*)iface; - TRACE("(%p)\n", iface); + IWineASIOImpl *This = (IWineASIOImpl*)iface; + ASIOBufferInfo *buffer_info = bufferInfo; + int i, j, k; - This->callbacks = NULL; - __wrapped_IWineASIOImpl_stop(iface); + TRACE("iface: %p, bufferInfo: %p, numChannels: %i, bufferSize: %i, asioCallbacks: %p\n", iface, bufferInfo, numChannels, bufferSize, asioCallbacks); - for (i = This->active_inputs; --i >= 0; ) + if (This->asio_driver_state != Initialized) { - if (This->input[i].active) - { - HeapFree(GetProcessHeap(), 0, This->input[i].buffer); - This->input[i].buffer = NULL; - This->input[i].active = ASIOFalse; - } - This->active_inputs--; + WARN("Unable to create buffers, WineASIO is not in the initialized state\n"); + return ASE_NotPresent; } - for (i = This->active_outputs; --i >= 0; ) + if (This->wineasio_fixed_buffersize) { - if (This->output[i].active) + if (This->asio_current_buffersize != bufferSize) { - HeapFree(GetProcessHeap(), 0, This->output[i].buffer); - This->output[i].buffer = NULL; - This->output[i].active = ASIOFalse; + WARN("Invalid buffersize (%i) requested\n", bufferSize); + return ASE_InvalidMode; + } + TRACE("Buffersize fixed at %i\n", This->asio_current_buffersize); + } + else + { /* fail if not a power of two and if out of range */ + if (!(bufferSize > 0 && !(bufferSize&(bufferSize-1)) + && bufferSize >= ASIO_MINIMUM_BUFFERSIZE + && bufferSize <= ASIO_MAXIMUM_BUFFERSIZE)) + { + WARN("Invalid buffersize %i requested\n", bufferSize); + return ASE_InvalidMode; + } + else + { + if (This->asio_current_buffersize == bufferSize) + TRACE("Buffer size already set to %i\n", This->asio_current_buffersize); + else + { + This->asio_current_buffersize = bufferSize; + if (jack_set_buffer_size(This->jack_client, This->asio_current_buffersize)) + { + WARN("JACK is unable to set buffersize to %i\n", This->asio_current_buffersize); + return ASE_HWMalfunction; + } + TRACE("Buffer size changed to %i\n", This->asio_current_buffersize); + } } - This->active_outputs--; } - return ASE_OK; -} + This->asio_callbacks = asioCallbacks; + if (!This->asio_callbacks->asioMessage) + { + WARN("No asioMessage callback supplied\n"); + return ASE_InvalidMode; + } -WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_createBuffers, (LPWINEASIO iface, ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks)) -{ - IWineASIOImpl * This = (IWineASIOImpl*)iface; - ASIOBufferInfo * info = bufferInfos; - int i, j; - TRACE("(%p, %p, %ld, %ld, %p)\n", iface, bufferInfos, numChannels, bufferSize, callbacks); + TRACE("The ASIO host supports ASIO v%i\n", (int) This->asio_callbacks->asioMessage(kAsioEngineVersion, 0, 0, 0)); + + if (This->asio_callbacks->asioMessage(kAsioSelectorSupported, kAsioResetRequest, 0 , 0)) + TRACE("The ASIO host supports kAsioResetRequest\n"); - /* Just to be on the safe side: */ - This->active_inputs = 0; - for(i = 0; i < This->num_inputs; i++) This->input[i].active = ASIOFalse; - This->active_outputs = 0; - for(i = 0; i < This->num_outputs; i++) This->output[i].active = ASIOFalse; + if (This->asio_callbacks->asioMessage(kAsioSelectorSupported, kAsioResyncRequest, 0 , 0)) + TRACE("The ASIO host supports kAsioResyncRequest\n"); - This->block_frames = bufferSize; - This->miliseconds = (long)((double)(This->block_frames * 1000) / This->sample_rate); + if (This->asio_callbacks->asioMessage(kAsioSelectorSupported, kAsioBufferSizeChange, 0 , 0)) + TRACE("The ASIO host supports kAsioBufferSizeChange\n"); - for (i = 0; i < numChannels; i++, info++) + /* Check for invalid channel numbers */ + for (i = j = k = 0; i < numChannels; i++, buffer_info++) { - if (info->isInput) + if (buffer_info->isInput) { - if (info->channelNum < 0 || info->channelNum >= This->num_inputs) - { - WARN("invalid input channel: %ld\n", info->channelNum); - goto ERROR_PARAM; - } - - if (This->active_inputs >= This->num_inputs) - { - WARN("too many inputs\n"); - goto ERROR_PARAM; - } - - This->input[This->active_inputs].buffer = HeapAlloc(GetProcessHeap(), 0, 2 * This->block_frames * sizeof(float)); /* ASIOSTFloat32LSB support only */ - if (This->input[This->active_inputs].buffer) - { - info->buffers[0] = &This->input[This->active_inputs].buffer[0]; - info->buffers[1] = &This->input[This->active_inputs].buffer[This->block_frames]; - for (j = 0; j < This->block_frames * 2; j++) - This->input[This->active_inputs].buffer[j] = 0; - } - else + if (j++ >= This->wineasio_number_inputs) { - HeapFree(GetProcessHeap(), 0, This->input[This->active_inputs].buffer); - info->buffers[0] = 0; - info->buffers[1] = 0; - WARN("no input buffer memory\n"); - goto ERROR_MEM; + WARN("Invalid input channel requested\n"); + return ASE_InvalidMode; } - This->input[This->active_inputs].active = ASIOTrue; - This->active_inputs++; } else { - if (info->channelNum < 0 || info->channelNum >= This->num_outputs) + if (k++ >= This->wineasio_number_outputs) { - WARN("invalid output channel: %ld\n", info->channelNum); - goto ERROR_PARAM; + WARN("Invalid output channel requested\n"); + return ASE_InvalidMode; } - - if (This->active_outputs >= This->num_outputs) - { - WARN("too many outputs\n"); - goto ERROR_PARAM; - } - - This->output[This->active_outputs].buffer = HeapAlloc(GetProcessHeap(), 0, 2 * This->block_frames * sizeof(float)); /* ASIOSTFloat32LSB support only */ - if (This->output[This->active_outputs].buffer) - { - info->buffers[0] = &This->output[This->active_outputs].buffer[0]; - info->buffers[1] = &This->output[This->active_outputs].buffer[This->block_frames]; - for (j = 0; j < This->block_frames * 2; j++) - This->output[This->active_outputs].buffer[j] = 0; - } - else - { - HeapFree(GetProcessHeap(), 0, This->output[This->active_inputs].buffer); - info->buffers[0] = 0; - info->buffers[1] = 0; - WARN("no input buffer memory\n"); - goto ERROR_MEM; - } - This->output[This->active_outputs].active = ASIOTrue; - This->active_outputs++; } } - This->callbacks = callbacks; - - if (This->callbacks->asioMessage) + /* Allocate audio buffers */ + This->callback_audio_buffer = HeapAlloc(GetProcessHeap(), 0, + (This->wineasio_number_inputs + This->wineasio_number_outputs) * 2 * This->asio_current_buffersize * sizeof(jack_default_audio_sample_t)); + if (!This->callback_audio_buffer) + { + ERR("Unable to allocate %i ASIO audio buffers\n", This->wineasio_number_inputs + This->wineasio_number_outputs); + return ASE_NoMemory; + } + TRACE("%i ASIO audio buffers allocated (%i kB)\n", This->wineasio_number_inputs + This->wineasio_number_outputs, + (int) ((This->wineasio_number_inputs + This->wineasio_number_outputs) * 2 * This->asio_current_buffersize * sizeof(jack_default_audio_sample_t) / 1024)); + + for (i = 0; i < This->wineasio_number_inputs; i++) + This->input_channel[i].audio_buffer = This->callback_audio_buffer + (i * 2 * This->asio_current_buffersize); + for (i = 0; i < This->wineasio_number_outputs; i++) + This->output_channel[i].audio_buffer = This->callback_audio_buffer + ((This->wineasio_number_inputs + i) * 2 * This->asio_current_buffersize); + + /* initialize ASIOBufferInfo structures */ + buffer_info = bufferInfo; + This->asio_active_inputs = This->asio_active_outputs = 0; + for (i = 0; i < numChannels; i++, buffer_info++) { - if (This->callbacks->asioMessage(kAsioSupportsTimeInfo, 0, 0, 0)) + if (buffer_info->isInput) { - This->time_info_mode = TRUE; - This->asio_time.timeInfo.speed = 1; - This->asio_time.timeInfo.systemTime.hi = 0; - This->asio_time.timeInfo.systemTime.lo = 0; - This->asio_time.timeInfo.samplePosition.hi = 0; - This->asio_time.timeInfo.samplePosition.lo = 0; - This->asio_time.timeInfo.sampleRate = This->sample_rate; - This->asio_time.timeInfo. flags = kSystemTimeValid | kSamplePositionValid | kSampleRateValid; - - This->asio_time.timeCode.speed = 1; - This->asio_time.timeCode.timeCodeSamples.hi = 0; - This->asio_time.timeCode.timeCodeSamples.lo = 0; - This->asio_time.timeCode.flags = kTcValid | kTcRunning; + buffer_info->buffers[0] = &This->input_channel[This->asio_active_inputs].audio_buffer[0]; + buffer_info->buffers[1] = &This->input_channel[This->asio_active_inputs].audio_buffer[This->asio_current_buffersize]; + This->input_channel[This->asio_active_inputs].active = ASIOTrue; + This->asio_active_inputs++; + /* TRACE("ASIO audio buffer for channel %i as input %li created\n", i, This->asio_active_inputs); */ } else - This->time_info_mode = FALSE; + { + buffer_info->buffers[0] = &This->output_channel[This->asio_active_outputs].audio_buffer[0]; + buffer_info->buffers[1] = &This->output_channel[This->asio_active_outputs].audio_buffer[This->asio_current_buffersize]; + This->output_channel[This->asio_active_outputs].active = ASIOTrue; + This->asio_active_outputs++; + /* TRACE("ASIO audio buffer for channel %i as output %li created\n", i, This->asio_active_outputs); */ + } } - else + TRACE("%i audio channels initialized\n", This->asio_active_inputs + This->asio_active_outputs); + + /* check for TimeInfo or TimeCode mode */ + if (This->asio_callbacks->asioMessage(kAsioSupportsTimeInfo, 0, 0, 0)) { - This->time_info_mode = FALSE; - WARN("asioMessage callback not supplied\n"); - goto ERROR_PARAM; + This->asio_time_info_mode = TRUE; + if (This->asio_callbacks->asioMessage(kAsioSupportsTimeCode, 0, 0, 0)) + This->asio_can_time_code = TRUE; } + else + This->asio_time_info_mode = FALSE; + This->asio_driver_state = Prepared; return ASE_OK; +} -ERROR_MEM: - __wrapped_IWineASIOImpl_disposeBuffers(iface); - WARN("no memory\n"); - return ASE_NoMemory; +/* + * ASIOError DisposeBuffers(void); + * Function: Release allocated buffers + * Returns: ASE_InvalidMode if no buffers were previously allocated + * ASE_NotPresent on missing IO + * Implies: ASIOStop() + */ -ERROR_PARAM: - __wrapped_IWineASIOImpl_disposeBuffers(iface); - WARN("invalid parameter\n"); - return ASE_InvalidParameter; +DEFINE_THISCALL_WRAPPER(DisposeBuffers,4) +HIDDEN ASIOError STDMETHODCALLTYPE DisposeBuffers(LPWINEASIO iface) +{ + IWineASIOImpl *This = (IWineASIOImpl*)iface; + int i; + + TRACE("iface: %p\n", iface); + + if (This->asio_driver_state == Running) + Stop (iface); + if (This->asio_driver_state != Prepared) + { + WARN("Unable to dispose buffers, wrong driver state\n"); + return ASE_NotPresent; + } + + This->asio_callbacks = NULL; + + for (i = 0; i < This->wineasio_number_inputs; i++) + { + This->input_channel[i].audio_buffer = NULL; + This->input_channel[i].active = ASIOFalse; + } + for (i = 0; i < This->wineasio_number_outputs; i++) + { + This->output_channel[i].audio_buffer = NULL; + This->output_channel[i].active = ASIOFalse; + } + This->asio_active_inputs = This->asio_active_outputs = 0; + + if (This->callback_audio_buffer) + HeapFree(GetProcessHeap(), 0, This->callback_audio_buffer); + + This->asio_driver_state = Initialized; + return ASE_OK; } -WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_controlPanel, (LPWINEASIO iface)) +/* + * ASIOError ControlPanel(void); + * Function: Open a control panel for driver settings + * Returns: ASE_NotPresent if no control panel exists. Actually return code should be ignored + * Note: Call the asioMessage callback if something has changed + */ + +DEFINE_THISCALL_WRAPPER(ControlPanel,4) +HIDDEN ASIOError STDMETHODCALLTYPE ControlPanel(LPWINEASIO iface) { - char* arg_list[] = { "qjackctl", NULL }; + char *arg_list[] = { strdup ("qjackctl"), NULL }; - TRACE ("Opening ASIO control panel\n"); + TRACE("iface: %p\n", iface); - if (fork() == 0) + if (!fork()) execvp (arg_list[0], arg_list); return ASE_OK; } -WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_future, (LPWINEASIO iface, long selector, void *opt)) +/* + * ASIOError Future(LONG selector, void *opt); + * Function: Various, See asio.h for more detail + * Returns: Depends on the selector but in general ASE_InvalidParameter on invalid selector + * ASE_InvalidParameter if function is unsupported to disable further calls + * ASE_SUCCESS on success, do not use AES_OK + */ + +DEFINE_THISCALL_WRAPPER(Future,12) +HIDDEN ASIOError STDMETHODCALLTYPE Future(LPWINEASIO iface, LONG selector, void *opt) { - IWineASIOImpl * This = (IWineASIOImpl*)iface; - TRACE("(%p, %ld, %p)\n", iface, selector, opt); + IWineASIOImpl *This = (IWineASIOImpl *) iface; + + TRACE("iface: %p, selector: %i, opt: %p\n", iface, selector, opt); switch (selector) { - case kAsioEnableTimeCodeRead: - This->tc_read = TRUE; - return ASE_SUCCESS; - case kAsioDisableTimeCodeRead: - This->tc_read = FALSE; - return ASE_SUCCESS; - case kAsioSetInputMonitor: - return ASE_SUCCESS; - case kAsioCanInputMonitor: - return ASE_SUCCESS; - case kAsioCanTimeInfo: - return ASE_SUCCESS; - case kAsioCanTimeCode: - return ASE_SUCCESS; + case kAsioEnableTimeCodeRead: + This->asio_can_time_code = TRUE; + TRACE("The ASIO host enabled TimeCode\n"); + return ASE_SUCCESS; + case kAsioDisableTimeCodeRead: + This->asio_can_time_code = FALSE; + TRACE("The ASIO host disabled TimeCode\n"); + return ASE_SUCCESS; + case kAsioSetInputMonitor: + TRACE("The driver denied request to set input monitor\n"); + return ASE_NotPresent; + case kAsioTransport: + TRACE("The driver denied request for ASIO Transport control\n"); + return ASE_InvalidParameter; + case kAsioSetInputGain: + TRACE("The driver denied request to set input gain\n"); + return ASE_InvalidParameter; + case kAsioGetInputMeter: + TRACE("The driver denied request to get input meter \n"); + return ASE_InvalidParameter; + case kAsioSetOutputGain: + TRACE("The driver denied request to set output gain\n"); + return ASE_InvalidParameter; + case kAsioGetOutputMeter: + TRACE("The driver denied request to get output meter\n"); + return ASE_InvalidParameter; + case kAsioCanInputMonitor: + TRACE("The driver does not support input monitor\n"); + return ASE_InvalidParameter; + case kAsioCanTimeInfo: + TRACE("The driver supports TimeInfo\n"); + return ASE_SUCCESS; + case kAsioCanTimeCode: + TRACE("The driver supports TimeCode\n"); + return ASE_SUCCESS; + case kAsioCanTransport: + TRACE("The driver denied request for ASIO Transport\n"); + return ASE_InvalidParameter; + case kAsioCanInputGain: + TRACE("The driver does not support input gain\n"); + return ASE_InvalidParameter; + case kAsioCanInputMeter: + TRACE("The driver does not support input meter\n"); + return ASE_InvalidParameter; + case kAsioCanOutputGain: + TRACE("The driver does not support output gain\n"); + return ASE_InvalidParameter; + case kAsioCanOutputMeter: + TRACE("The driver does not support output meter\n"); + return ASE_InvalidParameter; + case kAsioSetIoFormat: + TRACE("The driver denied request to set DSD IO format\n"); + return ASE_NotPresent; + case kAsioGetIoFormat: + TRACE("The driver denied request to get DSD IO format\n"); + return ASE_NotPresent; + case kAsioCanDoIoFormat: + TRACE("The driver does not support DSD IO format\n"); + return ASE_NotPresent; + default: + TRACE("ASIOFuture() called with undocumented selector\n"); + return ASE_InvalidParameter; } +} + +/* + * ASIOError OutputReady(void); + * Function: Tells the driver that output bufffers are ready + * Returns: ASE_OK if supported + * ASE_NotPresent to disable + */ +DEFINE_THISCALL_WRAPPER(OutputReady,4) +HIDDEN ASIOError STDMETHODCALLTYPE OutputReady(LPWINEASIO iface) +{ + /* disabled to stop stand alone NI programs from spamming the console + TRACE("iface: %p\n", iface); */ return ASE_NotPresent; } -WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_outputReady, (LPWINEASIO iface)) +/**************************************************************************** + * JACK callbacks + */ + +static int bufsize_callback(jack_nframes_t nframes, void *arg) { - TRACE("(%p)\n", iface); + IWineASIOImpl *This = (IWineASIOImpl*)arg; - return ASE_NotPresent; + if(This->asio_driver_state != Running) + return 1; + + if (This->asio_callbacks->asioMessage(kAsioSelectorSupported, kAsioResetRequest, 0 , 0)) + This->asio_callbacks->asioMessage(kAsioResetRequest, 0, 0, 0); + return 0; } -static const IWineASIOVtbl WineASIO_Vtbl = +static int process_callback(jack_nframes_t nframes, void *arg) { - IWineASIOImpl_QueryInterface, - IWineASIOImpl_AddRef, - IWineASIOImpl_Release, - IWineASIOImpl_init, - IWineASIOImpl_getDriverName, - IWineASIOImpl_getDriverVersion, - IWineASIOImpl_getErrorMessage, - IWineASIOImpl_start, - IWineASIOImpl_stop, - IWineASIOImpl_getChannels, - IWineASIOImpl_getLatencies, - IWineASIOImpl_getBufferSize, - IWineASIOImpl_canSampleRate, - IWineASIOImpl_getSampleRate, - IWineASIOImpl_setSampleRate, - IWineASIOImpl_getClockSources, - IWineASIOImpl_setClockSource, - IWineASIOImpl_getSamplePosition, - IWineASIOImpl_getChannelInfo, - IWineASIOImpl_createBuffers, - IWineASIOImpl_disposeBuffers, - IWineASIOImpl_controlPanel, - IWineASIOImpl_future, - IWineASIOImpl_outputReady, -}; + IWineASIOImpl *This = (IWineASIOImpl*)arg; + int i; + jack_transport_state_t jack_transport_state; + jack_position_t jack_position; + +#ifdef ASIOST32INT + jack_default_audio_sample_t *in, *out; + jack_nframes_t j; +#endif -HRESULT asioCreateInstance(REFIID riid, LPVOID *ppobj) -{ - IWineASIOImpl * pobj; - TRACE("(%s, %p)\n", debugstr_guid(riid), ppobj); +#ifndef _WIN64 + DWORD temp_time; +#endif - pobj = HeapAlloc(GetProcessHeap(), 0, sizeof(*pobj)); - if (pobj == NULL) { - WARN("out of memory\n"); - return E_OUTOFMEMORY; + if (This->asio_driver_state != Running) + return 1; + + /* get the input data from JACK and copy it to the ASIO buffers */ +#ifdef ASIOST32INT + for (i = 0; i < This->asio_active_inputs; i++) + if (This->input_channel[i].active == ASIOTrue) + { + in = jack_port_get_buffer(This->input_channel[i].port, nframes); + for (j = 0; j < nframes; j++) + ((int *)This->input_channel[i].audio_buffer)[nframes * This->asio_buffer_index + j] = in[j] * 0x7fffffff; + } +#else + for (i = 0; i < This->asio_active_inputs; i++) + if (This->input_channel[i].active == ASIOTrue) + memcpy (&This->input_channel[i].audio_buffer[nframes * This->asio_buffer_index], + jack_port_get_buffer(This->input_channel[i].port, nframes), + sizeof (jack_default_audio_sample_t) * nframes); +#endif + +#ifdef _WIN64 + This->asio_sample_position += nframes; + This->asio_sample_position & 0xffffffff; /* make 32bit since JACK's position is 32bit anyways */ + This->asio_time_stamp = timeGetTime() * 1000000; +#else + This->asio_sample_position.lo += nframes; + temp_time = timeGetTime(); + This->asio_time_stamp.lo = temp_time * 1000000; + This->asio_time_stamp.hi = ((unsigned long long) temp_time * 1000000) >> 32; +#endif + + /* time info & time code */ + if (This->asio_time_info_mode) + { +#ifdef _WIN64 + This->asio_time.timeInfo.samplePosition = This->asio_sample_position; + This->asio_time.timeInfo.systemTime = This->asio_time_stamp; +#else + This->asio_time.timeInfo.samplePosition.lo = This->asio_sample_position.lo; + This->asio_time.timeInfo.samplePosition.hi = 0; + This->asio_time.timeInfo.systemTime.lo = This->asio_time_stamp.lo; + This->asio_time.timeInfo.systemTime.hi = This->asio_time_stamp.hi; +#endif + This->asio_time.timeInfo.sampleRate = This->asio_sample_rate; + This->asio_time.timeInfo.flags = kSystemTimeValid | kSamplePositionValid | kSampleRateValid; + + if (This->asio_can_time_code) + { + jack_transport_state = jack_transport_query(This->jack_client, &jack_position); +#ifdef _WIN64 + This->asio_time.timeCode.timeCodeSamples = jack_position.frame; +#else + This->asio_time.timeCode.timeCodeSamples.lo = jack_position.frame; + This->asio_time.timeCode.timeCodeSamples.hi = 0; +#endif + This->asio_time.timeCode.flags = kTcValid; + if (jack_transport_state == JackTransportRolling) + This->asio_time.timeCode.flags |= kTcRunning; + } + This->asio_callbacks->bufferSwitchTimeInfo(&This->asio_time, This->asio_buffer_index, ASIOTrue); } + else + This->asio_callbacks->bufferSwitch(This->asio_buffer_index, ASIOTrue); - pobj->lpVtbl = &WineASIO_Vtbl; - pobj->ref = 1; - TRACE("pobj = %p\n", pobj); - *ppobj = pobj; - TRACE("return %p\n", *ppobj); - return S_OK; + /* copy the ASIO data to JACK */ +#ifdef ASIOST32INT + for (i = 0; i < This->asio_active_outputs; i++) + if (This->output_channel[i].active == ASIOTrue) + { + out = jack_port_get_buffer(This->output_channel[i].port, nframes); + for (j = 0; j < nframes; j++) + out[j] = ((int *) This->output_channel[i].audio_buffer)[nframes * This->asio_buffer_index + j] / (float) 0x7fffffff; + } +#else + for (i = 0; i < This->asio_active_outputs; i++) + if (This->output_channel[i].active == ASIOTrue) + memcpy(jack_port_get_buffer(This->output_channel[i].port, nframes), + &This->output_channel[i].audio_buffer[nframes * This->asio_buffer_index], + sizeof (jack_default_audio_sample_t) * nframes); +#endif + This->asio_buffer_index = This->asio_buffer_index ? 0 : 1; + return 0; } -static void getNanoSeconds(ASIOTimeStamp* ts) +static int srate_callback(jack_nframes_t nframes, void *arg) { - double nanoSeconds = (double)((unsigned long)timeGetTime ()) * 1000000.; - ts->hi = (unsigned long)(nanoSeconds / twoRaisedTo32); - ts->lo = (unsigned long)(nanoSeconds - (ts->hi * twoRaisedTo32)); + IWineASIOImpl *This = (IWineASIOImpl*)arg; + + if(This->asio_driver_state != Running) + return 1; + + This->asio_sample_rate = nframes; + This->asio_callbacks->sampleRateDidChange(nframes); + return 0; } -/* - * The ASIO callback can make WIN32 calls which require a WIN32 thread. - * Do the callback in this thread and then switch back to the Jack callback thread. +static int xrun_callback(void *arg) +{ + IWineASIOImpl *This = (IWineASIOImpl*)arg; + + if(This->asio_driver_state != Running) + return 1; + + if (This->asio_callbacks->asioMessage(kAsioSelectorSupported, kAsioResyncRequest, 0 , 0)) + This->asio_callbacks->asioMessage(kAsioResyncRequest, 0, 0, 0); + return 0; +} + +/***************************************************************************** + * Support functions */ -static DWORD CALLBACK win32_callback(LPVOID arg) + +#ifdef __WINESRC__ +/* Function called by JACK to create a thread in the wine process context, + * uses the global structure jack_thread_creator_privates to communicate with jack_thread_creator_helper() */ +static int jack_thread_creator(pthread_t* thread_id, const pthread_attr_t* attr, void *(*function)(void*), void* arg) { - IWineASIOImpl * This = (IWineASIOImpl*)arg; - TRACE("(%p) Win32 thread starting\n", arg); + TRACE("arg: %p, thread_id: %p, attr: %p, function: %p\n", arg, thread_id, attr, function); + + jack_thread_creator_privates.jack_callback_thread = function; + jack_thread_creator_privates.arg = arg; + jack_thread_creator_privates.jack_callback_thread_created = CreateEventW(NULL, FALSE, FALSE, NULL); + CreateThread( NULL, 0, jack_thread_creator_helper, arg, 0,0 ); + WaitForSingleObject(jack_thread_creator_privates.jack_callback_thread_created, INFINITE); + *thread_id = jack_thread_creator_privates.jack_callback_pthread_id; + return 0; +} - /* let IWineASIO_Init know we are alive */ - SetEvent(This->start_event); +/* internal helper function for returning the posix thread_id of the newly created callback thread */ +static DWORD WINAPI jack_thread_creator_helper(LPVOID arg) +{ + TRACE("arg: %p\n", arg); - /* wait for JACK client to be connected and set sched_priority */ - sem_wait(&This->semaphore1); - /* set the priority of the win32 callback thread as suggested by JACK */ -#ifndef JackWASIO - if (This->jack_client_priority.sched_priority != -1) /* skip if not running realtime */ + jack_thread_creator_privates.jack_callback_pthread_id = pthread_self(); + SetEvent(jack_thread_creator_privates.jack_callback_thread_created); + jack_thread_creator_privates.jack_callback_thread(jack_thread_creator_privates.arg); + return 0; +} +#endif + +static BOOL configure_driver(IWineASIOImpl *This) +{ + HKEY hkey; + LONG result, value; + DWORD type, size; + WCHAR application_path [MAX_PATH]; + WCHAR *application_name; + char environment_variable[MAX_ENVIRONMENT_SIZE]; + + /* Unicode strings used for the registry */ + static const WCHAR key_software_wine_wineasio[] = + { 'S','o','f','t','w','a','r','e','\\', + 'W','i','n','e','\\', + 'W','i','n','e','A','S','I','O',0 }; + static const WCHAR value_wineasio_number_inputs[] = + { 'N','u','m','b','e','r',' ','o','f',' ','i','n','p','u','t','s',0 }; + static const WCHAR value_wineasio_number_outputs[] = + { 'N','u','m','b','e','r',' ','o','f',' ','o','u','t','p','u','t','s',0 }; + static const WCHAR value_wineasio_fixed_buffersize[] = + { 'F','i','x','e','d',' ','b','u','f','f','e','r','s','i','z','e',0 }; + static const WCHAR value_wineasio_preferred_buffersize[] = + { 'P','r','e','f','e','r','r','e','d',' ','b','u','f','f','e','r','s','i','z','e',0 }; + static const WCHAR wineasio_autostart_server[] = + { 'A','u','t','o','s','t','a','r','t',' ','s','e','r','v','e','r',0 }; + static const WCHAR value_wineasio_connect_to_hardware[] = + { 'C','o','n','n','e','c','t',' ','t','o',' ','h','a','r','d','w','a','r','e',0 }; + + /* Initialise most member variables, + * asio_sample_position, asio_time, & asio_time_stamp are initialized in Start() + * jack_num_input_ports & jack_num_output_ports are initialized in Init() */ + This->asio_active_inputs = 0; + This->asio_active_outputs = 0; + This->asio_buffer_index = 0; + This->asio_callbacks = NULL; + This->asio_can_time_code = FALSE; + This->asio_current_buffersize = 0; + This->asio_driver_state = Loaded; + This->asio_sample_rate = 0; + This->asio_time_info_mode = FALSE; + This->asio_version = 90; + + This->wineasio_number_inputs = 16; + This->wineasio_number_outputs = 16; + This->wineasio_autostart_server = FALSE; + This->wineasio_connect_to_hardware = TRUE; + This->wineasio_fixed_buffersize = TRUE; + This->wineasio_preferred_buffersize = ASIO_PREFERRED_BUFFERSIZE; + + This->jack_client = NULL; + This->jack_client_name[0] = 0; + This->jack_input_ports = NULL; + This->jack_output_ports = NULL; + This->callback_audio_buffer = NULL; + This->input_channel = NULL; + This->output_channel = NULL; + + /* create registry entries with defaults if not present */ + result = RegCreateKeyExW(HKEY_CURRENT_USER, key_software_wine_wineasio, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL); + if (result != ERROR_SUCCESS) { - if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &This->jack_client_priority) == 0) - TRACE ("win32 callback set to SCHED_FIFO priority %d\n", This->jack_client_priority.sched_priority); - else - TRACE ("Error trying to set realtime priority of win32 callback\n"); + ERR("Unable to open registry\n"); + return FALSE; + } + + /* get/set number of asio inputs */ + size = sizeof(DWORD); + if (RegQueryValueExW(hkey, value_wineasio_number_inputs, NULL, &type, (LPBYTE) &value, &size) == ERROR_SUCCESS) + { + if (type == REG_DWORD) + This->wineasio_number_inputs = value; } else - TRACE ("Unable to set realtime priority of win32 callback, not running with realtime priority\n"); -#else - setThreadToPriority(pthread_self(),96,TRUE,10000000); -#endif + { + type = REG_DWORD; + size = sizeof(DWORD); + value = This->wineasio_number_inputs; + result = RegSetValueExW(hkey, value_wineasio_number_inputs, 0, REG_DWORD, (LPBYTE) &value, size); + } + + /* get/set number of asio outputs */ + size = sizeof(DWORD); + if (RegQueryValueExW(hkey, value_wineasio_number_outputs, NULL, &type, (LPBYTE) &value, &size) == ERROR_SUCCESS) + { + if (type == REG_DWORD) + This->wineasio_number_outputs = value; + } + else + { + type = REG_DWORD; + size = sizeof(DWORD); + value = This->wineasio_number_outputs; + result = RegSetValueExW(hkey, value_wineasio_number_outputs, 0, REG_DWORD, (LPBYTE) &value, size); + } - while (1) + /* allow changing of asio buffer sizes */ + size = sizeof(DWORD); + if (RegQueryValueExW(hkey, value_wineasio_fixed_buffersize, NULL, &type, (LPBYTE) &value, &size) == ERROR_SUCCESS) { - /* wait to be woken up by the JACK callback thread */ - sem_wait(&This->semaphore1); + if (type == REG_DWORD) + This->wineasio_fixed_buffersize = value; + } + else + { + type = REG_DWORD; + size = sizeof(DWORD); + value = This->wineasio_fixed_buffersize; + result = RegSetValueExW(hkey, value_wineasio_fixed_buffersize, 0, REG_DWORD, (LPBYTE) &value, size); + } - if (This->terminate) - { - TRACE("Win32 thread terminating\n"); - SetEvent(This->stop_event); - return 0; - } + /* preferred buffer size (if changing buffersize is allowed) */ + size = sizeof(DWORD); + if (RegQueryValueExW(hkey, value_wineasio_preferred_buffersize, NULL, &type, (LPBYTE) &value, &size) == ERROR_SUCCESS) + { + if (type == REG_DWORD) + This->wineasio_preferred_buffersize = value; + } + else + { + type = REG_DWORD; + size = sizeof(DWORD); + value = This->wineasio_preferred_buffersize; + result = RegSetValueExW(hkey, value_wineasio_preferred_buffersize, 0, REG_DWORD, (LPBYTE) &value, size); + } - if (This->state != Run) - return 0; + /* get/set JACK autostart */ + size = sizeof(DWORD); + if (RegQueryValueExW(hkey, wineasio_autostart_server, NULL, &type, (LPBYTE) &value, &size) == ERROR_SUCCESS) + { + if (type == REG_DWORD) + This->wineasio_autostart_server = value; + } + else + { + type = REG_DWORD; + size = sizeof(DWORD); + value = This->wineasio_autostart_server; + result = RegSetValueExW(hkey, wineasio_autostart_server, 0, REG_DWORD, (LPBYTE) &value, size); + } - if (This->time_info_mode) - { - getNanoSeconds(&This->system_time); - This->sample_position += This->block_frames; + /* get/set JACK connect to physical io */ + size = sizeof(DWORD); + if (RegQueryValueExW(hkey, value_wineasio_connect_to_hardware, NULL, &type, (LPBYTE) &value, &size) == ERROR_SUCCESS) + { + if (type == REG_DWORD) + This->wineasio_connect_to_hardware = value; + } + else + { + type = REG_DWORD; + size = sizeof(DWORD); + value = This->wineasio_connect_to_hardware; + result = RegSetValueExW(hkey, value_wineasio_connect_to_hardware, 0, REG_DWORD, (LPBYTE) &value, size); + } - __wrapped_IWineASIOImpl_getSamplePosition((LPWINEASIO)This, - &This->asio_time.timeInfo.samplePosition, &This->asio_time.timeInfo.systemTime); - if (This->tc_read) - { - This->asio_time.timeCode.timeCodeSamples.lo = This->asio_time.timeInfo.samplePosition.lo; - This->asio_time.timeCode.timeCodeSamples.hi = 0; - } - This->callbacks->bufferSwitchTimeInfo(&This->asio_time, This->toggle, ASIOTrue); - This->asio_time.timeInfo.flags &= ~(kSampleRateChanged | kClockSourceChanged); - } - else - This->callbacks->bufferSwitch(This->toggle, ASIOTrue); + /* get client name by stripping path and extension */ + GetModuleFileNameW(0, application_path, MAX_PATH); + application_name = strrchrW(application_path, L'.'); + *application_name = 0; + application_name = strrchrW(application_path, L'\\'); + application_name++; + WideCharToMultiByte(CP_ACP, WC_SEPCHARS, application_name, -1, This->jack_client_name, ASIO_MAX_NAME_LENGTH, NULL, NULL); - /* let the JACK thread know we are done */ - sem_post(&This->semaphore2); + RegCloseKey(hkey); + + /* Look for environment variables to override registry config values */ + + if (GetEnvironmentVariableA("WINEASIO_NUMBER_INPUTS", environment_variable, MAX_ENVIRONMENT_SIZE)) + { + errno = 0; + result = strtol(environment_variable, 0, 10); + if (errno != ERANGE) + This->wineasio_number_inputs = result; } - return 0; -} + if (GetEnvironmentVariableA("WINEASIO_NUMBER_OUTPUTS", environment_variable, MAX_ENVIRONMENT_SIZE)) + { + errno = 0; + result = strtol(environment_variable, 0, 10); + if (errno != ERANGE) + This->wineasio_number_outputs = result; + } -static int jack_process(jack_nframes_t nframes, void * arg) -{ - IWineASIOImpl * This = (IWineASIOImpl*)arg; - int i; -/* - jack_position_t transport; - jack_transport_state ts = jack_transport_query(This->client, &transport); - if (ts == JackTransportRolling) - This->sample_position = transport.frame; - else - */ - This->sample_position += nframes; + if (GetEnvironmentVariableA("WINEASIO_AUTOSTART_SERVER", environment_variable, MAX_ENVIRONMENT_SIZE)) + { + if (!strcasecmp(environment_variable, "on")) + This->wineasio_autostart_server = TRUE; + else if (!strcasecmp(environment_variable, "off")) + This->wineasio_autostart_server = FALSE; + } - /* copy the JACK date to ASIO */ - for (i = 0; i < This->active_inputs; i++) - if (This->input[i].active == ASIOTrue) - memcpy( - &This->input[i].buffer[This->block_frames * This->toggle], /* dest: ASIO */ - jack_port_get_buffer(This->input[i].port, nframes), /* src: JACK */ - This->block_frames * sizeof(float)); + if (GetEnvironmentVariableA("WINEASIO_CONNECT_TO_HARDWARE", environment_variable, MAX_ENVIRONMENT_SIZE)) + { + if (!strcasecmp(environment_variable, "on")) + This->wineasio_autostart_server = TRUE; + else if (!strcasecmp(environment_variable, "off")) + This->wineasio_autostart_server = FALSE; + } - /* wake up the WIN32 thread so it can do its callback */ - sem_post(&This->semaphore1); + if (GetEnvironmentVariableA("WINEASIO_FIXED_BUFFERSIZE", environment_variable, MAX_ENVIRONMENT_SIZE)) + { + if (!strcasecmp(environment_variable, "on")) + This->wineasio_fixed_buffersize = TRUE; + if (!strcasecmp(environment_variable, "off")) + This->wineasio_fixed_buffersize = FALSE; + } - /* wait for the WIN32 thread to complete before continuing */ - sem_wait(&This->semaphore2); + if (GetEnvironmentVariableA("WINEASIO_PREFERRED_BUFFERSIZE", environment_variable, MAX_ENVIRONMENT_SIZE)) + { + errno = 0; + result = strtol(environment_variable, 0, 10); + if (errno != ERANGE) + This->wineasio_preferred_buffersize = result; + } - /* copy the ASIO data to JACK */ - for (i = 0; i < This->num_outputs; i++) - if (This->output[i].active == ASIOTrue) - memcpy( - jack_port_get_buffer(This->output[i].port, nframes), /* dest: JACK */ - &This->output[i].buffer[This->block_frames * This->toggle], /* src: ASIO */ - This->block_frames * sizeof(float) - ); + /* over ride the JACK client name gotten from the application name */ + size = GetEnvironmentVariableA("WINEASIO_CLIENT_NAME", environment_variable, ASIO_MAX_NAME_LENGTH); + if (size > 0 && size < ASIO_MAX_NAME_LENGTH) + strcpy(This->jack_client_name, environment_variable); - This->toggle = This->toggle ? 0 : 1; + /* if wineasio_preferred_buffersize is not a power of two or if out of range, then set to ASIO_PREFERRED_BUFFERSIZE */ + if (!(This->wineasio_preferred_buffersize > 0 && !(This->wineasio_preferred_buffersize&(This->wineasio_preferred_buffersize-1)) + && This->wineasio_preferred_buffersize >= ASIO_MINIMUM_BUFFERSIZE + && This->wineasio_preferred_buffersize <= ASIO_MAXIMUM_BUFFERSIZE)) + This->wineasio_preferred_buffersize = ASIO_PREFERRED_BUFFERSIZE; - return 0; + return TRUE; +} + +/* Allocate the interface pointer and associate it with the vtbl/WineASIO object */ +HRESULT WINAPI WineASIOCreateInstance(REFIID riid, LPVOID *ppobj) +{ + IWineASIOImpl *pobj; + + TRACE("riid: %s, ppobj: %p\n", debugstr_guid(riid), ppobj); + + pobj = HeapAlloc(GetProcessHeap(), 0, sizeof(*pobj)); + if (pobj == NULL) + { + WARN("out of memory\n"); + return E_OUTOFMEMORY; + } + + pobj->lpVtbl = &WineASIO_Vtbl; + pobj->ref = 1; + TRACE("pobj = %p\n", pobj); + *ppobj = pobj; + TRACE("return %p\n", *ppobj); + return S_OK; } diff --git a/config.h b/config.h deleted file mode 100644 index 06e1b21..0000000 --- a/config.h +++ /dev/null @@ -1,1070 +0,0 @@ -/* include/config.h. Generated by configure. */ -/* include/config.h.in. Generated from configure.ac by autoheader. */ - -#define __WINE_CONFIG_H - -/* Specifies the compiler flag that forces a short wchar_t */ -#define CC_FLAG_SHORT_WCHAR "-fshort-wchar" - -/* Define if you have ALSA 1.x including devel headers */ -#define HAVE_ALSA 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_ALSA_ASOUNDLIB_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_ARPA_INET_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_ARPA_NAMESER_H 1 - -/* Define if you have ARTS sound server */ -#define HAVE_ARTS 1 - -/* Define to 1 if you have the `asctime_r' function. */ -#define HAVE_ASCTIME_R 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_ASM_TYPES_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_AUDIOUNIT_AUDIOUNIT_H */ - -/* Define to 1 if you have the