summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS14
-rw-r--r--COPYING340
-rw-r--r--Makefile.am36
-rw-r--r--TODO20
-rw-r--r--alsa_driver.c1226
-rwxr-xr-xautogen.sh6
-rw-r--r--client.c1206
-rw-r--r--configure.in64
-rwxr-xr-xconftestbin0 -> 6650 bytes
-rw-r--r--conftest.c1
-rw-r--r--doc/Makefile62
-rw-r--r--doc/compiling.m44
-rw-r--r--doc/configuring.m44
-rw-r--r--doc/contributors.m44
-rw-r--r--doc/download.m44
-rw-r--r--doc/faq.m44
-rw-r--r--doc/features.m44
-rw-r--r--doc/header.html54
-rw-r--r--doc/helping.m44
-rw-r--r--doc/index.m47
-rw-r--r--doc/intro.m44
-rw-r--r--doc/issues.m485
-rw-r--r--doc/links.m44
-rw-r--r--doc/mailinglist.m44
-rw-r--r--doc/manual.m44
-rw-r--r--doc/news.m44
-rw-r--r--doc/requirements.m44
-rw-r--r--doc/todo.m44
-rw-r--r--doc/trailer.html23
-rw-r--r--driver.c238
-rw-r--r--engine.c2250
-rw-r--r--fltk_client.cc98
-rw-r--r--generic_hw.c57
-rw-r--r--hammerfall.c293
-rw-r--r--jack.pc.in11
-rw-r--r--jack/Makefile.am4
-rw-r--r--jack/alsa_driver.h159
-rw-r--r--jack/driver.h136
-rw-r--r--jack/engine.h87
-rw-r--r--jack/error.h8
-rw-r--r--jack/generic.h7
-rw-r--r--jack/hammerfall.h17
-rw-r--r--jack/hardware.h27
-rw-r--r--jack/internal.h215
-rw-r--r--jack/jack.h228
-rw-r--r--jack/memops.h89
-rw-r--r--jack/pool.h29
-rw-r--r--jack/port.h89
-rw-r--r--jack/types.h69
-rw-r--r--jackd.c157
-rw-r--r--memops.c257
-rw-r--r--monitor_client.c23
-rw-r--r--pool.c31
-rw-r--r--port.h51
-rw-r--r--simple_client.c83
55 files changed, 7913 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..3d442a3
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,14 @@
+JACK was inspired by and partially designed during discussions on the
+Linux Audio Developers mailing list. Particularly significant
+contributions to those discussions came from (in alphabetical order):
+
+ Paul Davis
+ David Olofson
+ Benno Sennoner
+ Kai Vehamen
+
+Many other members of LAD contributed ideas to JACK.
+
+Paul Davis was the principal author of the JACK API and of the sample
+implementation contained here.
+
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d60c31a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+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 Program or any portion
+of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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 Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) 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; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, 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 executable. However, as a
+special exception, the source code 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.
+
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+ 5. 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 Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program 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 to
+this License.
+
+ 7. 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 Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+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.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies 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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. 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 PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..ee2a6a7
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,36 @@
+MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure config.h.in \
+ stamp-h.in \
+ config.guess mkinstalldirs \
+ missing install-sh config.sub ltconfig \
+ ltmain.sh acinclude.m4
+
+SUBDIRS = jack doc
+
+EXTRA_PROGRAMS = jack_fltk_client
+
+bin_PROGRAMS = jackd jack_simple_client jack_monitor_client @XTRA@
+
+jackd_SOURCES = jackd.c
+jackd_LDFLAGS = -L. -ljack -lltdl -lpthread
+
+jack_simple_client_SOURCES = simple_client.c
+jack_simple_client_LDFLAGS = -L. -ljack -lltdl -lpthread
+
+jack_monitor_client_SOURCES = monitor_client.c
+jack_monitor_client_LDFLAGS = -L. -ljack -lltdl -lpthread
+
+jack_fltk_client_SOURCES = fltk_client.cc
+jack_fltk_client_LDFLAGS = -L. -L/usr/X11R6/lib -lfltk -lX11 -lXext -ljack -lltdl -lpthread
+
+lib_LTLIBRARIES = libjack.la jack_alsa.la
+
+libjack_la_SOURCES = engine.c client.c pool.c driver.c
+
+jack_alsa_la_LDFLAGS = -module
+jack_alsa_la_SOURCES = alsa_driver.c hammerfall.c generic_hw.c memops.c
+jack_alsa_la_LIBADD = -lasound
+
+EXTRA_DIST = jack
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = jack.pc
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..9cc9801
--- /dev/null
+++ b/TODO
@@ -0,0 +1,20 @@
+TODO
+
+fix graph sorting to use same approach as in ARDOUR::Session for
+ routes
+
+figure out how to have pools of buffers for ports by type
+figure out how to use the same buffer over and over when possible
+
+passing args to drivers and clients?
+getting callbacks and args from dynamically loaded clients?
+how do dynamically loaded clients (un)register ports, activate, etc.
+pool based malloc for rt client-local mem allocation
+
+TO THINK ABOUT:
+
+multiple port buffer shm segments (i.e. dynamically
+ increase the total number of ports in the system)
+
+
+
diff --git a/alsa_driver.c b/alsa_driver.c
new file mode 100644
index 0000000..688c552
--- /dev/null
+++ b/alsa_driver.c
@@ -0,0 +1,1226 @@
+/*
+ Copyright (C) 2001 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <math.h>
+#include <stdio.h>
+#include <memory.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <asm/msr.h>
+#include <glib.h>
+#include <stdarg.h>
+
+#include <jack/alsa_driver.h>
+#include <jack/types.h>
+#include <jack/internal.h>
+#include <jack/engine.h>
+#include <jack/hammerfall.h>
+#include <jack/generic.h>
+
+static int config_max_level = 0;
+static int config_min_level = 0;
+
+static unsigned long current_usecs () {
+ unsigned long now;
+ rdtscl (now);
+ return now / 450;
+}
+
+static void
+alsa_driver_release_channel_dependent_memory (alsa_driver_t *driver)
+
+{
+ if (driver->playback_addr) {
+ free (driver->playback_addr);
+ driver->playback_addr = 0;
+ }
+
+ if (driver->capture_addr) {
+ free (driver->capture_addr);
+ driver->capture_addr = 0;
+ }
+
+ if (driver->silent) {
+ free (driver->silent);
+ driver->silent = 0;
+ }
+
+ if (driver->input_monitor_requests) {
+ free (driver->input_monitor_requests);
+ driver->input_monitor_requests = 0;
+ }
+}
+
+static int
+alsa_driver_check_capabilities (alsa_driver_t *driver)
+
+{
+ return 0;
+}
+
+static int
+alsa_driver_check_card_type (alsa_driver_t *driver)
+
+{
+ int err;
+ snd_ctl_card_info_t *card_info;
+
+ snd_ctl_card_info_alloca (&card_info);
+
+ if ((err = snd_ctl_open (&driver->ctl_handle, driver->alsa_name, 0)) < 0) {
+ jack_error ("control open \"%s\" (%s)", driver->alsa_name, snd_strerror(err));
+ return -1;
+ }
+
+ if ((err = snd_ctl_card_info(driver->ctl_handle, card_info)) < 0) {
+ jack_error ("control hardware info \"%s\" (%s)", driver->alsa_name, snd_strerror (err));
+ snd_ctl_close (driver->ctl_handle);
+ return -1;
+ }
+
+ driver->alsa_driver = strdup(snd_ctl_card_info_get_driver (card_info));
+
+ return alsa_driver_check_capabilities (driver);
+}
+
+static int
+alsa_driver_hammerfall_hardware (alsa_driver_t *driver)
+
+{
+ driver->hw = jack_alsa_hammerfall_hw_new (driver);
+ return 0;
+}
+
+static int
+alsa_driver_generic_hardware (alsa_driver_t *driver)
+
+{
+ driver->hw = jack_alsa_generic_hw_new (driver);
+ return 0;
+}
+
+static int
+alsa_driver_hw_specific (alsa_driver_t *driver)
+
+{
+ int err;
+
+ if (!strcmp(driver->alsa_driver, "RME9652")) {
+ if ((err = alsa_driver_hammerfall_hardware (driver)) != 0) {
+ return err;
+ }
+ } else {
+ if ((err = alsa_driver_generic_hardware (driver)) != 0) {
+ return err;
+ }
+ }
+
+ if (driver->hw->capabilities & Cap_HardwareMonitoring) {
+ driver->has_hw_monitoring = TRUE;
+ } else {
+ driver->has_hw_monitoring = FALSE;
+ }
+
+ /* XXX need to ensure that this is really FALSE */
+
+ driver->hw_monitoring = FALSE;
+
+ if (driver->hw->capabilities & Cap_ClockLockReporting) {
+ driver->has_clock_sync_reporting = TRUE;
+ } else {
+ driver->has_clock_sync_reporting = FALSE;
+ }
+
+ return 0;
+}
+
+static void
+alsa_driver_setup_io_function_pointers (alsa_driver_t *driver)
+
+{
+ switch (driver->sample_bytes) {
+ case 2:
+ if (driver->interleaved) {
+ driver->channel_copy = memcpy_interleave_d16_s16;
+ } else {
+ driver->channel_copy = memcpy_fake;
+ }
+
+ driver->write_via_copy = sample_move_d16_sS;
+ driver->read_via_copy = sample_move_dS_s16;
+ break;
+
+ case 4:
+ if (driver->interleaved) {
+ driver->channel_copy = memcpy_interleave_d32_s32;
+ } else {
+ driver->channel_copy = memcpy_fake;
+ }
+
+ driver->write_via_copy = sample_move_d32u24_sS;
+ driver->read_via_copy = sample_move_dS_s32u24;
+
+ break;
+ }
+}
+
+static int
+alsa_driver_configure_stream (alsa_driver_t *driver,
+ const char *stream_name,
+ snd_pcm_t *handle,
+ snd_pcm_hw_params_t *hw_params,
+ snd_pcm_sw_params_t *sw_params,
+ unsigned long *nchns)
+{
+ int err;
+
+ if ((err = snd_pcm_hw_params_any (handle, hw_params)) < 0) {
+ jack_error ("ALSA: no playback configurations available");
+ return -1;
+ }
+
+ if ((err = snd_pcm_hw_params_set_periods_integer (handle, hw_params)) < 0) {
+ jack_error ("ALSA: cannot restrict period size to integral value.");
+ return -1;
+ }
+
+ if ((err = snd_pcm_hw_params_set_access (handle, hw_params, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) < 0) {
+ if ((err = snd_pcm_hw_params_set_access (handle, hw_params, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0) {
+ jack_error ("ALSA: mmap-based access is not possible for the %s "
+ "stream of this audio interface", stream_name);
+ return -1;
+ }
+ }
+
+ if ((err = snd_pcm_hw_params_set_format (handle, hw_params, SND_PCM_FORMAT_S32_LE)) < 0) {
+ if ((err = snd_pcm_hw_params_set_format (handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
+ jack_error ("Sorry. The audio interface \"%s\""
+ "doesn't support either of the two hardware sample formats that ardour can use.",
+ driver->alsa_name);
+ return -1;
+ }
+ }
+
+ if ((err = snd_pcm_hw_params_set_rate (handle, hw_params, driver->frame_rate, 0)) < 0) {
+ jack_error ("ALSA: cannot set sample/frame rate to %u for %s", driver->frame_rate, stream_name);
+ return -1;
+ }
+
+ *nchns = snd_pcm_hw_params_get_channels_max (hw_params);
+
+ if ((err = snd_pcm_hw_params_set_channels (handle, hw_params, *nchns)) < 0) {
+ jack_error ("ALSA: cannot set channel count to %u for %s", *nchns, stream_name);
+ return -1;
+ }
+
+ if ((err = snd_pcm_hw_params_set_periods (handle, hw_params, 2, 0)) < 0) {
+ jack_error ("ALSA: cannot set fragment count minimum to 2 for %s", stream_name);
+ return -1;
+ }
+
+ if ((err = snd_pcm_hw_params_set_period_size (handle, hw_params, driver->frames_per_cycle, 0)) < 0) {
+ jack_error ("ALSA: cannot set fragment length to %u for %s", stream_name);
+ return -1;
+ }
+
+ if ((err = snd_pcm_hw_params_set_buffer_size (handle, hw_params, 2 * driver->frames_per_cycle)) < 0) {
+ jack_error ("ALSA: cannot set buffer length to %u for %s", 2 * driver->frames_per_cycle, stream_name);
+ return -1;
+ }
+
+ if ((err = snd_pcm_hw_params (handle, hw_params)) < 0) {
+ jack_error ("ALSA: cannot set hardware parameters for %s", stream_name);
+ return -1;
+ }
+
+ snd_pcm_sw_params_current (handle, sw_params);
+
+ if ((err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, ~0U)) < 0) {
+ jack_error ("ALSA: cannot set start mode for %s", stream_name);
+ return -1;
+ }
+
+ if ((err = snd_pcm_sw_params_set_stop_threshold (handle, sw_params, ~0U)) < 0) {
+ jack_error ("ALSA: cannot set start mode for %s", stream_name);
+ return -1;
+ }
+
+ if ((err = snd_pcm_sw_params_set_silence_threshold (handle, sw_params, 0)) < 0) {
+ jack_error ("ALSA: cannot set start mode for %s", stream_name);
+ return -1;
+ }
+
+ if ((err = snd_pcm_sw_params_set_silence_size (handle, sw_params, driver->frames_per_cycle * driver->nfragments)) < 0) {
+ jack_error ("ALSA: cannot set start mode for %s", stream_name);
+ return -1;
+ }
+
+ if ((err = snd_pcm_sw_params_set_avail_min (handle, sw_params, driver->frames_per_cycle)) < 0) {
+ jack_error ("ALSA: cannot set avail min for %s", stream_name);
+ return -1;
+ }
+
+ if ((err = snd_pcm_sw_params (handle, sw_params)) < 0) {
+ jack_error ("ALSA: cannot set software parameters for %s", stream_name);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+alsa_driver_set_parameters (alsa_driver_t *driver, nframes_t frames_per_cycle, nframes_t rate)
+
+{
+ int p_noninterleaved;
+ int c_noninterleaved;
+ snd_pcm_format_t c_format, p_format;
+ int dir;
+ unsigned int p_period_size, c_period_size;
+ unsigned int p_nfragments, c_nfragments;
+ channel_t chn;
+
+ driver->frame_rate = rate;
+ driver->frames_per_cycle = frames_per_cycle;
+
+ if (alsa_driver_configure_stream (driver, "capture",
+ driver->capture_handle,
+ driver->capture_hw_params,
+ driver->capture_sw_params,
+ &driver->capture_nchannels)) {
+ jack_error ("ALSA-MCD: cannot configure capture channel");
+ return -1;
+ }
+
+ if (alsa_driver_configure_stream (driver, "playback",
+ driver->playback_handle,
+ driver->playback_hw_params,
+ driver->playback_sw_params,
+ &driver->playback_nchannels)) {
+ jack_error ("ALSA-MCD: cannot configure playback channel");
+ return -1;
+ }
+
+ /* check the fragment size, since thats non-negotiable */
+
+ p_period_size = snd_pcm_hw_params_get_period_size (driver->playback_hw_params, &dir);
+ c_period_size = snd_pcm_hw_params_get_period_size (driver->capture_hw_params, &dir);
+
+ if (c_period_size != driver->frames_per_cycle || p_period_size != driver->frames_per_cycle) {
+ jack_error ("ALSA I/O: requested an interrupt every %u frames but got %uc%up frames",
+ driver->frames_per_cycle, c_period_size, p_period_size);
+ return -1;
+ }
+
+ p_nfragments = snd_pcm_hw_params_get_periods (driver->playback_hw_params, &dir);
+ c_nfragments = snd_pcm_hw_params_get_periods (driver->capture_hw_params, &dir);
+
+ if (p_nfragments != c_nfragments) {
+ jack_error ("ALSA I/O: different period counts for playback and capture!");
+ return -1;
+ }
+
+ driver->nfragments = c_nfragments;
+ driver->buffer_frames = driver->frames_per_cycle * driver->nfragments;
+
+ /* Check that we are using the same sample format on both streams */
+
+ p_format = (snd_pcm_format_t) snd_pcm_hw_params_get_format (driver->playback_hw_params);
+ c_format = (snd_pcm_format_t) snd_pcm_hw_params_get_format (driver->capture_hw_params);
+
+ if (p_format != c_format) {
+ jack_error ("Sorry. The audio interface \"%s\""
+ "doesn't support the same sample format for capture and playback."
+ "Ardour cannot use this hardware.", driver->alsa_name);
+ return -1;
+ }
+
+ driver->sample_format = p_format;
+ driver->sample_bytes = snd_pcm_format_physical_width (driver->sample_format) / 8;
+ driver->bytes_per_cycle = driver->sample_bytes * driver->frames_per_cycle;
+
+ switch (driver->sample_format) {
+ case SND_PCM_FORMAT_S32_LE:
+
+ /* XXX must handle the n-bits of 24-in-32 problems here */
+
+ if (config_max_level) {
+ driver->max_level = config_max_level;
+ } else {
+ driver->max_level = INT_MAX;
+ }
+
+ if (config_min_level) {
+ driver->min_level = config_min_level;
+ } else {
+ driver->min_level = INT_MIN;
+ }
+ break;
+
+ case SND_PCM_FORMAT_S16_LE:
+
+ if (config_max_level) {
+ driver->max_level = config_max_level;
+ } else {
+ driver->max_level = SHRT_MAX;
+ }
+
+ if (config_min_level) {
+ driver->min_level = config_min_level;
+ } else {
+ driver->min_level = SHRT_MIN;
+ }
+ break;
+
+ default:
+ jack_error ("programming error: unhandled format type");
+ exit (1);
+ }
+
+ /* check interleave setup */
+
+ p_noninterleaved = (snd_pcm_hw_params_get_access (driver->playback_hw_params) == SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
+ c_noninterleaved = (snd_pcm_hw_params_get_access (driver->capture_hw_params) == SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
+
+ if (c_noninterleaved != p_noninterleaved) {
+ jack_error ("ALSA: the playback and capture components of this audio interface differ "
+ "in their use of channel interleaving. Ardour cannot use this h/w.");
+ return -1;
+ }
+
+ driver->interleaved = !c_noninterleaved;
+
+ if (driver->interleaved) {
+ driver->interleave_unit = snd_pcm_format_physical_width (driver->sample_format) / 8;
+ driver->playback_interleave_skip = driver->interleave_unit * driver->playback_nchannels;
+ driver->capture_interleave_skip = driver->interleave_unit * driver->capture_nchannels;
+ } else {
+ driver->interleave_unit = 0; /* NOT USED */
+ driver->playback_interleave_skip = snd_pcm_format_physical_width (driver->sample_format) / 8;
+ driver->capture_interleave_skip = driver->playback_interleave_skip;
+ }
+
+ if (driver->playback_nchannels > driver->capture_nchannels) {
+ driver->max_nchannels = driver->playback_nchannels;
+ driver->user_nchannels = driver->capture_nchannels;
+ } else {
+ driver->max_nchannels = driver->capture_nchannels;
+ driver->user_nchannels = driver->playback_nchannels;
+ }
+
+ alsa_driver_setup_io_function_pointers (driver);
+
+ /* Allocate and initialize structures that rely on
+ the channels counts.
+ */
+
+ driver->playback_addr = (char **) malloc (sizeof (char *) * driver->playback_nchannels);
+ driver->capture_addr = (char **) malloc (sizeof (char *) * driver->capture_nchannels);
+
+ memset (driver->playback_addr, 0, sizeof (char *) * driver->playback_nchannels);
+ memset (driver->capture_addr, 0, sizeof (char *) * driver->capture_nchannels);
+
+ driver->silent = (unsigned long *) malloc (sizeof (unsigned long) * driver->playback_nchannels);
+
+ for (chn = 0; chn < driver->playback_nchannels; chn++) {
+ driver->silent[chn] = 0;
+ }
+
+ driver->input_monitor_requests = (unsigned long *) malloc (sizeof (unsigned long) * driver->max_nchannels);
+ memset (driver->input_monitor_requests, 0, sizeof (unsigned long) * driver->max_nchannels);
+
+ driver->clock_sync_data = (ClockSyncStatus *) malloc (sizeof (ClockSyncStatus) *
+ driver->capture_nchannels > driver->playback_nchannels ?
+ driver->capture_nchannels : driver->playback_nchannels);
+
+ /* set up the bit pattern that is used to record which
+ channels require action on every cycle. any bits that are
+ not set after the engine's process() call indicate channels
+ that potentially need to be silenced.
+
+ XXX this is limited to <wordsize> channels. Use a bitset
+ type instead.
+ */
+
+ driver->channel_done_bits = 0;
+ for (chn = 0; chn < driver->playback_nchannels; chn++) {
+ driver->channel_done_bits |= (1<<chn);
+ }
+
+ driver->period_interval = (unsigned long) floor ((((float) driver->frames_per_cycle) / driver->frame_rate) * 1000.0);
+
+ if (driver->engine) {
+ driver->engine->set_buffer_size (driver->engine, driver->frames_per_cycle);
+ }
+
+ return 0;
+}
+
+static int
+alsa_driver_reset_parameters (alsa_driver_t *driver, nframes_t frames_per_cycle, nframes_t rate)
+{
+ /* XXX unregister old ports ? */
+ alsa_driver_release_channel_dependent_memory (driver);
+ return alsa_driver_set_parameters (driver, frames_per_cycle, rate);
+}
+
+static int
+alsa_driver_get_channel_addresses (alsa_driver_t *driver,
+ snd_pcm_uframes_t *capture_avail,
+ snd_pcm_uframes_t *playback_avail,
+ snd_pcm_uframes_t *capture_offset,
+ snd_pcm_uframes_t *playback_offset)
+
+{
+ unsigned long err;
+ channel_t chn;
+
+ if (capture_avail) {
+ if ((err = snd_pcm_mmap_begin (driver->capture_handle, &driver->capture_areas,
+ (snd_pcm_uframes_t *) capture_offset,
+ (snd_pcm_uframes_t *) capture_avail)) < 0) {
+ jack_error ("ALSA-HW: %s: mmap areas info error", driver->alsa_name);
+ return -1;
+ }
+
+ for (chn = 0; chn < driver->capture_nchannels; chn++) {
+ const snd_pcm_channel_area_t *a = &driver->capture_areas[chn];
+ driver->capture_addr[chn] = (char *) a->addr + ((a->first + a->step * *capture_offset) / 8);
+ }
+ }
+
+ if (playback_avail) {
+ if ((err = snd_pcm_mmap_begin (driver->playback_handle, &driver->playback_areas,
+ (snd_pcm_uframes_t *) playback_offset,
+ (snd_pcm_uframes_t *) playback_avail)) < 0) {
+ jack_error ("ALSA-HW: %s: mmap areas info error ", driver->alsa_name);
+ return -1;
+ }
+
+ for (chn = 0; chn < driver->playback_nchannels; chn++) {
+ const snd_pcm_channel_area_t *a = &driver->playback_areas[chn];
+ driver->playback_addr[chn] = (char *) a->addr + ((a->first + a->step * *playback_offset) / 8);
+ }
+ }
+
+ return 0;
+}
+
+static int
+alsa_driver_audio_start (alsa_driver_t *driver)
+
+{
+ int err;
+ snd_pcm_uframes_t poffset, pavail;
+ channel_t chn;
+
+ if ((err = snd_pcm_prepare (driver->playback_handle)) < 0) {
+ jack_error ("ALSA-HW: prepare error for playback on \"%s\" (%s)", driver->alsa_name, snd_strerror(err));
+ return -1;
+ }
+
+ if (driver->capture_and_playback_not_synced) {
+ if ((err = snd_pcm_prepare (driver->capture_handle)) < 0) {
+ jack_error ("ALSA-HW: prepare error for capture on \"%s\" (%s)", driver->alsa_name, snd_strerror(err));
+ return -1;
+ }
+ }
+
+ if (driver->hw_monitoring) {
+ driver->hw->set_input_monitor_mask (driver->hw, driver->input_monitor_mask);
+ }
+
+ /* fill playback buffer with zeroes, and mark
+ all fragments as having data.
+ */
+
+ pavail = snd_pcm_avail_update (driver->playback_handle);
+
+ if (pavail != driver->buffer_frames) {
+ jack_error ("ALSA-HW: full buffer not available at start");
+ return -1;
+ }
+
+ if (alsa_driver_get_channel_addresses (driver, 0, &pavail, 0, &poffset)) {
+ return -1;
+ }
+
+ for (chn = 0; chn < driver->playback_nchannels; chn++) {
+ alsa_driver_silence_on_channel (driver, chn, driver->buffer_frames);
+ }
+
+ snd_pcm_mmap_commit (driver->playback_handle, poffset, driver->buffer_frames);
+
+ if ((err = snd_pcm_start (driver->playback_handle)) < 0) {
+ jack_error ("could not start playback (%s)", snd_strerror (err));
+ return -1;
+ }
+
+ if (driver->capture_and_playback_not_synced) {
+ if ((err = snd_pcm_start (driver->capture_handle)) < 0) {
+ jack_error ("could not start capture (%s)", snd_strerror (err));
+ return -1;
+ }
+ }
+
+ if (driver->hw_monitoring && (driver->input_monitor_mask || driver->all_monitor_in)) {
+ if (driver->all_monitor_in) {
+ driver->hw->set_input_monitor_mask (driver->hw, ~0U);
+ } else {
+ driver->hw->set_input_monitor_mask (driver->hw, driver->input_monitor_mask);
+ }
+ }
+
+ snd_pcm_poll_descriptors (driver->playback_handle, &driver->pfd, 1);
+ driver->pfd.events = POLLOUT | POLLERR;
+
+ return 0;
+}
+
+static int
+alsa_driver_audio_stop (alsa_driver_t *driver)
+
+{
+ int err;
+
+ if ((err = snd_pcm_drop (driver->playback_handle)) < 0) {
+ jack_error ("ALSA I/O: channel flush for playback failed (%s)", snd_strerror (err));
+ return -1;
+ }
+
+ if (driver->capture_and_playback_not_synced) {
+ if ((err = snd_pcm_drop (driver->capture_handle)) < 0) {
+ jack_error ("ALSA I/O: channel flush for capture failed (%s)", snd_strerror (err));
+ return -1;
+ }
+ }
+
+ driver->hw->set_input_monitor_mask (driver->hw, 0);
+
+ return 0;
+}
+
+static int
+alsa_driver_xrun_recovery (alsa_driver_t *driver)
+
+{
+ snd_pcm_sframes_t capture_delay;
+ int err;
+
+ if ((err = snd_pcm_delay (driver->capture_handle, &capture_delay))) {
+ jack_error ("ALSA I/O: cannot determine capture delay (%s)", snd_strerror (err));
+ exit (1);
+ }
+
+ fprintf (stderr, "ALSA I/O: xrun of %lu frames, (%.3f msecs)\n", capture_delay,
+ ((float) capture_delay / (float) driver->frame_rate) * 1000.0);
+
+#if ENGINE
+ if (!engine->xrun_recoverable ()) {
+ /* don't report an error here, its distracting */
+ return -1;
+ }
+#endif
+
+ if (alsa_driver_audio_stop (driver) || alsa_driver_audio_start (driver)) {
+ return -1;
+
+ }
+
+ return 0;
+}
+
+static void
+alsa_driver_silence_untouched_channels (alsa_driver_t *driver, nframes_t nframes)
+
+{
+ channel_t chn;
+
+ for (chn = 0; chn < driver->playback_nchannels; chn++) {
+ if ((driver->channels_not_done & (1<<chn))) {
+ if (driver->silent[chn] < driver->buffer_frames) {
+ alsa_driver_silence_on_channel (driver, chn, nframes);
+ driver->silent[chn] += nframes;
+ }
+ }
+ }
+}
+
+void
+alsa_driver_set_clock_sync_status (alsa_driver_t *driver, channel_t chn, ClockSyncStatus status)
+
+{
+ driver->clock_sync_data[chn] = status;
+ jack_driver_clock_sync_notify ((jack_driver_t *) driver, chn, status);
+}
+
+static int under_gdb = FALSE;
+
+static int
+alsa_driver_wait (alsa_driver_t *driver)
+
+{
+ snd_pcm_sframes_t avail = 0;
+ snd_pcm_sframes_t contiguous = 0;
+ snd_pcm_sframes_t capture_avail = 0;
+ snd_pcm_sframes_t playback_avail = 0;
+ snd_pcm_uframes_t capture_offset = 0;
+ snd_pcm_uframes_t playback_offset = 0;
+ int xrun_detected;
+ channel_t chn;
+ GSList *node;
+ sample_t *buffer;
+
+ again:
+ if (poll (&driver->pfd, 1, 1000) < 0) {
+ if (errno == EINTR) {
+ printf ("poll interrupt\n");
+ // this happens mostly when run
+ // under gdb, or when exiting due to a signal
+ if (under_gdb) {
+ goto again;
+ }
+ return 1;
+ }
+
+ jack_error ("ALSA::Device: poll call failed (%s)", strerror (errno));
+ return -1;
+ }
+
+ driver->time_at_interrupt = current_usecs();
+
+ if (driver->pfd.revents & POLLERR) {
+ jack_error ("ALSA-MCD: poll reports error.");
+ return -1;
+ }
+
+ if (driver->pfd.revents == 0) {
+ // timed out, such as when the device is paused
+ return 0;
+ }
+
+ xrun_detected = FALSE;
+
+ if ((capture_avail = snd_pcm_avail_update (driver->capture_handle)) < 0) {
+ if (capture_avail == -EPIPE) {
+ xrun_detected = TRUE;
+ } else {
+ jack_error ("unknown ALSA avail_update return value (%u)", capture_avail);
+ }
+ }
+
+ if ((playback_avail = snd_pcm_avail_update (driver->playback_handle)) < 0) {
+ if (playback_avail == -EPIPE) {
+ xrun_detected = TRUE;
+ } else {
+ jack_error ("unknown ALSA avail_update return value (%u)", playback_avail);
+ }
+ }
+
+ if (xrun_detected) {
+ if (alsa_driver_xrun_recovery (driver)) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ avail = capture_avail < playback_avail ? capture_avail : playback_avail;
+
+ while (avail) {
+
+ capture_avail = (avail > driver->frames_per_cycle) ? driver->frames_per_cycle : avail;
+ playback_avail = (avail > driver->frames_per_cycle) ? driver->frames_per_cycle : avail;
+
+ if (alsa_driver_get_channel_addresses (driver,
+ (snd_pcm_uframes_t *) &capture_avail,
+ (snd_pcm_uframes_t *) &playback_avail,
+ &capture_offset, &playback_offset) < 0) {
+ return -1;
+ }
+
+ contiguous = capture_avail < playback_avail ? capture_avail : playback_avail;
+
+ /* XXX possible race condition here with silence_pending */
+
+ /* XXX this design is wrong. cf. ardour/audioengine *** FIX ME *** */
+
+ if (driver->silence_pending) {
+ for (chn = 0; chn < driver->playback_nchannels; chn++) {
+ if (driver->silence_pending & (1<<chn)) {
+ alsa_driver_silence_on_channel (driver, chn, contiguous);
+ }
+ }
+ driver->silence_pending = 0;
+ }
+
+ driver->channels_not_done = driver->channel_done_bits;
+
+ if ((driver->hw->input_monitor_mask != driver->input_monitor_mask) &&
+ driver->hw_monitoring && !driver->all_monitor_in) {
+ driver->hw->set_input_monitor_mask (driver->hw, driver->input_monitor_mask);
+ }
+
+ /* XXX race condition on engine ptr */
+
+ if (driver->engine && driver->engine->process (driver->engine, contiguous)) {
+ jack_error ("ALSA I/O: engine processing error - stopping.");
+ return -1;
+ }
+
+ /* now move data from ports to channels */
+
+ for (chn = 0, node = driver->playback_ports; node; node = g_slist_next (node), chn++) {
+
+ jack_port_t *port = (jack_port_t *) node->data;
+
+ /* optimize needless data copying away */
+
+ if (port->connections == 0) {
+ continue;
+ }
+
+ buffer = (sample_t *) jack_port_get_buffer (port, contiguous);
+ alsa_driver_write_to_channel (driver, chn, buffer, contiguous, 0, 1.0);
+ }
+
+ /* Now handle input monitoring */
+
+ if (!driver->hw_monitoring) {
+ if (driver->all_monitor_in) {
+ for (chn = 0; chn < driver->playback_nchannels; chn++) {
+ alsa_driver_copy_channel (driver, chn, chn, contiguous);
+ }
+ } else if (driver->input_monitor_mask) {
+ for (chn = 0; chn < driver->playback_nchannels; chn++) {
+ if (driver->input_monitor_mask & (1<<chn)) {
+ alsa_driver_copy_channel (driver, chn, chn, contiguous);
+ }
+ }
+ }
+ }
+
+ if (driver->channels_not_done) {
+ alsa_driver_silence_untouched_channels (driver, contiguous);
+ }
+
+ snd_pcm_mmap_commit (driver->capture_handle, capture_offset, contiguous);
+ snd_pcm_mmap_commit (driver->playback_handle, playback_offset, contiguous);
+
+ avail -= contiguous;
+ }
+
+ return 0;
+}
+
+static int
+alsa_driver_process (nframes_t nframes, void *arg)
+
+{
+ alsa_driver_t *driver = (alsa_driver_t *) arg;
+ channel_t chn;
+ jack_port_t *port;
+ GSList *node;
+
+ for (chn = 0, node = driver->capture_ports; node; node = g_slist_next (node), chn++) {
+
+ port = (jack_port_t *) node->data;
+
+ if (port->connections == 0) {
+ continue;
+ }
+
+ alsa_driver_read_from_channel (driver, chn, port->shared->buffer, nframes, 0);
+ }
+
+ return 0;
+}
+
+static void
+alsa_driver_port_monitor_handler (jack_port_id_t port_id, int onoff, void *arg)
+{
+ alsa_driver_t *driver = (alsa_driver_t *) arg;
+ jack_port_shared_t *port;
+ int channel;
+
+ port = &driver->engine->control->ports[port_id];
+ sscanf (port->name, "%*s%*s%*s%d", &channel);
+ driver->request_monitor_input ((jack_driver_t *) driver, channel, onoff);
+}
+
+static void
+alsa_driver_attach (alsa_driver_t *driver, jack_engine_t *engine)
+
+{
+ char buf[32];
+ channel_t chn;
+ jack_port_t *port;
+
+ driver->engine = engine;
+
+ driver->engine->set_buffer_size (engine, driver->frames_per_cycle);
+ driver->engine->set_sample_rate (engine, driver->frame_rate);
+
+ /* Now become a client of the engine */
+
+ if ((driver->client = jack_driver_become_client ("ALSA I/O")) == NULL) {
+ jack_error ("ALSA: cannot become client");
+ return;
+ }
+
+ jack_set_process_callback (driver->client, alsa_driver_process, driver);
+ jack_set_port_monitor_callback (driver->client, alsa_driver_port_monitor_handler, driver);
+
+ for (chn = 0; chn < driver->capture_nchannels; chn++) {
+ snprintf (buf, sizeof(buf) - 1, "Input %lu", chn+1);
+ port = jack_port_register (driver->client, buf,
+ JACK_DEFAULT_AUDIO_TYPE,
+ JackPortIsOutput|JackPortIsPhysical|JackPortCanMonitor, 0);
+ if (port == 0) {
+ jack_error ("ALSA: cannot register port for %s", buf);
+ break;
+ }
+ driver->capture_ports = g_slist_append (driver->capture_ports, port);
+ printf ("registered %s\n", port->shared->name);
+ }
+
+ for (chn = 0; chn < driver->playback_nchannels; chn++) {
+ snprintf (buf, sizeof(buf) - 1, "Output %lu", chn+1);
+ port = jack_port_register (driver->client, buf,
+ JACK_DEFAULT_AUDIO_TYPE,
+ JackPortIsInput|JackPortIsPhysical, 0);
+ if (port == 0) {
+ jack_error ("ALSA: cannot register port for %s", buf);
+ break;
+ }
+ driver->playback_ports = g_slist_append (driver->playback_ports, port);
+ printf ("registered %s\n", port->shared->name);
+ }
+
+ printf ("ports registered, starting client\n");
+
+ jack_activate (driver->client);
+}
+
+static void
+alsa_driver_detach (alsa_driver_t *driver, jack_engine_t *engine)
+
+{
+ GSList *node;
+
+ for (node = driver->capture_ports; node; node = g_slist_next (node)) {
+ jack_port_unregister (driver->client, ((jack_port_t *) node->data));
+ }
+
+ g_slist_free (driver->capture_ports);
+ driver->capture_ports = 0;
+
+ for (node = driver->playback_ports; node; node = g_slist_next (node)) {
+ jack_port_unregister (driver->client, ((jack_port_t *) node->data));
+ }
+
+ g_slist_free (driver->playback_ports);
+ driver->playback_ports = 0;
+
+ driver->engine = 0;
+}
+
+static int
+alsa_driver_change_sample_clock (alsa_driver_t *driver, SampleClockMode mode)
+
+{
+ return driver->hw->change_sample_clock (driver->hw, mode);
+}
+
+static void
+alsa_driver_mark_channel_silent (alsa_driver_t *driver, unsigned long chn)
+{
+ driver->silence_pending |= (1<<chn);
+}
+
+static void
+alsa_driver_request_monitor_input (alsa_driver_t *driver, unsigned long chn, int yn)
+
+{
+ int changed;
+
+ if (chn >= driver->max_nchannels) {
+ return;
+ }
+
+ changed = FALSE;
+
+ if (yn) {
+ if (++driver->input_monitor_requests[chn] == 1) {
+ if (!(driver->input_monitor_mask & (1<<chn))) {
+ driver->input_monitor_mask |= (1<<chn);
+ changed = TRUE;
+ }
+ }
+ } else {
+ if (driver->input_monitor_requests[chn] && --driver->input_monitor_requests[chn] == 0) {
+ if (driver->input_monitor_mask & (1<<chn)) {
+ driver->input_monitor_mask &= ~(1<<chn);
+ changed = TRUE;
+ }
+ }
+ }
+
+ if (changed) {
+ if (!driver->hw_monitoring && !yn) {
+ alsa_driver_mark_channel_silent (driver, chn);
+ }
+
+ /* Tell anyone who cares about the state of input monitoring */
+
+ jack_driver_input_monitor_notify ((jack_driver_t *) driver, chn, yn);
+ }
+}
+
+static void
+alsa_driver_request_all_monitor_input (alsa_driver_t *driver, int yn)
+
+{
+ if (driver->hw_monitoring) {
+ if (yn) {
+ driver->hw->set_input_monitor_mask (driver->hw, ~0U);
+ } else {
+ driver->hw->set_input_monitor_mask (driver->hw, driver->input_monitor_mask);
+ }
+ }
+
+ driver->all_monitor_in = yn;
+}
+
+static void
+alsa_driver_set_hw_monitoring (alsa_driver_t *driver, int yn)
+
+{
+ if (yn) {
+ driver->hw_monitoring = TRUE;
+
+ if (driver->all_monitor_in) {
+ driver->hw->set_input_monitor_mask (driver->hw, ~0U);
+ } else {
+ driver->hw->set_input_monitor_mask (driver->hw, driver->input_monitor_mask);
+ }
+ } else {
+ driver->hw_monitoring = FALSE;
+ driver->hw->set_input_monitor_mask (driver->hw, 0);
+ }
+}
+
+static nframes_t
+alsa_driver_frames_since_cycle_start (alsa_driver_t *driver)
+{
+ return (nframes_t) ((driver->frame_rate / 1000000.0) * ((float) (current_usecs() - driver->time_at_interrupt)));
+}
+
+static ClockSyncStatus
+alsa_driver_clock_sync_status (channel_t chn)
+
+{
+ return Lock;
+}
+
+static void
+alsa_driver_delete (alsa_driver_t *driver)
+
+{
+ if (driver->capture_handle) {
+ snd_pcm_close (driver->capture_handle);
+ driver->capture_handle = 0;
+ }
+
+ if (driver->playback_handle) {
+ snd_pcm_close (driver->playback_handle);
+ driver->capture_handle = 0;
+ }
+
+ if (driver->capture_hw_params) {
+ snd_pcm_hw_params_free (driver->capture_hw_params);
+ driver->capture_hw_params = 0;
+ }
+
+ if (driver->playback_hw_params) {
+ snd_pcm_hw_params_free (driver->playback_hw_params);
+ driver->playback_hw_params = 0;
+ }
+
+ if (driver->capture_sw_params) {
+ snd_pcm_sw_params_free (driver->capture_sw_params);
+ driver->capture_sw_params = 0;
+ }
+
+ if (driver->playback_sw_params) {
+ snd_pcm_sw_params_free (driver->playback_sw_params);
+ driver->playback_sw_params = 0;
+ }
+
+ if (driver->hw) {
+ driver->hw->release (driver->hw);
+ driver->hw = 0;
+ }
+ free(driver->alsa_name);
+ free(driver->alsa_driver);
+
+ alsa_driver_release_channel_dependent_memory (driver);
+ jack_driver_release ((jack_driver_t *) driver);
+ free (driver);
+}
+
+static jack_driver_t *
+alsa_driver_new (char *name, char *alsa_device,
+ nframes_t frames_per_cycle,
+ nframes_t rate)
+{
+ int err;
+
+ alsa_driver_t *driver;
+
+ printf ("creating alsa driver ... %s|%lu|%lu\n", alsa_device, frames_per_cycle, rate);
+
+ driver = (alsa_driver_t *) calloc (1, sizeof (alsa_driver_t));
+
+ jack_driver_init ((jack_driver_t *) driver);
+
+ driver->attach = (JackDriverAttachFunction) alsa_driver_attach;
+ driver->detach = (JackDriverDetachFunction) alsa_driver_detach;
+ driver->wait = (JackDriverWaitFunction) alsa_driver_wait;
+
+ driver->audio_stop = (JackDriverAudioStopFunction) alsa_driver_audio_stop;
+ driver->audio_start = (JackDriverAudioStartFunction) alsa_driver_audio_start;
+ driver->set_hw_monitoring = (JackDriverSetHwMonitoringFunction) alsa_driver_set_hw_monitoring ;
+ driver->reset_parameters = (JackDriverResetParametersFunction) alsa_driver_reset_parameters;
+ driver->mark_channel_silent = (JackDriverMarkChannelSilentFunction) alsa_driver_mark_channel_silent;
+ driver->request_monitor_input = (JackDriverRequestMonitorInputFunction) alsa_driver_request_monitor_input;
+ driver->request_all_monitor_input = (JackDriverRequestAllMonitorInputFunction) alsa_driver_request_all_monitor_input;
+ driver->frames_since_cycle_start = (JackDriverFramesSinceCycleStartFunction) alsa_driver_frames_since_cycle_start;
+ driver->clock_sync_status = (JackDriverClockSyncStatusFunction) alsa_driver_clock_sync_status;
+ driver->change_sample_clock = (JackDriverChangeSampleClockFunction) alsa_driver_change_sample_clock;
+
+ driver->ctl_handle = 0;
+ driver->hw = 0;
+ driver->capture_and_playback_not_synced = FALSE;
+ driver->nfragments = 0;
+ driver->max_nchannels = 0;
+ driver->user_nchannels = 0;
+ driver->playback_nchannels = 0;
+ driver->capture_nchannels = 0;
+ driver->playback_addr = 0;
+ driver->capture_addr = 0;
+ driver->silence_pending = 0;
+ driver->silent = 0;
+ driver->input_monitor_requests = 0;
+ driver->all_monitor_in = FALSE;
+
+ driver->clock_mode = ClockMaster; /* XXX is it? */
+ driver->input_monitor_mask = 0; /* XXX is it? */
+
+ driver->capture_ports = 0;
+ driver->playback_ports = 0;
+
+ if ((err = snd_pcm_open (&driver->playback_handle, alsa_device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
+ jack_error ("ALSA-MCD: Cannot open PCM device %s/%s", name, alsa_device);
+ free (driver);
+ return 0;
+ }
+
+ driver->alsa_name = strdup (alsa_device);
+
+ if ((err = snd_pcm_open (&driver->capture_handle, alsa_device, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
+ jack_error ("ALSA-MCD: Cannot open PCM device %s", name);
+ free (driver);
+ return 0;
+ }
+
+ if (alsa_driver_check_card_type (driver)) {
+ free (driver);
+ return 0;
+ }
+
+ driver->playback_hw_params = 0;
+ driver->capture_hw_params = 0;
+ driver->playback_sw_params = 0;
+ driver->capture_hw_params = 0;
+
+ if ((err = snd_pcm_hw_params_malloc (&driver->playback_hw_params)) < 0) {
+ jack_error ("ALSA: could no allocate playback hw params structure");
+ alsa_driver_delete (driver);
+ return 0;
+ }
+
+ if ((err = snd_pcm_hw_params_malloc (&driver->capture_hw_params)) < 0) {
+ jack_error ("ALSA: could no allocate capture hw params structure");
+ alsa_driver_delete (driver);
+ return 0;
+ }
+
+ if ((err = snd_pcm_sw_params_malloc (&driver->playback_sw_params)) < 0) {
+ jack_error ("ALSA: could no allocate playback sw params structure");
+ alsa_driver_delete (driver);
+ return 0;
+ }
+
+ if ((err = snd_pcm_sw_params_malloc (&driver->capture_sw_params)) < 0) {
+ jack_error ("ALSA: could no allocate capture sw params structure");
+ alsa_driver_delete (driver);
+ return 0;
+ }
+
+ if (alsa_driver_set_parameters (driver, frames_per_cycle, rate)) {
+ alsa_driver_delete (driver);
+ return 0;
+ }
+
+ if (snd_pcm_link (driver->capture_handle, driver->playback_handle) != 0) {
+ driver->capture_and_playback_not_synced = TRUE;
+ } else {
+ driver->capture_and_playback_not_synced = FALSE;
+ }
+
+ alsa_driver_hw_specific (driver);
+
+ return (jack_driver_t *) driver;
+}
+
+/* PLUGIN INTERFACE */
+
+jack_driver_t *
+driver_initialize (va_list ap)
+{
+ nframes_t srate;
+ nframes_t frames_per_interrupt;
+ char *pcm_name;
+
+ pcm_name = va_arg (ap, char *);
+ frames_per_interrupt = va_arg (ap, nframes_t);
+ srate = va_arg (ap, nframes_t);
+
+ return alsa_driver_new ("ALSA I/O", pcm_name, frames_per_interrupt, srate);
+}
+
+void
+driver_finish (jack_driver_t *driver)
+{
+ alsa_driver_delete ((alsa_driver_t *) driver);
+}
+
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..f763969
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+libtoolize --force && aclocal $ACLOCAL_FLAGS && automake --add-missing --foreign && autoconf
+
+exit 0
+
diff --git a/client.c b/client.c
new file mode 100644
index 0000000..eb1a1d2
--- /dev/null
+++ b/client.c
@@ -0,0 +1,1206 @@
+/*
+ Copyright (C) 2001 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <pthread.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/poll.h>
+#include <stdio.h>
+#include <asm/msr.h>
+
+#include <jack/jack.h>
+#include <jack/internal.h>
+#include <jack/engine.h>
+#include <jack/pool.h>
+
+static pthread_mutex_t client_lock;
+static pthread_cond_t client_ready;
+static void *jack_zero_filled_buffer = 0;
+
+struct _jack_client {
+
+ jack_control_t *engine;
+ jack_client_control_t *control;
+ struct pollfd *pollfd;
+ int pollmax;
+ int graph_next_fd;
+ int request_fd;
+ GSList *port_segments;
+ GSList *ports;
+ pthread_t thread;
+ char fifo_prefix[FIFO_NAME_SIZE+1];
+ char thread_ok : 1;
+ char first_active : 1;
+};
+
+#define event_fd pollfd[0].fd
+#define graph_wait_fd pollfd[1].fd
+
+typedef struct {
+ int status;
+ struct _jack_client *client;
+ const char *client_name;
+} client_info;
+
+jack_client_t *
+jack_client_alloc ()
+
+{
+ jack_client_t *client;
+
+ client = (jack_client_t *) malloc (sizeof (jack_client_t));
+ client->pollfd = (struct pollfd *) malloc (sizeof (struct pollfd) * 2);
+ client->pollmax = 2;
+
+ client->request_fd = -1;
+ client->event_fd = -1;
+ client->graph_wait_fd = -1;
+ client->graph_next_fd = -1;
+ client->port_segments = NULL;
+ client->ports = NULL;
+ client->engine = NULL;
+ client->control = 0;
+ client->thread_ok = FALSE;
+ client->first_active = TRUE;
+
+ return client;
+}
+
+static jack_port_shared_t *
+jack_port_shared_by_id (jack_client_t *client, jack_port_id_t id)
+
+{
+ return &client->engine->ports[id];
+}
+
+static jack_port_t *
+jack_port_by_id (jack_client_t *client, jack_port_id_t id)
+
+{
+ GSList *node;
+
+ for (node = client->ports; node; node = g_slist_next (node)) {
+ if (((jack_port_t *) node->data)->shared->id == id) {
+ return (jack_port_t *) node->data;
+ }
+ }
+
+ return NULL;
+}
+
+static jack_port_id_t
+jack_port_id_by_name (jack_client_t *client, const char *port_name)
+
+{
+ jack_port_id_t id, limit;
+ jack_port_shared_t *port;
+
+ limit = client->engine->port_max;
+ port = &client->engine->ports[0];
+
+ for (id = 0; id < limit; id++) {
+ if (port[id].in_use && strcmp (port[id].name, port_name) == 0) {
+ return port[id].id;
+ }
+ }
+
+ return NoPort;
+}
+
+static void
+jack_client_invalidate_port_buffers (jack_client_t *client)
+
+{
+ GSList *node;
+ jack_port_t *port;
+
+ /* This releases all local memory owned by input ports
+ and sets the buffer pointer to NULL. This will cause
+ jack_port_get_buffer() to reallocate space for the
+ buffer on the next call (if there is one).
+ */
+
+ for (node = client->ports; node; node = g_slist_next (node)) {
+ port = (jack_port_t *) node->data;
+
+ if (port->shared->flags & JackPortIsInput) {
+ /* XXX release buffer */
+ port->shared->buffer = NULL;
+ }
+ }
+}
+
+int
+jack_client_handle_port_connection (jack_client_t *client, jack_event_t *event)
+
+{
+ jack_port_t *control_port;
+ jack_port_shared_t *shared;
+ GSList *node;
+
+ switch (event->type) {
+ case PortConnected:
+ shared = jack_port_shared_by_id (client, event->y.other_id);
+ control_port = jack_port_by_id (client, event->x.self_id);
+ control_port->connections = g_slist_prepend (control_port->connections, shared);
+ printf ("%s connected to %s\n", control_port->shared->name, shared->name);
+ break;
+
+ case PortDisconnected:
+ shared = jack_port_shared_by_id (client, event->y.other_id);
+ control_port = jack_port_by_id (client, event->x.self_id);
+
+ for (node = control_port->connections; node; node = g_slist_next (node)) {
+ if (((jack_port_shared_t *) node->data) == shared) {
+ control_port->connections = g_slist_remove_link (control_port->connections, node);
+ g_slist_free_1 (node);
+ break;
+ }
+ }
+ printf ("%s DIS-connected and %s\n", control_port->shared->name, shared->name);
+ break;
+
+ default:
+ /* impossible */
+ break;
+ }
+
+ return 0;
+}
+
+static int
+jack_handle_reorder (jack_client_t *client, jack_event_t *event)
+{
+ char path[FIFO_NAME_SIZE+1];
+
+ if (client->graph_wait_fd >= 0) {
+ close (client->graph_wait_fd);
+ client->graph_wait_fd = -1;
+ }
+
+ if (client->graph_next_fd >= 0) {
+ close (client->graph_next_fd);
+ client->graph_next_fd = -1;
+ }
+
+ sprintf (path, "%s-%lu", client->fifo_prefix, event->x.n);
+
+ if ((client->graph_wait_fd = open (path, O_RDONLY)) <= 0) {
+ jack_error ("cannot open specified fifo [%s] for reading (%s)", path, strerror (errno));
+ return -1;
+ }
+
+ sprintf (path, "%s-%lu", client->fifo_prefix, event->x.n+1);
+
+ if ((client->graph_next_fd = open (path, O_WRONLY)) < 0) {
+ jack_error ("cannot open specified fifo [%s] for writing (%s)", path, strerror (errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+server_connect (int which)
+
+{
+ int fd;
+ struct sockaddr_un addr;
+
+ if ((fd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) {
+ jack_error ("cannot create client socket (%s)", strerror (errno));
+ return -1;
+ }
+
+ addr.sun_family = AF_UNIX;
+ g_snprintf (addr.sun_path, sizeof (addr.sun_path) - 1, "/tmp/jack_%d", which);
+
+ if (connect (fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) {
+ jack_error ("cannot connect to jack server", strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static int
+server_event_connect (jack_client_t *client)
+
+{
+ int fd;
+ struct sockaddr_un addr;
+ jack_client_connect_ack_request_t req;
+ jack_client_connect_ack_result_t res;
+
+ if ((fd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) {
+ jack_error ("cannot create client event socket (%s)", strerror (errno));
+ return -1;
+ }
+
+ addr.sun_family = AF_UNIX;
+ g_snprintf (addr.sun_path, sizeof (addr.sun_path) - 1, "/tmp/jack_ack_0");
+
+ if (connect (fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) {
+ jack_error ("cannot connect to jack server for events", strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ req.client_id = client->control->id;
+
+ if (write (fd, &req, sizeof (req)) != sizeof (req)) {
+ jack_error ("cannot write event connect request to server (%s)", strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ if (read (fd, &res, sizeof (res)) != sizeof (res)) {
+ jack_error ("cannot read event connect result from server (%s)", strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ if (res.status != 0) {
+ close (fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+jack_client_t *
+jack_client_new (const char *client_name)
+
+{
+ int req_fd = -1;
+ int ev_fd = -1;
+ void *addr;
+ jack_client_connect_request_t req;
+ jack_client_connect_result_t res;
+ jack_port_segment_info_t *si;
+ jack_client_t *client;
+ int client_shm_id;
+ int control_shm_id;
+ int port_segment_shm_id;
+ int n;
+
+ if (strlen (client_name) > sizeof (req.name) - 1) {
+ jack_error ("\"%s\" is too long to be used as a JACK client name.\n"
+ "Please use %lu characters or less.",
+ sizeof (req.name) - 1);
+ return NULL;
+ }
+
+ if ((req_fd = server_connect (0)) < 0) {
+ jack_error ("cannot connect to default JACK server");
+ return NULL;
+ }
+
+ req.type = ClientOutOfProcess;
+ strncpy (req.name, client_name, sizeof (req.name) - 1);
+
+ if (write (req_fd, &req, sizeof (req)) != sizeof (req)) {
+ jack_error ("cannot send request to jack server (%s)", strerror (errno));
+ close (req_fd);
+ return NULL;
+ }
+
+ if ((n = read (req_fd, &res, sizeof (res))) != sizeof (res)) {
+ jack_error ("cannot read response from jack server (%s)", strerror (errno));
+ close (req_fd);
+ return NULL;
+ }
+
+ if (res.status) {
+ close (req_fd);
+ jack_error ("could not attach as client");
+ return NULL;
+ }
+
+ client = jack_client_alloc ();
+
+ strcpy (client->fifo_prefix, res.fifo_prefix);
+ client->request_fd = req_fd;
+
+ client->pollfd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL;
+ client->pollfd[1].events = POLLIN|POLLERR|POLLHUP|POLLNVAL;
+
+ /* Lookup, attach and register the port/buffer segments in use
+ right now.
+ */
+
+ if ((port_segment_shm_id = shmget (res.port_segment_key, 0, 0)) < 0) {
+ jack_error ("cannot determine shared memory segment for port segment key 0x%x", res.port_segment_key);
+ goto fail;
+ }
+
+ if ((addr = shmat (port_segment_shm_id, res.port_segment_address, 0)) == (void *) -1) {
+ jack_error ("cannot attached port segment shared memory at 0x%", res.port_segment_address);
+ goto fail;
+ }
+
+ si = (jack_port_segment_info_t *) malloc (sizeof (jack_port_segment_info_t));
+ si->shm_key = res.port_segment_key;
+ si->address = addr;
+
+ /* the first chunk of the first port segment is always set by the engine
+ to be a conveniently-sized, zero-filled lump of memory.
+ */
+
+ if (client->port_segments == NULL) {
+ jack_zero_filled_buffer = si->address;
+ }
+
+ client->port_segments = g_slist_prepend (client->port_segments, si);
+
+ /* attach the engine control/info block */
+
+ if ((control_shm_id = shmget (res.control_key, 0, 0)) < 0) {
+ jack_error ("cannot determine shared memory segment for control key 0x%x", res.control_key);
+ goto fail;
+ }
+
+ if ((addr = shmat (control_shm_id, 0, 0)) == (void *) -1) {
+ jack_error ("cannot attached engine control shared memory segment");
+ goto fail;
+ }
+
+ client->engine = (jack_control_t *) addr;
+
+ /* now attach the client control block */
+
+ if ((client_shm_id = shmget (res.client_key, 0, 0)) < 0) {
+ jack_error ("cannot determine shared memory segment for client key 0x%x", res.client_key);
+ goto fail;
+ }
+
+ if ((addr = shmat (client_shm_id, 0, 0)) == (void *) -1) {
+ jack_error ("cannot attached client control shared memory segment");
+ goto fail;
+ }
+
+ client->control = (jack_client_control_t *) addr;
+
+ if ((ev_fd = server_event_connect (client)) < 0) {
+ jack_error ("cannot connect to server for event stream (%s)", strerror (errno));
+ goto fail;
+ }
+
+ client->event_fd = ev_fd;
+
+ return client;
+
+ fail:
+ if (client->engine) {
+ shmdt (client->engine);
+ }
+ if (client->control) {
+ shmdt ((char *) client->control);
+ }
+ if (req_fd >= 0) {
+ close (req_fd);
+ }
+ if (ev_fd >= 0) {
+ close (ev_fd);
+ }
+
+ return 0;
+}
+
+static void *
+jack_client_thread (void *arg)
+
+{
+ jack_client_t *client = (jack_client_t *) arg;
+ jack_client_control_t *control = client->control;
+ jack_event_t event;
+ char status = 0;
+ char c;
+ int err = 0;
+
+ pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+
+ pthread_mutex_lock (&client_lock);
+ client->thread_ok = TRUE;
+ pthread_cond_signal (&client_ready);
+ pthread_mutex_unlock (&client_lock);
+
+ while (err == 0) {
+
+ if (poll (client->pollfd, client->pollmax, 1000) < 0) {
+ if (errno == EINTR) {
+ printf ("poll interrupted\n");
+ continue;
+ }
+ jack_error ("poll failed in client (%s)", strerror (errno));
+ status = -1;
+ break;
+ }
+
+ if (client->pollfd[0].revents & ~POLLIN) {
+ jack_error ("engine has shut down socket; thread exiting");
+ pthread_exit (0);
+ }
+
+ if (client->pollfd[0].revents & POLLIN) {
+
+ /* server has sent us an event. process the event and reply */
+
+ if (read (client->event_fd, &event, sizeof (event)) != sizeof (event)) {
+ jack_error ("cannot read server event (%s)", strerror (errno));
+ err++;
+ break;
+ }
+
+ status = 0;
+
+ switch (event.type) {
+ case PortRegistered:
+ if (control->port_register) {
+ control->port_register (event.x.port_id, TRUE, control->port_register_arg);
+ }
+ break;
+
+ case PortUnregistered:
+ if (control->port_register) {
+ control->port_register (event.x.port_id, FALSE, control->port_register_arg);
+ }
+ break;
+
+ case GraphReordered:
+ status = jack_handle_reorder (client, &event);
+ break;
+
+ case PortConnected:
+ case PortDisconnected:
+ status = jack_client_handle_port_connection (client, &event);
+ break;
+
+ case BufferSizeChange:
+ jack_client_invalidate_port_buffers (client);
+
+ if (control->bufsize) {
+ status = control->bufsize (control->nframes, control->bufsize_arg);
+ }
+ break;
+
+ case SampleRateChange:
+ if (control->srate) {
+ status = control->srate (control->nframes, control->srate_arg);
+ }
+ break;
+
+ case NewPortBufferSegment:
+ break;
+
+ case PortMonitor:
+ if (control->port_monitor) {
+ control->port_monitor (event.x.port_id, TRUE, control->port_monitor_arg);
+ }
+ break;
+
+ case PortUnMonitor:
+ if (control->port_monitor) {
+ control->port_monitor (event.x.port_id, FALSE, control->port_monitor_arg);
+ }
+ break;
+
+ }
+
+ if (write (client->event_fd, &status, sizeof (status)) != sizeof (status)) {
+ jack_error ("cannot send event response to engine (%s)", strerror (errno));
+ err++;
+ break;
+ }
+
+ }
+
+ if (client->pollfd[1].revents & POLLIN) {
+
+ /* the previous stage of the graph has told us to
+ process().
+ */
+
+ control->state = JACK_CLIENT_STATE_TRIGGERED;
+
+ if (read (client->graph_wait_fd, &c, sizeof (c)) != sizeof (c)) {
+ jack_error ("cannot clean up byte from inter-client pipe (%s)", strerror (errno));
+ err++;
+ break;
+ }
+
+ status = control->process (control->nframes, control->process_arg);
+
+ if (!status) {
+ control->state = JACK_CLIENT_STATE_FINISHED;
+ }
+
+ /* this may fail. if it does, the engine will discover
+ that for itcontrol due a cycle timeout, which is about
+ the best we can do without a lot of mostly wasted
+ effort.
+ */
+
+ write (client->graph_next_fd, &c, 1);
+ }
+ }
+
+ return (void *) err;
+}
+
+static int
+jack_start_thread (jack_client_t *client)
+
+{
+ pthread_attr_t *attributes = 0;
+
+ if (client->engine->real_time) {
+
+ /* Get the client thread to run as an RT-FIFO
+ scheduled thread of appropriate priority.
+ */
+
+ struct sched_param rt_param;
+
+ attributes = (pthread_attr_t *) malloc (sizeof (pthread_attr_t));
+
+ pthread_attr_init (attributes);
+
+ if (pthread_attr_setschedpolicy (attributes, SCHED_FIFO)) {
+ jack_error ("cannot set FIFO scheduling class for RT thread");
+ return -1;
+ }
+
+ if (pthread_attr_setscope (attributes, PTHREAD_SCOPE_SYSTEM)) {
+ jack_error ("Cannot set scheduling scope for RT thread");
+ return -1;
+ }
+
+ memset (&rt_param, 0, sizeof (rt_param));
+ rt_param.sched_priority = client->engine->client_priority;
+
+ if (pthread_attr_setschedparam (attributes, &rt_param)) {
+ jack_error ("Cannot set scheduling priority for RT thread (%s)", strerror (errno));
+ return -1;
+ }
+ }
+
+ if (pthread_create (&client->thread, attributes, jack_client_thread, client)) {
+ return -1;
+ }
+ return 0;
+}
+
+int
+jack_activate (jack_client_t *client)
+
+{
+ jack_request_t req;
+
+ if (client->control->type == ClientOutOfProcess && client->first_active) {
+
+ pthread_mutex_init (&client_lock, NULL);
+ pthread_cond_init (&client_ready, NULL);
+
+ pthread_mutex_lock (&client_lock);
+
+ if (jack_start_thread (client)) {
+ pthread_mutex_unlock (&client_lock);
+ return -1;
+ }
+
+ pthread_cond_wait (&client_ready, &client_lock);
+ pthread_mutex_unlock (&client_lock);
+
+ if (!client->thread_ok) {
+ jack_error ("could not start client thread");
+ return -1;
+ }
+
+ client->first_active = FALSE;
+ }
+
+ req.type = ActivateClient;
+ req.x.client_id = client->control->id;
+
+ if (write (client->request_fd, &req, sizeof (req)) != sizeof (req)) {
+ jack_error ("cannot send activate client request to server");
+ return -1;
+ }
+
+ if (read (client->request_fd, &req, sizeof (req)) != sizeof (req)) {
+ jack_error ("cannot read activate client result from server (%s)", strerror (errno));
+ return -1;
+ }
+
+ return req.status;
+}
+
+int
+jack_deactivate (jack_client_t *client)
+
+{
+ jack_request_t req;
+
+ req.type = DeactivateClient;
+ req.x.client_id = client->control->id;
+
+ if (write (client->request_fd, &req, sizeof (req)) != sizeof (req)) {
+ jack_error ("cannot send activate client request to server");
+ return -1;
+ }
+
+ if (read (client->request_fd, &req, sizeof (req)) != sizeof (req)) {
+ jack_error ("cannot read activate client result from server (%s)", strerror (errno));
+ return -1;
+ }
+
+ return req.status;
+}
+
+int
+jack_client_close (jack_client_t *client)
+
+{
+ GSList *node;
+ jack_request_t req;
+ void *status;
+
+ req.type = DropClient;
+ req.x.client_id = client->control->id;
+
+ /* stop the thread */
+
+ pthread_cancel (client->thread);
+ pthread_join (client->thread, &status);
+
+ shmdt ((char *) client->control);
+ shmdt (client->engine);
+
+ for (node = client->port_segments; node; node = g_slist_next (node)) {
+ jack_port_segment_info_t *si;
+ si = (jack_port_segment_info_t *) node->data;
+ shmdt (si->address);
+ free (si);
+ }
+
+ g_slist_free (client->port_segments);
+ g_slist_free (client->ports);
+
+ if (client->graph_wait_fd) {
+ close (client->graph_wait_fd);
+ }
+
+ if (client->graph_next_fd) {
+ close (client->graph_next_fd);
+ }
+
+ if (write (client->request_fd, &req, sizeof (req)) != sizeof (req)) {
+ jack_error ("cannot send drop client request to server");
+ req.status = -1;
+ }
+
+ close (client->event_fd);
+ close (client->request_fd);
+
+ free (client->pollfd);
+ free (client);
+
+ return req.status;
+}
+
+int
+jack_load_client (const char *client_name, const char *path_to_so)
+
+{
+ int fd;
+ jack_client_connect_request_t req;
+ jack_client_connect_result_t res;
+
+ if ((fd = server_connect (0)) < 0) {
+ jack_error ("cannot connect to jack server");
+ return 0;
+ }
+
+ req.type = ClientDynamic;
+
+ strncpy (req.name, client_name, sizeof (req.name) - 1);
+ req.name[sizeof(req.name)-1] = '\0';
+ strncpy (req.object_path, path_to_so, sizeof (req.name) - 1);
+ req.object_path[sizeof(req.object_path)-1] = '\0';
+
+ if (write (fd, &req, sizeof (req)) != sizeof (req)) {
+ jack_error ("cannot send request to jack server (%s)", strerror (errno));
+ close (fd);
+ return 0;
+ }
+
+ if (read (fd, &res, sizeof (res)) != sizeof (res)) {
+ jack_error ("cannot read response from jack server (%s)", strerror (errno));
+ close (fd);
+ return 0;
+ }
+
+ close (fd);
+ return res.status;
+}
+
+jack_client_t *
+jack_driver_become_client (const char *client_name)
+
+
+{
+ int fd;
+ jack_client_connect_request_t req;
+ jack_client_connect_result_t res;
+ jack_client_t *client = 0;
+
+ if ((fd = server_connect (0)) < 0) {
+ jack_error ("cannot connect to jack server");
+ return 0;
+ }
+
+ req.type = ClientDriver;
+ strncpy (req.name, client_name, sizeof (req.name) - 1);
+ req.name[sizeof(req.name)-1] = '\0';
+
+ if (write (fd, &req, sizeof (req)) != sizeof (req)) {
+ jack_error ("cannot send request to jack server (%s)", strerror (errno));
+ close (fd);
+ return 0;
+ }
+
+ if (read (fd, &res, sizeof (res)) != sizeof (res)) {
+ jack_error ("cannot read response from jack server (%s)", strerror (errno));
+ close (fd);
+ return 0;
+ }
+
+ if (res.status) {
+ return 0;
+ }
+
+ client = jack_client_alloc ();
+
+ client->request_fd = fd;
+ client->control = res.client_control;
+ client->engine = res.engine_control;
+
+ /* allow the engine to act on the client's behalf
+ when dealing with in-process clients.
+ */
+
+ client->control->private_internal_client = client;
+
+ return client;
+}
+
+unsigned long jack_get_buffer_size (jack_client_t *client)
+
+{
+ return client->engine->buffer_size;
+}
+
+unsigned long jack_get_sample_rate (jack_client_t *client)
+
+{
+ return client->engine->sample_rate;
+}
+
+static jack_port_t *
+jack_port_new (jack_port_id_t port_id, jack_control_t *control)
+
+{
+ jack_port_t *port;
+ jack_port_shared_t *shared;
+
+ shared = &control->ports[port_id];
+
+ port = (jack_port_t *) malloc (sizeof (jack_port_t));
+
+ port->shared = shared;
+ port->connections = 0;
+ port->tied = NULL;
+
+ return port;
+}
+
+jack_port_t *
+jack_port_register (jack_client_t *client,
+ const char *port_name,
+ const char *port_type,
+ unsigned long flags,
+ unsigned long buffer_size)
+{
+ jack_request_t req;
+ jack_port_t *port = 0;
+
+ /* before we get started, check a few basics */
+
+ if (flags & JackPortCanMonitor) {
+ if (client->control->port_monitor == NULL) {
+ jack_error ("you cannot register ports with PortCanMonitor "
+ "without a port monitor callback");
+ return NULL;
+ }
+ }
+
+ req.type = RegisterPort;
+
+ strcpy ((char *) req.x.port_info.name, (const char *) client->control->name);
+ strcat ((char *) req.x.port_info.name, ":");
+ strcat ((char *) req.x.port_info.name, port_name);
+
+ strncpy (req.x.port_info.type, port_type, sizeof (req.x.port_info.type) - 1);
+ req.x.port_info.flags = flags;
+ req.x.port_info.buffer_size = buffer_size;
+ req.x.port_info.client_id = client->control->id;
+
+ if (write (client->request_fd, &req, sizeof (req)) != sizeof (req)) {
+ jack_error ("cannot send port registration request to server");
+ return 0;
+ }
+
+ if (read (client->request_fd, &req, sizeof (req)) != sizeof (req)) {
+ jack_error ("cannot read port registration result from server");
+ return 0;
+ }
+
+ if (req.status != 0) {
+ return NULL;
+ }
+
+ port = jack_port_new (req.x.port_info.port_id, client->engine);
+ client->ports = g_slist_prepend (client->ports, port);
+
+ return port;
+}
+
+int
+jack_port_unregister (jack_client_t *client, jack_port_t *port)
+
+{
+ jack_request_t req;
+
+ req.type = UnRegisterPort;
+ req.x.port_info.port_id = port->shared->id;
+ req.x.port_info.client_id = client->control->id;
+
+ if (write (client->request_fd, &req, sizeof (req)) != sizeof (req)) {
+ jack_error ("cannot send port registration request to server");
+ return -1;
+ }
+
+ if (read (client->request_fd, &req, sizeof (req)) != sizeof (req)) {
+ jack_error ("cannot read port registration result from server");
+ return -1;
+ }
+
+ return req.status;
+}
+
+int
+jack_port_connect (jack_client_t *client, const char *source_port, const char *destination_port)
+
+{
+ jack_request_t req;
+
+ req.type = ConnectPorts;
+
+ strncpy (req.x.connect.source_port, source_port, sizeof (req.x.connect.source_port) - 1);
+ req.x.connect.source_port[sizeof(req.x.connect.source_port) - 1] = '\0';
+ strncpy (req.x.connect.destination_port, destination_port, sizeof (req.x.connect.destination_port) - 1);
+ req.x.connect.destination_port[sizeof(req.x.connect.destination_port) - 1] = '\0';
+
+ if (write (client->request_fd, &req, sizeof (req)) != sizeof (req)) {
+ jack_error ("cannot send port connection request to server");
+ return -1;
+ }
+
+ if (read (client->request_fd, &req, sizeof (req)) != sizeof (req)) {
+ jack_error ("cannot read port connection result from server");
+ return -1;
+ }
+
+ return req.status;
+}
+
+int
+jack_port_disconnect (jack_client_t *client, const char *source_port, const char *destination_port)
+
+{
+ jack_request_t req;
+
+ req.type = DisconnectPorts;
+
+ strncpy (req.x.connect.source_port, source_port, sizeof (req.x.connect.source_port) - 1);
+ req.x.connect.source_port[sizeof(req.x.connect.source_port) - 1] = '\0';
+ strncpy (req.x.connect.destination_port, destination_port, sizeof (req.x.connect.destination_port) - 1);
+ req.x.connect.destination_port[sizeof(req.x.connect.destination_port) - 1] = '\0';
+
+ if (write (client->request_fd, &req, sizeof (req)) != sizeof (req)) {
+ jack_error ("cannot send port connection request to server");
+ return -1;
+ }
+
+ if (read (client->request_fd, &req, sizeof (req)) != sizeof (req)) {
+ jack_error ("cannot read port connection result from server");
+ return -1;
+ }
+
+ return req.status;
+}
+
+int
+jack_engine_takeover_timebase (jack_client_t *client)
+
+{
+ jack_request_t req;
+
+ req.type = SetTimeBaseClient;
+ req.x.client_id = client->control->id;
+
+ if (write (client->request_fd, &req, sizeof (req)) != sizeof (req)) {
+ jack_error ("cannot send set time base request to server");
+ return -1;
+ }
+
+ if (read (client->request_fd, &req, sizeof (req)) != sizeof (req)) {
+ jack_error ("cannot read set time base result from server");
+ return -1;
+ }
+
+ return req.status;
+}
+
+void
+jack_update_time (jack_client_t *client, nframes_t time)
+
+{
+ client->control->frame_time = time;
+}
+
+void
+jack_set_error_function (void (*func) (const char *, ...))
+{
+ jack_error = func;
+}
+
+void *
+jack_port_get_buffer (jack_port_t *port, nframes_t nframes)
+
+{
+ GSList *node, *next;
+
+ /* Output port. The buffer was assigned by the engine
+ when the port was registered.
+ */
+
+ if (port->shared->flags & JackPortIsOutput) {
+ if (port->tied) {
+ return jack_port_get_buffer (port->tied, nframes);
+ }
+ return port->shared->buffer;
+ }
+
+ /* Input port.
+ */
+
+ if ((node = port->connections) == NULL) {
+
+ /* no connections; return a zero-filled buffer */
+
+ return jack_zero_filled_buffer;
+ }
+
+ if ((next = g_slist_next (node)) == NULL) {
+
+ /* one connection: use zero-copy mode - just pass
+ the buffer of the connected (output) port.
+ */
+
+ return ((jack_port_shared_t *) node->data)->buffer;
+ }
+
+ /* multiple connections. use a local buffer and mixdown
+ the incoming data to that buffer. we have already
+ established the existence of a mixdown function
+ during the connection process.
+ */
+
+ if (port->shared->buffer == NULL) {
+ port->shared->buffer = jack_pool_alloc
+ (port->shared->type_info.buffer_scale_factor * sizeof (sample_t) * nframes);
+ }
+
+ port->shared->type_info.mixdown (port, nframes);
+
+ return port->shared->buffer;
+}
+
+int
+jack_port_tie (jack_port_t *dst, jack_port_t *src)
+
+{
+ if (dst->shared->client_id != src->shared->client_id) {
+ jack_error ("cannot tie ports not owned by the same client");
+ return -1;
+ }
+
+ if (dst->shared->flags & JackPortIsOutput) {
+ jack_error ("cannot tie an input port");
+ return -1;
+ }
+
+ dst->own_buffer = dst->shared->buffer;
+ dst->tied = src;
+ return 0;
+}
+
+int
+jack_port_untie (jack_port_t *port)
+
+{
+ if (port->tied == NULL) {
+ jack_error ("port \"%s\" is not tied", port->shared->name);
+ return -1;
+ }
+ port->shared->buffer = port->own_buffer;
+ port->tied = NULL;
+ return 0;
+}
+
+int
+jack_set_process_callback (jack_client_t *client, JackProcessCallback callback, void *arg)
+
+{
+ if (client->control->active) {
+ return -1;
+ }
+ client->control->process_arg = arg;
+ client->control->process = callback;
+ return 0;
+}
+
+int
+jack_set_buffer_size_callback (jack_client_t *client, JackBufferSizeCallback callback, void *arg)
+
+{
+ if (client->control->active) {
+ return -1;
+ }
+ client->control->bufsize_arg = arg;
+ client->control->bufsize = callback;
+
+ /* Now invoke it */
+
+ callback (client->engine->buffer_size, arg);
+
+ return 0;
+}
+
+int
+jack_set_sample_rate_callback (jack_client_t *client, JackSampleRateCallback callback, void *arg)
+
+{
+ if (client->control->active) {
+ return -1;
+ }
+ client->control->srate_arg = arg;
+ client->control->srate = callback;
+
+ /* Now invoke it */
+
+ callback (client->engine->sample_rate, arg);
+
+ return 0;
+}
+
+int
+jack_set_port_registration_callback(jack_client_t *client, JackPortRegistrationCallback callback, void *arg)
+
+{
+ if (client->control->active) {
+ return -1;
+ }
+ client->control->port_register_arg = arg;
+ client->control->port_register = callback;
+ return 0;
+}
+
+int
+jack_set_port_monitor_callback (jack_client_t *client, JackPortMonitorCallback callback, void *arg)
+
+{
+ if (client->control->active) {
+ return -1;
+ }
+ client->control->port_monitor_arg = arg;
+ client->control->port_monitor = callback;
+ return 0;
+}
+
+int
+jack_get_process_start_fd (jack_client_t *client)
+{
+ /* once this has been called, the client thread
+ does not sleep on the graph wait fd.
+ */
+
+ client->pollmax = 1;
+ return client->graph_wait_fd;
+
+}
+
+int
+jack_get_process_done_fd (jack_client_t *client)
+{
+ return client->graph_next_fd;
+}
+
+int
+jack_port_request_monitor (jack_client_t *client, const char *port_name, int onoff)
+
+{
+ jack_request_t req;
+ int n;
+
+ req.type = (onoff ? RequestPortMonitor : RequestPortUnMonitor);
+ req.x.port_info.port_id = jack_port_id_by_name (client, port_name);
+
+ if (write (client->request_fd, &req, sizeof (req)) != sizeof (req)) {
+ jack_error ("cannot send request to jack server (%s)", strerror (errno));
+ return -1;
+ }
+
+ if ((n = read (client->request_fd, &req, sizeof (req))) != sizeof (req)) {
+ jack_error ("cannot read response from jack server (%s)", strerror (errno));
+ return -1;
+ }
+
+ return req.status;
+}
+
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000..9fd0cb7
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,64 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(client.c)
+
+AC_CONFIG_AUX_DIR(.)
+
+JACK_MAJOR_VERSION=0
+JACK_MINOR_VERSION=2
+JACK_MICRO_VERSION=4
+
+BETA=
+
+AC_SUBST(JACK_MAJOR_VERSION)
+AC_SUBST(JACK_MINOR_VERSION)
+AC_SUBST(JACK_MICRO_VERSION)
+
+JACK_SO_VERSION=${JACK_MAJOR_VERSION}:${JACK_MINOR_VERSION}:${JACK_MICRO_VERSION}${BETA}
+JACK_VERSION=$JACK_MAJOR_VERSION.$JACK_MINOR_VERSION.${JACK_MICRO_VERSION}${BETA}
+JACK_RELEASE=$JACK_MAJOR_VERSION-$JACK_MINOR_VERSION-${JACK_MICRO_VERSION}${BETA}
+
+AC_SUBST(JACK_SO_VERSION)
+AC_SUBST(JACK_VERSION)
+AC_SUBST(JACK_RELEASE)
+
+AM_INIT_AUTOMAKE(jack,${JACK_VERSION})
+
+CFLAGS="-g -Wall -I.. -D_REENTRANT"
+OPT_CFLAGS="-D_REENTRANT -O6 -Wall -fomit-frame-pointer -ffast-math -fstrength-reduce -funroll-loops -fmove-all-movables"
+
+AM_PATH_GLIB(1.0.0,,[AC_MSG_ERROR([*** JACK requires glib, but it doesn't appear to be installed])])
+
+CFLAGS="$CFLAGS $GLIB_CFLAGS"
+CXXFLAGS="$CFLAGS"
+LIBS="$LIBS $GLIB_LIBS"
+
+AC_ARG_ENABLE(optimize,
+ [ --enable-optimize ask the compiler for its best optimizations.],
+ [ if test "x$enable_optimize" != "xno" ; then CFLAGS="$CFLAGS $OPT_CFLAGS" ; fi ])
+
+XTRA=""
+
+AC_ARG_ENABLE(fltk-client,
+ [ --enable-fltk-client build the FLTK test client.])
+
+if test "x$enable_fltk_client" != "no" ; then
+ AC_CHECK_LIB(fltk,main,
+ [ XTRA="$XTRA jack_fltk_client" ],
+ [ AC_MSG_ERROR([*** you can't build the FLTK client without the FLTK library])],
+ [ -L/usr/X11R6/lib -lX11 -lXext ])
+fi
+
+AC_SUBST(XTRA)
+
+AC_PROG_CXX
+AC_PROG_LD
+AM_PROG_LIBTOOL
+
+dnl check for the libraries we need
+
+AC_OUTPUT(Makefile
+ jack.pc
+ jack/Makefile)
+
+
+
diff --git a/conftest b/conftest
new file mode 100755
index 0000000..39e41ff
--- /dev/null
+++ b/conftest
Binary files differ
diff --git a/conftest.c b/conftest.c
new file mode 100644
index 0000000..cb8c7a5
--- /dev/null
+++ b/conftest.c
@@ -0,0 +1 @@
+static int dummy;
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 0000000..37fbfe3
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,62 @@
+WEBSITE = ardour.sourceforge.net
+
+HTML-FILES := index.html \
+ issues.html \
+ manual.html \
+ features.html \
+ mailinglist.html \
+ contributors.html \
+ intro.html \
+ download.html \
+ index.html \
+ news.html \
+ links.html \
+ requirements.html \
+ configuring.html \
+ helping.html \
+ compiling.html \
+ todo.html
+
+user = $(shell whoami)
+fullname = $(shell awk -F: '/$(user)/ { print $$5}' /etc/passwd)
+
+%.html: %.m4 header.html trailer.html
+ m4 -P -E -I. $< > $@
+ sed -e "s/@LASTMOD@/`date`/" -e "s/@USER@/$(fullname)/" \
+ < $@ > $@.XXX && mv $@.XXX $@
+
+.PHONY: manual
+.PHONY: upload
+
+html: $(HTML-FILES)
+
+upload: build-updir
+ cd updir ; \
+ if [ "`ls`" ] ; then \
+ (echo "cd /home/groups/j/ja/jackit/htdocs && tar -zxvf - ; exit"; tar cf - *.html | gzip) | \
+ ssh \$(user)@shell.sourceforge.net; \
+ cd .. ; \
+ touch last-upload ; \
+ fi
+
+upload-images: build-updir
+ (echo "cd /home/groups/j/ja/jackit/htdocs && tar -zxvf - ; exit"; tar cf - *.png | gzip) | \
+ ssh \$(user)@shell.sourceforge.net; \
+
+build-updir:
+ if [ ! -d updir ] ; then mkdir updir ; else rm -rf updir/* ; fi ; \
+ if [ ! -f last-upload ] ; then touch --date="Jan 1st 1970" last-upload; fi ; \
+ uptime=`ls -l --full-time last-upload | awk '{printf ("%s %s %s %s %s\n", $$6, $$7, $$8, $$9, $$10)}'`; \
+ tar -chf - --newer="$$uptime" \
+ --exclude=updir \
+ --exclude="*.m4" \
+ --exclude=Makefile \
+ --exclude=CVS \
+ --exclude=manual \
+ --exclude=header.html \
+ --exclude=trailer.html \
+ --exclude=last-upload . | (cd updir; tar xf - ) ; \
+
+clean:
+ rm -f $(HTML-FILES)
+
diff --git a/doc/compiling.m4 b/doc/compiling.m4
new file mode 100644
index 0000000..c1525b3
--- /dev/null
+++ b/doc/compiling.m4
@@ -0,0 +1,4 @@
+<html>
+
+m4_include(`header.html')
+m4_include(`trailer.html')
diff --git a/doc/configuring.m4 b/doc/configuring.m4
new file mode 100644
index 0000000..c1525b3
--- /dev/null
+++ b/doc/configuring.m4
@@ -0,0 +1,4 @@
+<html>
+
+m4_include(`header.html')
+m4_include(`trailer.html')
diff --git a/doc/contributors.m4 b/doc/contributors.m4
new file mode 100644
index 0000000..c1525b3
--- /dev/null
+++ b/doc/contributors.m4
@@ -0,0 +1,4 @@
+<html>
+
+m4_include(`header.html')
+m4_include(`trailer.html')
diff --git a/doc/download.m4 b/doc/download.m4
new file mode 100644
index 0000000..c1525b3
--- /dev/null
+++ b/doc/download.m4
@@ -0,0 +1,4 @@
+<html>
+
+m4_include(`header.html')
+m4_include(`trailer.html')
diff --git a/doc/faq.m4 b/doc/faq.m4
new file mode 100644
index 0000000..c1525b3
--- /dev/null
+++ b/doc/faq.m4
@@ -0,0 +1,4 @@
+<html>
+
+m4_include(`header.html')
+m4_include(`trailer.html')
diff --git a/doc/features.m4 b/doc/features.m4
new file mode 100644
index 0000000..c1525b3
--- /dev/null
+++ b/doc/features.m4
@@ -0,0 +1,4 @@
+<html>
+
+m4_include(`header.html')
+m4_include(`trailer.html')
diff --git a/doc/header.html b/doc/header.html
new file mode 100644
index 0000000..5f52aac
--- /dev/null
+++ b/doc/header.html
@@ -0,0 +1,54 @@
+<!-- jackit standard header -->
+
+<HEAD>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+ <META NAME="GENERATOR" CONTENT="Emacs 20.2">
+ <TITLE>Ardour</TITLE>
+</HEAD>
+<BODY>
+
+<table border=0 cellspacing=10 cellpadding=0>
+<tr valign="top">
+
+<!-- Sidebar -->
+<td>
+
+<a href=http://ardour.sourceforge.net/>
+<img border=0 src="jack2.png" alt="JACK audio connection kit"></a>
+</p>
+</font>
+
+<p align="center">
+<ul>
+ <li><a href="http://sourceforge.net/project/?group_id=2218">Project Page</a>
+ <li><a href="intro.html">Introduction</a>
+ <li><a href="features.html">Features</a>
+ <li><a href="requirements.html">Requirements</a>
+ <p></p>
+ <li><a href="news.html">News and History</a>
+ <p></p>
+ <li><a href="download.html">Download</a>
+ <li><a href="compiling.html">Compiling</a>
+ <li><a href="configuring.html">Configuring</a>
+ <p></p>
+ <li><a href="manual.html">Manual</a>
+ <li><a href="faq.html">FAQ</a>
+ <p></p>
+ <li><a href="helping.html">Getting Involved</a>
+ <li><a href="todo.html">To Do List</a>
+ <li><a href="mailinglist.html">Mailing Lists</a>
+ <li><a href="links.html">Links</a>
+ <li><a href="contributors.html">Contributors</a>
+</ul>
+</p>
+<p>
+Web: <a href=http://ardour.sourceforge.net/>http://jackit.sourceforge.net/</a><br>
+Email: (MEMBER-ONLY) <a href=mailto:jackit@lists.sourceforge.net>jackit@lists.sourceforge.net</a>
+</p>
+</td>
+
+<td>
+<!-- Main body -->
+<div class=mainbody>
+
+<!-- Standard header ends -------------------------------------------------->
diff --git a/doc/helping.m4 b/doc/helping.m4
new file mode 100644
index 0000000..c1525b3
--- /dev/null
+++ b/doc/helping.m4
@@ -0,0 +1,4 @@
+<html>
+
+m4_include(`header.html')
+m4_include(`trailer.html')
diff --git a/doc/index.m4 b/doc/index.m4
new file mode 100644
index 0000000..32d14e6
--- /dev/null
+++ b/doc/index.m4
@@ -0,0 +1,7 @@
+<html>
+
+m4_include(`header.html')
+
+<h2>About JACK</h2>
+
+m4_include(`trailer.html')
diff --git a/doc/intro.m4 b/doc/intro.m4
new file mode 100644
index 0000000..c1525b3
--- /dev/null
+++ b/doc/intro.m4
@@ -0,0 +1,4 @@
+<html>
+
+m4_include(`header.html')
+m4_include(`trailer.html')
diff --git a/doc/issues.m4 b/doc/issues.m4
new file mode 100644
index 0000000..c4080d4
--- /dev/null
+++ b/doc/issues.m4
@@ -0,0 +1,85 @@
+<html>
+
+m4_include(`header.html')
+
+<h2>Issues to consider when porting programs to JACK</h2>
+
+<h4>Sample bit width assumptions</h4>
+
+A lot existing Linux audio software tends to assume that audio samples
+are 8 or 16 bits wide, and uses <code>short</code> to store them. This
+does not work with JACK, where all sample data, regardless of the
+original data format in which it was obtained (e.g. from disk), is
+stored as a floating point value normalized to the range -1.0 to +1.0.
+
+<h4>Channel interleaving assumptions</h4>
+
+Almost all existing Linux audio software assumes that when delivering
+a sample stream with more than one channel, the samples should be
+interleaved. This does not work with JACK, where all sample streams
+are mono.
+
+<h4>Block-on-write or block-on-read assumptions</h4>
+
+Quite a lot of existing Linux audio software tends to be structured
+around the blocking behaviour of a call to write(2) or read(2) when
+the file descriptor concerned refers to the audio interface. They
+often have this structure:
+
+<verbatim>
+
+ // Playback
+
+ while (1) {
+ get_sample_date_from_somewhere (buf);
+ write (audiofd, buf, bufsize);
+ }
+
+ // Capture
+
+ while (1) {
+ read (audiofd, buf, bufsize);
+ put_sample_data_somewhere (buf);
+ }
+
+</verbatim>
+
+These structures don't work with JACK, which is entirely callback
+driven and moves audio data by copying it to and from memory
+locations, not files. Instead, its necessary to define a
+<code>process()</code> callback which does this:
+
+<verbatim>
+
+ // playback
+
+ int
+ process (nframes_t nframes)
+ {
+ get_nframes_of_data_from_somewhere_without_blocking (buf);
+ sample_t *addr = jack_port_get_buffer (playback_port);
+ memcpy (addr, buf, nframes * sizeof (sample_t));
+ }
+
+ // capture
+
+ int
+ process (nframes_t nframes)
+ {
+ sample_t *addr = jack_port_get_buffer (capture_port);
+ memcpy (buf, addr, nframes * sizeof (sample_t));
+ put_nframes_of_data_somewhere_without_blocking (buf);
+ }
+
+</verbatim>
+
+The code in the <code>process()</code> function should not under
+(almost) any circumstances block: that is, it may not read/write data
+from/to a file, it may not call malloc(), it may not use
+pthread_mutex_lock(), and it should generally avoid system calls. the
+<code>process()</code> callback will be executed when the JACK server
+decides it should be, and it cannot be used to time other parts of the
+program.
+
+m4_include(`trailer.html')
+
diff --git a/doc/links.m4 b/doc/links.m4
new file mode 100644
index 0000000..c1525b3
--- /dev/null
+++ b/doc/links.m4
@@ -0,0 +1,4 @@
+<html>
+
+m4_include(`header.html')
+m4_include(`trailer.html')
diff --git a/doc/mailinglist.m4 b/doc/mailinglist.m4
new file mode 100644
index 0000000..c1525b3
--- /dev/null
+++ b/doc/mailinglist.m4
@@ -0,0 +1,4 @@
+<html>
+
+m4_include(`header.html')
+m4_include(`trailer.html')
diff --git a/doc/manual.m4 b/doc/manual.m4
new file mode 100644
index 0000000..c1525b3
--- /dev/null
+++ b/doc/manual.m4
@@ -0,0 +1,4 @@
+<html>
+
+m4_include(`header.html')
+m4_include(`trailer.html')
diff --git a/doc/news.m4 b/doc/news.m4
new file mode 100644
index 0000000..c1525b3
--- /dev/null
+++ b/doc/news.m4
@@ -0,0 +1,4 @@
+<html>
+
+m4_include(`header.html')
+m4_include(`trailer.html')
diff --git a/doc/requirements.m4 b/doc/requirements.m4
new file mode 100644
index 0000000..c1525b3
--- /dev/null
+++ b/doc/requirements.m4
@@ -0,0 +1,4 @@
+<html>
+
+m4_include(`header.html')
+m4_include(`trailer.html')
diff --git a/doc/todo.m4 b/doc/todo.m4
new file mode 100644
index 0000000..c1525b3
--- /dev/null
+++ b/doc/todo.m4
@@ -0,0 +1,4 @@
+<html>
+
+m4_include(`header.html')
+m4_include(`trailer.html')
diff --git a/doc/trailer.html b/doc/trailer.html
new file mode 100644
index 0000000..f447800
--- /dev/null
+++ b/doc/trailer.html
@@ -0,0 +1,23 @@
+<!-- Standard trailer begins ----------------------------------------------->
+
+<p class="lastmod" align="right">
+<br>
+<br>
+<small>
+<i>Last modified: @LASTMOD@ by @USER@</i>
+<br><a href="http://www.anybrowser.org/campaign/">[Best Viewed with Any Browser]</a>
+</small>
+</p>
+
+</div>
+</font>
+
+</td></tr>
+</table>
+
+<A href="http://sourceforge.net">
+<IMG src="http://sourceforge.net/sflogo.php?group_id=2218&type=1" width="88" height="31" border="0"></A>
+</td>
+
+</BODY>
+</HTML>
diff --git a/driver.c b/driver.c
new file mode 100644
index 0000000..8860543
--- /dev/null
+++ b/driver.c
@@ -0,0 +1,238 @@
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include <jack/driver.h>
+#include <jack/internal.h>
+#include <jack/error.h>
+
+static int dummy_attach (jack_driver_t *drv, jack_engine_t *eng) { return 0; }
+static int dummy_detach (jack_driver_t *drv, jack_engine_t *eng) { return 0; }
+static int dummy_wait (jack_driver_t *drv) { return 0; }
+static nframes_t dummy_frames_since_cycle_start (jack_driver_t *drv) { return 0; }
+static ClockSyncStatus dummy_clock_sync_status (jack_driver_t *drv, channel_t chn) { return ClockMaster; }
+static int dummy_audio_stop (jack_driver_t *drv) { return 0; }
+static int dummy_audio_start (jack_driver_t *drv) { return 0;; }
+static void dummy_set_hw_monitoring (jack_driver_t *drv, int yn) { return; }
+static int dummy_change_sample_clock (jack_driver_t *drv, SampleClockMode mode) { return 0; }
+static int dummy_reset_parameters (jack_driver_t *drv, nframes_t frames_per_cycle, nframes_t rate) { return 0; }
+static void dummy_mark_channel_silent (jack_driver_t *drv, unsigned long chn) { return; }
+static void dummy_request_monitor_input (jack_driver_t *drv, unsigned long chn, int yn) { return ; }
+static void dummy_request_all_monitor_input (jack_driver_t *drv, int yn) { return; }
+
+int
+jack_driver_monitoring_input (jack_driver_t *driver, channel_t chn)
+{
+ return chn != NoChannel && (driver->all_monitor_in || (driver->input_monitor_mask & (1<<chn)));
+}
+
+void
+jack_driver_init (jack_driver_t *driver)
+
+{
+ memset (driver, 0, sizeof (*driver));
+
+ driver->input_monitor_mask = 0;
+
+ driver->attach = dummy_attach;
+ driver->detach = dummy_detach;
+ driver->wait = dummy_wait;
+ driver->frames_since_cycle_start = dummy_frames_since_cycle_start;
+ driver->clock_sync_status = dummy_clock_sync_status;
+ driver->audio_stop = dummy_audio_stop;
+ driver->audio_start = dummy_audio_start;
+ driver->set_hw_monitoring = dummy_set_hw_monitoring ;
+ driver->change_sample_clock = dummy_change_sample_clock;
+ driver->reset_parameters = dummy_reset_parameters;
+ driver->mark_channel_silent = dummy_mark_channel_silent;
+ driver->request_monitor_input = dummy_request_monitor_input;
+ driver->request_all_monitor_input = dummy_request_all_monitor_input;
+ driver->monitoring_input = jack_driver_monitoring_input;
+ driver->engine = 0;
+
+ pthread_mutex_init (&driver->clock_sync_lock, 0);
+ driver->clock_sync_listeners = 0;
+
+ pthread_mutex_init (&driver->input_monitor_lock, 0);
+ driver->input_monitor_listeners = 0;
+}
+
+void
+jack_driver_release (jack_driver_t *driver)
+
+{
+ GSList *node;
+
+ for (node = driver->clock_sync_listeners; node; node = g_slist_next (node)) {
+ free (node->data);
+ }
+ g_slist_free (driver->clock_sync_listeners);
+
+ for (node = driver->input_monitor_listeners; node; node = g_slist_next (node)) {
+ free (node->data);
+ }
+ g_slist_free (driver->input_monitor_listeners);
+}
+
+int
+jack_driver_listen_for_clock_sync_status (jack_driver_t *driver,
+ ClockSyncListenerFunction func,
+ void *arg)
+{
+ ClockSyncListener *csl;
+
+ csl = (ClockSyncListener *) malloc (sizeof (ClockSyncListener));
+ csl->function = func;
+ csl->arg = arg;
+ csl->id = driver->next_clock_sync_listener_id++;
+
+ pthread_mutex_lock (&driver->clock_sync_lock);
+ driver->clock_sync_listeners = g_slist_prepend (driver->clock_sync_listeners, csl);
+ pthread_mutex_unlock (&driver->clock_sync_lock);
+ return csl->id;
+}
+
+int
+jack_driver_stop_listening_to_clock_sync_status (jack_driver_t *driver, int which)
+
+{
+ GSList *node;
+ int ret = -1;
+ pthread_mutex_lock (&driver->clock_sync_lock);
+ for (node = driver->clock_sync_listeners; node; node = g_slist_next (node)) {
+ if (((ClockSyncListener *) node->data)->id == which) {
+ driver->clock_sync_listeners = g_slist_remove_link (driver->clock_sync_listeners, node);
+ free (node->data);
+ g_slist_free_1 (node);
+ ret = 0;
+ break;
+ }
+ }
+ pthread_mutex_unlock (&driver->clock_sync_lock);
+ return ret;
+}
+
+int
+jack_driver_listen_for_input_monitor_status (jack_driver_t *driver,
+ InputMonitorListenerFunction func,
+ void *arg)
+{
+ InputMonitorListener *iml;
+
+ iml = (InputMonitorListener *) malloc (sizeof (InputMonitorListener));
+ iml->function = func;
+ iml->arg = arg;
+ iml->id = driver->next_input_monitor_listener_id++;
+
+ pthread_mutex_lock (&driver->input_monitor_lock);
+ driver->input_monitor_listeners = g_slist_prepend (driver->input_monitor_listeners, iml);
+ pthread_mutex_unlock (&driver->input_monitor_lock);
+ return iml->id;
+}
+
+int
+jack_driver_stop_listening_to_input_monitor_status (jack_driver_t *driver, int which)
+
+{
+ GSList *node;
+ int ret = -1;
+
+ pthread_mutex_lock (&driver->input_monitor_lock);
+ for (node = driver->input_monitor_listeners; node; node = g_slist_next (node)) {
+ if (((InputMonitorListener *) node->data)->id == which) {
+ driver->input_monitor_listeners = g_slist_remove_link (driver->input_monitor_listeners, node);
+ free (node->data);
+ g_slist_free_1 (node);
+ ret = 0;
+ break;
+ }
+ }
+ pthread_mutex_unlock (&driver->input_monitor_lock);
+ return ret;
+}
+
+void jack_driver_clock_sync_notify (jack_driver_t *driver, channel_t chn, ClockSyncStatus status)
+{
+ GSList *node;
+
+ pthread_mutex_lock (&driver->input_monitor_lock);
+ for (node = driver->input_monitor_listeners; node; node = g_slist_next (node)) {
+ ClockSyncListener *csl = (ClockSyncListener *) node->data;
+ csl->function (chn, status, csl->arg);
+ }
+ pthread_mutex_unlock (&driver->input_monitor_lock);
+
+}
+
+void jack_driver_input_monitor_notify (jack_driver_t *driver, channel_t chn, int status)
+{
+ GSList *node;
+
+ pthread_mutex_lock (&driver->input_monitor_lock);
+ for (node = driver->input_monitor_listeners; node; node = g_slist_next (node)) {
+ InputMonitorListener *iml = (InputMonitorListener *) node->data;
+ iml->function (chn, status, iml->arg);
+ }
+ pthread_mutex_unlock (&driver->input_monitor_lock);
+}
+
+
+jack_driver_t *
+jack_driver_load (const char *path_to_so, ...)
+
+{
+ va_list ap;
+ const char *errstr;
+ dlhandle handle;
+ jack_driver_t *driver;
+ jack_driver_t *(*initialize)(va_list);
+ void (*finish)(jack_driver_t *);
+
+ va_start (ap, path_to_so);
+ handle = dlopen (path_to_so, RTLD_NOW|RTLD_GLOBAL);
+
+ if (handle == 0) {
+ if ((errstr = dlerror ()) != 0) {
+ jack_error ("can't load \"%s\": %s", path_to_so, errstr);
+ } else {
+ jack_error ("bizarre error loading driver shared object %s", path_to_so);
+ }
+ va_end (ap);
+ return 0;
+ }
+
+ initialize = dlsym (handle, "driver_initialize");
+
+ if ((errstr = dlerror ()) != 0) {
+ jack_error ("no initialize function in shared object %s\n", path_to_so);
+ dlclose (handle);
+ va_end (ap);
+ return 0;
+ }
+
+ finish = dlsym (handle, "driver_finish");
+
+ if ((errstr = dlerror ()) != 0) {
+ jack_error ("no finish function in in shared driver object %s", path_to_so);
+ dlclose (handle);
+ va_end (ap);
+ return 0;
+ }
+
+ if ((driver = initialize (ap)) != 0) {
+ driver->handle = handle;
+ driver->finish = finish;
+ }
+
+ va_end (ap);
+
+ return driver;
+
+}
+
+void
+jack_driver_unload (jack_driver_t *driver)
+{
+ driver->finish (driver);
+ dlclose (driver->handle);
+}
diff --git a/engine.c b/engine.c
new file mode 100644
index 0000000..4bfa112
--- /dev/null
+++ b/engine.c
@@ -0,0 +1,2250 @@
+/*
+ Copyright (C) 2001 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/shm.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <sys/ipc.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/mman.h>
+
+#include <asm/msr.h>
+
+#include <jack/internal.h>
+#include <jack/engine.h>
+#include <jack/driver.h>
+
+typedef struct {
+
+ jack_port_internal_t *source;
+ jack_port_internal_t *destination;
+
+} jack_connection_internal_t;
+
+typedef struct _jack_client_internal {
+
+ jack_client_control_t *control;
+
+ int request_fd;
+ int event_fd;
+ int subgraph_start_fd;
+ int subgraph_wait_fd;
+ GSList *ports; /* protected by engine->graph_lock */
+ int shm_id;
+ int shm_key;
+ unsigned long rank;
+ struct _jack_client_internal *next_client; /* not a linked list! */
+ dlhandle handle;
+
+} jack_client_internal_t;
+
+static int jack_port_assign_buffer (jack_engine_t *, jack_port_internal_t *);
+static jack_port_internal_t *jack_get_port_by_name (jack_engine_t *, const char *name);
+
+static void jack_client_delete (jack_engine_t *, jack_client_internal_t *);
+static void jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client);
+
+static jack_client_internal_t *jack_client_internal_new (jack_engine_t *engine, int fd, jack_client_connect_request_t *);
+static jack_client_internal_t *jack_client_internal_by_id (jack_engine_t *engine, jack_client_id_t id);
+
+static void jack_sort_graph (jack_engine_t *engine, int take_lock);
+static int jack_rechain_graph (jack_engine_t *engine, int take_lock);
+static int jack_get_fifo_fd (jack_engine_t *engine, int which_fifo);
+static int jack_create_fifo (jack_engine_t *engine, int which_fifo);
+static int jack_port_do_connect (jack_engine_t *engine, const char *source_port, const char *destination_port);
+static int jack_port_do_disconnect (jack_engine_t *engine, const char *source_port, const char *destination_port);
+
+static int jack_port_do_unregister (jack_engine_t *engine, jack_request_t *);
+static int jack_port_do_register (jack_engine_t *engine, jack_request_t *);
+static void jack_port_release (jack_engine_t *engine, jack_port_internal_t *);
+static void jack_port_clear_connections (jack_engine_t *engine, jack_port_internal_t *port);
+static int jack_port_disconnect_internal (jack_engine_t *engine, jack_port_internal_t *src,
+ jack_port_internal_t *dst, int sort_graph);
+
+static void jack_port_registration_notify (jack_engine_t *, jack_port_id_t, int);
+static int jack_send_connection_notification (jack_engine_t *, jack_client_id_t, jack_port_id_t, jack_port_id_t, int);
+static int jack_deliver_event (jack_engine_t *, jack_client_internal_t *, jack_event_t *);
+
+static void jack_audio_port_mixdown (jack_port_t *port, nframes_t nframes);
+
+/* This is a disgusting kludge to work around issues with shmat.
+ A more robust solution is needed.
+*/
+
+static char *top_end_of_unmapped_memory = (char *) (1048576 * 1536); /* 1.5GB */
+static char *low_end_of_unmapped_memory = (char *) (1048576 * 1024); /* 1GB */
+
+static char *
+fixed_shmat (int shmid, char *shmaddr, int shmflg, size_t size)
+{
+ char *addr;
+ char *attempt;
+
+ if (shmaddr != 0) {
+ return shmat (shmid, shmaddr, shmflg);
+ }
+
+ attempt = (char *) (top_end_of_unmapped_memory - size);
+
+ while (attempt > low_end_of_unmapped_memory) {
+ if ((addr = (char *) shmat (shmid, attempt, shmflg|SHM_RND)) != (char *) -1) {
+ top_end_of_unmapped_memory = addr;
+ return addr;
+ }
+ attempt -= size;
+ }
+ return (char *) -1;
+}
+
+jack_port_type_info_t builtin_port_types[] = {
+ { JACK_DEFAULT_AUDIO_TYPE, jack_audio_port_mixdown, 1 },
+ { 0, NULL }
+};
+
+static inline int
+jack_client_is_inprocess (jack_client_internal_t *client)
+{
+ return (client->control->type == ClientDynamic) || (client->control->type == ClientDriver);
+}
+
+static void
+default_jack_error (const char *fmt, ...)
+
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ vfprintf (stderr, fmt, ap);
+ va_end (ap);
+ fputc ('\n', stderr);
+}
+
+void (*jack_error)(const char *fmt, ...) = &default_jack_error;
+
+static
+void shm_destroy (int status, void *arg)
+
+{
+ int shm_id = (int) arg;
+ shmctl (shm_id, IPC_RMID, 0);
+}
+
+static
+void unlink_path (int status, void *arg)
+{
+ char *path = (char *) arg;
+ unlink (path);
+ free (arg);
+}
+
+static int
+make_sockets (int fd[2])
+{
+ struct sockaddr_un addr;
+ int i;
+
+ /* First, the master server socket */
+
+ if ((fd[0] = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) {
+ jack_error ("cannot create server socket (%s)", strerror (errno));
+ return -1;
+ }
+
+ addr.sun_family = AF_UNIX;
+ for (i = 0; i < 999; i++) {
+ snprintf (addr.sun_path, sizeof (addr.sun_path) - 1, "/tmp/jack_%d", i);
+ if (access (addr.sun_path, F_OK) != 0) {
+ break;
+ }
+ }
+
+ if (i == 999) {
+ jack_error ("all possible server socket names in use!!!");
+ close (fd[0]);
+ return -1;
+ }
+
+ on_exit (unlink_path, (void *) strdup (addr.sun_path));
+
+ if (bind (fd[0], (struct sockaddr *) &addr, sizeof (addr)) < 0) {
+ jack_error ("cannot bind server to socket (%s)", strerror (errno));
+ close (fd[0]);
+ return -1;
+ }
+
+ if (listen (fd[0], 1) < 0) {
+ jack_error ("cannot enable listen on server socket (%s)", strerror (errno));
+ close (fd[0]);
+ return -1;
+ }
+
+ /* Now the client/server event ack server socket */
+
+ if ((fd[1] = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) {
+ jack_error ("cannot create event ACK socket (%s)", strerror (errno));
+ close (fd[0]);
+ return -1;
+ }
+
+ addr.sun_family = AF_UNIX;
+ for (i = 0; i < 999; i++) {
+ snprintf (addr.sun_path, sizeof (addr.sun_path) - 1, "/tmp/jack_ack_%d", i);
+ if (access (addr.sun_path, F_OK) != 0) {
+ break;
+ }
+ }
+
+ if (i == 999) {
+ jack_error ("all possible server ACK socket names in use!!!");
+ close (fd[0]);
+ close (fd[1]);
+ return -1;
+ }
+
+ on_exit (unlink_path, (void *) strdup (addr.sun_path));
+
+ if (bind (fd[1], (struct sockaddr *) &addr, sizeof (addr)) < 0) {
+ jack_error ("cannot bind server to socket (%s)", strerror (errno));
+ close (fd[0]);
+ close (fd[1]);
+ return -1;
+ }
+
+ if (listen (fd[1], 1) < 0) {
+ jack_error ("cannot enable listen on server socket (%s)", strerror (errno));
+ close (fd[0]);
+ close (fd[1]);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+jack_cleanup_clients (jack_engine_t *engine)
+
+{
+ jack_client_control_t *ctl;
+ jack_client_internal_t *client;
+ GSList *node;
+ GSList *remove = 0;
+ static int x = 0;
+
+ x++;
+
+ pthread_mutex_lock (&engine->graph_lock);
+ for (node = engine->clients; node; node = g_slist_next (node)) {
+
+ client = (jack_client_internal_t *) node->data;
+ ctl = client->control;
+
+ if (ctl->state > JACK_CLIENT_STATE_NOT_TRIGGERED) {
+ remove = g_slist_prepend (remove, node->data);
+ printf ("%d: removing failed client %s\n", x, client->control->name);
+ }
+ }
+ pthread_mutex_unlock (&engine->graph_lock);
+
+ if (remove) {
+ for (node = remove; node; node = g_slist_next (node)) {
+ jack_remove_client (engine, (jack_client_internal_t *) node->data);
+ }
+ g_slist_free (remove);
+ }
+}
+
+static int
+jack_add_port_segment (jack_engine_t *engine, unsigned long nports)
+
+{
+ jack_port_segment_info_t *si;
+ key_t key;
+ int id;
+ char *addr;
+ int offset;
+ size_t size;
+ size_t step;
+
+ key = random();
+ size = nports * sizeof (sample_t) * engine->control->buffer_size;
+
+ if ((id = shmget (key, size, IPC_CREAT|0666)) < 0) {
+ jack_error ("cannot create new port segment of %d bytes, key = 0x%x (%s)", size, key, strerror (errno));
+ return -1;
+ }
+
+ if ((addr = fixed_shmat (id, 0, 0, size)) == (char *) -1) {
+ jack_error ("cannot attach new port segment (%s)", strerror (errno));
+ shmctl (id, IPC_RMID, 0);
+ return -1;
+ }
+
+ on_exit (shm_destroy, (void *) id);
+
+ si = (jack_port_segment_info_t *) malloc (sizeof (jack_port_segment_info_t));
+ si->shm_key = key;
+ si->address = addr;
+
+ engine->port_segments = g_slist_prepend (engine->port_segments, si);
+ engine->port_segment_key = key; /* XXX fix me */
+ engine->port_segment_address = addr; /* XXX fix me */
+
+ pthread_mutex_lock (&engine->buffer_lock);
+
+ offset = 0;
+
+ step = engine->control->buffer_size * sizeof (sample_t);
+
+ while (offset < size) {
+ jack_port_buffer_info_t *bi;
+
+ bi = (jack_port_buffer_info_t *) malloc (sizeof (jack_port_buffer_info_t));
+ bi->shm_key = key;
+ bi->offset = offset;
+
+ /* we append because we want the list to be in memory-address order */
+
+ engine->port_buffer_freelist = g_slist_append (engine->port_buffer_freelist, bi);
+
+ offset += step;
+ }
+
+ /* convert the first chunk of the segment into a zero-filled area */
+
+ if (engine->silent_buffer == 0) {
+ engine->silent_buffer = (jack_port_buffer_info_t *) engine->port_buffer_freelist->data;
+
+ engine->port_buffer_freelist = g_slist_remove_link (engine->port_buffer_freelist, engine->port_buffer_freelist);
+
+ memset (engine->port_segment_address + engine->silent_buffer->offset, 0,
+ sizeof (sample_t) * engine->control->buffer_size);
+ }
+
+ pthread_mutex_unlock (&engine->buffer_lock);
+
+ /* XXX notify all clients of new segment */
+
+ return 0;
+}
+
+static int
+jack_set_buffer_size (jack_engine_t *engine, nframes_t nframes)
+{
+ /* XXX this is not really right, since it only works for
+ audio ports.
+ */
+
+ engine->control->buffer_size = nframes;
+ jack_add_port_segment (engine, engine->control->port_max);
+ return 0;
+}
+
+static int
+jack_set_sample_rate (jack_engine_t *engine, nframes_t nframes)
+
+{
+ engine->control->sample_rate = nframes;
+ return 0;
+}
+
+static int
+jack_process (jack_engine_t *engine, nframes_t nframes)
+{
+ int err = 0;
+ jack_client_internal_t *client;
+ jack_client_control_t *ctl;
+ GSList *node;
+ struct pollfd pollfd[1];
+ char c;
+
+// unsigned long then, now;
+// rdtscl (then);
+
+ if (pthread_mutex_trylock (&engine->graph_lock) != 0) {
+ return 0;
+ }
+
+ for (node = engine->clients; node; node = g_slist_next (node)) {
+ ctl = ((jack_client_internal_t *) node->data)->control;
+ ctl->state = JACK_CLIENT_STATE_NOT_TRIGGERED;
+ ctl->nframes = nframes;
+ }
+
+ if (engine->timebase_client) {
+ engine->control->frame_time = engine->timebase_client->control->frame_time;
+ }
+
+ for (node = engine->clients; err == 0 && node; ) {
+
+ client = (jack_client_internal_t *) node->data;
+
+ if (!client->control->active) {
+ node = g_slist_next (node);
+ continue;
+ }
+
+ ctl = client->control;
+
+ if (jack_client_is_inprocess (client)) {
+
+ /* in-process client ("plugin") */
+
+ if (ctl->process (nframes, ctl->process_arg) == 0) {
+ ctl->state = JACK_CLIENT_STATE_FINISHED;
+ } else {
+ jack_error ("in-process client %s failed", client->control->name);
+ ctl->state = JACK_CLIENT_STATE_TRIGGERED;
+ err++;
+ break;
+ }
+
+ node = g_slist_next (node);
+
+ } else {
+
+ /* out of process subgraph */
+
+ if (write (client->subgraph_start_fd, &c, sizeof (c)) != sizeof (c)) {
+ jack_error ("cannot initiate graph processing (%s)", strerror (errno));
+ err++;
+ break;
+ }
+
+ /* now wait for the result. use poll instead of read so that we
+ can timeout effectively.
+ */
+
+ pollfd[0].fd = client->subgraph_wait_fd;
+ pollfd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL;
+
+ if (poll (pollfd, 1, engine->driver->period_interval) < 0) {
+ jack_error ("engine cannot poll for graph completion (%s)", strerror (errno));
+ err++;
+ break;
+ }
+
+ if (pollfd[0].revents == 0) {
+ jack_error ("subgraph starting at %s timed out (state = %d)",
+ client->control->name, client->control->state);
+ err++;
+ break;
+ } else if (pollfd[0].revents & ~POLLIN) {
+ jack_error ("error/hangup on graph wait fd");
+ err++;
+ break;
+ } else {
+ if (read (client->subgraph_wait_fd, &c, sizeof (c)) != sizeof (c)) {
+ jack_error ("cannot clean up byte from graph wait fd (%s)", strerror (errno));
+ err++;
+ break;
+ }
+ }
+
+ /* Move to next in-process client (or end of client list) */
+
+ while (node) {
+ if (jack_client_is_inprocess (((jack_client_internal_t *) node->data))) {
+ break;
+ }
+ node = g_slist_next (node);
+ }
+ }
+ }
+
+ pthread_mutex_unlock (&engine->graph_lock);
+
+ if (err) {
+ jack_cleanup_clients (engine);
+ }
+
+// rdtscl (now);
+// printf ("engine cycle time: %.6f usecs\n", ((float) (now - then)) / 450.00f);
+ return 0;
+}
+
+static int
+jack_load_client (jack_engine_t *engine, jack_client_internal_t *client, const char *path_to_so)
+{
+ const char *errstr;
+ dlhandle handle;
+
+ handle = dlopen (path_to_so, RTLD_NOW|RTLD_GLOBAL);
+
+ if (handle == 0) {
+ if ((errstr = dlerror ()) != 0) {
+ jack_error ("can't load \"%s\": %s", path_to_so, errstr);
+ } else {
+ jack_error ("bizarre error loading driver shared object %s", path_to_so);
+ }
+ return -1;
+ }
+
+ client->handle = handle;
+
+#if 0
+ initialize = dlsym (handle, "client_initialize");
+
+ if ((errstr = dlerror ()) != 0) {
+ jack_error ("no initialize function in shared object %s\n", path_to_so);
+ dlclose (handle);
+ return -1;
+ }
+
+ finish = dlsym (handle, "client_finish");
+
+ if ((errstr = dlerror ()) != 0) {
+ jack_error ("no finish function in in shared driver object %s", path_to_so);
+ dlclose (handle);
+ return -1;
+ }
+#endif
+
+ return 0;
+
+}
+
+static void
+jack_client_unload (jack_client_internal_t *client)
+{
+ if (client->handle) {
+// client->finish (client);
+ dlclose (client->handle);
+ }
+}
+
+static int
+handle_new_client (jack_engine_t *engine, int client_fd)
+
+{
+ jack_client_internal_t *client;
+ jack_client_connect_request_t req;
+ jack_client_connect_result_t res;
+
+ if (read (client_fd, &req, sizeof (req)) != sizeof (req)) {
+ jack_error ("cannot read connection request from client");
+ return -1;
+ }
+
+ res.status = 0;
+
+ if ((client = jack_client_internal_new (engine, client_fd, &req)) == 0) {
+ jack_error ("cannot create new client object");
+ return -1;
+ }
+
+ printf ("new client: %s, type %d @ %p\n", client->control->name, req.type, client->control);
+
+ res.status = 0;
+ res.client_key = client->shm_key;
+ res.control_key = engine->control_key;
+ res.port_segment_key = engine->port_segment_key;
+ res.port_segment_address = engine->port_segment_address;
+ res.realtime = engine->control->real_time;
+ res.realtime_priority = engine->rtpriority - 1;
+
+ if (jack_client_is_inprocess (client)) {
+
+ res.client_control = client->control;
+ res.engine_control = engine->control;
+
+ } else {
+ strcpy (res.fifo_prefix, engine->fifo_prefix);
+ }
+
+ res.status = 0;
+
+ if (write (client->request_fd, &res, sizeof (res)) != sizeof (res)) {
+ jack_error ("cannot write connection response to client");
+ jack_client_delete (engine, client);
+ return -1;
+ }
+
+ if (res.status) {
+ return res.status;
+ }
+
+ pthread_mutex_lock (&engine->graph_lock);
+ engine->clients = g_slist_prepend (engine->clients, client);
+ pthread_mutex_unlock (&engine->graph_lock);
+
+ if (client->control->type != ClientDynamic) {
+ if (engine->pfd_max >= engine->pfd_size) {
+ engine->pfd = (struct pollfd *) realloc (engine->pfd, sizeof (struct pollfd) * engine->pfd_size + 16);
+ engine->pfd_size += 16;
+ }
+
+ engine->pfd[engine->pfd_max].fd = client->request_fd;
+ engine->pfd[engine->pfd_max].events = POLLIN|POLLPRI|POLLERR|POLLHUP|POLLNVAL;
+ engine->pfd_max++;
+ }
+
+
+ return 0;
+}
+
+static int
+handle_client_ack_connection (jack_engine_t *engine, int client_fd)
+
+{
+ jack_client_internal_t *client;
+ jack_client_connect_ack_request_t req;
+ jack_client_connect_ack_result_t res;
+
+ if (read (client_fd, &req, sizeof (req)) != sizeof (req)) {
+ jack_error ("cannot read ACK connection request from client");
+ return -1;
+ }
+
+ if ((client = jack_client_internal_by_id (engine, req.client_id)) == NULL) {
+ jack_error ("unknown client ID in ACK connection request");
+ return -1;
+ }
+
+ fprintf (stderr, "client %s is on event fd %d\n", client->control->name, client_fd);
+
+ client->event_fd = client_fd;
+
+ res.status = 0;
+
+ if (write (client->event_fd, &res, sizeof (res)) != sizeof (res)) {
+ jack_error ("cannot write ACK connection response to client");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+jack_client_drop (jack_engine_t *engine, jack_client_id_t id)
+
+{
+ jack_client_internal_t *client;
+
+ if ((client = jack_client_internal_by_id (engine, id)) == 0) {
+ jack_error ("unknown client ID in DropClient request");
+ return -1;
+ }
+
+ jack_remove_client (engine, client);
+ return 0;
+}
+
+#if 0
+static int
+jack_client_has_connections (jack_client_internal_t *client)
+
+{
+ GSList *node;
+
+ for (node = client->ports; node; node = g_slist_next (node)) {
+ if (((jack_port_internal_t *) node->data)->connections) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+#endif
+
+static int
+jack_client_activate (jack_engine_t *engine, jack_client_id_t id)
+
+{
+ jack_client_internal_t *client;
+ GSList *node;
+ int ret = -1;
+
+ pthread_mutex_lock (&engine->graph_lock);
+
+ for (node = engine->clients; node; node = g_slist_next (node)) {
+
+ if (((jack_client_internal_t *) node->data)->control->id == id) {
+
+ client = (jack_client_internal_t *) node->data;
+
+ if (!jack_client_is_inprocess (client)) {
+ jack_create_fifo (engine, ++engine->external_client_cnt);
+ }
+
+ client->control->active = TRUE;
+
+ jack_rechain_graph (engine, FALSE);
+
+ ret = 0;
+ break;
+ }
+ }
+
+ pthread_mutex_unlock (&engine->graph_lock);
+ return ret;
+}
+
+static int
+jack_client_do_deactivate (jack_engine_t *engine, jack_client_internal_t *client)
+
+{
+ /* called must hold engine->graph_lock and must have checked for and/or
+ cleared all connections held by client.
+ */
+
+ client->control->active = FALSE;
+
+ if (!jack_client_is_inprocess (client)) {
+ engine->external_client_cnt--;
+ }
+
+ jack_sort_graph (engine, FALSE);
+ return 0;
+}
+
+static void
+jack_client_disconnect (jack_engine_t *engine, jack_client_internal_t *client)
+
+{
+ GSList *node;
+ jack_port_internal_t *port;
+
+ /* call tree **** MUST HOLD *** engine->graph_lock */
+
+ for (node = client->ports; node; node = g_slist_next (node)) {
+ port = (jack_port_internal_t *) node->data;
+ jack_port_clear_connections (engine, port);
+ jack_port_release (engine, port);
+ }
+
+ g_slist_free (client->ports);
+ client->ports = 0;
+}
+
+static int
+jack_client_deactivate (jack_engine_t *engine, jack_client_id_t id, int to_wait)
+
+{
+ GSList *node;
+ int ret = -1;
+
+ pthread_mutex_lock (&engine->graph_lock);
+
+ printf ("trying ... deactivating client id %d\n", id);
+
+ for (node = engine->clients; node; node = g_slist_next (node)) {
+
+ jack_client_internal_t *client = (jack_client_internal_t *) node->data;
+
+ if (client->control->id == id) {
+
+ printf ("deactivating client id %d\n", id);
+
+ if (client == engine->timebase_client) {
+ engine->timebase_client = 0;
+ engine->control->frame_time = 0;
+ }
+
+ jack_client_disconnect (engine, client);
+ ret = jack_client_do_deactivate (engine, node->data);
+ break;
+ }
+ }
+
+ pthread_mutex_unlock (&engine->graph_lock);
+
+ return ret;
+}
+
+static int
+jack_set_timebase (jack_engine_t *engine, jack_client_id_t client)
+{
+ int ret = -1;
+
+ pthread_mutex_lock (&engine->graph_lock);
+
+ if ((engine->timebase_client = jack_client_internal_by_id (engine, client)) != 0) {
+ engine->control->frame_time = engine->timebase_client->control->frame_time;
+ ret = 0;
+ }
+ pthread_mutex_unlock (&engine->graph_lock);
+ return ret;
+}
+
+static int
+handle_client_jack_error (jack_engine_t *engine, int fd)
+
+{
+ jack_client_internal_t *client = 0;
+ GSList *node;
+
+ pthread_mutex_lock (&engine->graph_lock);
+
+ for (node = engine->clients; node; node = g_slist_next (node)) {
+ if (((jack_client_internal_t *) node->data)->request_fd == fd) {
+ client = (jack_client_internal_t *) node->data;
+ break;
+ }
+ }
+
+ pthread_mutex_unlock (&engine->graph_lock);
+
+ if (client == 0) {
+ jack_error ("i/o error on unknown client fd %d", fd);
+ return -1;
+ }
+
+ jack_remove_client (engine, client);
+ return 0;
+}
+
+static int
+jack_client_port_monitor (jack_engine_t *engine, jack_port_id_t port_id, int onoff)
+
+{
+ jack_port_shared_t *port;
+ jack_client_internal_t *client = NULL;
+ jack_event_t event;
+
+ if (port_id < 0 || port_id >= engine->port_max) {
+ jack_error ("illegal port ID in port monitor request");
+ return -1;
+ }
+
+ port = &engine->control->ports[port_id];
+
+ if (!(port->flags & JackPortCanMonitor)) {
+ jack_error ("port monitor request made on a port (%s) that doesn't support monitoring",
+ port->name);
+ return -1;
+ }
+
+ pthread_mutex_lock (&engine->graph_lock);
+ if ((client = jack_client_internal_by_id (engine, port->client_id)) == NULL) {
+ jack_error ("unknown client owns port %d!!", port_id);
+ pthread_mutex_unlock (&engine->graph_lock);
+ return -1;
+ }
+ pthread_mutex_unlock (&engine->graph_lock);
+
+ event.type = (onoff ? PortMonitor : PortUnMonitor);
+ event.x.port_id = port_id;
+
+ return jack_deliver_event (engine, client, &event);
+}
+
+static int
+handle_client_io (jack_engine_t *engine, int fd)
+
+{
+ jack_request_t req;
+ jack_client_internal_t *client = 0;
+ int reply_fd;
+ GSList *node;
+
+ pthread_mutex_lock (&engine->graph_lock);
+
+ for (node = engine->clients; node; node = g_slist_next (node)) {
+ if (((jack_client_internal_t *) node->data)->request_fd == fd) {
+ client = (jack_client_internal_t *) node->data;
+ break;
+ }
+ }
+
+ pthread_mutex_unlock (&engine->graph_lock);
+
+ if (client == 0) {
+ jack_error ("client input on unknown fd %d!", fd);
+ return -1;
+ }
+
+ if (read (client->request_fd, &req, sizeof (req)) < sizeof (req)) {
+ jack_error ("cannot read request from client");
+ jack_remove_client (engine, client);
+ return -1;
+ }
+
+ reply_fd = client->request_fd;
+
+ switch (req.type) {
+ case RegisterPort:
+ req.status = jack_port_do_register (engine, &req);
+ break;
+
+ case UnRegisterPort:
+ req.status = jack_port_do_unregister (engine, &req);
+ break;
+
+ case ConnectPorts:
+ req.status = jack_port_do_connect (engine, req.x.connect.source_port, req.x.connect.destination_port);
+ break;
+
+ case DisconnectPorts:
+ req.status = jack_port_do_disconnect (engine, req.x.connect.source_port, req.x.connect.destination_port);
+ break;
+
+ case DropClient:
+ req.status = jack_client_drop (engine, req.x.client_id);
+ reply_fd = -1;
+ break;
+
+ case ActivateClient:
+ req.status = jack_client_activate (engine, req.x.client_id);
+ break;
+
+ case DeactivateClient:
+ req.status = jack_client_deactivate (engine, req.x.client_id, TRUE);
+ break;
+
+ case SetTimeBaseClient:
+ req.status = jack_set_timebase (engine, req.x.client_id);
+ break;
+
+ case RequestPortMonitor:
+ req.status = jack_client_port_monitor (engine, req.x.port_info.port_id, TRUE);
+ break;
+
+ case RequestPortUnMonitor:
+ req.status = jack_client_port_monitor (engine, req.x.port_info.port_id, FALSE);
+ break;
+ }
+
+ if (reply_fd >= 0) {
+ if (write (reply_fd, &req, sizeof (req)) < sizeof (req)) {
+ jack_error ("cannot write request result to client");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void *
+jack_server_thread (void *arg)
+
+{
+ jack_engine_t *engine = (jack_engine_t *) arg;
+ struct sockaddr_un client_addr;
+ socklen_t client_addrlen;
+ struct pollfd *pfd;
+ int client_socket;
+ int done = 0;
+ int i;
+ int max;
+
+ pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+
+ engine->pfd[0].fd = engine->fds[0];
+ engine->pfd[0].events = POLLIN|POLLERR;
+ engine->pfd[1].fd = engine->fds[1];
+ engine->pfd[1].events = POLLIN|POLLERR;
+ engine->pfd_max = 2;
+
+ while (!done) {
+
+ /* XXX race here with new external clients
+ causing engine->pfd to be reallocated.
+ I don't know how to solve this
+ short of copying the entire
+ contents of the pfd struct. Ick.
+ */
+
+ max = engine->pfd_max;
+ pfd = engine->pfd;
+
+ if (poll (pfd, max, 10000) < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ jack_error ("poll failed (%s)", strerror (errno));
+ break;
+ }
+
+ /* check the master server socket */
+
+ if (pfd[0].revents & POLLERR) {
+ jack_error ("error on server socket");
+ break;
+ }
+
+ if (pfd[0].revents & POLLIN) {
+
+ memset (&client_addr, 0, sizeof (client_addr));
+ client_addrlen = sizeof (client_addr);
+
+ if ((client_socket = accept (engine->fds[0], (struct sockaddr *) &client_addr, &client_addrlen)) < 0) {
+ jack_error ("cannot accept new connection (%s)", strerror (errno));
+ } else if (handle_new_client (engine, client_socket) < 0) {
+ jack_error ("cannot complete new client connection process");
+ close (client_socket);
+ }
+ }
+
+ /* check the ACK server socket */
+
+ if (pfd[1].revents & POLLERR) {
+ jack_error ("error on server ACK socket");
+ break;
+ }
+
+ if (pfd[1].revents & POLLIN) {
+
+ memset (&client_addr, 0, sizeof (client_addr));
+ client_addrlen = sizeof (client_addr);
+
+ if ((client_socket = accept (engine->fds[1], (struct sockaddr *) &client_addr, &client_addrlen)) < 0) {
+ jack_error ("cannot accept new ACK connection (%s)", strerror (errno));
+ } else if (handle_client_ack_connection (engine, client_socket)) {
+ jack_error ("cannot complete client ACK connection process");
+ close (client_socket);
+ }
+ }
+
+ /* check each client socket */
+
+ for (i = 2; i < max; i++) {
+ if (pfd[i].fd < 0) {
+ continue;
+ }
+
+ if (pfd[i].revents & ~POLLIN) {
+ handle_client_jack_error (engine, pfd[i].fd);
+ } else if (pfd[i].revents & POLLIN) {
+ if (handle_client_io (engine, pfd[i].fd)) {
+ jack_error ("bad hci\n");
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void
+jack_start_server (jack_engine_t *engine)
+
+{
+ pthread_create (&engine->server_thread, 0, &jack_server_thread, engine);
+ pthread_detach (engine->server_thread);
+}
+
+jack_engine_t *
+jack_engine_new (int realtime, int rtpriority)
+{
+ jack_engine_t *engine;
+ size_t control_size;
+ void *addr;
+ int i;
+
+ engine = (jack_engine_t *) malloc (sizeof (jack_engine_t));
+
+ engine->driver = 0;
+ engine->process = jack_process;
+ engine->set_sample_rate = jack_set_sample_rate;
+ engine->set_buffer_size = jack_set_buffer_size;
+
+ engine->next_client_id = 1;
+ engine->timebase_client = 0;
+ engine->port_max = 128;
+ engine->rtpriority = rtpriority;
+ engine->silent_buffer = 0;
+ engine->getthehelloutathere = FALSE;
+
+ pthread_mutex_init (&engine->graph_lock, 0);
+ pthread_mutex_init (&engine->buffer_lock, 0);
+ pthread_mutex_init (&engine->port_lock, 0);
+
+ engine->clients = 0;
+
+ engine->port_segments = 0;
+ engine->port_buffer_freelist = 0;
+
+ engine->pfd_size = 16;
+ engine->pfd_max = 0;
+ engine->pfd = (struct pollfd *) malloc (sizeof (struct pollfd) * engine->pfd_size);
+
+ engine->fifo_size = 16;
+ engine->fifo = (int *) malloc (sizeof (int) * engine->fifo_size);
+ for (i = 0; i < engine->fifo_size; i++) {
+ engine->fifo[i] = -1;
+ }
+
+ /* Build a linked list of known port types. We use a list so that
+ we can easily manage other data types without messing with
+ reallocation of arrays, etc.
+ */
+
+ engine->port_types = NULL;
+ for (i = 0; builtin_port_types[i].type_name; i++) {
+ engine->port_types = g_slist_append (engine->port_types, &builtin_port_types[i]);
+ }
+
+ engine->external_client_cnt = 0;
+
+ srandom (time ((time_t *) 0));
+
+ engine->control_key = random();
+ control_size = sizeof (jack_control_t) + (sizeof (jack_port_shared_t) * engine->port_max);
+
+ if ((engine->control_shm_id = shmget (engine->control_key, control_size, IPC_CREAT|0644)) < 0) {
+ jack_error ("cannot create engine control shared memory segment (%s)", strerror (errno));
+ return 0;
+ }
+
+ if ((addr = fixed_shmat (engine->control_shm_id, 0, 0, control_size)) == (void *) -1) {
+ jack_error ("cannot attach control shared memory segment (%s)", strerror (errno));
+ shmctl (engine->control_shm_id, IPC_RMID, 0);
+ return 0;
+ }
+
+ on_exit (shm_destroy, (void *) engine->control_shm_id);
+
+ engine->control = (jack_control_t *) addr;
+
+ /* Mark all ports as available */
+
+ for (i = 0; i < engine->port_max; i++) {
+ engine->control->ports[i].in_use = 0;
+ engine->control->ports[i].id = i;
+ }
+
+ /* allocate internal port structures so that we can keep
+ track of port connections.
+ */
+
+ engine->internal_ports = (jack_port_internal_t *) malloc (sizeof (jack_port_internal_t) * engine->port_max);
+
+ for (i = 0; i < engine->port_max; i++) {
+ engine->internal_ports[i].connections = 0;
+ }
+
+ if (make_sockets (engine->fds) < 0) {
+ jack_error ("cannot create server sockets");
+ return 0;
+ }
+
+ engine->control->port_max = engine->port_max;
+ engine->control->real_time = realtime;
+ engine->control->client_priority = engine->rtpriority - 1;
+
+ engine->control->sample_rate = 0;
+ engine->control->buffer_size = 0;
+ engine->control->frame_time = 0;
+
+ sprintf (engine->fifo_prefix, "/tmp/jack_fifo_%d", getpid());
+
+ jack_create_fifo (engine, 0);
+ jack_start_server (engine);
+
+ return engine;
+}
+
+static int
+jack_become_real_time (pthread_t thread, int priority)
+
+{
+ struct sched_param rtparam;
+ int x;
+
+ memset (&rtparam, 0, sizeof (rtparam));
+ rtparam.sched_priority = priority;
+
+ if ((x = pthread_setschedparam (thread, SCHED_FIFO, &rtparam)) != 0) {
+ jack_error ("cannot set thread to real-time priority (FIFO/%d) (%d: %s)", rtparam.sched_priority, x, strerror (errno));
+ }
+
+ if (mlockall (MCL_CURRENT | MCL_FUTURE) != 0) {
+ jack_error ("cannot lock down memory for RT thread (%s)", strerror (errno));
+ }
+
+ return 0;
+}
+
+void
+cancel_cleanup1 (void *arg)
+
+{
+ jack_engine_t *engine = (jack_engine_t *) arg;
+ printf ("audio thread cancelled or finished\n");
+ engine->driver->audio_stop (engine->driver);
+}
+
+void
+cancel_cleanup2 (int status, void *arg)
+
+{
+ jack_engine_t *engine = (jack_engine_t *) arg;
+ engine->driver->audio_stop (engine->driver);
+ engine->driver->finish (engine->driver);
+}
+
+static void *
+jack_audio_thread (void *arg)
+
+{
+ jack_engine_t *engine = (jack_engine_t *) arg;
+ jack_driver_t *driver = engine->driver;
+ unsigned long start, end;
+
+ if (engine->control->real_time) {
+ jack_become_real_time (pthread_self(), engine->rtpriority);
+ }
+
+ pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+ on_exit (cancel_cleanup2, engine);
+
+ if (driver->audio_start (driver)) {
+ jack_error ("cannot start driver");
+ pthread_exit (0);
+ }
+
+ while (1) {
+ start = end;
+ if (driver->wait (driver)) {
+ break;
+ }
+ rdtscl (end);
+// printf ("driver cycle time: %.6f usecs\n", ((float) (end - start)) / 450.00f);
+ }
+
+ pthread_exit (0);
+}
+
+int
+jack_run (jack_engine_t *engine)
+
+{
+ if (engine->driver == 0) {
+ jack_error ("engine driver not set; cannot start");
+ return -1;
+ }
+ return pthread_create (&engine->audio_thread, 0, jack_audio_thread, engine);
+}
+int
+jack_wait (jack_engine_t *engine)
+
+{
+ void *ret = 0;
+ int err;
+
+ if ((err = pthread_join (engine->audio_thread, &ret)) != 0) {
+ switch (err) {
+ case EINVAL:
+ jack_error ("cannot join with audio thread (thread detached, or another thread is waiting)");
+ break;
+ case ESRCH:
+ jack_error ("cannot join with audio thread (thread no longer exists)");
+ break;
+ case EDEADLK:
+ jack_error ("programming error: jack_wait() called by audio thread");
+ break;
+ default:
+ jack_error ("cannot join with audio thread (%s)", strerror (errno));
+ }
+ }
+ return (int) ret;
+}
+
+int
+jack_engine_delete (jack_engine_t *engine)
+
+{
+ pthread_cancel (engine->audio_thread);
+ return 0;
+}
+
+static jack_client_internal_t *
+jack_client_internal_new (jack_engine_t *engine, int fd, jack_client_connect_request_t *req)
+
+{
+ jack_client_internal_t *client;
+ key_t shm_key = 0;
+ int shm_id = 0;
+ void *addr = 0;
+
+ switch (req->type) {
+ case ClientDynamic:
+ case ClientDriver:
+ break;
+
+ case ClientOutOfProcess:
+
+ shm_key = random();
+
+ if ((shm_id = shmget (shm_key, sizeof (jack_client_control_t), IPC_CREAT|0666)) < 0) {
+ jack_error ("cannot create client control block");
+ return 0;
+ }
+
+ if ((addr = fixed_shmat (shm_id, 0, 0, sizeof (jack_client_control_t))) == (void *) -1) {
+ jack_error ("cannot attach new client control block");
+ shmctl (shm_id, IPC_RMID, 0);
+ return 0;
+ }
+
+ break;
+ }
+
+ client = (jack_client_internal_t *) malloc (sizeof (jack_client_internal_t));
+
+ client->request_fd = fd;
+ client->event_fd = -1;
+ client->ports = 0;
+ client->rank = UINT_MAX;
+ client->next_client = NULL;
+ client->handle = NULL;
+
+ if (req->type != ClientOutOfProcess) {
+
+ client->control = (jack_client_control_t *) malloc (sizeof (jack_client_control_t));
+
+ } else {
+
+ client->shm_id = shm_id;
+ client->shm_key = shm_key;
+ client->control = (jack_client_control_t *) addr;
+ }
+
+ client->control->type = req->type;
+ client->control->active = FALSE;
+ client->control->dead = FALSE;
+ client->control->id = engine->next_client_id++;
+ strcpy ((char *) client->control->name, req->name);
+
+ client->control->process = NULL;
+ client->control->process_arg = NULL;
+ client->control->bufsize = NULL;
+ client->control->bufsize_arg = NULL;
+ client->control->srate = NULL;
+ client->control->srate_arg = NULL;
+ client->control->port_register = NULL;
+ client->control->port_register_arg = NULL;
+ client->control->port_monitor = NULL;
+ client->control->port_monitor_arg = NULL;
+
+ if (req->type == ClientDynamic) {
+ if (jack_load_client (engine, client, req->object_path)) {
+ jack_error ("cannot dynamically load client from \"%s\"", req->object_path);
+ jack_client_delete (engine, client);
+ return 0;
+ }
+ }
+
+ return client;
+}
+
+static void
+jack_port_clear_connections (jack_engine_t *engine, jack_port_internal_t *port)
+{
+ GSList *node, *next;
+
+ for (node = port->connections; node; ) {
+ next = g_slist_next (node);
+ jack_port_disconnect_internal (engine,
+ ((jack_connection_internal_t *) node->data)->source,
+ ((jack_connection_internal_t *) node->data)->destination,
+ FALSE);
+ node = next;
+ }
+
+ g_slist_free (port->connections);
+ port->connections = 0;
+}
+
+
+static void
+jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client)
+{
+ GSList *node;
+ int i;
+
+ printf ("removing client %s\n", client->control->name);
+
+ pthread_mutex_lock (&engine->graph_lock);
+
+ client->control->dead = TRUE;
+
+ if (client == engine->timebase_client) {
+ engine->timebase_client = 0;
+ engine->control->frame_time = 0;
+ }
+
+ jack_client_disconnect (engine, client);
+ jack_client_do_deactivate (engine, client);
+
+ for (node = engine->clients; node; node = g_slist_next (node)) {
+ if (((jack_client_internal_t *) node->data)->control->id == client->control->id) {
+ engine->clients = g_slist_remove_link (engine->clients, node);
+ g_slist_free_1 (node);
+ break;
+ }
+ }
+
+ /* rearrange the pollfd array so that things work right the
+ next time we go into poll(2).
+ */
+
+ for (i = 0; i < engine->pfd_max; i++) {
+ if (engine->pfd[i].fd == client->request_fd) {
+ if (i+1 < engine->pfd_max) {
+ memcpy (&engine->pfd[i], &engine->pfd[i+1], sizeof (struct pollfd) * (engine->pfd_max - i));
+ }
+ engine->pfd_max--;
+ }
+ }
+
+ close (client->event_fd);
+ close (client->request_fd);
+
+ jack_client_delete (engine, client);
+
+ pthread_mutex_unlock (&engine->graph_lock);
+}
+
+static void
+jack_client_delete (jack_engine_t *engine, jack_client_internal_t *client)
+
+{
+ jack_client_disconnect (engine, client);
+
+ if (jack_client_is_inprocess (client)) {
+ jack_client_unload (client);
+ free ((char *) client->control);
+ } else {
+ shmdt ((void *) client->control);
+ }
+
+ free (client);
+}
+
+jack_client_internal_t *
+jack_client_by_name (jack_engine_t *engine, const char *name)
+
+{
+ jack_client_internal_t *client = NULL;
+ GSList *node;
+
+ pthread_mutex_lock (&engine->graph_lock);
+
+ for (node = engine->clients; node; node = g_slist_next (node)) {
+ if (strcmp ((const char *) ((jack_client_internal_t *) node->data)->control->name, name) == 0) {
+ client = (jack_client_internal_t *) node->data;
+ break;
+ }
+ }
+
+ pthread_mutex_unlock (&engine->graph_lock);
+ return client;
+}
+
+jack_client_internal_t *
+jack_client_internal_by_id (jack_engine_t *engine, jack_client_id_t id)
+
+{
+ jack_client_internal_t *client = NULL;
+ GSList *node;
+
+ /* call tree ***MUST HOLD*** engine->graph_lock */
+
+ for (node = engine->clients; node; node = g_slist_next (node)) {
+ if (((jack_client_internal_t *) node->data)->control->id == id) {
+ client = (jack_client_internal_t *) node->data;
+ break;
+ }
+ }
+
+ return client;
+}
+
+static int
+jack_deliver_event (jack_engine_t *engine, jack_client_internal_t *client, jack_event_t *event)
+{
+ char status;
+
+ if (client->control->dead) {
+ return 0;
+ }
+
+ if (jack_client_is_inprocess (client)) {
+
+ switch (event->type) {
+ case PortConnected:
+ case PortDisconnected:
+ jack_client_handle_port_connection (client->control->private_internal_client, event);
+ break;
+
+ case GraphReordered:
+ jack_error ("reorder event delivered to internal client!");
+ break;
+
+ case BufferSizeChange:
+ if (client->control->bufsize) {
+ client->control->bufsize (event->x.n, client->control->bufsize_arg);
+ }
+ break;
+
+ case SampleRateChange:
+ if (client->control->srate) {
+ client->control->srate (event->x.n, client->control->bufsize_arg);
+ }
+ break;
+
+ case PortMonitor:
+ if (client->control->port_monitor) {
+ client->control->port_monitor (event->x.port_id, TRUE, client->control->port_monitor_arg);
+ }
+ break;
+
+ case PortUnMonitor:
+ if (client->control->port_monitor) {
+ client->control->port_monitor (event->x.port_id, FALSE, client->control->port_monitor_arg);
+ }
+ break;
+
+ default:
+ /* internal clients don't need to know */
+ break;
+ }
+
+ } else {
+ if (write (client->event_fd, event, sizeof (*event)) != sizeof (*event)) {
+ jack_error ("cannot send event to client [%s] (%s)", client->control->name, strerror (errno));
+ return -1;
+ }
+
+ if (read (client->event_fd, &status, sizeof (status)) != sizeof (status)) {
+ jack_error ("cannot read event response from client [%s] (%s)", client->control->name, strerror (errno));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int
+jack_client_set_order (jack_engine_t *engine, jack_client_internal_t *client)
+
+{
+ jack_event_t event;
+
+ event.type = GraphReordered;
+ event.x.n = client->rank;
+
+ return jack_deliver_event (engine, client, &event);
+}
+
+int
+jack_rechain_graph (jack_engine_t *engine, int take_lock)
+
+{
+ GSList *node, *next;
+ unsigned long n;
+ int err = 0;
+ int set;
+ jack_client_internal_t *client, *subgraph_client, *next_client;
+
+ if (take_lock) {
+ pthread_mutex_lock (&engine->graph_lock);
+ }
+
+ /* We're going to try to avoid reconnecting clients that
+ don't need to be reconnected. This is slightly tricky,
+ but worth it for performance reasons.
+ */
+
+ subgraph_client = 0;
+
+ if ((node = engine->clients) == 0) {
+ goto done;
+ }
+
+ client = (jack_client_internal_t *) node->data;
+ if ((next = g_slist_next (node)) == NULL) {
+ next_client = 0;
+ } else {
+ next_client = (jack_client_internal_t *) next->data;
+ }
+ n = 0;
+
+ do {
+ if (client->rank != n || client->next_client != next_client) {
+ client->rank = n;
+ client->next_client = next_client;
+ set = TRUE;
+ } else {
+ set = FALSE;
+ }
+
+ if (jack_client_is_inprocess (client)) {
+ if (subgraph_client) {
+ subgraph_client->subgraph_wait_fd = jack_get_fifo_fd (engine, n);
+ }
+
+ subgraph_client = 0;
+
+ } else {
+
+ if (subgraph_client == 0) {
+ subgraph_client = client;
+ subgraph_client->subgraph_start_fd = jack_get_fifo_fd (engine, n);
+ }
+
+ if (set) {
+ jack_client_set_order (engine, client);
+ }
+
+ n++;
+ }
+
+ if (next == 0) {
+ break;
+ }
+
+ node = next;
+ client = (jack_client_internal_t *) node->data;
+
+ if ((next = g_slist_next (node)) == 0) {
+ next_client = 0;
+ } else {
+ next_client = (jack_client_internal_t *) next->data;
+ }
+
+ } while (1);
+
+ if (subgraph_client) {
+ subgraph_client->subgraph_wait_fd = jack_get_fifo_fd (engine, n);
+ }
+
+ done:
+ if (take_lock) {
+ pthread_mutex_unlock (&engine->graph_lock);
+ }
+
+ return err;
+}
+
+int
+jack_client_connected (jack_client_internal_t *a, jack_client_internal_t *b)
+
+{
+ GSList *pnode, *cnode;
+ int ret = 0;
+
+ /* Check every port on the first client for a connection to
+ the second client.
+
+ Return -1 if a should execute before b
+ Return 1 if a should execute after b
+ Return 0 if they are not connected (ie. it doesn't matter)
+
+ If there is a feedback loop between a and b, the result
+ is undefined (the first connected port will be used to
+ determine the result). This is "OK", since there is no
+ correct execution order in that case.
+ */
+
+ for (pnode = a->ports; pnode; pnode = g_slist_next (pnode)) {
+ jack_port_internal_t *port;
+
+ port = (jack_port_internal_t *) pnode->data;
+
+ for (cnode = port->connections; cnode; cnode = g_slist_next (cnode)) {
+ jack_connection_internal_t *c;
+
+ c = (jack_connection_internal_t *) cnode->data;
+
+ if (c->source->shared->client_id == b->control->id) {
+
+ /* b is the source, so a should be
+ executed *after* b.
+ */
+
+ ret = 1;
+ break;
+
+ } else if (c->source->shared->client_id == a->control->id) {
+
+ /* a is the source, so a should be
+ executed *before* b
+ */
+
+ ret = -1;
+ break;
+ }
+ }
+
+ if (ret) {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static void
+jack_sort_graph (jack_engine_t *engine, int take_lock)
+
+{
+ if (take_lock) {
+ pthread_mutex_lock (&engine->graph_lock);
+ }
+
+ engine->clients = g_slist_sort (engine->clients, (GCompareFunc) jack_client_connected);
+ jack_rechain_graph (engine, FALSE);
+
+ if (take_lock) {
+ pthread_mutex_unlock (&engine->graph_lock);
+ }
+}
+
+static int
+jack_port_do_connect (jack_engine_t *engine,
+ const char *source_port,
+ const char *destination_port)
+{
+ jack_connection_internal_t *connection;
+ jack_port_internal_t *srcport, *dstport;
+ jack_port_id_t src_id, dst_id;
+
+ fprintf (stderr, "trying to connect %s and %s\n", source_port, destination_port);
+
+ if ((srcport = jack_get_port_by_name (engine, source_port)) == 0) {
+ jack_error ("unknown source port in attempted connection [%s]", source_port);
+ return -1;
+ }
+
+ if ((dstport = jack_get_port_by_name (engine, destination_port)) == 0) {
+ jack_error ("unknown destination port in attempted connection [%s]", destination_port);
+ return -1;
+ }
+
+ if ((dstport->shared->flags & JackPortIsInput) == 0) {
+ jack_error ("destination port in attempted connection is not an input port");
+ return -1;
+ }
+
+ if ((srcport->shared->flags & JackPortIsOutput) == 0) {
+ jack_error ("source port in attempted connection is not an output port");
+ return -1;
+ }
+
+ if (strcmp (srcport->shared->type_info.type_name,
+ dstport->shared->type_info.type_name) != 0) {
+ jack_error ("ports used in attemped connection are not of the same data type");
+ return -1;
+ }
+
+ connection = (jack_connection_internal_t *) malloc (sizeof (jack_connection_internal_t));
+
+ connection->source = srcport;
+ connection->destination = dstport;
+
+ src_id = srcport->shared->id;
+ dst_id = dstport->shared->id;
+
+ pthread_mutex_lock (&engine->graph_lock);
+
+ if (dstport->shared->type_info.mixdown == NULL && dstport->connections) {
+ jack_error ("cannot make multiple connections to a port of type [%s]", dstport->shared->type_info.type_name);
+ free (connection);
+ } else {
+ dstport->connections = g_slist_prepend (dstport->connections, connection);
+ srcport->connections = g_slist_prepend (srcport->connections, connection);
+
+ jack_sort_graph (engine, FALSE);
+
+ jack_send_connection_notification (engine, srcport->shared->client_id, src_id, dst_id, TRUE);
+ jack_send_connection_notification (engine, dstport->shared->client_id, dst_id, src_id, TRUE);
+ }
+
+ pthread_mutex_unlock (&engine->graph_lock);
+
+ return 0;
+}
+
+int
+jack_port_disconnect_internal (jack_engine_t *engine,
+ jack_port_internal_t *srcport,
+ jack_port_internal_t *dstport,
+ int sort_graph)
+
+{
+ GSList *node;
+ jack_connection_internal_t *connect;
+ int ret = -1;
+ jack_port_id_t src_id, dst_id;
+
+ /* call tree **** MUST HOLD **** engine->graph_lock. */
+
+ printf ("disconnecting %s and %s\n", srcport->shared->name, dstport->shared->name);
+
+ for (node = srcport->connections; node; node = g_slist_next (node)) {
+
+ connect = (jack_connection_internal_t *) node->data;
+
+ if (connect->source == srcport && connect->destination == dstport) {
+
+ srcport->connections = g_slist_remove (srcport->connections, connect);
+ dstport->connections = g_slist_remove (dstport->connections, connect);
+
+ src_id = srcport->shared->id;
+ dst_id = dstport->shared->id;
+
+ jack_send_connection_notification (engine, srcport->shared->client_id, src_id, dst_id, FALSE);
+ jack_send_connection_notification (engine, dstport->shared->client_id, dst_id, src_id, FALSE);
+
+ free (connect);
+ ret = 0;
+ break;
+ }
+ }
+
+ if (sort_graph) {
+ jack_sort_graph (engine, FALSE);
+ }
+
+ return ret;
+}
+
+static int
+jack_port_do_disconnect (jack_engine_t *engine,
+ const char *source_port,
+ const char *destination_port)
+{
+ jack_port_internal_t *srcport, *dstport;
+ int ret = -1;
+
+ if ((srcport = jack_get_port_by_name (engine, source_port)) == 0) {
+ jack_error ("unknown source port in attempted connection [%s]", source_port);
+ return -1;
+ }
+
+ if ((dstport = jack_get_port_by_name (engine, destination_port)) == 0) {
+ jack_error ("unknown destination port in attempted connection [%s]", destination_port);
+ return -1;
+ }
+
+ pthread_mutex_lock (&engine->graph_lock);
+
+ ret = jack_port_disconnect_internal (engine, srcport, dstport, TRUE);
+
+ pthread_mutex_unlock (&engine->graph_lock);
+ return ret;
+}
+
+static int
+jack_create_fifo (jack_engine_t *engine, int which_fifo)
+
+{
+ char path[FIFO_NAME_SIZE+1];
+
+ sprintf (path, "%s-%d", engine->fifo_prefix, which_fifo);
+
+ if (mknod (path, 0666|S_IFIFO, 0) < 0) {
+ if (errno != EEXIST) {
+ jack_error ("cannot create inter-client FIFO [%s] (%s)", path, strerror (errno));
+ return -1;
+ }
+
+ } else {
+ on_exit (unlink_path, strdup (path));
+ }
+
+ jack_get_fifo_fd (engine, which_fifo);
+
+ return 0;
+}
+
+static int
+jack_get_fifo_fd (jack_engine_t *engine, int which_fifo)
+
+{
+ char path[FIFO_NAME_SIZE+1];
+
+ sprintf (path, "%s-%d", engine->fifo_prefix, which_fifo);
+
+ if (which_fifo >= engine->fifo_size) {
+ int i;
+
+ engine->fifo = (int *) realloc (engine->fifo, sizeof (int) * engine->fifo_size + 16);
+ for (i = engine->fifo_size; i < engine->fifo_size + 16; i++) {
+ engine->fifo[i] = -1;
+ }
+ engine->fifo_size += 16;
+ }
+
+ if (engine->fifo[which_fifo] < 0) {
+ if ((engine->fifo[which_fifo] = open (path, O_RDWR|O_CREAT, 0666)) < 0) {
+ jack_error ("cannot open fifo [%s] (%s)", path, strerror (errno));
+ return -1;
+ }
+ }
+
+ return engine->fifo[which_fifo];
+}
+
+int
+jack_use_driver (jack_engine_t *engine, jack_driver_t *driver)
+
+{
+ if (driver) {
+ driver->detach (driver, engine);
+ engine->driver = 0;
+ }
+
+ if (driver->attach (driver, engine)) {
+ return -1;
+ }
+
+ engine->driver = driver;
+ return 0;
+}
+
+/* PORT RELATED FUNCTIONS */
+
+
+jack_port_id_t
+jack_get_free_port (jack_engine_t *engine)
+
+{
+ jack_port_id_t i;
+
+ pthread_mutex_lock (&engine->port_lock);
+
+ for (i = 0; i < engine->port_max; i++) {
+ if (engine->control->ports[i].in_use == 0) {
+ engine->control->ports[i].in_use = 1;
+ break;
+ }
+ }
+
+ pthread_mutex_unlock (&engine->port_lock);
+
+ if (i == engine->port_max) {
+ return NoPort;
+ }
+
+ return i;
+}
+
+static void
+jack_port_release (jack_engine_t *engine, jack_port_internal_t *port)
+
+{
+ /* XXX add the buffer used by the port back the (correct) freelist */
+
+ pthread_mutex_lock (&engine->port_lock);
+ port->shared->in_use = 0;
+ pthread_mutex_unlock (&engine->port_lock);
+}
+
+jack_port_internal_t *
+jack_get_port_internal_by_name (jack_engine_t *engine, const char *name)
+{
+ jack_port_id_t id;
+
+ pthread_mutex_lock (&engine->port_lock);
+
+ for (id = 0; id < engine->port_max; id++) {
+ if (strcmp (engine->control->ports[id].name, name) == 0) {
+ break;
+ }
+ }
+
+ pthread_mutex_unlock (&engine->port_lock);
+
+ if (id != engine->port_max) {
+ return &engine->internal_ports[id];
+ } else {
+ return NULL;
+ }
+}
+
+int
+jack_port_do_register (jack_engine_t *engine, jack_request_t *req)
+
+{
+ GSList *node;
+ jack_port_id_t port_id;
+ jack_port_shared_t *shared;
+ jack_port_internal_t *port;
+ jack_client_internal_t *client;
+ jack_port_type_info_t *type_info;
+
+ pthread_mutex_lock (&engine->graph_lock);
+ if ((client = jack_client_internal_by_id (engine, req->x.port_info.client_id)) == 0) {
+ jack_error ("unknown client id in port registration request");
+ return -1;
+ }
+ pthread_mutex_unlock (&engine->graph_lock);
+
+ if ((port_id = jack_get_free_port (engine)) == NoPort) {
+ jack_error ("no ports available!");
+ return -1;
+ }
+
+ shared = &engine->control->ports[port_id];
+
+ strcpy (shared->name, req->x.port_info.name);
+
+ shared->client_id = req->x.port_info.client_id;
+ shared->flags = req->x.port_info.flags;
+ shared->locked = 0;
+ shared->buffer_size = req->x.port_info.buffer_size;
+
+ port = &engine->internal_ports[port_id];
+
+ port->shared = shared;
+ port->connections = 0;
+
+ type_info = NULL;
+
+ for (node = engine->port_types; node; node = g_slist_next (node)) {
+
+ if (strcmp (req->x.port_info.type, ((jack_port_type_info_t *) node->data)->type_name) == 0) {
+ type_info = (jack_port_type_info_t *) node->data;
+ break;
+ }
+ }
+
+ if (type_info == NULL) {
+
+ /* not a builtin type, so allocate a new type_info structure,
+ and fill it appropriately.
+ */
+
+ type_info = (jack_port_type_info_t *) malloc (sizeof (jack_port_type_info_t));
+
+ type_info->type_name = strdup (req->x.port_info.type);
+ type_info->mixdown = NULL; /* we have no idea how to mix this */
+ type_info->buffer_scale_factor = -1; /* use specified port buffer size */
+
+ engine->port_types = g_slist_prepend (engine->port_types, type_info);
+ }
+
+
+ memcpy (&port->shared->type_info, type_info, sizeof (jack_port_type_info_t));
+
+ if (jack_port_assign_buffer (engine, port)) {
+ jack_error ("cannot assign buffer for port");
+ return -1;
+ }
+
+ pthread_mutex_lock (&engine->graph_lock);
+ client->ports = g_slist_prepend (client->ports, port);
+ jack_port_registration_notify (engine, port_id, TRUE);
+ pthread_mutex_unlock (&engine->graph_lock);
+
+ req->x.port_info.port_id = port_id;
+
+ return 0;
+}
+
+int
+jack_port_do_unregister (jack_engine_t *engine, jack_request_t *req)
+
+
+{
+ jack_client_internal_t *client;
+ jack_port_shared_t *shared;
+ jack_port_internal_t *port;
+
+ if (req->x.port_info.port_id < 0 || req->x.port_info.port_id > engine->port_max) {
+ jack_error ("invalid port ID %d in unregister request\n", req->x.port_info.port_id);
+ return -1;
+ }
+
+ shared = &engine->control->ports[req->x.port_info.port_id];
+
+ pthread_mutex_lock (&engine->graph_lock);
+ if ((client = jack_client_internal_by_id (engine, shared->client_id)) == NULL) {
+ jack_error ("unknown client id in port registration request");
+ return -1;
+ }
+ pthread_mutex_unlock (&engine->graph_lock);
+
+ port = &engine->internal_ports[req->x.port_info.port_id];
+
+ jack_port_release (engine, &engine->internal_ports[req->x.port_info.port_id]);
+
+ pthread_mutex_lock (&engine->graph_lock);
+ client->ports = g_slist_remove (client->ports, port);
+ jack_port_registration_notify (engine, req->x.port_info.port_id, FALSE);
+ pthread_mutex_unlock (&engine->graph_lock);
+
+ return 0;
+}
+
+void
+jack_port_registration_notify (jack_engine_t *engine, jack_port_id_t port_id, int yn)
+
+{
+ jack_event_t event;
+ jack_client_internal_t *client;
+ GSList *node;
+
+ event.type = (yn ? PortRegistered : PortUnregistered);
+ event.x.port_id = port_id;
+
+ for (node = engine->clients; node; node = g_slist_next (node)) {
+
+ client = (jack_client_internal_t *) node->data;
+
+ if (!client->control->active) {
+ continue;
+ }
+
+ if (client->control->port_register) {
+ if (jack_deliver_event (engine, client, &event)) {
+ jack_error ("cannot send port registration notification to %s (%s)",
+ client->control->name, strerror (errno));
+ }
+ }
+ }
+}
+
+int
+jack_port_assign_buffer (jack_engine_t *engine, jack_port_internal_t *port)
+{
+ GSList *node;
+ jack_port_segment_info_t *psi;
+ jack_port_buffer_info_t *bi;
+
+ port->shared->buffer = NULL;
+
+ if (port->shared->flags & JackPortIsInput) {
+ return 0;
+ }
+
+ pthread_mutex_lock (&engine->buffer_lock);
+
+ if (engine->port_buffer_freelist == NULL) {
+ jack_error ("no more buffers available!");
+ goto out;
+ }
+
+ bi = (jack_port_buffer_info_t *) engine->port_buffer_freelist->data;
+
+ for (node = engine->port_segments; node; node = g_slist_next (node)) {
+
+ psi = (jack_port_segment_info_t *) node->data;
+
+ if (bi->shm_key == psi->shm_key) {
+ port->shared->buffer = psi->address + bi->offset;
+ break;
+ }
+ }
+
+ if (port->shared->buffer) {
+ engine->port_buffer_freelist = g_slist_remove (engine->port_buffer_freelist, bi);
+ } else {
+ jack_error ("port segment info for 0x%x:%d not found!", bi->shm_key, bi->offset);
+ }
+
+ out:
+ pthread_mutex_unlock (&engine->buffer_lock);
+
+ if (port->shared->buffer == NULL) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+static jack_port_internal_t *
+jack_get_port_by_name (jack_engine_t *engine, const char *name)
+
+{
+ jack_port_id_t id;
+
+ /* Note the potential race on "in_use". Other design
+ elements prevent this from being a problem.
+ */
+
+ for (id = 0; id < engine->port_max; id++) {
+ if (engine->control->ports[id].in_use && strcmp (engine->control->ports[id].name, name) == 0) {
+ return &engine->internal_ports[id];
+ }
+ }
+
+ return NULL;
+}
+
+static int
+jack_send_connection_notification (jack_engine_t *engine, jack_client_id_t client_id,
+ jack_port_id_t self_id, jack_port_id_t other_id, int connected)
+
+{
+ jack_client_internal_t *client;
+ jack_event_t event;
+
+ if ((client = jack_client_internal_by_id (engine, client_id)) == 0) {
+ jack_error ("no such client %d during connection notification", client_id);
+ return -1;
+ }
+
+ fprintf (stderr, "sending connection to client %s\n", client->control->name);
+
+ event.type = (connected ? PortConnected : PortDisconnected);
+ event.x.self_id = self_id;
+ event.y.other_id = other_id;
+
+ if (jack_deliver_event (engine, client, &event)) {
+ jack_error ("cannot send port connection notification to client %s (%s)",
+ client->control->name, strerror (errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+jack_audio_port_mixdown (jack_port_t *port, nframes_t nframes)
+{
+ GSList *node;
+ jack_port_shared_t *input;
+ nframes_t n;
+ sample_t *dst, *src;
+
+ /* by the time we've called this, we've already established
+ the existence of more than 1 connection to this input port.
+ */
+
+ node = port->connections;
+ input = (jack_port_shared_t *) node->data;
+ memcpy (port->shared->buffer, input->buffer, sizeof (sample_t) * nframes);
+
+ for (node = g_slist_next (node); node; node = g_slist_next (node)) {
+ input = (jack_port_shared_t *) node->data;
+ n = nframes;
+ dst = port->shared->buffer;
+ src = input->buffer;
+ while (--n) {
+ *dst++ += *src++;
+ }
+ }
+}
+
+
diff --git a/fltk_client.cc b/fltk_client.cc
new file mode 100644
index 0000000..9c7b5a4
--- /dev/null
+++ b/fltk_client.cc
@@ -0,0 +1,98 @@
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+
+extern "C"
+{
+#include <jack/jack.h>
+}
+
+#include <FL/Fl.H>
+#include <FL/Fl_Window.H>
+#include <FL/Fl_Slider.H>
+
+jack_port_t *my_input_port;
+jack_port_t *my_output_port;
+
+float gain = 0.0; /* slider starts out with zero gain */
+
+int
+process (nframes_t nframes, void *arg)
+
+{
+ sample_t *out = (sample_t *) jack_port_get_buffer (my_output_port, nframes);
+ sample_t *in = (sample_t *) jack_port_get_buffer (my_input_port, nframes);
+
+ while (nframes--)
+ out[nframes] = in[nframes] * gain;
+
+ return 0;
+}
+
+int
+bufsize (nframes_t nframes, void *arg)
+
+{
+ printf ("the maximum buffer size is now %lu\n", nframes);
+ return 0;
+}
+
+int
+srate (nframes_t nframes, void *arg)
+
+{
+ printf ("the sample rate is now %lu/sec\n", nframes);
+ return 0;
+}
+
+void callback(Fl_Slider* s)
+{
+ gain = s->value();
+}
+
+int
+main (int argc, char *argv[])
+
+{
+ Fl_Window w(0,0,100,120);
+ Fl_Slider s(10,10,20,100);
+ w.show();
+ s.callback((Fl_Callback*) callback);
+
+ jack_client_t *client;
+
+ if ((client = jack_client_new ("fltktest")) == 0) {
+ fprintf (stderr, "jack server not running?\n");
+ return 1;
+ }
+
+ jack_set_process_callback (client, process, 0);
+ jack_set_buffer_size_callback (client, bufsize, 0);
+ jack_set_sample_rate_callback (client, srate, 0);
+
+ printf ("engine sample rate: %lu\n", jack_get_sample_rate (client));
+
+ my_input_port = jack_port_register (client, "myinput", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
+ my_output_port = jack_port_register (client, "myoutput", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+
+ if (jack_activate (client)) {
+ fprintf (stderr, "cannot activate client");
+ }
+
+ printf ("client activated\n");
+
+ if (jack_port_connect (client, "ALSA I/O:Input 1", my_input_port->shared->name)) {
+ fprintf (stderr, "cannot connect input ports\n");
+ }
+
+ if (jack_port_connect (client, my_output_port->shared->name, "ALSA I/O:Output 1")) {
+ fprintf (stderr, "cannot connect output ports\n");
+ }
+
+ Fl::run();
+
+ printf ("done sleeping, now closing...\n");
+ jack_client_close (client);
+ exit (0);
+}
+
diff --git a/generic_hw.c b/generic_hw.c
new file mode 100644
index 0000000..75287cd
--- /dev/null
+++ b/generic_hw.c
@@ -0,0 +1,57 @@
+/*
+ Copyright (C) 2001 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <jack/hardware.h>
+#include <jack/alsa_driver.h>
+
+static int generic_set_input_monitor_mask (jack_hardware_t *hw, unsigned long mask)
+{
+ return -1;
+}
+
+static int generic_change_sample_clock (jack_hardware_t *hw, SampleClockMode mode)
+{
+ return -1;
+}
+
+void
+jack_alsa_generic_hw_release (jack_hardware_t *hw)
+
+{
+ return;
+}
+
+jack_hardware_t *
+jack_alsa_generic_hw_new (alsa_driver_t *driver)
+
+{
+ jack_hardware_t *hw;
+
+ hw = (jack_hardware_t *) malloc (sizeof (jack_hardware_t));
+
+ hw->capabilities = 0;
+ hw->input_monitor_mask = 0;
+
+ hw->set_input_monitor_mask = generic_set_input_monitor_mask;
+ hw->change_sample_clock = generic_change_sample_clock;
+ hw->release = jack_alsa_generic_hw_release;
+
+ return hw;
+}
diff --git a/hammerfall.c b/hammerfall.c
new file mode 100644
index 0000000..9efa7cd
--- /dev/null
+++ b/hammerfall.c
@@ -0,0 +1,293 @@
+/*
+ Copyright (C) 2001 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <jack/hardware.h>
+#include <jack/alsa_driver.h>
+#include <jack/hammerfall.h>
+#include <jack/error.h>
+
+static void
+set_control_id (snd_ctl_elem_id_t *ctl, const char *name)
+{
+ snd_ctl_elem_id_set_name (ctl, name);
+ snd_ctl_elem_id_set_numid (ctl, 0);
+ snd_ctl_elem_id_set_interface (ctl, SND_CTL_ELEM_IFACE_PCM);
+ snd_ctl_elem_id_set_device (ctl, 0);
+ snd_ctl_elem_id_set_subdevice (ctl, 0);
+ snd_ctl_elem_id_set_index (ctl, 0);
+}
+
+static void
+hammerfall_broadcast_channel_status_change (hammerfall_t *h, int lock, int sync, channel_t lowchn, channel_t highchn)
+
+{
+ channel_t chn;
+ ClockSyncStatus status = 0;
+
+ if (lock) {
+ status |= Lock;
+ } else {
+ status |= NoLock;
+ }
+
+ if (sync) {
+ status |= Sync;
+ } else {
+ status |= NoSync;
+ }
+
+ for (chn = lowchn; chn < highchn; chn++) {
+ alsa_driver_set_clock_sync_status (h->driver, chn, status);
+ }
+}
+
+static void
+hammerfall_check_sync_state (hammerfall_t *h, int val, int adat_id)
+
+{
+ int lock;
+ int sync;
+
+ /* S/PDIF channel is always locked and synced, but we only
+ need tell people once that this is TRUE.
+
+ XXX - maybe need to make sure that the rate matches our
+ idea of the current rate ?
+ */
+
+ if (!h->said_that_spdif_is_fine) {
+ ClockSyncStatus status;
+
+ status = Lock|Sync;
+
+ /* XXX broken! fix for hammerfall light ! */
+
+ alsa_driver_set_clock_sync_status (h->driver, 24, status);
+ alsa_driver_set_clock_sync_status (h->driver, 25, status);
+
+ h->said_that_spdif_is_fine = TRUE;
+ }
+
+ lock = (val & 0x1) ? TRUE : FALSE;
+ sync = (val & 0x2) ? TRUE : FALSE;
+
+ if (h->lock_status[adat_id] != lock ||
+ h->sync_status[adat_id] != sync) {
+ hammerfall_broadcast_channel_status_change (h, lock, sync, adat_id*8, (adat_id*8)+8);
+ }
+
+ h->lock_status[adat_id] = lock;
+ h->sync_status[adat_id] = sync;
+}
+
+static void
+hammerfall_check_sync (hammerfall_t *h, snd_ctl_elem_value_t *ctl)
+
+{
+ const char *name;
+ int val;
+ snd_ctl_elem_id_t *ctl_id;
+
+ snd_ctl_elem_id_alloca (&ctl_id);
+ snd_ctl_elem_value_get_id (ctl, ctl_id);
+
+ name = snd_ctl_elem_id_get_name (ctl_id);
+
+ if (strcmp (name, "ADAT1 Sync Check") == 0) {
+ val = snd_ctl_elem_value_get_enumerated (ctl, 0);
+ hammerfall_check_sync_state (h, val, 0);
+ } else if (strcmp (name, "ADAT2 Sync Check") == 0) {
+ val = snd_ctl_elem_value_get_enumerated (ctl, 0);
+ hammerfall_check_sync_state (h, val, 1);
+ } else if (strcmp (name, "ADAT3 Sync Check") == 0) {
+ val = snd_ctl_elem_value_get_enumerated (ctl, 0);
+ hammerfall_check_sync_state (h, val, 2);
+ } else {
+ jack_error ("Hammerfall: unknown control \"%s\"", name);
+ }
+}
+
+static int
+hammerfall_set_input_monitor_mask (jack_hardware_t *hw, unsigned long mask)
+{
+ hammerfall_t *h = (hammerfall_t *) hw->private;
+ snd_ctl_elem_value_t *ctl;
+ snd_ctl_elem_id_t *ctl_id;
+ int err;
+ int i;
+
+ snd_ctl_elem_value_alloca (&ctl);
+ snd_ctl_elem_id_alloca (&ctl_id);
+ set_control_id (ctl_id, "Channels Thru");
+ snd_ctl_elem_value_set_id (ctl, ctl_id);
+
+ for (i = 0; i < 26; i++) {
+ snd_ctl_elem_value_set_integer (ctl, i, (mask & (1<<i)) ? 1 : 0);
+ }
+
+ if ((err = snd_ctl_elem_write (h->driver->ctl_handle, ctl)) != 0) {
+ jack_error ("ALSA/Hammerfall: cannot set input monitoring (%s)", snd_strerror (err));
+ return -1;
+ }
+
+ hw->input_monitor_mask = mask;
+
+ return 0;
+}
+
+static int
+hammerfall_change_sample_clock (jack_hardware_t *hw, SampleClockMode mode)
+{
+ hammerfall_t *h = (hammerfall_t *) hw->private;
+ snd_ctl_elem_value_t *ctl;
+ snd_ctl_elem_id_t *ctl_id;
+ int err;
+
+ snd_ctl_elem_value_alloca (&ctl);
+ snd_ctl_elem_id_alloca (&ctl_id);
+ set_control_id (ctl_id, "Sync Mode");
+ snd_ctl_elem_value_set_id (ctl, ctl_id);
+
+ switch (mode) {
+ case AutoSync:
+ snd_ctl_elem_value_set_enumerated (ctl, 0, 0);
+ break;
+ case ClockMaster:
+ snd_ctl_elem_value_set_enumerated (ctl, 0, 1);
+ break;
+ case WordClock:
+ snd_ctl_elem_value_set_enumerated (ctl, 0, 2);
+ break;
+ }
+
+ if ((err = snd_ctl_elem_write (h->driver->ctl_handle, ctl)) < 0) {
+ jack_error ("ALSA-Hammerfall: cannot set clock mode");
+ }
+
+ return 0;
+}
+
+static void
+hammerfall_release (jack_hardware_t *hw)
+
+{
+ hammerfall_t *h = (hammerfall_t *) hw->private;
+ void *status;
+
+ if (h == 0) {
+ return;
+ }
+
+ pthread_cancel (h->monitor_thread);
+ pthread_join (h->monitor_thread, &status);
+
+ free (h);
+}
+
+static void *
+hammerfall_monitor_controls (void *arg)
+
+{
+ jack_hardware_t *hw = (jack_hardware_t *) arg;
+ hammerfall_t *h = (hammerfall_t *) hw->private;
+ snd_ctl_elem_id_t *switch_id[3];
+ snd_ctl_elem_value_t *sw[3];
+
+ pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+
+ snd_ctl_elem_id_malloc (&switch_id[0]);
+ snd_ctl_elem_id_malloc (&switch_id[1]);
+ snd_ctl_elem_id_malloc (&switch_id[2]);
+
+ snd_ctl_elem_value_malloc (&sw[0]);
+ snd_ctl_elem_value_malloc (&sw[1]);
+ snd_ctl_elem_value_malloc (&sw[2]);
+
+ set_control_id (switch_id[0], "ADAT1 Sync Check");
+ set_control_id (switch_id[1], "ADAT2 Sync Check");
+ set_control_id (switch_id[2], "ADAT3 Sync Check");
+
+ snd_ctl_elem_value_set_id (sw[0], switch_id[0]);
+ snd_ctl_elem_value_set_id (sw[1], switch_id[1]);
+ snd_ctl_elem_value_set_id (sw[2], switch_id[2]);
+
+ while (1) {
+ if (snd_ctl_elem_read (h->driver->ctl_handle, sw[0])) {
+ jack_error ("cannot read control switch 0 ...");
+ }
+ hammerfall_check_sync (h, sw[0]);
+
+ if (snd_ctl_elem_read (h->driver->ctl_handle, sw[1])) {
+ jack_error ("cannot read control switch 0 ...");
+ }
+ hammerfall_check_sync (h, sw[1]);
+
+ if (snd_ctl_elem_read (h->driver->ctl_handle, sw[2])) {
+ jack_error ("cannot read control switch 0 ...");
+ }
+ hammerfall_check_sync (h, sw[2]);
+
+ if (nanosleep (&h->monitor_interval, 0)) {
+ break;
+ }
+ }
+
+ pthread_exit (0);
+}
+
+jack_hardware_t *
+jack_alsa_hammerfall_hw_new (alsa_driver_t *driver)
+
+{
+ jack_hardware_t *hw;
+ hammerfall_t *h;
+
+ hw = (jack_hardware_t *) malloc (sizeof (jack_hardware_t));
+
+ hw->capabilities = Cap_HardwareMonitoring|Cap_AutoSync|Cap_WordClock|Cap_ClockMaster|Cap_ClockLockReporting;
+ hw->input_monitor_mask = 0;
+ hw->private = 0;
+
+ hw->set_input_monitor_mask = hammerfall_set_input_monitor_mask;
+ hw->change_sample_clock = hammerfall_change_sample_clock;
+ hw->release = hammerfall_release;
+
+ h = (hammerfall_t *) malloc (sizeof (hammerfall_t));
+
+ h->lock_status[0] = FALSE;
+ h->sync_status[0] = FALSE;
+ h->lock_status[1] = FALSE;
+ h->sync_status[1] = FALSE;
+ h->lock_status[2] = FALSE;
+ h->sync_status[2] = FALSE;
+ h->said_that_spdif_is_fine = FALSE;
+ h->driver = driver;
+
+ h->monitor_interval.tv_sec = 1;
+ h->monitor_interval.tv_nsec = 0;
+
+ hw->private = h;
+
+ if (pthread_create (&h->monitor_thread, 0, hammerfall_monitor_controls, hw)) {
+ jack_error ("ALSA/Hammerfall: cannot create sync monitor thread");
+ }
+
+ return hw;
+}
diff --git a/jack.pc.in b/jack.pc.in
new file mode 100644
index 0000000..f3a624e
--- /dev/null
+++ b/jack.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: jack
+Description: the Jack Audio Connection Kit: a low-latency synchronous callback-based media server
+Requires: glib >= 1.0.0
+Version: @JACK_VERSION@
+Libs: -L${libdir} -ljack
+Cflags: -I${includedir}
diff --git a/jack/Makefile.am b/jack/Makefile.am
new file mode 100644
index 0000000..d2acbbf
--- /dev/null
+++ b/jack/Makefile.am
@@ -0,0 +1,4 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+libjackincludedir = $(includedir)/jack
+libjackinclude_HEADERS = $(wildcard *.h)
diff --git a/jack/alsa_driver.h b/jack/alsa_driver.h
new file mode 100644
index 0000000..a8bb759
--- /dev/null
+++ b/jack/alsa_driver.h
@@ -0,0 +1,159 @@
+/*
+ Copyright (C) 2001 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __jack_alsa_driver_h__
+#define __jack_alsa_driver_h__
+
+#include <sys/asoundlib.h>
+
+#include <jack/types.h>
+#include <jack/hardware.h>
+#include <jack/driver.h>
+#include <jack/memops.h>
+#include <jack/jack.h>
+
+typedef void (*ReadCopyFunction) (sample_t *dst, char *src,
+ unsigned long src_bytes,
+ unsigned long src_skip_bytes);
+typedef void (*WriteCopyFunction) (char *dst, sample_t *src,
+ unsigned long src_bytes,
+ unsigned long dst_skip_bytes,
+ gain_t gain);
+typedef void (*CopyCopyFunction) (char *dst, char *src,
+ unsigned long src_bytes,
+ unsigned long dst_skip_bytes,
+ unsigned long src_skip_byte);
+
+typedef struct {
+
+ JACK_DRIVER_DECL
+
+ char **playback_addr;
+ char **capture_addr;
+ const snd_pcm_channel_area_t *capture_areas;
+ const snd_pcm_channel_area_t *playback_areas;
+ unsigned long time_at_interrupt;
+ struct pollfd pfd;
+ unsigned long interleave_unit;
+ unsigned long capture_interleave_skip;
+ unsigned long playback_interleave_skip;
+ unsigned long max_nchannels;
+ unsigned long user_nchannels;
+ unsigned long playback_nchannels;
+ unsigned long capture_nchannels;
+ unsigned long sample_bytes;
+ unsigned long *silent;
+ char *alsa_name;
+ char *alsa_driver;
+ snd_pcm_uframes_t buffer_frames;
+ unsigned long bytes_per_cycle; /* per channel */
+ unsigned long channels_not_done;
+ unsigned long channel_done_bits;
+ snd_pcm_format_t sample_format;
+ float max_sample_val;
+ unsigned long nfragments;
+ int max_level;
+ int min_level;
+ unsigned long last_mask;
+ unsigned long silence_pending;
+ snd_ctl_t *ctl_handle;
+ snd_pcm_t *playback_handle;
+ snd_pcm_t *capture_handle;
+ unsigned long *input_monitor_requests;
+ snd_pcm_hw_params_t *playback_hw_params;
+ snd_pcm_sw_params_t *playback_sw_params;
+ snd_pcm_hw_params_t *capture_hw_params;
+ snd_pcm_sw_params_t *capture_sw_params;
+ jack_hardware_t *hw;
+ ClockSyncStatus *clock_sync_data;
+ jack_client_t *client;
+ GSList *capture_ports;
+ GSList *playback_ports;
+
+ char capture_and_playback_not_synced : 1;
+ char interleaved : 1;
+
+ ReadCopyFunction read_via_copy;
+ WriteCopyFunction write_via_copy;
+ CopyCopyFunction channel_copy;
+
+} alsa_driver_t;
+
+static __inline__ void alsa_driver_mark_channel_done (alsa_driver_t *driver, channel_t chn) {
+ driver->channels_not_done &= ~(1<<chn);
+ driver->silent[chn] = 0;
+}
+
+static __inline__ void alsa_driver_silence_on_channel (alsa_driver_t *driver, channel_t chn, nframes_t nframes) {
+ if (driver->interleaved) {
+ memset_interleave
+ (driver->playback_addr[chn],
+ 0, nframes * driver->sample_bytes,
+ driver->interleave_unit,
+ driver->playback_interleave_skip);
+ } else {
+ memset (driver->playback_addr[chn], 0, nframes * driver->sample_bytes);
+ }
+ alsa_driver_mark_channel_done (driver,chn);
+}
+
+static __inline__ void alsa_driver_read_from_channel (alsa_driver_t *driver,
+ channel_t channel, sample_t *buf,
+ nframes_t nsamples,
+ unsigned long offset)
+{
+ driver->read_via_copy (buf,
+ driver->capture_addr[channel] + offset,
+ nsamples,
+ driver->capture_interleave_skip);
+}
+
+static __inline__ void alsa_driver_write_to_channel (alsa_driver_t *driver,
+ channel_t channel,
+ sample_t *buf,
+ nframes_t nsamples,
+ unsigned long offset,
+ gain_t gain)
+{
+ driver->write_via_copy (driver->playback_addr[channel] + offset,
+ buf,
+ nsamples,
+ driver->playback_interleave_skip,
+ gain);
+ alsa_driver_mark_channel_done (driver, channel);
+}
+
+static __inline__ void alsa_driver_copy_channel (alsa_driver_t *driver,
+ channel_t input_channel,
+ channel_t output_channel,
+ nframes_t nsamples) {
+
+ driver->channel_copy (driver->playback_addr[output_channel],
+ driver->capture_addr[input_channel],
+ nsamples * driver->sample_bytes,
+ driver->playback_interleave_skip,
+ driver->capture_interleave_skip);
+ alsa_driver_mark_channel_done (driver, output_channel);
+}
+
+void alsa_driver_set_clock_sync_status (alsa_driver_t *driver, channel_t chn, ClockSyncStatus status);
+
+
+#endif /* __jack_alsa_driver_h__ */
diff --git a/jack/driver.h b/jack/driver.h
new file mode 100644
index 0000000..2294d03
--- /dev/null
+++ b/jack/driver.h
@@ -0,0 +1,136 @@
+/*
+ Copyright (C) 2001 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __jack_driver_h__
+#define __jack_driver_h__
+
+#include <glib.h>
+#include <ltdl.h>
+#include <pthread.h>
+#include <jack/types.h>
+
+typedef void (*ClockSyncListenerFunction)(channel_t,ClockSyncStatus,void*);
+
+typedef struct {
+ unsigned long id;
+ ClockSyncListenerFunction function;
+ void *arg;
+} ClockSyncListener;
+
+typedef void (*InputMonitorListenerFunction)(channel_t,int,void*);
+
+typedef struct {
+ unsigned long id;
+ InputMonitorListenerFunction function;
+ void *arg;
+} InputMonitorListener;
+
+struct _jack_engine;
+struct _jack_driver;
+
+typedef int (*JackDriverAttachFunction)(struct _jack_driver *, struct _jack_engine *);
+typedef int (*JackDriverDetachFunction)(struct _jack_driver *, struct _jack_engine *);
+typedef int (*JackDriverWaitFunction)(struct _jack_driver *);
+typedef nframes_t (*JackDriverFramesSinceCycleStartFunction)(struct _jack_driver *);
+typedef ClockSyncStatus (*JackDriverClockSyncStatusFunction)(struct _jack_driver *, channel_t);
+typedef int (*JackDriverAudioStopFunction)(struct _jack_driver *);
+typedef int (*JackDriverAudioStartFunction)(struct _jack_driver *);
+typedef void (*JackDriverSetHwMonitoringFunction) (struct _jack_driver *, int);
+typedef int (*JackDriverChangeSampleClockFunction) (struct _jack_driver *, SampleClockMode mode);
+typedef int (*JackDriverResetParametersFunction) (struct _jack_driver *, nframes_t frames_per_cycle, nframes_t rate);
+typedef void (*JackDriverMarkChannelSilentFunction) (struct _jack_driver *, unsigned long chn);
+typedef void (*JackDriverRequestMonitorInputFunction) (struct _jack_driver *, unsigned long chn, int yn);
+typedef void (*JackDriverRequestAllMonitorInputFunction) (struct _jack_driver *, int yn);
+typedef int (*JackDriverMonitoringInputFunction)(struct _jack_driver *,channel_t chn);
+
+#define JACK_DRIVER_DECL \
+ nframes_t frame_rate; \
+ nframes_t frames_per_cycle; \
+ nframes_t capture_frame_latency; \
+ nframes_t playback_frame_latency; \
+ unsigned long period_interval; \
+ SampleClockMode clock_mode; \
+ unsigned long input_monitor_mask; \
+ struct _jack_engine *engine; \
+ GSList *clock_sync_listeners; \
+ pthread_mutex_t clock_sync_lock; \
+ unsigned long next_clock_sync_listener_id; \
+ GSList *input_monitor_listeners; \
+ pthread_mutex_t input_monitor_lock; \
+ unsigned long next_input_monitor_listener_id; \
+ char hw_monitoring : 1; \
+ char all_monitor_in : 1; \
+ char has_clock_sync_reporting : 1; \
+ char has_hw_monitoring : 1; \
+ lt_dlhandle handle; \
+ void (*finish)(struct _jack_driver *);\
+\
+ /* These are the "core" driver functions */ \
+\
+ JackDriverAttachFunction attach; \
+ JackDriverDetachFunction detach; \
+ JackDriverWaitFunction wait; \
+\
+ /* These support the "audio" side of a driver, which arguably \
+ could/should be provided by a different kind of object. \
+ */ \
+\
+ JackDriverFramesSinceCycleStartFunction frames_since_cycle_start; \
+ JackDriverClockSyncStatusFunction clock_sync_status; \
+ JackDriverAudioStopFunction audio_stop; \
+ JackDriverAudioStartFunction audio_start; \
+ JackDriverSetHwMonitoringFunction set_hw_monitoring; \
+ JackDriverChangeSampleClockFunction change_sample_clock; \
+ JackDriverResetParametersFunction reset_parameters; \
+ JackDriverMarkChannelSilentFunction mark_channel_silent; \
+ JackDriverRequestMonitorInputFunction request_monitor_input; \
+ JackDriverRequestAllMonitorInputFunction request_all_monitor_input; \
+ JackDriverMonitoringInputFunction monitoring_input;
+
+typedef struct _jack_driver {
+
+ JACK_DRIVER_DECL
+
+} jack_driver_t;
+
+void jack_driver_init (jack_driver_t *);
+void jack_driver_release (jack_driver_t *);
+
+jack_driver_t *jack_driver_load (const char *path_to_so, ...);
+void jack_driver_unload (jack_driver_t *);
+
+int jack_driver_listen_for_clock_sync_status (jack_driver_t *, ClockSyncListenerFunction, void *arg);
+int jack_driver_stop_listen_for_clock_sync_status (jack_driver_t *, int);
+
+int jack_driver_listen_for_input_monitor_status (jack_driver_t *, InputMonitorListenerFunction, void *arg);
+int jack_driver_stop_listen_for_input_monitor_status (jack_driver_t *, int);
+
+void jack_driver_clock_sync_notify (jack_driver_t *, channel_t chn, ClockSyncStatus);
+void jack_driver_input_monitor_notify (jack_driver_t *, channel_t chn, int);
+
+#endif /* __jack_driver_h__ */
+
+
+
+
+
+
+
+
diff --git a/jack/engine.h b/jack/engine.h
new file mode 100644
index 0000000..332b650
--- /dev/null
+++ b/jack/engine.h
@@ -0,0 +1,87 @@
+/*
+ Copyright (C) 2001 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __jack_engine_h__
+#define __jack_engine_h__
+
+#include <jack/jack.h>
+#include <jack/internal.h>
+
+struct _jack_driver;
+struct _jack_client_internal;
+struct _jack_port_internal;
+
+struct _jack_engine {
+ jack_control_t *control;
+ struct _jack_driver *driver;
+ int (*process)(struct _jack_engine *, nframes_t frames);
+ int (*set_buffer_size)(struct _jack_engine *, nframes_t frames);
+ int (*set_sample_rate)(struct _jack_engine *, nframes_t frames);
+ pthread_mutex_t graph_lock;
+ pthread_mutex_t buffer_lock;
+ pthread_mutex_t port_lock;
+ int period_msecs;
+ int port_max;
+ int control_shm_id;
+ key_t control_key;
+ key_t port_segment_key; /* XXX fix me */
+ void *port_segment_address; /* XXX fix me */
+ pthread_t audio_thread;
+ pthread_t server_thread;
+
+ /* these lists are protected by `buffer_lock' */
+
+ GSList *port_segments;
+ GSList *port_buffer_freelist;
+
+ /* these lists are all protected by `graph_lock' */
+
+ GSList *clients;
+ GSList *clients_waiting;
+ GSList *connections;
+
+ struct _jack_port_internal *internal_ports;
+
+ GSList *port_types; /* holds ptrs to jack_port_type_info_t */
+
+ int fds[2];
+ jack_client_id_t next_client_id;
+ size_t pfd_size;
+ size_t pfd_max;
+ struct pollfd *pfd;
+ struct _jack_client_internal *timebase_client;
+ jack_port_buffer_info_t *silent_buffer;
+ char fifo_prefix[FIFO_NAME_SIZE+1];
+ int *fifo;
+ unsigned long fifo_size;
+ unsigned long external_client_cnt;
+ int rtpriority;
+ int getthehelloutathere;
+};
+
+/* public functions */
+
+jack_engine_t *jack_engine_new (int real_time, int real_time_priority);
+int jack_engine_delete (jack_engine_t *);
+int jack_run (jack_engine_t *engine);
+int jack_wait (jack_engine_t *engine);
+int jack_use_driver (jack_engine_t *, struct _jack_driver *);
+
+#endif /* __jack_engine_h__ */
diff --git a/jack/error.h b/jack/error.h
new file mode 100644
index 0000000..bdc5ce5
--- /dev/null
+++ b/jack/error.h
@@ -0,0 +1,8 @@
+#ifndef __jack_error_h__
+#define __jack_error_h__
+
+extern void (*jack_error)(const char *fmt, ...);
+void jack_set_error_function (void (*func)(const char *, ...));
+
+
+#endif /* __jack_error_h__ */
diff --git a/jack/generic.h b/jack/generic.h
new file mode 100644
index 0000000..00a8caa
--- /dev/null
+++ b/jack/generic.h
@@ -0,0 +1,7 @@
+#ifndef __jack_generic_h__
+#define __jack_generic_h__
+
+jack_hardware_t *
+jack_alsa_generic_hw_new (alsa_driver_t *driver);
+
+#endif /* __jack_generic_h__*/
diff --git a/jack/hammerfall.h b/jack/hammerfall.h
new file mode 100644
index 0000000..d183f47
--- /dev/null
+++ b/jack/hammerfall.h
@@ -0,0 +1,17 @@
+#ifndef __jack_hammerfall_h__
+#define __jack_hammerfall_h__
+
+#include <sys/time.h>
+
+typedef struct {
+ int lock_status[3];
+ int sync_status[3];
+ int said_that_spdif_is_fine;
+ pthread_t monitor_thread;
+ alsa_driver_t *driver;
+ struct timespec monitor_interval;
+} hammerfall_t;
+
+jack_hardware_t *jack_alsa_hammerfall_hw_new (alsa_driver_t *driver);
+
+#endif /* __jack_hammerfall_h__*/
diff --git a/jack/hardware.h b/jack/hardware.h
new file mode 100644
index 0000000..1e35ee2
--- /dev/null
+++ b/jack/hardware.h
@@ -0,0 +1,27 @@
+#ifndef __jack_hardware_h__
+#define __jack_hardware_h__
+
+#include <jack/types.h>
+
+struct _jack_hardware;
+
+typedef void (*JackHardwareReleaseFunction)(struct _jack_hardware *);
+typedef int (*JackHardwareSetInputMonitorMaskFunction)(struct _jack_hardware *, unsigned long);
+typedef int (*JackHardwareChangeSampleClockFunction)(struct _jack_hardware *, SampleClockMode);
+
+typedef struct _jack_hardware {
+
+ unsigned long capabilities;
+ unsigned long input_monitor_mask;
+
+ JackHardwareChangeSampleClockFunction change_sample_clock;
+ JackHardwareSetInputMonitorMaskFunction set_input_monitor_mask;
+ JackHardwareReleaseFunction release;
+
+ void *private;
+
+} jack_hardware_t;
+
+jack_hardware_t * jack_hardware_new ();
+
+#endif /* __jack_hardware_h__ */
diff --git a/jack/internal.h b/jack/internal.h
new file mode 100644
index 0000000..fb09da0
--- /dev/null
+++ b/jack/internal.h
@@ -0,0 +1,215 @@
+/*
+ Copyright (C) 2001 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __jack_internal_h__
+#define __jack_internal_h__
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <dlfcn.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <pthread.h>
+#include <glib.h>
+
+#include <jack/jack.h>
+#include <jack/types.h>
+#include <jack/port.h>
+
+#define FIFO_NAME_SIZE 32
+
+typedef void * dlhandle;
+
+typedef struct {
+ int shm_key;
+ size_t offset;
+} jack_port_buffer_info_t;
+
+typedef struct {
+ int shm_key;
+ char *address;
+} jack_port_segment_info_t;
+
+typedef struct {
+
+ nframes_t frame_time;
+ pid_t engine_pid;
+ unsigned long sample_rate;
+ unsigned long buffer_size;
+ char real_time;
+ int client_priority;
+ unsigned long port_max;
+ jack_port_shared_t ports[0];
+
+} jack_control_t;
+
+typedef enum {
+ BufferSizeChange,
+ SampleRateChange,
+ NewPortBufferSegment,
+ PortConnected,
+ PortDisconnected,
+ GraphReordered,
+ PortRegistered,
+ PortUnregistered,
+ PortMonitor,
+ PortUnMonitor,
+} AudioEngineEventType;
+
+typedef struct {
+ AudioEngineEventType type;
+ union {
+ unsigned long n;
+ jack_port_id_t port_id;
+ jack_port_id_t self_id;
+ } x;
+ union {
+ unsigned long n;
+ jack_port_id_t other_id;
+ } y;
+} jack_event_t;
+
+typedef enum {
+ ClientDynamic, /* connect request just names .so */
+ ClientDriver, /* code is loaded along with driver */
+ ClientOutOfProcess /* client is in another process */
+} ClientType;
+
+#define JACK_CLIENT_STATE_NOT_TRIGGERED 19
+#define JACK_CLIENT_STATE_TRIGGERED 23
+#define JACK_CLIENT_STATE_FINISHED 24
+
+typedef volatile struct {
+
+ volatile nframes_t frame_time; /* w: client r: engine (if client is timebase) */
+ volatile int id; /* w: engine r: engine and client */
+ volatile nframes_t nframes; /* w: engine r: client */
+ volatile char state; /* w: engine and client r: engine JACK_CLIENT_STATE_... */
+ volatile char name[JACK_CLIENT_NAME_SIZE+1];
+ volatile ClientType type; /* w: engine r: engine and client */
+ volatile char active : 1; /* w: engine r: engine and client */
+ volatile char dead : 1; /* r/w: engine */
+
+ /* callbacks */
+
+ JackProcessCallback process;
+ void *process_arg;
+ JackBufferSizeCallback bufsize;
+ void *bufsize_arg;
+ JackSampleRateCallback srate;
+ void *srate_arg;
+ JackPortRegistrationCallback port_register;
+ void *port_register_arg;
+ JackPortMonitorCallback port_monitor;
+ void *port_monitor_arg;
+
+ /* for engine use only */
+
+ void *private_internal_client;
+
+} jack_client_control_t;
+
+typedef struct {
+
+ ClientType type;
+
+ char name[JACK_CLIENT_NAME_SIZE+1];
+ char object_path[PATH_MAX+1];
+
+} jack_client_connect_request_t;
+
+typedef struct {
+
+ int status;
+
+ int client_key;
+ int control_key;
+
+ char fifo_prefix[FIFO_NAME_SIZE+1];
+
+ int realtime;
+ int realtime_priority;
+
+ /* these two are valid only if the connect request
+ was for type == ClientDriver.
+ */
+
+ jack_client_control_t *client_control;
+ jack_control_t *engine_control;
+
+ /* XXX need to be able to use more than one port segment key */
+
+ key_t port_segment_key;
+ void *port_segment_address;
+
+} jack_client_connect_result_t;
+
+typedef struct {
+ jack_client_id_t client_id;
+} jack_client_connect_ack_request_t;
+
+typedef struct {
+ char status;
+} jack_client_connect_ack_result_t;
+
+typedef enum {
+ RegisterPort = 1,
+ UnRegisterPort = 2,
+ ConnectPorts = 3,
+ DisconnectPorts = 4,
+ SetTimeBaseClient = 5,
+ DropClient = 6,
+ ActivateClient = 7,
+ DeactivateClient = 8,
+ RequestPortMonitor = 9,
+ RequestPortUnMonitor = 10
+} AudioEngineRequestType;
+
+typedef struct {
+
+ AudioEngineRequestType type;
+ union {
+ struct {
+ char name[JACK_PORT_NAME_SIZE+1];
+ char type[JACK_PORT_TYPE_SIZE+1];
+ unsigned long flags;
+ unsigned long buffer_size;
+ jack_port_id_t port_id;
+ jack_client_id_t client_id;
+ } port_info;
+ struct {
+ char source_port[JACK_PORT_NAME_SIZE+1];
+ char destination_port[JACK_PORT_NAME_SIZE+1];
+ } connect;
+ jack_client_id_t client_id;
+ } x;
+ int status;
+} jack_request_t;
+
+extern int jack_client_handle_port_connection (jack_client_t *client, jack_event_t *event);
+
+jack_client_t *jack_driver_become_client (const char *client_name);
+
+#endif /* __jack_internal_h__ */
+
+
+
+
diff --git a/jack/jack.h b/jack/jack.h
new file mode 100644
index 0000000..439f97b
--- /dev/null
+++ b/jack/jack.h
@@ -0,0 +1,228 @@
+
+/*
+ Copyright (C) 2001 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __jack_h__
+#define __jack_h__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <glib.h>
+#include <jack/types.h>
+#include <jack/port.h>
+#include <jack/error.h>
+
+jack_client_t *jack_client_new (const char *client_name);
+int jack_client_close (jack_client_t *client);
+
+int jack_set_process_callback (jack_client_t *, JackProcessCallback, void *arg);
+int jack_set_buffer_size_callback (jack_client_t *, JackBufferSizeCallback, void *arg);
+int jack_set_sample_rate_callback (jack_client_t *, JackSampleRateCallback, void *arg);
+int jack_set_port_registration_callback (jack_client_t *, JackPortRegistrationCallback, void *);
+int jack_set_port_monitor_callback (jack_client_t *, JackPortMonitorCallback, void *);
+
+int jack_get_process_start_fd (jack_client_t *);
+int jack_get_process_done_fd (jack_client_t *);
+
+int jack_activate (jack_client_t *client);
+int jack_deactivate (jack_client_t *client);
+
+/* this creates a new port for the client.
+
+ a port is an object used for moving data in or out of the client.
+ the data may be of any type. ports may be connected to each other
+ in various ways.
+
+ a port has a short name, which may be any non-NULL and non-zero
+ length string, and is passed as the first argument. a port's full
+ name is the name of the client concatenated with a colon (:) and
+ then its short name.
+
+ a port has a type, which may be any non-NULL and non-zero length
+ string, and is passed as the second argument. for types that are
+ not built into the jack API (currently just
+ JACK_DEFAULT_AUDIO_TYPE) the client MUST supply a non-zero size
+ for the buffer as the fourth argument. for builtin types, the
+ fourth argument is ignored.
+
+ a port has a set of flags, enumerated below and passed as the third
+ argument in the form of a bitmask created by AND-ing together the
+ desired flags. the flags "IsInput" and "IsOutput" are mutually
+ exclusive and it is an error to use them both.
+
+*/
+
+enum JackPortFlags {
+
+ JackPortIsInput = 0x1,
+ JackPortIsOutput = 0x2,
+ JackPortIsPhysical = 0x4, /* refers to a physical connection */
+
+ /* if JackPortCanMonitor is set, then a call to
+ jack_port_request_monitor() makes sense.
+
+ Precisely what this means is dependent on the client. A typical
+ result of it being called with TRUE as the second argument is
+ that data that would be available from an output port (with
+ JackPortIsPhysical set) is sent to a physical output connector
+ as well, so that it can be heard/seen/whatever.
+
+ Clients that do not control physical interfaces
+ should never create ports with this bit set.
+
+ Clients that do set this bit must have provided a
+ port_monitor callback before creating any ports with
+ this bit set.
+ */
+
+ JackPortCanMonitor = 0x8
+};
+
+#define JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio"
+
+jack_port_t *
+jack_port_register (jack_client_t *,
+ const char *port_name,
+ const char *port_type,
+ unsigned long flags,
+ unsigned long buffer_size);
+
+/* this removes the port from the client */
+
+int jack_port_unregister (jack_client_t *, jack_port_t *);
+
+/* This returns a pointer to the memory area associated with the
+ specified port. It can only be called from within the client's
+ "process" callback. For an output port, it will be a memory area
+ that can be written to; for an input port, it will be an area
+ containing the data from the port's connection(s), or
+ zero-filled. if there are multiple inbound connections, the data
+ will be mixed appropriately.
+*/
+
+void *jack_port_get_buffer (jack_port_t *, nframes_t);
+
+/* these two functions establish and disestablish a connection
+ between two ports. when a connection exists, data written
+ to the source port will be available to be read at the destination
+ port.
+
+ the types of both ports must be identical to establish a connection.
+
+ the flags of the source port must include PortIsOutput.
+ the flags of the destination port must include PortIsInput.
+*/
+
+int jack_port_connect (jack_client_t *,
+ const char *source_port,
+ const char *destination_port);
+
+int jack_port_disconnect (jack_client_t *,
+ const char *source_port,
+ const char *destination_port);
+
+/* A client may call this on a pair of its own ports to
+ semi-permanently wire them together. This means that
+ a client that wants to direct-wire an input port to
+ an output port can call this and then no longer
+ have to worry about moving data between them. Any data
+ arriving at the input port will appear automatically
+ at the output port.
+
+ The `destination' port must be an output port. The `source'
+ port must be an input port. Both ports must belong to
+ the same client. You cannot use this to tie ports between
+ clients. Thats what a connection is for.
+*/
+
+int jack_port_tie (jack_port_t *dst, jack_port_t *src);
+
+/* This undoes the effect of jack_port_tie(). The port
+ should be same as the `destination' port passed to
+ jack_port_tie().
+*/
+
+int jack_port_untie (jack_port_t *port);
+
+/* a client may call this function to prevent other objects
+ from changing the connection status of the named port.
+*/
+
+int jack_port_lock (jack_client_t *, jack_port_t *);
+int jack_port_unlock (jack_client_t *, jack_port_t *);
+
+
+/* if JackPortCanMonitor is set for a port, then this function will
+ turn on/off input monitoring for the port. if JackPortCanMonitor
+ is not set, then this function will do nothing.
+*/
+
+int jack_port_request_monitor (jack_client_t *, const char *port_name, int onoff);
+
+/* this returns the sample rate of the jack */
+
+unsigned long jack_get_sample_rate (jack_client_t *);
+
+/* this returns the current maximum size that will
+ ever be passed to the "process" callback. it should only
+ be used *before* the client has been activated.
+*/
+
+nframes_t jack_get_buffer_size (jack_client_t *);
+
+/* This function returns a list of ports that match the specified
+ arguments.
+
+ port_name_pattern: a regular expression used to select ports by name.
+ if NULL or of zero length, no selection based on
+ name will be carried out.
+
+ type_name_pattern: a regular expression used to select ports by type.
+ if NULL or of zero length, no selection based on
+ type will be carried out.
+
+ flags: a value used to select ports by their flags. if
+ zero, no selection based on flags will be carried out.
+*/
+
+GSList *jack_get_ports (jack_client_t *,
+ const char *port_name_pattern,
+ const char *type_name_pattern,
+ unsigned long flags);
+
+/* If a client is told to become the timebase for the entire system,
+ it calls this function. If it returns zero, then the client has
+ the responsibility to call jack_update_time() at the end
+ of its process() callback. Whatever time it provides (in frames
+ since its reference zero time) becomes the current timebase
+ for the entire system.
+*/
+
+int jack_engine_takeover_timebase (jack_client_t *);
+void jack_update_time (jack_client_t *, nframes_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __jack_h__ */
+
diff --git a/jack/memops.h b/jack/memops.h
new file mode 100644
index 0000000..9951eb8
--- /dev/null
+++ b/jack/memops.h
@@ -0,0 +1,89 @@
+/*
+ Copyright (C) 1999-2000 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __jack_memops_h__
+#define __jack_memops_h__
+
+#include <jack/types.h>
+
+void sample_move_d32u24_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip, gain_t);
+void sample_move_d16_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip, gain_t);
+
+void sample_move_dS_s32u24 (sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip);
+void sample_move_dS_s16 (sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip);
+
+void sample_merge_d16_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip, gain_t);
+void sample_merge_d32u24_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip, gain_t);
+
+static __inline__ void
+sample_merge (sample_t *dst, sample_t *src, unsigned long cnt, gain_t gain)
+
+{
+ if (gain == 1.0) {
+ while (cnt--) {
+ *dst += *src;
+ dst++;
+ src++;
+ }
+ } else {
+ while (cnt--) {
+ *dst += (*src * gain);
+ dst++;
+ src++;
+ }
+ }
+}
+
+static __inline__ void
+sample_memcpy (sample_t *dst, sample_t *src, unsigned long cnt, gain_t gain)
+
+{
+ if (gain == 1.0) {
+ memcpy (dst, src, cnt * sizeof (sample_t));
+ } else {
+ while (cnt--) {
+ *dst = (*src * gain);
+ dst++;
+ src++;
+ }
+ }
+}
+
+void memset_interleave (char *dst, char val, unsigned long bytes, unsigned long unit_bytes, unsigned long skip_bytes);
+void memcpy_fake (char *dst, char *src, unsigned long src_bytes, unsigned long foo, unsigned long bar);
+
+void memcpy_interleave_d16_s16 (char *dst, char *src, unsigned long src_bytes, unsigned long dst_skip_bytes, unsigned long src_skip_bytes);
+void memcpy_interleave_d32_s32 (char *dst, char *src, unsigned long src_bytes, unsigned long dst_skip_bytes, unsigned long src_skip_bytes);
+
+void merge_memcpy_interleave_d16_s16 (char *dst, char *src, unsigned long src_bytes, unsigned long dst_skip_bytes, unsigned long src_skip_bytes);
+void merge_memcpy_interleave_d32_s32 (char *dst, char *src, unsigned long src_bytes, unsigned long dst_skip_bytes, unsigned long src_skip_bytes);
+
+void merge_memcpy_d16_s16 (char *dst, char *src, unsigned long src_bytes, unsigned long foo, unsigned long bar);
+void merge_memcpy_d32_s32 (char *dst, char *src, unsigned long src_bytes, unsigned long foo, unsigned long bar);
+
+#endif /* __jack_memops_h__ */
+
+
+
+
+
+
+
+
diff --git a/jack/pool.h b/jack/pool.h
new file mode 100644
index 0000000..d1d80d6
--- /dev/null
+++ b/jack/pool.h
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 2001 Paul Davis
+x
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __jack_pool_h__
+#define __jack_pool_h__
+
+#include <sys/types.h>
+
+void * jack_pool_alloc (size_t bytes);
+void jack_pool_release (void *);
+
+#endif /* __jack_pool_h__ */
diff --git a/jack/port.h b/jack/port.h
new file mode 100644
index 0000000..6eed4b5
--- /dev/null
+++ b/jack/port.h
@@ -0,0 +1,89 @@
+/*
+ Copyright (C) 2001 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __jack_port_h__
+#define __jack_port_h__
+
+#include <pthread.h>
+#include <jack/types.h>
+
+typedef struct _jack_port_type_info {
+ const char *type_name; /* what do you think ? */
+
+ void (*mixdown)(jack_port_t *, nframes_t); /* function to mixdown multiple inputs to a buffer. can be
+ NULL, indicating that multiple input connections
+ are not legal for this data type.
+ */
+
+ long buffer_scale_factor; /* If == 1, then a buffer to handle nframes worth of
+ data is sizeof(sample_t) * nframes bytes large.
+
+ If anything other than 1, the buffer allocated
+ for input mixing will be this value times
+ sizeof (sample_t) * nframes bytes in size.
+
+ Obviously, for non-audio data types, it may have
+ a different value.
+
+ if < 0, then the value should be ignored, and
+ port->shared->buffer_size should be used.
+ */
+} jack_port_type_info_t;
+
+/* This is the data structure allocated in shared memory
+ by the engine.
+*/
+
+typedef struct _jack_port_shared {
+ void *buffer;
+ unsigned long flags;
+ unsigned long buffer_size;
+ jack_port_id_t id;
+ char name[JACK_CLIENT_NAME_SIZE+JACK_PORT_NAME_SIZE+2];
+ jack_port_type_info_t type_info;
+ jack_client_id_t client_id;
+
+ char in_use : 1;
+ char locked : 1;
+} jack_port_shared_t;
+
+/* This is the data structure allocated by the client
+ in local memory. The `shared' pointer points
+ the the corresponding structure in shared memory.
+*/
+
+struct _jack_port {
+ struct _jack_port_shared *shared;
+ GSList *connections;
+ struct _jack_port *tied;
+ void *own_buffer;
+};
+
+/* this is the structure allocated by the engine in local
+ memory.
+*/
+
+typedef struct _jack_port_internal {
+ struct _jack_port_shared *shared;
+ GSList *connections;
+} jack_port_internal_t;
+
+#endif /* __jack_port_h__ */
+
diff --git a/jack/types.h b/jack/types.h
new file mode 100644
index 0000000..c0f478e
--- /dev/null
+++ b/jack/types.h
@@ -0,0 +1,69 @@
+/*
+ Copyright (C) 2001 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __jack_engine_types_h__
+#define __jack_engine_types_h__
+
+typedef float sample_t;
+typedef unsigned long nframes_t;
+typedef long jack_port_id_t;
+typedef unsigned long jack_client_id_t;
+typedef float gain_t;
+typedef long channel_t;
+
+typedef int (*JackProcessCallback)(nframes_t, void *);
+typedef int (*JackBufferSizeCallback)(nframes_t, void *);
+typedef int (*JackSampleRateCallback)(nframes_t, void *);
+typedef void (*JackPortRegistrationCallback)(jack_port_id_t,int,void*);
+typedef void (*JackPortMonitorCallback)(jack_port_id_t,int,void*);
+
+#define NoChannel -1
+#define NoPort -1
+
+typedef struct _jack_engine jack_engine_t;
+typedef struct _jack_port jack_port_t;
+typedef struct _jack_client jack_client_t;
+
+#define JACK_PORT_NAME_SIZE 32
+#define JACK_PORT_TYPE_SIZE 32
+#define JACK_CLIENT_NAME_SIZE 32
+
+typedef enum {
+ Cap_HardwareMonitoring = 0x1,
+ Cap_AutoSync = 0x2,
+ Cap_WordClock = 0x4,
+ Cap_ClockMaster = 0x8,
+ Cap_ClockLockReporting = 0x10
+} Capabilities;
+
+typedef enum {
+ AutoSync,
+ WordClock,
+ ClockMaster
+} SampleClockMode;
+
+typedef enum {
+ Lock = 0x1,
+ NoLock = 0x2,
+ Sync = 0x4,
+ NoSync = 0x8
+} ClockSyncStatus;
+
+#endif /* __jack_engine_types_h__ */
diff --git a/jackd.c b/jackd.c
new file mode 100644
index 0000000..858c5ad
--- /dev/null
+++ b/jackd.c
@@ -0,0 +1,157 @@
+#include <stdio.h>
+#include <signal.h>
+#include <getopt.h>
+
+#include <jack/engine.h>
+#include <jack/internal.h>
+#include <jack/driver.h>
+
+static sigset_t signals;
+
+static jack_engine_t *engine;
+
+static void *
+signal_thread (void *arg)
+
+{
+ int sig;
+ int err;
+
+ pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+
+ err = sigwait (&signals, &sig);
+ fprintf (stderr, "exiting due to signal %d\n", sig);
+ jack_engine_delete (engine);
+ exit (err);
+
+ /*NOTREACHED*/
+ return 0;
+}
+
+int
+catch_signals (void)
+
+{
+ pthread_t thread_id;
+
+ sigemptyset (&signals);
+ sigaddset(&signals, SIGHUP);
+ sigaddset(&signals, SIGINT);
+ sigaddset(&signals, SIGQUIT);
+ sigaddset(&signals, SIGILL);
+ sigaddset(&signals, SIGTRAP);
+ sigaddset(&signals, SIGABRT);
+ sigaddset(&signals, SIGIOT);
+ sigaddset(&signals, SIGFPE);
+ sigaddset(&signals, SIGKILL);
+ sigaddset(&signals, SIGPIPE);
+ sigaddset(&signals, SIGTERM);
+ sigaddset(&signals, SIGCHLD);
+ sigaddset(&signals, SIGCONT);
+ sigaddset(&signals, SIGSTOP);
+ sigaddset(&signals, SIGTSTP);
+ sigaddset(&signals, SIGTTIN);
+ sigaddset(&signals, SIGTTOU);
+
+ /* all child threads will inherit this mask */
+
+ pthread_sigmask (SIG_BLOCK, &signals, 0);
+
+ /* start a thread to wait for signals */
+
+ if (pthread_create (&thread_id, 0, signal_thread, 0)) {
+ fprintf (stderr, "cannot create signal catching thread");
+ return -1;
+ }
+
+ pthread_detach (thread_id);
+
+ return 0;
+}
+
+static char *alsa_pcm_name = "default";
+static nframes_t frames_per_interrupt = 64;
+static nframes_t srate = 48000;
+static int realtime = 0;
+static int realtime_priority = 10;
+
+static void usage ()
+
+{
+ fprintf (stderr, "usage: engine [ -d ALSA PCM device ] [ -r sample-rate ] [ -p frames_per_interrupt ] [ -R [ -P priority ] ]\n");
+}
+
+int
+main (int argc, char *argv[])
+
+{
+ jack_driver_t *driver;
+ const char *options = "hd:r:p:RP:";
+ struct option long_options[] =
+ {
+ { "device", 1, 0, 'd' },
+ { "srate", 1, 0, 'r' },
+ { "frames-per-interrupt", 1, 0, 'p' },
+ { "help", 0, 0, 'h' },
+ { "realtime", 0, 0, 'R' },
+ { "realtime-priority", 1, 0, 'P' },
+ { 0, 0, 0, 0 }
+ };
+ int option_index;
+ int opt;
+
+ catch_signals ();
+
+ opterr = 0;
+ while ((opt = getopt_long (argc, argv, options, long_options, &option_index)) != EOF) {
+ switch (opt) {
+ case 'd':
+ alsa_pcm_name = optarg;
+ break;
+
+ case 'r':
+ srate = atoi (optarg);
+ break;
+
+ case 'p':
+ frames_per_interrupt = atoi (optarg);
+ break;
+
+ case 'R':
+ realtime = 1;
+ break;
+
+ case 'P':
+ realtime_priority = atoi (optarg);
+ break;
+
+ case 'h':
+ default:
+ fprintf (stderr, "unknown option character %c\n", opt);
+ usage();
+ return -1;
+ }
+ }
+
+ if ((engine = jack_engine_new (realtime, realtime_priority)) == 0) {
+ fprintf (stderr, "cannot create engine\n");
+ return 1;
+ }
+
+ if ((driver = jack_driver_load ("/usr/local/lib/jack_alsa.so", alsa_pcm_name, frames_per_interrupt, srate)) == 0) {
+ fprintf (stderr, "cannot load ALSA driver module\n");
+ return 1;
+ }
+
+ jack_use_driver (engine, driver);
+
+ printf ("start engine ...\n");
+
+ jack_run (engine);
+ jack_wait (engine);
+
+ return 0;
+}
+
+
+
diff --git a/memops.c b/memops.c
new file mode 100644
index 0000000..76804cb
--- /dev/null
+++ b/memops.c
@@ -0,0 +1,257 @@
+/*
+ Copyright (C) 2000 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <memory.h>
+#include <stdlib.h>
+
+#include <jack/memops.h>
+
+#define SAMPLE_MAX_24BIT 8388608.0f
+#define SAMPLE_MAX_16BIT 32767.0f
+
+/* XXX we could use rint(), but for now we'll accept whatever default
+ floating-point => int conversion the compiler provides.
+*/
+
+void sample_move_d32u24_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip, gain_t gain)
+
+{
+ /* ALERT: signed sign-extension portability !!! */
+
+ if (gain == 1.0) {
+ while (nsamples--) {
+ *((int *) dst) = ((int) (*src * SAMPLE_MAX_24BIT)) << 8;
+ dst += dst_skip;
+ src++;
+ }
+ } else {
+ while (nsamples--) {
+ *((int *) dst) = ((int) ((*src * gain) * SAMPLE_MAX_24BIT)) << 8;
+ dst += dst_skip;
+ src++;
+ }
+ }
+}
+
+void sample_move_dS_s32u24 (sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip)
+{
+ /* ALERT: signed sign-extension portability !!! */
+
+ while (nsamples--) {
+ *dst = (*((int *) src) >> 8) / SAMPLE_MAX_24BIT;
+ dst++;
+ src += src_skip;
+ }
+}
+
+void sample_move_d16_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip, gain_t gain)
+
+{
+ /* ALERT: signed sign-extension portability !!! */
+
+ /* XXX good to use x86 assembler here, since float->short
+ sucks that h/w.
+ */
+
+
+ if (gain == 1.0) {
+ while (nsamples--) {
+ *((short *) dst) = (short) (*src * SAMPLE_MAX_16BIT);
+ dst += dst_skip;
+ src++;
+ }
+ } else {
+ while (nsamples--) {
+ *((short *) dst) = (short) ((*src * gain) * SAMPLE_MAX_16BIT);
+ dst += dst_skip;
+ src++;
+ }
+ }
+}
+
+void sample_move_dS_s16 (sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip)
+
+{
+ /* ALERT: signed sign-extension portability !!! */
+ while (nsamples--) {
+ *dst = (*((short *) src)) / SAMPLE_MAX_16BIT;
+ dst++;
+ src += src_skip;
+ }
+}
+
+void sample_merge_d16_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip, gain_t gain)
+
+{
+ /* ALERT: signed sign-extension portability !!! */
+
+ if (gain == 1.0) {
+ while (nsamples--) {
+ *((short *) dst) += (short) (*src * SAMPLE_MAX_16BIT);
+ dst += dst_skip;
+ src++;
+ }
+ } else {
+ while (nsamples--) {
+ *((short *) dst) += (short) ((*src * gain) * SAMPLE_MAX_16BIT);
+ dst += dst_skip;
+ src++;
+ }
+ }
+}
+
+void sample_merge_d32u24_sS (char *dst, sample_t *src, unsigned long nsamples, unsigned long dst_skip, gain_t gain)
+
+{
+ /* ALERT: signed sign-extension portability !!! */
+
+ if (gain == 1.0) {
+ while (nsamples--) {
+ *((int *) dst) += (((int) (*src * SAMPLE_MAX_24BIT)) << 8);
+ dst += dst_skip;
+ src++;
+ }
+ } else {
+ while (nsamples--) {
+ *((int *) dst) += (((int) ((*src * gain) * SAMPLE_MAX_24BIT)) << 8);
+ dst += dst_skip;
+ src++;
+ }
+ }
+}
+
+void memset_interleave (char *dst, char val, unsigned long bytes,
+ unsigned long unit_bytes,
+ unsigned long skip_bytes)
+{
+ switch (unit_bytes) {
+ case 1:
+ while (bytes--) {
+ *dst = val;
+ dst += skip_bytes;
+ }
+ break;
+ case 2:
+ while (bytes) {
+ *((short *) dst) = (short) val;
+ dst += skip_bytes;
+ bytes -= 2;
+ }
+ break;
+ case 4:
+ while (bytes) {
+ *((int *) dst) = (int) val;
+ dst += skip_bytes;
+ bytes -= 4;
+ }
+ break;
+ }
+}
+
+/* COPY FUNCTIONS: used to move data from an input channel to an
+ output channel. Note that we assume that the skip distance
+ is the same for both channels. This is completely fine
+ unless the input and output were on different audio interfaces that
+ were interleaved differently. We don't try to handle that.
+*/
+
+void
+memcpy_fake (char *dst, char *src, unsigned long src_bytes, unsigned long foo, unsigned long bar)
+{
+ memcpy (dst, src, src_bytes);
+}
+
+void
+merge_memcpy_d16_s16 (char *dst, char *src, unsigned long src_bytes,
+ unsigned long dst_skip_bytes, unsigned long src_skip_bytes)
+{
+ while (src_bytes) {
+ *((short *) dst) += *((short *) src);
+ dst += 2;
+ src += 2;
+ src_bytes -= 2;
+ }
+}
+
+void
+merge_memcpy_d32_s32 (char *dst, char *src, unsigned long src_bytes,
+ unsigned long dst_skip_bytes, unsigned long src_skip_bytes)
+
+{
+ while (src_bytes) {
+ *((int *) dst) += *((int *) src);
+ dst += 4;
+ src += 4;
+ src_bytes -= 4;
+ }
+}
+
+void
+merge_memcpy_interleave_d16_s16 (char *dst, char *src, unsigned long src_bytes,
+ unsigned long dst_skip_bytes, unsigned long src_skip_bytes)
+
+{
+ while (src_bytes) {
+ *((short *) dst) += *((short *) src);
+ dst += dst_skip_bytes;
+ src += src_skip_bytes;
+ src_bytes -= 2;
+ }
+}
+
+void
+merge_memcpy_interleave_d32_s32 (char *dst, char *src, unsigned long src_bytes,
+ unsigned long dst_skip_bytes, unsigned long src_skip_bytes)
+{
+ while (src_bytes) {
+ *((int *) dst) += *((int *) src);
+ dst += dst_skip_bytes;
+ src += src_skip_bytes;
+ src_bytes -= 4;
+ }
+}
+
+void
+memcpy_interleave_d16_s16 (char *dst, char *src, unsigned long src_bytes,
+ unsigned long dst_skip_bytes, unsigned long src_skip_bytes)
+{
+ while (src_bytes) {
+ *((short *) dst) = *((short *) src);
+ dst += dst_skip_bytes;
+ src += src_skip_bytes;
+ src_bytes -= 2;
+ }
+}
+
+void
+memcpy_interleave_d32_s32 (char *dst, char *src, unsigned long src_bytes,
+ unsigned long dst_skip_bytes, unsigned long src_skip_bytes)
+
+{
+ while (src_bytes) {
+ *((int *) dst) = *((int *) src);
+ dst += dst_skip_bytes;
+ src += src_skip_bytes;
+ src_bytes -= 4;
+ }
+}
diff --git a/monitor_client.c b/monitor_client.c
new file mode 100644
index 0000000..123458e
--- /dev/null
+++ b/monitor_client.c
@@ -0,0 +1,23 @@
+#include <stdio.h>
+#include <unistd.h>
+
+#include <jack/jack.h>
+
+int
+main (int argc, char *argv[])
+
+{
+ jack_client_t *client;
+
+ if ((client = jack_client_new ("input monitoring")) == 0) {
+ fprintf (stderr, "jack server not running?\n");
+ return 1;
+ }
+
+ jack_port_request_monitor (client, "ALSA I/O:Input 1", TRUE);
+ sleep (10);
+ jack_port_request_monitor (client, "ALSA I/O:Input 1", FALSE);
+ jack_client_close (client);
+ exit (0);
+}
+
diff --git a/pool.c b/pool.c
new file mode 100644
index 0000000..5314b63
--- /dev/null
+++ b/pool.c
@@ -0,0 +1,31 @@
+/*
+ Copyright (C) 2001 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <stdlib.h>
+#include <jack/pool.h>
+
+void *
+jack_pool_alloc (size_t bytes)
+
+{
+ /* XXX need RT-pool based allocator here */
+
+ return malloc (bytes);
+}
diff --git a/port.h b/port.h
new file mode 100644
index 0000000..3eb8456
--- /dev/null
+++ b/port.h
@@ -0,0 +1,51 @@
+/*
+ Copyright (C) 2001 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __audioengine_port_h__
+#endif __audioengine_port_h__
+
+#define AUDIOENGINE_PORT_NAME_SIZE 32
+#define AUDIOENGINE_PORT_TYPE_SIZE 32
+
+struct _audioengine_port_t {
+
+ /* clients can use this value, and only this value */
+
+ void *buffer;
+
+ /* the rest of this is private, for use by the engine only */
+
+ unsigned long flags;
+ GSList *connections;
+ void *own_buffer;
+ audioengine_port_t *tied;
+ unsigned long buffer_size;
+ char name[AUDIOENGINE_PORT_NAME_SIZE+1];
+ char type[AUDIOENGINE_PORT_TYPE_SIZE+1];
+ char client[AUDIOENGINE_CLIENT_NAME_SIZE+1];
+ pthread_mutex_t lock;
+ audioengine_port_id_t id;
+ unsigned long client_id;
+ char in_use : 1;
+ char builtin : 1;
+ char locked : 1;
+};
+
+#endif /* __audioengine_port_h__ */
diff --git a/simple_client.c b/simple_client.c
new file mode 100644
index 0000000..83bbb9a
--- /dev/null
+++ b/simple_client.c
@@ -0,0 +1,83 @@
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <jack/jack.h>
+
+jack_port_t *my_input_port;
+jack_port_t *my_output_port;
+
+int
+process (nframes_t nframes, void *arg)
+
+{
+ sample_t *out = (sample_t *) jack_port_get_buffer (my_output_port, nframes);
+ sample_t *in = (sample_t *) jack_port_get_buffer (my_input_port, nframes);
+
+ memcpy (out, in, sizeof (sample_t) * nframes);
+
+ return 0;
+}
+
+int
+bufsize (nframes_t nframes, void *arg)
+
+{
+ printf ("the maximum buffer size is now %lu\n", nframes);
+ return 0;
+}
+
+int
+srate (nframes_t nframes, void *arg)
+
+{
+ printf ("the sample rate is now %lu/sec\n", nframes);
+ return 0;
+}
+
+int
+main (int argc, char *argv[])
+
+{
+ jack_client_t *client;
+
+ if (argc < 2) {
+ fprintf (stderr, "usage: aeclient <name>\n");
+ return 1;
+ }
+
+ if ((client = jack_client_new (argv[1])) == 0) {
+ fprintf (stderr, "jack server not running?\n");
+ return 1;
+ }
+
+ jack_set_process_callback (client, process, 0);
+ jack_set_buffer_size_callback (client, bufsize, 0);
+ jack_set_sample_rate_callback (client, srate, 0);
+
+ printf ("engine sample rate: %lu\n", jack_get_sample_rate (client));
+
+ my_input_port = jack_port_register (client, "myinput", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
+ my_output_port = jack_port_register (client, "myoutput", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+
+ if (jack_activate (client)) {
+ fprintf (stderr, "cannot activate client");
+ }
+
+ printf ("client activated\n");
+
+ if (jack_port_connect (client, "ALSA I/O:Input 1", my_input_port->shared->name)) {
+ fprintf (stderr, "cannot connect input ports\n");
+ }
+
+ if (jack_port_connect (client, my_output_port->shared->name, "ALSA I/O:Output 1")) {
+ fprintf (stderr, "cannot connect output ports\n");
+ }
+
+ sleep (5);
+
+ printf ("done sleeping, now closing...\n");
+ jack_client_close (client);
+ exit (0);
+}
+