#####################################
# MAC 5759 - EP 4                   #
# Prof.: Fabio Kon                  #
# Aluno: Nelson Posse Lago          #
#####################################

################
COMPILING
################
To compile, you need to have jack installed in /tmp/jack (or somewhere else if
you edit the makefile and the dotest.sh script) and:

glib http://www.gtk.org/ (some jack structures use this)
fltk http://www.fltk.org/ (only needed by fltk_client)

And some other basic libraries (like Xlib and pthreads). After all the needed
libraries are available, just type "make".

################
OVERVIEW
################
This is an initial attempt at the creation of a system for network distribution
of a JACK setup. The program "masterclient" attaches itself to a running JACK
server and creates two jack clients; as they are called back from jack at each
jack cicle, the first of them transmits the data received from jack to a peer
on the network and the other receives the data from the network, serving it
back to jack. If the first one is on or near the beginning of the cicle and the
second is on or near the end, then it is possible for the remote machine to
process the data in parallel with the processing being carried on on the local
machine.

To help us set up the graph ordering as needed, each one of the clients has a
pair of "dummy" ports, so that by attaching these dummy ports to other elements
on the graph we can force each of the clients to be processed before or after
the nodes we want. In the future, the graph reordering algorithm in jack should
be modified so that this is no longer necessary.

On the remote machine, it is expected that another jack setup is running; this
other jack setup is based on a network driver that blocks on a socket and wakes
jackd up when new data has arrived; after all processing, this driver sends the
data back to the sender and sleeps again on the socket. An initial sketch of
this driver is is slave_driver.cc, but it is far from complete. Much code,
however, may be reused from the classes built for the clients.

Just like the current implementation of jack, this system is limited to audio.

################
IMPLEMENTATION
################
To understand this, you really need to have a basic understanding on the
principles of the JACK system.

The system is based on the "chain" class; this class encapsulates the creation
and manipulation of the two jack clients that perform the network I/O (The
"transmitter" and the "receiver" could have been implemented on separate
classes; in that case, the chain class should only manage a pair of one
transmitter and one receiver, making shure that they are activated and
deactivated simultaneously etc. The program should be rewritten to
reflect this design).

Since jack can handle an arbitrary number of channels, a chain can also have
any number of channels, even a different number of jack "inputs" and "outputs"
(a stereo simulator, for instance, may output two channels of audio but be fed
by only one); these are held on a pair of arrays (one for input ports and the
other for output ports). For each jack input or output "port", the chain
maintains a udp socket, used to send or receive to/from the peer the data
associated with that port; these sockets are also kept together on a pair of
arrays (one for network delivery and the other for network capture).
Encapsulating data from all input and all output channels onto a single pair of
sockets might be possible too, but only testing will show which approach is
faster.

The network sockets operations and state are encapsulated on the udpReadSocket
and udpWriteSocket classes. Both of them get the parameters of IP/port to bind
or send data to by means of the class myIPAddress. This class exists mainly to
ease the passing of lists of IPs as parameters to some chain methods. It should
be modified in order to become a full wrapper around sockadd_in and to be
easily transmitted by corba, so that network configuration of both the local
and the remote machine can be performed easily, without the need for manually
inserted command-line arguments.

The udpReadSocket can be instantiated in blocking or non-blocking mode. The
blocking mode is useful for the remote machine driver; the non-blocking mode
is adequate for execution within the process callback issued by jackd. In
non-blocking mode, the socket is "polled" continuously until a timeout is
reached or data is available. The timeout is important because since the
programs run with priority SCHED_FIFO, without it the machine might be
locked-up polling the socket forever.

We use a non-blocking socket because if we were ever blocked when running
within the callback, the kernel would probably start some other,
non-jack-related, process; as soon as data to us arrives, the kernel would
schedule us back, but this might take too long for jack's design: instead, all
callbacks must be non-blocking to ensure real-time operation.

Both in blocking and in non-blocking mode, if there's more than one udp packet
waiting on the O.S. buffer, all of them are discarted and only the last one is
read; the reason is that jack must run synchronously: if there's more than one
packet on the queue, it means we've been late at some point in the past. It's
useless to process this data now, we'd be processing data behind jack *and* the
audible dropout has already been heard. We need to restart with the current
frame.

################
RUNNING/TESTING
################
To test the system, a simple program that reads data from the network and sends
it back to the sender will be used to "play the role" of the remote jack
system. The user starts "echoserver" on the remote machine to accomplish this.
Then, starts the script "dotest.sh" on the local machine, which starts jackd,
the masterclient, a gain control application and alsaplayer. Then the script
reconnects the jack ports so that one channel is processed locally, like any
other jack setup, and the other is sent/received over the network.
