Preface
This is the reference manual of LuaJack, which is a Lua binding library for the JACK Audio Connection Kit. [1]
It is assumed that the reader is familiar with both JACK and the Lua programming language.
For convenience of reference, this document contains external (deep) links to the Lua Reference Manual and to the JACK API documentation.
Getting and installing
For installation intructions, refer to the README file in the LuaJack official repository on GitHub.
Module organization
The LuaJack module is loaded using Lua’s require() and returns a table containing the functions it provides (as usual with Lua modules). This manual assumes that such table is named jack, i.e. that it is loaded with:
jack = require("luajack")
but nothing forbids the use of a different name.
Examples
A few examples can be found in the examples/ directory of the release package (some of them are LuaJack versions of the examples that come with the original JACK1 and JACK2 software).
License
LuaJack is released under the MIT/X11 license (same as Lua, and with the same only requirement to give proper credits to the original author). The copyright notice is in the LICENSE file in the base directory of the official repository on GitHub.
Introduction
An example application
A LuaJack application, apart from being written in Lua instead of C, looks very similar to a standard JACK application.
An example is shown below: it creates a JACK client with a couple of ports, then it registers some callbacks, activates the client and eventually sleeps while waiting for JACK to call back.
Important
|
LuaJack applications must implement their main loop using the jack.sleep() function. |
jack = require("luajack")
-- This is the 'main script', executed in the 'main context'.
-- Open a JACK client:
c = jack.client_open("myclient")
-- Create two ports:
p_in = jack.input_audio_port("port_in")
p_out = jack.output_audio_port("port_out")
-- Load the process chunk:
jack.process_load(c, [[
c, p_in, p_out = table.unpack(arg)
function process(nframes)
-- Audio processing happens here (in this example, we just
-- copy the samples from the input port to the output port).
jack.get_buffer(p_in)
jack.get_buffer(p_out)
jack.copy(p_out, p_in)
end
-- Register the (rt) process callback:
jack.process_callback(c, process)
]], c, p_in, p_out)
-- Register a non-rt callback:
jack.shutdown_callback(c, function() error("shutdown from server") end)
-- Activate the client:
jack.activate(c)
-- Sleep, waiting for JACK to call back:
jack.sleep()
The main deviation from an hypothetical standard implementation of the same example is in the use of the jack.process_load() function to 'load' the real-time part of the application. This difference should be (hopefully) clear after reading the next subsection.
LuaJack contexts
In this document we use the word 'context' to refer to the combination of the pthread and the Lua state a chunk of Lua code is executed in.
Since LuaJack relies on libjack, which is a multithreaded library, it has more than one context where to execute the Lua chunks of an application. More precisely, there are (basically) three types of contexts in LuaJack:
-
The main context, where the main script is executed. Clients, ports, etc. are created here, and the main loop and the (Lua) non real-time callbacks are executed here, too. The main context is composed of the main pthread and the main Lua state, i.e. the state created by the Lua interpreter. There is one main context in a LuaJack application, and it is shared by all the clients the application creates.
-
The process context, where the real-time callbacks are executed. It is composed of the (possibly) real-time pthread created by JACK for processing audio, and a dedicated Lua state. Each client has its own process context, which it creates with the jack.process_load( ) function, passing it the Lua code to be executed there (the 'process chunk').
-
The thread context, where a client thread is executed. It is composed of the pthread created for it by JACK and a dedicated Lua state. Each client thread has its own thread context, which is created with the client thread itself by means of the jack.thread_load( ) function, passing it the Lua code to be executed there (the 'thread chunk').
All the above Lua states are unrelated (that is, independent) and thus the corresponding contexts are insulated one from each other (they have separated namespaces and don’t share memory) as far as Lua code is concerned.
Communication between different contexts is achievable by means of parameter passing (when creating a context), and via the lock-free ringbuffers provided by JACK. As another mechanism of communication, client threads may also wait in their context to be signalled from other contexts (each client thread has a pthread condition variable associated with it for this purpose).
The LuaJack module is to be explicitly loaded (i.e. jack = require("luajack")) only in the main script. There is no need to load it in process chunks and thread chunks because they are executed in Lua states where the LuaJack module is automatically pre-loaded in a global table named 'jack'. This pre-loaded module is actually a limited version of the full LuaJack module, in that it contains only the subset of functions that can be directly used in the process or thread context.
LuaJack functions
LuaJack functions are described in the following subsections. All of them are members of the jack table.
The letters M, P and/or T beside a function description indicate whether it is available in the main context (M), in process contexts (P) and/or in thread contexts (T). For example, MP means that the function is available in the main context and in process contexts, but not in thread contexts.
On error, unless otherwise pointed out, all functions call Lua’s error() with a string message handler.
Clients
-
client = client_open( name [, options]) M
Creates a JACK client with the passed name and returns an opaque reference for subsequent operations. If the options parameter is passed, it must be a table containing zero or more of the following elements:
options.use_exact_name (boolean): if true, do not automatically assign a new name if name is already in use;
options.no_start_server (boolean): if true, do not start the JACK server if not already running;
options.server_name (string): select the JACK server with this name;
options.session_id (string): pass this string as SessionID Token.
-
maxsize = client_name_size( ) M
Returns the maximum allowed size for JACK clients names.
-
client_close( client ) M
Closes a client previously opened with jack.client().
Note
|
Clients are automatically closed by LuaJack when the application exits. |
-
name = client_name( client ) M
Returns the client’s name.
-
uuid = client_uuid( client ) M
Returns the client’s UUID.
-
name = client_uuid_to_name( client, uuid ) M
Returns the name of the client whose UUID is uuid, or nil if no such client exists.
-
uuid = client_name_to_uuid( client, name ) M
Returns the UUID of the client whose name is name, or nil if no such client exists.
-
activate( client ) M
Activates client.
-
deactivate( client ) M
Deactivates client.
-
boolean = is_realtime( client ) M
Returns true if JACK is running realtime, false otherwise.
-
cpuload = cpu_load( client ) MPT
Returns the current CPU load estimated by JACK.
-
sample_rate = sample_rate( client ) MPT
Returns the sample rate of the JACK system, as set by the user when the server was started.
-
set_buffer_size( client, nframes ) MP
Changes the buffer size passed to the process callback to nframes.
-
nframes = buffer_size( client ) MPT
Returns the current maximum buffer size that will ever be passed to the process callback (to be used only before the client is activated).
-
freewheel( client, onoff ) MP
Starts/stops JACK’s 'freewheel' mode. The onoff argument is a string and may be 'on' (to start) or 'off' (to stop).
Real-time callbacks
Rfr: Setting Client Callbacks.
Real-time callbacks are executed in the process context. This context is to be created by the main script using jack.process_load() or jack.process_loadfile() before the client is activated. The loaded Lua chunk is expected to define the callbacks and register them using the functions described in this section, which are available only in the process context.
-
process_load( client, chunk, … ) M
This function is to be used in the main context to create the process context for client.
The chunk argument is a string containing the Lua code to be executed in the process context (the process chunk).
Additional arguments in the … variadic part, if any, are passed as arguments to the chunk and may be of the following types only: nil, boolean, number, and string. References to LuaJack objects are allowed, since their type is number.
A limited LuaJack module is automatically pre-loaded in the process context, with the subset of JACK functionalities that can be accessed directly by the process chunk.
-
process_loadfile( client, filename, … ) M
Same as jack.process_load(), with the only difference that it loads the chunk from the file specified by filename. The file is searched for using the same mechanism used by Lua’s require() to search for modules.
-
process_callback( client, func ) P
Registers func as 'process' callback (func must be realtime safe).
The callback is executed as func(nframes), where nframes is the number of frames to process.
-
buffer_size_callback( client, func ) P
Registers func as callback for the 'buffer size change' event.
The callback is executed as func(nframes), where nframes is the new size of the JACK engine buffer.
-
sync_callback( client, func ) P
Registers client as a slow-sync client and func as its sync_callback (func must be realtime safe and is invoked just before the process callback).
The callback is executed as func(state, position), where the additional arguments are the current transport state and position. It is expected to return true if the client is ready to roll, or false (or nil) otherwise.
-
timebase_callback( client, func [, conditional] ) P
Registers client as the timebase master and func as its timebase callback (func must be realtime safe because and is invoked just after the process callback). If another client is already registered as timebase master, this client will take over unless the optional conditional argument (a boolean) is passed with the value true.
The callback is executed as func(state, nframes, position, new_position), where the additional arguments are the current transport state, the current transport position, and a boolean new_position having the value true for a newly requested position (which the client should provide using jack.transport_reposition() and jack.transport_locate()).
-
release_timebase( client ) P
Used by the client previously registered as the timebase master to withdraw that role and unregister the timebase callback.
Real-time considerations
The process chunk passed to jack.process_load() or jack.process_loadfile() is actually loaded in an hybrid context, in that its main part is executed in the Lua state of the process context while still in the non-rt pthread of the main context (the rt-pthread will be created by JACK later and does not yet exist at the time the process chunk is loaded). This means that it can perform non real-time safe operations, e.g. pre-allocate large tables for samples storage. On the other side, the rt-callbacks that the chunk registers will be executed in the pure process context, and thus they should be real-time safe.
Note also that Lua is a garbage-collected language with automatic memory management. This implies that the execution of Lua callbacks potentially involves calls to functions which are not real-time safe (e.g. malloc() and friends) and that, if not adeguately controlled, the GC may kick in at any time in the real-time pthread.
To mitigate the GC problem, LuaJack disables garbage collection in the Lua state of the process context, and automatically executes a basic garbage-collection step (with size=0) at the end of each rt-callback. This should suffice as long as the rt-callbacks do not allocate lots of memory (which would be a bad idea…).
Calls to malloc() and friends should not be a problem as long as they do not result in page faults. This can be accomplished first of all by avoiding the creation of large temporary objects (e.g. tables) in the rt-callbacks, and by pre-allocating them in the main part of the process chunk, instead.
As an additional measure one could use, via LD_PRELOAD, a custom malloc() implementation that does not return memory to the system (for example, ltalloc), and allocate+release 'enough' memory at the beginning of the program execution so that the allocator can serve subsequent memory requests without asking additional memory to the system.
(The real-time aspects of LuaJack are, however, still under study so take the above considerations with salt).
Non real-time callbacks
Rfr: Setting Client Callbacks.
The functions listed here are available only in the main context and can be used to register (Lua) callbacks for JACK events other than those that are handled in the real-time audio processing thread.
The registered callbacks are also executed by LuaJack in the main context. Notice that each callback receives the affected client as its first argument (this is because the main context is shared by all the clients created by the LuaJack application).
Note
|
All the non real-time Lua callbacks described here are executed by LuaJack in the main pthread. JACK itself may execute its C callbacks in different pthreads; this is a libjack implementation choice and may differ across different implementations as well as platforms. When JACK executes a C callback, LuaJack saves the event associated with it and promptly returns. The event is then translated in a Lua callback in the main pthread. |
-
shutdown_callback( client, func ) M
Registers func as callback for the 'shutdown' event.
The callback is executed as func(client, code, reason), where code is a string version of the JackStatus flags and reason is a string describing the shutdown reason. The callback need not be implemented as a signal handler (it may print, release resources etc.).
-
freewheel_callback( client, func ) M
Registers func as callback for the 'starting or stopping freewheel mode' event.
The callback is executed as func(client, operation), where operation is a string that may be either 'starting' or 'stopping'.
-
sample_rate_callback( client, func ) M
Registers func as callback for the 'sample rate change' event.
The callback is executed as func(client, nframes), where nframes is the new sample rate.
-
client_registration_callback( client, func ) M
Registers func as callback for the 'client (de)registration' event.
The callback is executed as func(client, name, operation), where name is the name of the affected client and operation is a string that may be either 'registered' or 'unregistered'.
-
port_registration_callback( client, func ) M
Registers func as callback for the 'port (de)registration' event.
The callback is executed as func(client, portname, operation), where portname is the (full) name of the affected port and operation is a string that may be either 'registered' or 'unregistered'.
-
port_rename_callback( client, func ) M
Registers func as callback for the 'port rename' event.
The callback is executed as func(client, portname, newname), where portname is the old (full) name of the affected port and newname is its newly assigned name.
-
port_connect_callback( client, func ) M
Registers func as callback for the 'ports connect or disconnect' event.
The callback is executed as func(client, srcname, dstname, operation), where srcname and dstname are the (full) names of the affected ports, and operation is a string that may be either 'connected' or 'disconnected'.
-
graph_order_callback( client, func ) M
Registers func as callback for the 'graph reorder' event.
The callback is executed as func(client).
-
xrun_callback( client, func ) M
Registers func as callback for the 'xrun' event.
The callback is executed as func(client).
-
latency_callback( client, func ) M
Registers func as callback for the 'latency recomputations needed' event.
The callback is executed as func(client, mode), where mode is a string that may be either 'capture' or 'playback'.
-
session_callback( client, func ) M
Registers func as callback for the 'session notification' event.
The callback is executed as command, flag1, flag2 = func(client, type, path, uuid), where:
type (a string) is the type of this session event and may be one of 'save', 'save_and_quit' or 'save_template';
path (a string) is the session directory path;
uuid (a string) is the client UUID which must be used when opening the client.
The callback is expected to return the following values, which are used by LuaJack to reply to the event:
command: the command line (a string) needed to restore the client;
flag1, flag2: optional session flags ('save_error' and/or 'need_terminal').
Client threads
-
thread = thread_load( client, chunk, … ) M
Creates a client thread with its dedicated thread context and returns a reference for subsequent operations. The chunk argument is a string containing the Lua code to be executed in the thread. Additional arguments in the variadic part (…), if any, are passed as arguments to the chunk and may be of the following types only: nil, boolean, number, string (references to LuaJack objects are allowed, since their type is number).
A limited LuaJack module is automatically pre-loaded in the thread context, with the subset of JACK functionalities that can be accessed directly by the thread chunk.
-
thread = thread_loadfile( client, filename, … ) M
Same as jack.thread_load(), with the only difference that it loads the chunk from the file specified by filename. The file is searched for using the same mechanism used by Lua’s require() to search for modules.
-
client, thread = self( ) T
Returns the client and thread references of the calling thread. This function is available only to thread chunks (i.e. in thread contexts).
-
signal( client, thread ) MPT
Signals the pthread condition variable associated with thread.
-
wait( ) T
Waits on the pthread condition variable associated with the calling thread. This function is available only to thread chunks (i.e. in thread contexts).
-
testcancel( ) T
Creates a cancellation point in the calling thread. This function is available only to thread chunks (i.e. in thread contexts).
-
priority = real_time_priority( client ) MPT
If JACK is running with realtime priority, returns the priority that any thread created by JACK will run at. Otherwise it returns nil.
-
priority = max_real_time_priority( client ) MPT
If JACK is running with realtime priority, returns the maximum priority that a realtime client thread should use. Otherwise it returns nil.
-
acquire_real_time_scheduling( priority ) MPT
Enables realtime scheduling, with the specified priority, for the calling thread.
-
drop_real_time_scheduling( ) MPT
Drops realtime scheduling for the calling thread.
Ports
Instead of a single port_register() function, LuaJack provides a few functions to create ports depending on the desired direction (input or output) and port type (default audio, default midi, or custom, i.e. not built-in). These port-creating functions are listed below, followed by the functions for port handling.
Notice that most of the port-handling functions are available in two versions: the version named jack.port_xxx acts on a port by reference, is usually faster because it does not involve querying the server to resolve the port name, but can be used only in the application that created the port; the version named jack.nport_xxx, on the other side, is slower because it acts on the port by its name, but it can be used also on ports not created by the invoking application.
-
port = input_audio_port( client , name [, options ] ) M
Creates an input port of the default audio type for client and returns a reference for subsequent operations.
The mandatory name argument (a string) is the short name to be assigned to the port, while the options parameter, if present, must be a table with zero or more of the following elements:
options.is_physical (boolean): JackPortIsPhysical flag;
options.can_monitor (boolean): JackPortCanMonitor flag;
options.is_terminal (boolean): JackPortIsTerminal flag.
-
port = output_audio_port( client , name [, options ] ) M
Creates an output port of the default audio type for client and returns a reference for subsequent operations.
The meaning of the arguments is the same as for jack.input_audio_port().
-
port = input_midi_port( client , name [, options ] ) M
Creates an input port of the default MIDI type for client and returns a reference for subsequent operations.
The meaning of the arguments is the same as for jack.input_audio_port().
-
port = output_midi_port( client , name [, options ] ) M
Creates an output port of the default MIDI type for client and returns a reference for subsequent operations.
The meaning of the arguments is the same as for jack.input_audio_port().
-
port = input_custom_port( client , name [, options ] ) M
Not available yet.
-
port = output_custom_port( client , name [, options ] ) M
Not available yet.
-
maxsize = port_name_size( ) M
Returns the maximum allowed size for JACK port names.
-
maxsize = port_type_size( ) M
Returns the maximum allowed size for JACK port types.
-
port_unregister( port ) M
Unregisters and deletes port.
Note
|
A port is automatically unregistered by LuaJack when the client that created it is closed. |
-
port_connect( port, portname2 ) M
nport_connect( client, portname1, portname2 ) alias connect M
Connects two ports. One of the two ports must be an output port (source of the connection) and the other must be an input port (destination). The two ports can be passed in any order.
-
port_disconnect( port [, portname2] ) M
nport_disconnect( client, portname1 [, portname2] ) alias disconnect M
Disconnects two ports. If portname2 is nil, the first port is disconnected from all the ports it is connected to.
-
fullname = port_name( port ) M
Returns the port’s full name.
-
shortname = port_short_name( port ) M
Returns the port’s short name.
-
flags, table = port_flags( port ) M
flags, table = nport_flags( client, portname ) M
Returns the port’s flags, in two equivalent formats:
flags : a string concatenating the flags that apply to the port, and
table: a table where table.<flag> is true if <flag> applies to the port, or nil otherwise.
(<flag> = 'input' | 'output' | 'audio' | 'midi' | 'custom' | 'is_physical' | 'can_monitor' | 'is_terminal' )
-
uuid = port_uuid( port ) M
uuid = nport_uuid( client, portname ) M
Returns the port’s UUID (a string), or nil if not found.
-
type = port_type( port ) M
type = nport_type( client, portname ) M
Returns the port’s type (a string), or nil if not found.
-
boolean = nport_exists( client, portname ) M
Returns true if the port with the full named portname exists, otherwise it returns false.
-
boolean = port_is_mine( client, port ) M
boolean = nport_is_mine( client, portname ) M
Returns true if the port is owned by the invoking client, otherwise it returns false.
-
port_set_alias( port, alias ) M
nport_set_alias( client, portname, alias ) M
Sets alias as an alias for the port.
-
port_unset_alias( port, alias ) M
nport_unset_alias( client, portname, alias ) M
Unsets alias as an alias for the port.
-
alias1, alias2 = port_aliases( port ) M
alias1, alias2 = nport_aliases( client, portname ) M
Returns the aliases for the port (if it has any).
-
N, { portname1, …, portnameN } = port_connections( port [, list ] ) M
N, { portname1, …, portnameN } = nport_connections( client, portname [, list ] ) M
Returns the number of connections the port is involved in. If list = true, it returns also a table containing the full names of the ports the port is connected to.
-
boolean = port_connected( port, portname2 ) M
boolean = nport_connected( client, portname1, portname2 ) M
Returns true if the two ports are connected, false otherwise.
-
port_monitor( port, onoff ) M
nport_monitor( client, portname, onoff ) M
Turns input monitoring on or off for the port (implemented with jack_port_ensure_monitor()). The onoff argument is a string and may be 'on' or 'off.
-
boolean = port_monitoring( port ) M
boolean = nport_monitoring( client, portname ) M
Returns true if input monitoring has been requested for the port, false otherwise or if no port with this name was found.
-
{ portname1, …, portnameN } = get_ports( client [, filter ]) M
Returns a list (table) of full port names. If filter is nil, all the ports are listed, otherwise the ports are selected according to the filter parameter, which must be a table containing zero or more of the following optional elements:
filter.name_pattern: a regular expression (string) used to select ports by name;
filter.type_pattern: a regular expression (string) used to select ports by type;
filter.direction: a string that may be 'input' or 'output', to select input ports only
or output ports only, respectively;
filter.is_physical (boolean): if true, list only physical ports;
filter.can_monitor (boolean): if true, list only ports that can monitor;
filter.is_terminal (boolean): if true, list only terminal ports.
Latency
-
min, max = latency_range( port, mode ) M
Gets the minimum and maximum latencies (in frames) defined by mode for the port. The mode parameter (a string) may be 'capture' or 'playback'.
-
set_latency_range( port, mode, min, max ) M
Sets the minimum and maximum latencies (in frames) defined by mode for port. The mode parameter (a string) may be 'capture' or playback. This function should only be used inside a latency callback.
-
recompute_total_latencies( client ) M
Requests a complete recomputation of all port latencies.
Time
Rfr: Handling time
-
useconds = time( ) MPT
Returns JACK’s current system time in microseconds.
-
frameno = frame( client ) alias frame_time
Returns the estimated current time in frames (to be used outside the process callback).
-
elapsed = since( useconds ) MPT
Returns the time, in microseconds, elapsed since JACK’s system time useconds.
-
nframes = since_frame( client, frameno ) MPT
Returns the number of frames elapsed since the estimated time frameno, in frames (to be used outside the process callback).
-
useconds = frames_to_time( client, nframes ) MPT
Returns the estimated time in microseconds of the specified time in frames.
-
nframes = time_to_frames( client, useconds ) MPT
Returns the estimated time in frames of the specified time in microseconds.
-
nframes = frames_since_cycle_start( client ) MPT
Returns the estimated time in frames that has passed since the JACK server began the current process cycle.
-
nframes = last_frame_time( client ) P
Returns the time, in frames, at the start of the current process cycle. Available in the process callback only, to be used to interpret timestamps generated with jack.frame() in other threads.
-
current_frames, current_usecs, next_usecs, period_usecs = cycle_times( client ) P
Available in the process callback only, returns internal cycle timing information (refer to jack_get_cycle_time() in the JACK documentation for more details).
Transport and timebase
-
frameno = current_transport_frame( client ) MPT
Returns an estimate of the current transport frame.
-
state = transport_state( client ) MPT
Returns the current transport state (a string that may have one of the following values: 'starting', 'rolling', or 'stopping').
-
state, position = transport_query( client ) MPT
Returns the current transport state (see jack.transport_state()) and the current transport position.
The transport position is a Lua-table representation of the jack_position_t struct. Its elements have the same name and meaning as the fields of the struct, except for the valid field, which in the table representation is not used (optional elements which are not present are set to nil, i.e. not set at all).
-
transport_start( client ) MPT
Starts the JACK transport rolling.
-
transport_stop( client ) MPT
Stops the JACK transport rolling.
-
transport_locate( client, frameno ) MPT
Repositions the JACK transport to the frame number frameno (realtime safe, may be used in the timebase callback).
-
transport_reposition( client, position ) MPT
Request a new transport position (realtime safe, may be used in the timebase callback).
-
set_sync_timeout( client, timeout ) MPT
Sets the timeout value (in microseconds) for slow-sync clients.
Reading and writing audio data
The functions described in this section are available only in the process callback, and allow to read and write samples from/to ports of the default audio type.
For this type of ports, a sample is represented by a Lua number.
-
nframes = get_buffer( port ) P
Retrieves the port buffer and initializes the buffer’s current position to the first sample.
Returns the buffer size in samples (which is the same value as the nframes parameter passed to the process callback).
This function must be invoked in the process callback, at each process cycle, before any other operation on the buffer.
-
pointer, nframes = raw_buffer( port ) P
Returns the port buffer pointer (as a light userdata) and the number of frames. It can be used only after having retrieved the buffer with jack.get_buffer().
-
position, available = seek( port [, position ] ) P
Returns the current position in the port’s buffer and the number of available samples from the current position to the end of the buffer.
If position is passed as argument, the function sets the current position to that value.
-
sample1, … = read( port [, count ] ) P
Returns up to count samples from the (input) port buffer.
Samples are read starting from the current position, which is then advanced by the number of returned samples.
If count exceeds the available samples, this function returns the available samples only. If no samples are available, it returns nil.
If count is not passed, it defaults to 'all the remaining samples'.
-
count = write( port [, sample1 , … ] ) P
Writes the passed samples to the (output) port’s buffer and returns the count of written samples.
Samples are written starting from the current position, which is then advanced by count. If the number of passed samples exceeds the available space in the buffer, the exceeding samples are discarded.
-
count = clear( port [, count ] ) P
Same as jack.write(), with the difference that it writes up to count samples to zero.
If count is not passed, it defaults to 'all the remaining samples'.
-
count = copy( dstport, srcport [, count ] ) P
Copies up to count samples from the buffer of the input port srcport to the buffer of the output port dstport and returns the number of copied samples.
This function uses the current positions of both ports, and advances them by the number of copied samples.
If count is not passed, it defaults to maxcount, which is defined as the minimum between the remaining samples in the input port’s buffer and the space left in the output port’s buffer.
If count is passed but it is greater than maxcount, only maxcount samples are copied.
Reading and writing MIDI data
The functions described in this section are available only in the process callback, and allow to read and write MIDI events from/to ports of the default MIDI type.
A MIDI event is represented in LuaJack as a couple of values (time, data), where:
-
time is the frame number of the event, relative to the current period, and
-
data is a binary string of length >= 1, containing the MIDI message (<status>…).
The data value may be handled with Lua’s native string.pack() and string.unpack() functions, or with the utilities provided in the luajack/midi.lua module.
-
eventcount, lostcount = get_buffer( inputport ) P
space = get_buffer( outputport ) P
Retrieves the port buffer.
If port is an input port, this function returns the number of events in the buffer (eventcount) and the number of lost events (lostcount). It also initializes the current index to 0, i.e. to the first event in the buffer.
If port is an output port, the function returns the space available in the buffer for writing MIDI event data.
This function must be invoked in the process callback, at each process cycle, before any other operation on the buffer.
-
index, available = seek( port [, index ] ) P
Returns the current index in the (input) port buffer and the number of available events from the current index to the end of the buffer.
If index is passed as argument, sets the current index to that value.
Note
|
Only input MIDI ports are seekable (output MIDI ports are not). |
-
time, data = read( port [, index ] ) P
Returns the MIDI event from the (input) port’s buffer at its current index, which it then advances by 1.
If an index is passed as argument, this function sets the current index to that value before reading the event. If the current index exceeds eventcount - 1, the function returns nil.
-
space = write( port, time, data ) P
Writes a MIDI event to the (output) port’s buffer and returns the space available in the buffer for the next event data. If there is not enough space to write the event, this function returns nil.
-
count = copy( dstport, srcport [, count ] ) P
Copies up to count MIDI events from the buffer of the input port srcport to the buffer of the output port dstport, and returns the number of copied events. Events are copied starting from the current index of srcport, which is then advanced by the number of copied events.
If count is not passed, this function attempts to copy all of the remaining events in the input port’s buffer that fit in the output port’s buffer.
Statistics
-
delay = max_delayed_usecs( client ) M
Returns the maximum delay reported by the backend since startup or reset.
-
delay = xrun_delayed_usecs( client ) M
Returns the delay in microseconds due to the most recent xrun occurrence.
-
reset_max_delayed_usecs( client ) M
Resets the maximum delay counter.
-
profile( client, what )
n, min, max, mean, var = profile( client )
Profile the real-time callbacks for client.
The what parameter may be one amongst start (reset the counters and start profiling), stop (stop profiling), and restart (start profiling, but do not reset the counters). If the what parameter is nil (or none), the function returns the number of profiled callbacks, followed by the minimum, maximum, mean and variance of the time (in seconds) they consumed. Please note that these are only rough estimates.
Session API
Session clients are expected to use jack.session_callback() to register a session callback in order to listen to events sent by the session manage, which instead uses the functions described below in this section.
-
{ reply1, …, replyN } = session_notify( client, target, type, path ) M
Sends an event to clients that registered a session callback.
The target argument (a string) is the name of the target client, or nil for 'all'.
The event type must be one amongst 'save', 'save_and_quit' and 'save_template'.
The path argument is the session directory path.
The function returns a table with the replies from the session clients. Each entry of the table is a subtable containing the following elements:
reply.uuid (a string): the client’s UUID;
reply.name (a string): the client’s name;
reply.command (a string): the command line needed to restore the client;
reply.save_error (true or nil): session flag;
reply.need_terminal (true or nil): session flag.
-
boolean = has_session_callback( client, clientname ) M
Returns true if the client named clientname has registered a session callback, false otherwise.
-
reserve_client_name( client, clientname ) M
Reserves the client name clientname and associates it with the UUID uuid.
Ringbuffers
LuaJack wraps the lock-free ringbuffers provided by JACK for inter-thread communication, giving them a message-oriented nature and adding means to use them in conjunction with sockets.
In LuaJack, data is written to and read from ringbuffers in form of tagged messages, each composed of a tag (an integer number) optionally followed by data (a Lua string). The tag can be used to represent the 'type' of the message or any other information (LuaJack by itself does not interpret it by any means).
-
rbuf = ringbuffer( client, size [, mlock [, usepipe ]] ) M
Creates a ringbuffer of the specified size (in bytes) and returns a ringbuffer reference for subsequent operations. The returned reference is an integer which may be passed as argument to thread scripts.
If mlock=true, the buffer is locked in memory.
If usepipe=true, a pipe is associated with the ringbuffer allowing its 'read' end to be turned in an object compatible with sockect.select() and thus to be used in conjunction with sockets or other fd-based communication mechanisms (of course, you don’t want to do this in an audio processing realtime thread…).
This function is only available in the main context and must be used before the client is activated.
-
ok = ringbuffer_write( rbuf, tag [, data ] ) MPT
Write a message to the ringbuffer rbuf.
Returns true on success, or false if there was not enough space for the message.
-
tag, data = ringbuffer_read( rbuf ) MPT
Read a message from the ringbuffer rbuf. Returns tag=nil if there are no messages available.
If the message is composed of the tag only, then data is returned as an empty string (so that data:len=0).
-
ringbuffer_reset( rbuf ) MPT
Resets the ringbuffer rbuf.
-
tag, data = ringbuffer_peek( rbuf ) MPT
Same as ringbuffer_read, but does not advance the message pointer.
-
ok = ringbuffer_read_advance( rbuf ) MPT
Advances the message read pointer without reading the message.
-
fd = ringbuffer_getfd( rbuf ) MPT
Returns the file descriptor of the pipe associated with the ringbuffer rbuf, or nil if it was created without pipe.
Selectable ringbuffers
@@ TODO
JACK server control API
Not implemented yet.
Non-callback API
Not supported.
Internal clients API
Not supported.
Metadata API
Not implemented yet.
Additional functions
-
sleep( [seconds] )
Sleeps for the specified number of seconds (if seconds is negative or nil, sleeps forever).
This function must be used to implement the main loop in the main context, and can optionally be used in thread contexts as well.
-
verbose( onoff )
If onoff='on', enables the LuaJack verbose mode. If onoff='off', it disables it. By default, the verbose mode is disabled.
MIDI utilities (luajack.midi)
The luajack.midi module is a LuaJack submodule that provides convenient utilities for dealing with MIDI messages in Lua.
Although it is part of the LuaJack package, luajack.midi is actually an independent module that can be used also in non-LuaJack applications. Since it is a self-contained script written in pure Lua, it can be easily customized or replaced (its source is in the luajack/midi.lua file in the LuaJack package).
The module is to be loaded in the usual Lua way, e.g.:
midi = require("luajack.midi")
and returns a table containing the utilities described in the subsections that follow (it is assumed here that the table is named 'midi' as in the above example, but a different name could be used as well).
Encoding MIDI messages
Each of the following routines returns a binary string containing an encoded MIDI message (starting from the MIDI status byte) with the passed parameters.
-
msg = note_off( channel, key [, velocity ] )
msg = note_on( channel, key [, velocity ] )
Parameters: channel = 1-16, key = 0-127, velocity = 0-127 (default=64).
-
msg = aftertouch( channel, key, pressure ) a.k.a. 'polyphonic key pressure'
Parameters: channel = 1-16, key = 0-127, pressure = 0-127.
-
msg = control_change( channel, control [, value ] )
Parameters: channel = 1-16, control = 0-127 or controller name, value = 0-127 or 'on'|'off' (default=0='off').
See the table in the following section for controller names.
-
msg = program_change( channel, number )
Parameters: channel = 1-16, number = 1-128.
-
msg = channel_pressure( channel, pressure )
Parameters: channel = 1-16, pressure = 0-127.
-
msg = pitch_wheel( channel, value ) a.k.a. 'pitch bend change'
Parameters: channel = 1-16, value = 0-16383 (0x3fff).
-
msg = mtc_quarter_frame( nnn, dddd ) a.k.a. 'time code quarter frame'
Parameters: nnn = 0-7 (MTC message type), dddd = 0-15 (MTC message value).
-
msg = song_position( value )
Parameters: value = 0-16383 (0x3fff).
-
msg = song_select( number )
Parameters: number = 1-128.
-
msg = tune_request( )
msg = end_of_exclusive( )
msg = clock( )
msg = start( )
msg = continue( )
msg = stop( )
msg = active_sensing( )
msg = reset( )
Parameters: none.
Decoding MIDI messages
-
name, channel, parameters = decode( msg [, stringize ] )
Decodes a MIDI message. The msg argument is expected to be a binary string containing the MIDI message starting from the MIDI status byte.
Returns the MIDI message name (or nil if not recognized), the channel (1-16, or nil if not applicable), and a table containing the decoded parameters (see the table below).
If stringize is true, the parameters are returned as a string instead of a table (this may be useful for dumps).
-
string = tostring( time, msg )
Returns a printable string containing the passed time (an integer) and the decoded msg (for dumps).
-
msg = tohex( msg )
Returns a string containing the msg bytes in hexadecimal format (e.g. '84 0c 2f').
Message name | Channel | Parameters table contents |
---|---|---|
'note off' |
1-16 |
key = 0-127, velocity = 0-127 |
'note on' |
1-16 |
key = 0-127, velocity = 0-127 |
'aftertouch' |
1-16 |
key = 0-127, pressure = 0-127 |
'control change' |
1-16 |
number = 0-127, value = 0-127, controller = 'controller name' (see table below) |
'program change' |
1-16 |
number = 1-128 |
'channel pressure' |
1-16 |
pressure = 0-127 |
'pitch wheel' |
1-16 |
value = 0-16383 (0x3fff), lsb = 0-127, msb = 0-127 |
'system exclusive' |
nil |
@@ TBD |
'mtc quarter frame' |
nil |
nnn = 0-7, dddd = 0-15, value = 0-127 (0nnndddd) |
'song position' |
nil |
value = 0-16383 (0x3fff), lsb = 0-127, msb = 0-127 |
'song select' |
nil |
number = 1-128 |
'tune request' |
nil |
none |
'end of exclusive' |
nil |
none |
'clock' |
nil |
none |
'start' |
nil |
none |
'continue' |
nil |
none |
'stop' |
nil |
none |
'active sensing' |
nil |
none |
'reset' |
nil |
none |
Controller number | Controller name |
---|---|
0 (0x00) |
'bank select (coarse)' |
1 (0x01) |
'modulation wheel (coarse)' |
2 (0x02) |
'breath controller (coarse)' |
4 (0x04) |
'foot pedal (coarse)' |
5 (0x05) |
'portamento time (coarse)' |
6 (0x06) |
'data entry (coarse)' |
7 (0x07) |
'volume (coarse)' |
8 (0x08) |
'balance (coarse)' |
10 (0x0a) |
'pan position (coarse)' |
11 (0x0b) |
'expression (coarse)' |
12 (0x0c) |
'effect control 1 (coarse)' |
13 (0x0d) |
'effect control 2 (coarse)' |
16 (0x10) |
'general purpose slider 1' |
17 (0x11) |
'general purpose slider 2' |
18 (0x12) |
'general purpose slider 3' |
19 (0x13) |
'general purpose slider 4' |
32 (0x20) |
'bank select (fine)' |
33 (0x21) |
'modulation wheel (fine)' |
34 (0x22) |
'breath controller (fine)' |
36 (0x24) |
'foot pedal (fine)' |
37 (0x25) |
'portamento time (fine)' |
38 (0x26) |
'data entry (fine)' |
39 (0x27) |
'volume (fine)' |
40 (0x28) |
'balance (fine)' |
42 (0x2a) |
'pan position (fine)' |
43 (0x2b) |
'expression (fine)' |
44 (0x2c) |
'effect control 1 (fine)' |
45 (0x2d) |
'effect control 2 (fine)' |
64 (0x40) |
'hold pedal' |
65 (0x41) |
'portamento' |
66 (0x42) |
'sustenuto pedal' |
67 (0x43) |
'soft pedal' |
68 (0x44) |
'legato pedal' |
69 (0x45) |
'hold 2 pedal' |
70 (0x46) |
'sound variation' |
71 (0x47) |
'sound timbre' |
72 (0x48) |
'sound release time' |
73 (0x49) |
'sound attack time' |
74 (0x4a) |
'sound brightness' |
75 (0x4b) |
'sound control 6' |
76 (0x4c) |
'sound control 7' |
77 (0x4d) |
'sound control 8' |
78 (0x4e) |
'sound control 9' |
79 (0x4f) |
'sound control 10' |
80 (0x50) |
'general purpose button 1' |
81 (0x51) |
'general purpose button 2' |
82 (0x52) |
'general purpose button 3' |
83 (0x53) |
'general purpose button 4' |
91 (0x5b) |
'effects level' |
92 (0x5c) |
'tremulo level' |
93 (0x5d) |
'chorus level' |
94 (0x5e) |
'celeste level' |
95 (0x5f) |
'phaser level' |
96 (0x60) |
'data button increment' |
97 (0x61) |
'data button decrement' |
98 (0x62) |
'non-registered parameter (fine)' |
99 (0x63) |
'non-registered parameter (coarse)' |
100 (0x64) |
'registered parameter (fine)' |
101 (0x65) |
'registered parameter (coarse)' |
120 (0x78) |
'all sound off' |
121 (0x79) |
'all controllers off' |
122 (0x7a) |
'local keyboard' |
123 (0x7b) |
'all notes off' |
124 (0x7c) |
'omni mode off' |
125 (0x7d) |
'omni mode on' |
126 (0x7e) |
'mono operation' |
127 (0x7f) |
'poly operation' |
Other MIDI utilities
-
msg = note_key( frequency )
Returns the nearest MIDI note key corresponding to frequency (Hz).
-
msg = note_frequency( key )
Returns the frequency (Hz) corresponding to the MIDI note key.
-
tmsg = tmsg( time, msg )
Returns a binary string obtained by concatenating the passed time (an integer) and the binary MIDI msg (this format can be convenient if one wants to send MIDI events over ringbuffers).
-
time, msg = time_msg( tmsg )
Extracts the time and msg from a tmsg binary string constructed with the midi.tmsg() function.