├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── doc ├── Makefile ├── additional.adoc ├── buffer.adoc ├── callbacks_nonrt.adoc ├── callbacks_rt.adoc ├── capi.adoc ├── client.adoc ├── colony.css ├── example.lua ├── examples.adoc ├── functions.adoc ├── index.adoc ├── index.html ├── intclient.adoc ├── introduction.adoc ├── jackctl.adoc ├── latency.adoc ├── metadata.adoc ├── midi.adoc ├── noncallback.adoc ├── ports.adoc ├── powered-by-lua.gif ├── powered-by-lua.png ├── preface.adoc ├── ringbuffers.adoc ├── session.adoc ├── statistics.adoc ├── summary.adoc ├── threads.adoc ├── time.adoc └── transport.adoc ├── examples ├── alias.lua ├── bufsize.lua ├── c_api │ ├── Makefile │ ├── example1 │ │ ├── Makefile │ │ ├── README.md │ │ ├── configure.sh │ │ ├── main.lua │ │ └── mymodule.c │ ├── example2 │ │ ├── Makefile │ │ ├── README.md │ │ ├── configure.sh │ │ ├── main.lua │ │ ├── mymodule.c │ │ └── thread.lua │ ├── example3 │ │ ├── Makefile │ │ ├── README.md │ │ ├── configure.sh │ │ ├── main.lua │ │ ├── mymodule.c │ │ └── thread.lua │ ├── example4 │ │ ├── Makefile │ │ ├── README.md │ │ ├── gui.lua │ │ ├── main.lua │ │ └── myecho.c │ └── example5 │ │ ├── Makefile │ │ ├── README.md │ │ ├── gui.lua │ │ ├── main.lua │ │ ├── myecho.c │ │ └── process.lua ├── connect.lua ├── control.lua ├── cpu_load.lua ├── echo.lua ├── evmon.lua ├── getopt │ ├── getopt_example.lua │ └── getopt_example.sh ├── gui_thru.lua ├── gui_thru2.lua ├── interactive.lua ├── latency.lua ├── metro.lua ├── midi_dump.lua ├── oscillator.lua ├── ringbuffers │ ├── pingpong.lua │ ├── select.lua │ └── selectpingpong.lua ├── showtime.lua ├── sinesynth.lua └── thru.lua ├── luajack ├── getopt.lua ├── midi.lua ├── selectable.lua └── shell.lua ├── src ├── Makefile ├── _make ├── alloc.c ├── buffer.c ├── callback.c ├── client.c ├── cud.c ├── evt.c ├── internal.h ├── latency.c ├── luajack.c ├── luajack.h ├── main.c ├── port.c ├── process.c ├── pud.c ├── queue.h ├── rbuf.c ├── ringbuffer.c ├── rud.c ├── session.c ├── srvctl.c ├── statistics.c ├── structs.h ├── syncpipe.c ├── thread.c ├── time.c ├── transport.c ├── tree.h ├── tud.c └── utils.c └── thirdparty ├── asciidoctor-styles-license ├── lua-alt-getopt-license ├── openbsd-queue-license ├── openbsd-tree-license └── powered-by-lua-license /.gitignore: -------------------------------------------------------------------------------- 1 | # luajack .gitignore 2 | 3 | TODO 4 | *.o 5 | *.so 6 | *.log 7 | core.* 8 | local/ 9 | tests/ 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Stefano Trettel 4 | 5 | Software repository: LuaJack, https://github.com/stetre/luajack 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | 26 | (See also the THIRD-PARTY LICENSES contained in the thirdparty/ directory 27 | of the Software repository.) 28 | 29 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | default: build 3 | 4 | build install uninstall where: 5 | @cd src; $(MAKE) $@ 6 | 7 | clean : 8 | @cd src; $(MAKE) $@ 9 | @cd doc; $(MAKE) $@ 10 | @cd examples/c_api; $(MAKE) $@ 11 | 12 | docs: 13 | @cd doc; $(MAKE) 14 | 15 | cleanall: clean 16 | 17 | backup: clean 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## LuaJack: Lua bindings for the JACK Audio Connection Kit 2 | 3 | LuaJack is a Lua binding library for the [JACK Audio Connection Kit](http://jackaudio.org/). 4 | 5 | It runs on GNU/Linux and requires [Lua](http://www.lua.org/) (>=5.3) 6 | and [JACK](http://jackaudio.org/downloads) (API >= v0.124.1). 7 | 8 | _Authored by:_ _[Stefano Trettel](https://www.linkedin.com/in/stetre)_ 9 | 10 | [![Lua logo](./doc/powered-by-lua.gif)](http://www.lua.org/) 11 | 12 | #### License 13 | 14 | MIT/X11 license (same as Lua). See [LICENSE](./LICENSE). 15 | 16 | #### Documentation 17 | 18 | See the [Reference Manual](https://stetre.github.io/luajack/doc/index.html). 19 | 20 | #### Getting and installing (Ubuntu) 21 | 22 | Setup the build environment as described [here](https://github.com/stetre/moonlibs), then: 23 | 24 | ```sh 25 | $ git clone https://github.com/stetre/luajack 26 | $ cd luajack 27 | luajack$ make 28 | luajack$ sudo make install 29 | ``` 30 | 31 | #### Example 32 | 33 | The example below creates a JACK client that simply copies samples from an 34 | input port to an output port. Other examples can be found in the **examples/** 35 | directory contained in the release package. 36 | 37 | 38 | ```lua 39 | -- Script: example.lua 40 | jack = require("luajack") 41 | 42 | -- Open a JACK client: 43 | c = jack.client_open("myclient") 44 | 45 | -- Create two ports: 46 | p_in = jack.input_audio_port(c, "port_in") 47 | p_out = jack.output_audio_port(c, "port_out") 48 | 49 | -- Load the 'process' chunk: 50 | jack.process_load(c, [[ 51 | c, p_in, p_out = table.unpack(arg) 52 | 53 | function process(nframes) 54 | -- Copy the samples from the input port to the output port: 55 | jack.get_buffer(p_in) 56 | jack.get_buffer(p_out) 57 | jack.copy(p_out, p_in) 58 | end 59 | 60 | -- Register the (rt) process callback: 61 | jack.process_callback(c, process) 62 | ]], c, p_in, p_out) 63 | 64 | -- Register a non-rt callback: 65 | jack.shutdown_callback(c, function() error("shutdown from server") end) 66 | 67 | -- Activate the client: 68 | jack.activate(c) 69 | 70 | -- Sleep, waiting for JACK to call back: 71 | jack.sleep() 72 | ``` 73 | 74 | The script can be executed at the shell prompt with the standard Lua interpreter: 75 | 76 | ```shell 77 | $ lua example.lua 78 | ``` 79 | 80 | #### See also 81 | 82 | * [MoonLibs - Graphics and Audio Lua Libraries](https://github.com/stetre/moonlibs). 83 | 84 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | TgtAdoc := index 2 | TgtPdf := LuaJack-RefMan 3 | 4 | Stylesdir = ./ 5 | Stylesheet = colony.css 6 | Style = -a stylesheet=$(Stylesheet) -a stylesdir=$(Stylesdir) 7 | Style = 8 | 9 | Fopub=~/tmp/github/asciidoctor-fopub/fopub 10 | 11 | ifdef TgtAdoc 12 | # one file only 13 | Src = $(TgtAdoc).adoc 14 | else 15 | # all .adoc files 16 | Src := $(wildcard *.adoc) 17 | endif 18 | 19 | Xml := $(Src:.adoc=.xml) 20 | Html := $(Src:.adoc=.html) 21 | Pdf := $(Src:.adoc=.pdf) 22 | 23 | 24 | default: html 25 | 26 | check: 27 | 28 | clean: 29 | @-rm -f *~ 30 | @-rm -f *.pdf 31 | @-rm -f *.pdfmarks 32 | @-rm -f *.xml 33 | @-rm -f *.html 34 | @-rm -f *.epub 35 | @-rm -f *.fo 36 | @-rm -f *.log 37 | 38 | install: 39 | 40 | html: clean 41 | @asciidoctor $(Style) $(Src) 42 | 43 | preview: html 44 | @google-chrome $(Html) 45 | 46 | docbook: clean 47 | @asciidoctor -b docbook -d book $(Src) 48 | @$(Fopub) $(Xml) 49 | @mv -f $(TgtAdoc).pdf $(TgtPdf).pdf 50 | 51 | ifdef TgtAdoc 52 | # one file only 53 | pdf: clean 54 | @asciidoctor-pdf $(TgtAdoc).adoc -o $(TgtPdf).pdf 55 | #@asciidoctor-pdf -a pdf-style=asciidoctor $(TgtAdoc).adoc -o $(TgtPdf).pdf 56 | #@asciidoctor-pdf -a pdf-style=default $(TgtAdoc).adoc -o $(TgtPdf).pdf 57 | #@asciidoctor -b pdf $(TgtAdoc).adoc -o $(TgtPdf).pdf 58 | else 59 | # all .adoc files 60 | pdf: clean 61 | @asciidoctor-pdf -a pdf-style=asciidoctor $(TgtAdoc).adoc 62 | endif 63 | 64 | docs: html pdf 65 | -------------------------------------------------------------------------------- /doc/additional.adoc: -------------------------------------------------------------------------------- 1 | 2 | === Additional functions 3 | 4 | [[jack.sleep]] 5 | * *sleep*( [_seconds_] ) + 6 | [small]#Sleeps for the specified number of _seconds_ (if _seconds_ is negative or _nil_, 7 | sleeps forever). + 8 | This function must be used to implement the main loop in the <>, 9 | and can optionally be used in thread contexts as well.# 10 | 11 | 12 | [[jack.verbose]] 13 | * *verbose*( _onoff_ ) + 14 | [small]#If _onoff='on'_, enables the LuaJack verbose mode. If _onoff='off'_, it disables it. 15 | By default, the verbose mode is disabled.# 16 | 17 | //// 18 | @@ TODO 19 | jack.getpid 20 | _VERSION LuaJack version 21 | _JACK_VERSION JACK version 22 | MAX_FRAMES (-> JACK_MAX_FRAMES) 23 | LOAD_INIT_LIMIT (->JACK_LOAD_INIT_LIMIT) 24 | DEFAULT_AUDIO_TYPE (-> JACK_DEFAULT_AUDIO_TYPE) 25 | DEFAULT_MIDI_TYPE (-> JACK_DEFAULT_MIDI_TYPE) 26 | //// 27 | 28 | -------------------------------------------------------------------------------- /doc/callbacks_nonrt.adoc: -------------------------------------------------------------------------------- 1 | 2 | === Non real-time callbacks 3 | 4 | [small]#Rfr: link:++http://jackaudio.org/api/group__ClientCallbacks.html++[Setting Client Callbacks].# 5 | 6 | The functions listed here are available only in the <> 7 | and can be used to register (Lua) callbacks for JACK events other than those that are 8 | handled in the real-time audio processing thread. 9 | 10 | The registered callbacks are also executed by LuaJack in the main context. Notice 11 | that each callback receives the affected _client_ as its first argument (this is 12 | because the main context is shared by all the clients created by the LuaJack application). 13 | 14 | NOTE: All the non real-time Lua callbacks described here are executed by LuaJack in 15 | the main pthread. JACK itself may execute its C callbacks in different pthreads; this 16 | is a libjack implementation choice and may differ across different implementations as 17 | well as platforms. When JACK executes a C callback, LuaJack saves the event associated 18 | with it and promptly returns. The event is then translated in a Lua callback in the 19 | main pthread. 20 | 21 | //// 22 | LuaJack callbacks and the functions to set them map (almost) one-to-one to 23 | http://jackaudio.org/api/group__ClientCallbacks.html[JACK callbacks] and are subject 24 | the same considerations and constraints described in the JACK documentation. 25 | //// 26 | 27 | 28 | [[jack.shutdown_callback]] 29 | * *shutdown_callback*( _client_, _func_ ) _M_ + 30 | [small]#Registers _func_ as callback for the 'shutdown' event. + 31 | The callback is executed as *_func(client, code, reason)_*, where _code_ is a string 32 | version of the JackStatus flags and _reason_ is a string describing the shutdown reason. 33 | The callback need not be implemented as a signal handler (it may print, release resources 34 | etc.).# 35 | 36 | 37 | [[jack.freewheel_callback]] 38 | * *freewheel_callback*( _client_, _func_ ) _M_ + 39 | [small]#Registers _func_ as callback for the 'starting or stopping freewheel mode' event. + 40 | The callback is executed as *_func(client, operation)_*, where _operation_ is a string 41 | that may be either _'starting'_ or _'stopping'_.# 42 | 43 | 44 | [[jack.sample_rate_callback]] 45 | * *sample_rate_callback*( _client_, _func_ ) _M_ + 46 | [small]#Registers _func_ as callback for the 'sample rate change' event. + 47 | The callback is executed as *_func(client, nframes)_*, where _nframes_ is the new 48 | sample rate.# 49 | 50 | 51 | [[jack.client_registration_callback]] 52 | * *client_registration_callback*( _client_, _func_ ) _M_ + 53 | [small]#Registers _func_ as callback for the 'client (de)registration' event. + 54 | The callback is executed as *_func(client, name, operation)_*, where _name_ is the name 55 | of the affected client and _operation_ is a string that may be either _'registered'_ 56 | or _'unregistered'_.# 57 | 58 | 59 | [[jack.port_registration_callback]] 60 | * *port_registration_callback*( _client_, _func_ ) _M_ + 61 | [small]#Registers _func_ as callback for the 'port (de)registration' event. + 62 | The callback is executed as *_func(client, portname, operation)_*, where _portname_ is 63 | the (full) name of the affected port and _operation_ is a string that may be 64 | either _'registered'_ or _'unregistered'_.# 65 | 66 | 67 | [[jack.port_rename_callback]] 68 | * *port_rename_callback*( _client_, _func_ ) _M_ + 69 | [small]#Registers _func_ as callback for the 'port rename' event. + 70 | The callback is executed as *_func(client, portname, newname)_*, where _portname_ is 71 | the old (full) name of the affected port and _newname_ is its newly assigned name.# 72 | 73 | 74 | [[jack.port_connect_callback]] 75 | * *port_connect_callback*( _client_, _func_ ) _M_ + 76 | [small]#Registers _func_ as callback for the 'ports connect or disconnect' event. + 77 | The callback is executed as *_func(client, srcname, dstname, operation)_*, where 78 | _srcname_ and _dstname_ are the (full) names of the affected ports, and _operation_ 79 | is a string that may be either _'connected'_ or _'disconnected'_.# 80 | 81 | 82 | [[jack.graph_order_callback]] 83 | * *graph_order_callback*( _client_, _func_ ) _M_ + 84 | [small]#Registers _func_ as callback for the 'graph reorder' event. + 85 | The callback is executed as *_func(client)_*.# 86 | 87 | 88 | [[jack.xrun_callback]] 89 | * *xrun_callback*( _client_, _func_ ) _M_ + 90 | [small]#Registers _func_ as callback for the 'xrun' event. + 91 | The callback is executed as *_func(client)_*.# 92 | 93 | 94 | [[jack.latency_callback]] 95 | * *latency_callback*( _client_, _func_ ) _M_ + 96 | [small]#Registers _func_ as callback for the 'latency recomputations needed' event. + 97 | The callback is executed as *_func(client, mode)_*, where _mode_ is a string that 98 | may be either _'capture'_ or _'playback'_.# 99 | 100 | 101 | [[jack.session_callback]] 102 | * *session_callback*( _client_, _func_ ) _M_ + 103 | [small]#Registers _func_ as callback for the 'session notification' event. + 104 | The callback is executed as 105 | *_command, flag1, flag2 = func(client, type, path, uuid)_*, 106 | where: + 107 | _type_ (a string) is the type of this session event and may be one of _'save'_, 108 | _'save_and_quit'_ or _'save_template'_; + 109 | _path_ (a string) is the session directory path; + 110 | _uuid_ (a string) is the client UUID which must be used when opening the client. + 111 | The callback is expected to return the following values, which are used by LuaJack to 112 | reply to the event: + 113 | _command_: the command line (a string) needed to restore the client; + 114 | _flag1_, _flag2_: optional session flags (_'save_error'_ and/or _'need_terminal'_).# 115 | 116 | -------------------------------------------------------------------------------- /doc/capi.adoc: -------------------------------------------------------------------------------- 1 | 2 | //// 3 | == LuaJack C API 4 | 5 | The LuaJack C API allows to implement the <<_real_time_callbacks, real-time callbacks>> 6 | entirely in C. 7 | 8 | @@ TODO 9 | 10 | http://www.lua.org/manual/5.3/manual.html#lua_CFunction[lua_CFunction] 11 | 12 | [source,C,indent=0] 13 | ---- 14 | luajack_t* luajack_checkclient(lua_State *L, int arg); 15 | luajack_t* luajack_checkport(lua_State *L, int arg); 16 | luajack_t* luajack_checkthread(lua_State *L, int arg); 17 | luajack_t* luajack_checkringbuffer(lua_State *L, int arg); 18 | ---- 19 | 20 | These functions work similarly to the _luaL_check_ functions in the Lua Auxiliary Library. 21 | Each of them checks if the value at the index _arg_ of the Lua stack is a valid object 22 | reference and returns a _luajack_t_ pointer representing the object in C (or raises an 23 | error if the reference is not valid). 24 | //// 25 | 26 | -------------------------------------------------------------------------------- /doc/client.adoc: -------------------------------------------------------------------------------- 1 | 2 | === Clients 3 | 4 | [small]#Rfr: link:++http://jackaudio.org/api/group__ClientFunctions.html++[Creating & manipulating clients].# 5 | 6 | [[jack.client_open]] 7 | * _client_ = *client_open*( _name_ [, _options_]) _M_ + 8 | [small]#Creates a JACK client with the passed _name_ and returns an opaque reference for subsequent 9 | operations. 10 | If the _options_ parameter is passed, it must be a table containing zero or more of 11 | the following elements: + 12 | *options.use_exact_name* (boolean): if _true_, do not automatically assign a new name if _name_ is already in use; + 13 | *options.no_start_server* (boolean): if _true_, do not start the JACK server if not already running; + 14 | *options.server_name* (string): select the JACK server with this name; + 15 | *options.session_id* (string): pass this string as SessionID Token.# 16 | 17 | 18 | 19 | [[jack.client_name_size]] 20 | * _maxsize_ = *client_name_size*( ) _M_ + 21 | [small]#Returns the maximum allowed size for JACK clients names.# 22 | 23 | 24 | [[jack.client_close]] 25 | * *client_close*( _client_ ) _M_ + 26 | [small]#Closes a client previously opened with <<_jack.client, jack.client>>().# 27 | 28 | NOTE: Clients are automatically closed by LuaJack when the application exits. 29 | 30 | 31 | [[jack.client_name]] 32 | * _name_ = *client_name*( _client_ ) _M_ + 33 | [small]#Returns the client's name.# 34 | 35 | 36 | [[jack.client_uuid]] 37 | * _uuid_ = *client_uuid*( _client_ ) _M_ + 38 | [small]#Returns the client's UUID.# 39 | 40 | 41 | [[jack.client_uuid_to_name]] 42 | * _name_ = *client_uuid_to_name*( _client_, _uuid_ ) _M_ + 43 | [small]#Returns the name of the client whose UUID is _uuid_, or _nil_ if no such client exists.# 44 | 45 | 46 | [[jack.client_name_to_uuid]] 47 | * _uuid_ = *client_name_to_uuid*( _client_, _name_ ) _M_ + 48 | [small]#Returns the UUID of the client whose name is _name_, or _nil_ if no such client exists.# 49 | 50 | 51 | [[jack.activate]] 52 | * *activate*( _client_ ) _M_ + 53 | [small]#Activates _client_.# 54 | 55 | 56 | [[jack.deactivate]] 57 | * *deactivate*( _client_ ) _M_ + 58 | [small]#Deactivates _client_.# 59 | 60 | 61 | [[jack.is_realtime]] 62 | * _boolean_ = *is_realtime*( _client_ ) _M_ + 63 | [small]#Returns _true_ if JACK is running realtime, _false_ otherwise.# 64 | 65 | 66 | [[jack.cpu_load]] 67 | * _cpuload_ = *cpu_load*( _client_ ) _MPT_ + 68 | [small]#Returns the current CPU load estimated by JACK.# 69 | 70 | 71 | [[jack.sample_rate]] 72 | * _sample_rate_ = *sample_rate*( _client_ ) _MPT_ + 73 | [small]#Returns the sample rate of the JACK system, as set by the user when the server was started.# 74 | 75 | 76 | [[jack.set_buffer_size]] 77 | * *set_buffer_size*( _client_, _nframes_ ) _MP_ + 78 | [small]#Changes the buffer size passed to the <> to _nframes_.# 79 | 80 | 81 | [[jack.buffer_size]] 82 | * _nframes_ = *buffer_size*( _client_ ) _MPT_ + 83 | [small]#Returns the current maximum buffer size that will ever be passed to the <> (to be used only before the client is activated).# 84 | 85 | 86 | [[jack.freewheel]] 87 | * *freewheel*( _client_, _onoff_ ) _MP_ + 88 | [small]#Starts/stops JACK's '_freewheel_' mode. The _onoff_ argument is a string and may be _'on'_ (to start) or _'off'_ (to stop).# 89 | 90 | 91 | -------------------------------------------------------------------------------- /doc/example.lua: -------------------------------------------------------------------------------- 1 | 2 | jack = require("luajack") 3 | 4 | -- Open a JACK client: 5 | c = jack.client_open("myclient") 6 | 7 | -- Create two ports: 8 | p_in = jack.input_audio_port("port_in") 9 | p_out = jack.output_audio_port("port_out") 10 | 11 | -- Load the process chunk: 12 | jack.process_load(c, [[ 13 | c, p_in, p_out = table.unpack(arg) 14 | 15 | function process(nframes) 16 | -- Audio processing happens here (in this example, we just 17 | -- copy the samples from the input port to the output port). 18 | jack.get_buffer(p_in) 19 | jack.get_buffer(p_out) 20 | jack.copy(p_out, p_in) 21 | end 22 | 23 | -- Register the (rt) process callback: 24 | jack.process_callback(c, process) 25 | ]], c, p_in, p_out) 26 | 27 | -- Register a non-rt callback: 28 | jack.shutdown_callback(c, function() error("shutdown from server") end) 29 | 30 | -- Activate the client: 31 | jack.activate(c) 32 | 33 | -- Sleep, waiting for JACK to call back: 34 | jack.sleep() 35 | -------------------------------------------------------------------------------- /doc/examples.adoc: -------------------------------------------------------------------------------- 1 | == Examples 2 | 3 | This section describes some of the examples that are contained in the `examples/` directory 4 | of the official LuaJack release. 5 | 6 | @@TODO 7 | 8 | //// 9 | include::example_helloworld.adoc[] 10 | //// 11 | 12 | -------------------------------------------------------------------------------- /doc/functions.adoc: -------------------------------------------------------------------------------- 1 | 2 | == LuaJack functions 3 | 4 | LuaJack functions are described in the following subsections. All of them are members of the *jack* table. 5 | 6 | The letters _M_, _P_ and/or _T_ beside a function description indicate whether it is available 7 | in the main context (_M_), in process contexts (_P_) and/or in thread contexts (_T_). 8 | For example, _MP_ means that the function is available in the main context and in process contexts, but 9 | not in thread contexts. 10 | 11 | On error, unless otherwise pointed out, all functions call Lua's 12 | http://www.lua.org/manual/5.3/manual.html#pdf-error[error]() with a string message handler. 13 | 14 | -------------------------------------------------------------------------------- /doc/index.adoc: -------------------------------------------------------------------------------- 1 | = LuaJack Reference Manual 2 | Stefano Trettel 3 | v0.5, 2016-07-03 4 | :toc: left 5 | :stylesdir: ./ 6 | :stylesheet: colony.css 7 | :source-highlighter: pygments 8 | :pygments-style: autumn 9 | :source-language: lua 10 | :examplesdir: ../examples 11 | 12 | image::powered-by-lua.png[Lua logo, link=http://www.lua.org] 13 | 14 | include::preface.adoc[] 15 | 16 | include::introduction.adoc[] 17 | 18 | include::functions.adoc[] 19 | 20 | include::client.adoc[] 21 | 22 | include::callbacks_rt.adoc[] 23 | 24 | include::callbacks_nonrt.adoc[] 25 | 26 | include::threads.adoc[] 27 | 28 | include::ports.adoc[] 29 | 30 | include::latency.adoc[] 31 | 32 | include::time.adoc[] 33 | 34 | include::transport.adoc[] 35 | 36 | include::buffer.adoc[] 37 | 38 | include::statistics.adoc[] 39 | 40 | include::session.adoc[] 41 | 42 | include::ringbuffers.adoc[] 43 | 44 | include::jackctl.adoc[] 45 | 46 | include::noncallback.adoc[] 47 | 48 | include::intclient.adoc[] 49 | 50 | include::metadata.adoc[] 51 | 52 | include::additional.adoc[] 53 | 54 | include::midi.adoc[] 55 | 56 | include::capi.adoc[] 57 | 58 | //// 59 | @@ include::examples.adoc[] 60 | 61 | include::summary.adoc[] 62 | //// 63 | -------------------------------------------------------------------------------- /doc/intclient.adoc: -------------------------------------------------------------------------------- 1 | 2 | === Internal clients API 3 | 4 | Not supported. 5 | 6 | -------------------------------------------------------------------------------- /doc/introduction.adoc: -------------------------------------------------------------------------------- 1 | 2 | == Introduction 3 | 4 | === An example application 5 | 6 | A LuaJack application, apart from being written in Lua instead of C, looks very 7 | similar to a standard JACK application. 8 | 9 | An example is shown below: it creates a JACK client with a couple of ports, then 10 | it registers some callbacks, activates the client and eventually sleeps while waiting 11 | for JACK to call back. 12 | 13 | IMPORTANT: LuaJack applications must implement their main loop using the 14 | <>() function. 15 | 16 | [source,lua,indent=1] 17 | ---- 18 | jack = require("luajack") 19 | -- This is the 'main script', executed in the 'main context'. 20 | 21 | -- Open a JACK client: 22 | c = jack.client_open("myclient") 23 | 24 | -- Create two ports: 25 | p_in = jack.input_audio_port("port_in") 26 | p_out = jack.output_audio_port("port_out") 27 | 28 | -- Load the process chunk: 29 | jack.process_load(c, [[ 30 | c, p_in, p_out = table.unpack(arg) 31 | 32 | function process(nframes) 33 | -- Audio processing happens here (in this example, we just 34 | -- copy the samples from the input port to the output port). 35 | jack.get_buffer(p_in) 36 | jack.get_buffer(p_out) 37 | jack.copy(p_out, p_in) 38 | end 39 | 40 | -- Register the (rt) process callback: 41 | jack.process_callback(c, process) 42 | ]], c, p_in, p_out) 43 | 44 | -- Register a non-rt callback: 45 | jack.shutdown_callback(c, function() error("shutdown from server") end) 46 | 47 | -- Activate the client: 48 | jack.activate(c) 49 | 50 | -- Sleep, waiting for JACK to call back: 51 | jack.sleep() 52 | ---- 53 | 54 | The main deviation from an hypothetical standard implementation of the same example 55 | is in the use of the <>() function to 'load' 56 | the real-time part of the application. This difference should be (hopefully) clear 57 | after reading the next subsection. 58 | 59 | //=== LuaJack contexts 60 | 61 | === LuaJack contexts 62 | 63 | [[luajack.contexts]] 64 | In this document we use the word *'context'* to refer to the combination of 65 | the pthread and the http://www.lua.org/manual/5.3/manual.html#lua_State[Lua state] 66 | a chunk of Lua code is executed in. 67 | 68 | Since LuaJack relies on libjack, which is a multithreaded library, it has more than 69 | one context where to execute the Lua chunks of an application. More precisely, 70 | there are (basically) three types of contexts in LuaJack: 71 | 72 | . The *main context*, where the main script is executed. Clients, ports, etc. 73 | are created here, and the <> and the (Lua) 74 | <<_non_real_time_callbacks, non real-time callbacks>> are executed here, too. 75 | The main context is composed of the main pthread and the main Lua state, i.e. the 76 | state created by the Lua interpreter. 77 | There is one main context in a LuaJack application, and it is shared 78 | by all the clients the application creates. + 79 | . The *process context*, where the <<_real_time_callbacks, real-time callbacks>> 80 | are executed. 81 | It is composed of the (possibly) real-time pthread created by JACK for processing audio, 82 | and a dedicated Lua state. Each client has its own process context, which it creates 83 | with the <>( ) function, passing it the Lua code 84 | to be executed there (the *'process chunk'*). + 85 | . The *thread context*, where a <<_client_threads, client thread>> is executed. 86 | It is composed of the pthread created for it by JACK and a dedicated Lua state. 87 | Each client thread has its own thread context, which is created with the client thread 88 | itself by means of the <>( ) function, passing it 89 | the Lua code to be executed there (the *'thread chunk'*). + 90 | 91 | All the above *Lua states are unrelated* (that is, independent) and thus the corresponding 92 | contexts are insulated one from each other (they have separated namespaces and don't share 93 | memory) as far as Lua code is concerned. 94 | 95 | Communication between different contexts is achievable by means of parameter passing (when 96 | creating a context), and via the lock-free <<_ringbuffers, ringbuffers>> provided by JACK. 97 | As another mechanism of communication, client threads may also <> in their 98 | context to be <> from other contexts (each client thread has a 99 | pthread condition variable associated with it for this purpose). 100 | 101 | 102 | [[loading_luajack]] 103 | The LuaJack module is to be explicitly loaded (i.e. _jack = require("luajack")_) 104 | only in the main script. There is no need to load it in process chunks and thread chunks 105 | because they are executed in Lua states where the LuaJack module is automatically pre-loaded 106 | in a global table named *'jack'*. This pre-loaded module is actually a limited version of 107 | the full LuaJack module, in that it contains only the subset of functions that can be 108 | directly used in the process or thread context. 109 | 110 | <<< 111 | 112 | -------------------------------------------------------------------------------- /doc/jackctl.adoc: -------------------------------------------------------------------------------- 1 | 2 | === JACK server control API 3 | 4 | Not implemented yet. 5 | 6 | -------------------------------------------------------------------------------- /doc/latency.adoc: -------------------------------------------------------------------------------- 1 | 2 | === Latency 3 | 4 | [small]#Rfr: link:++http://jackaudio.org/api/group__LatencyFunctions.html++[Managing and determining Latency]# 5 | 6 | [[jack.latency_range]] 7 | * _min_, _max_ = *latency_range*( _port_, _mode_ ) _M_ + 8 | [small]#Gets the minimum and maximum latencies (in frames) defined by _mode_ for the port. 9 | The _mode_ parameter (a string) may be _'capture'_ or _'playback'_.# 10 | 11 | 12 | [[jack.set_latency_range]] 13 | * *set_latency_range*( _port_, _mode_, _min_, _max_ ) _M_ + 14 | [small]#Sets the minimum and maximum latencies (in frames) defined by _mode_ for _port_. 15 | The _mode_ parameter (a string) may be _'capture'_ or _playback_. 16 | This function should only be used inside a <>.# 17 | 18 | 19 | [[jack.recompute_total_latencies]] 20 | * *recompute_total_latencies*( _client_ ) _M_ + 21 | [small]#Requests a complete recomputation of all port latencies.# 22 | 23 | 24 | -------------------------------------------------------------------------------- /doc/metadata.adoc: -------------------------------------------------------------------------------- 1 | 2 | === Metadata API 3 | 4 | Not implemented yet. 5 | 6 | -------------------------------------------------------------------------------- /doc/noncallback.adoc: -------------------------------------------------------------------------------- 1 | 2 | === Non-callback API 3 | 4 | Not supported. 5 | 6 | -------------------------------------------------------------------------------- /doc/powered-by-lua.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stetre/luajack/c7c26506127c402d0d2f538b07777273b59f1967/doc/powered-by-lua.gif -------------------------------------------------------------------------------- /doc/powered-by-lua.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stetre/luajack/c7c26506127c402d0d2f538b07777273b59f1967/doc/powered-by-lua.png -------------------------------------------------------------------------------- /doc/preface.adoc: -------------------------------------------------------------------------------- 1 | 2 | == Preface 3 | 4 | This is the reference manual of *LuaJack*, which is a 5 | http://www.lua.org[*Lua*] binding library for the 6 | http://jackaudio.org[*JACK Audio Connection Kit*]. 7 | footnote:[ 8 | This manual is written in 9 | http://www.methods.co.nz/asciidoc/[AsciiDoc], rendered with 10 | http://asciidoctor.org/[AsciiDoctor] and a CSS from the 11 | https://github.com/asciidoctor/asciidoctor-stylesheet-factory[AsciiDoctor Stylesheet Factory]. 12 | The PDF version is produced with 13 | https://github.com/asciidoctor/asciidoctor-pdf[AsciiDoctor-Pdf].] 14 | 15 | It is assumed that the reader is familiar with both JACK and the Lua programming language. 16 | 17 | For convenience of reference, this document contains external (deep) links to the 18 | http://www.lua.org/manual/5.3/manual.html[Lua Reference Manual] and to the 19 | http://jackaudio.org/api/[JACK API documentation]. 20 | 21 | === Getting and installing 22 | 23 | For installation intructions, refer to the README file in the 24 | https://github.com/stetre/luajack[*LuaJack official repository*] 25 | on GitHub. 26 | 27 | //// 28 | The *official repository* of LuaJack is on GitHub at the following link: 29 | *https://github.com/stetre/luajack* . 30 | 31 | LuaJack runs on GNU/Linux and requires 32 | *http://www.lua.org[Lua]* version 5.3 or greater, and 33 | *http://jackaudio.org[JACK]* (aligned to JACK API v0.124.1). 34 | 35 | By now, it has been used only on GNU/Linux Fedora 21 with JACK2 v1.9.10. 36 | It should compile and run also on any other GNU/Linux distribution and with 37 | JACK1 instead of JACK2, but this has not been tested. 38 | 39 | To install LuaJack, download the 40 | https://github.com/stetre/luajack/releases[latest release] and do the following: 41 | 42 | [source,shell] 43 | ---- 44 | # ... download luajack-0.1.tar.gz ... 45 | [ ]$ tar -zxpvf luajack-0.1.tar.gz 46 | [ ]$ cd luajack-0.1 47 | [luajack-0.1]$ make 48 | [luajack-0.1]$ make check 49 | [luajack-0.1]$ sudo make install 50 | ---- 51 | 52 | The _$make check_ command shows you what will be installed and where (please read 53 | its output before executing _$make install_). 54 | By default, LuaJack installs its components in subdirectories of `/usr/local/` 55 | (and creates such directories, if needed). 56 | This behaviour can be changed by defining PREFIX with the desired alternative 57 | base installation directory. For example, this will install the components 58 | in `/home/joe/local`: 59 | 60 | [source,shell] 61 | ---- 62 | [luajack-0.1]$ make 63 | [luajack-0.1]$ make install PREFIX=/home/joe/local 64 | ---- 65 | //// 66 | 67 | === Module organization 68 | 69 | The LuaJack module is loaded using Lua's 70 | http://www.lua.org/manual/5.3/manual.html#pdf-require[require]() and 71 | returns a table containing the functions it provides 72 | (as usual with Lua modules). This manual assumes that such 73 | table is named *jack*, i.e. that it is loaded with: 74 | 75 | [source,lua,indent=1] 76 | ---- 77 | jack = require("luajack") 78 | ---- 79 | 80 | but nothing forbids the use of a different name. 81 | 82 | === Examples 83 | 84 | A few examples can be found in the *examples/* directory of the release package 85 | (some of them are LuaJack versions of the examples that come with the original JACK1 86 | and JACK2 software). 87 | 88 | === License 89 | 90 | LuaJack is released under the *MIT/X11 license* (same as 91 | http://www.lua.org/license.html[Lua], and with the same only requirement to give proper 92 | credits to the original author). 93 | The copyright notice is in the LICENSE file in the base directory 94 | of the https://github.com/stetre/luajack[official repository] on GitHub. 95 | 96 | <<< 97 | -------------------------------------------------------------------------------- /doc/ringbuffers.adoc: -------------------------------------------------------------------------------- 1 | 2 | === Ringbuffers 3 | 4 | [small]#Rfr: link:++http://jackaudio.org/api/ringbuffer_8h.html++[ringbuffer.h File Reference]# 5 | 6 | LuaJack wraps the 7 | http://jackaudio.org/api/ringbuffer_8h.html[lock-free ringbuffers] 8 | provided by JACK for inter-thread communication, giving them a message-oriented 9 | nature and adding means to use them in conjunction with sockets. 10 | 11 | [[ringbuffersmessage]] 12 | In LuaJack, data is written to and read from ringbuffers in form of tagged 13 | *messages*, each composed of a _tag_ (an integer number) optionally followed 14 | by _data_ (a Lua string). The tag can be used to represent the 'type' of the 15 | message or any other information (LuaJack by itself does not interpret it by any means). 16 | 17 | 18 | [[jack.ringbuffer]] 19 | * _rbuf_ = *ringbuffer*( _client_, _size_ [, _mlock_ [, _usepipe_ ]] ) _M_ + 20 | [small]#Creates a ringbuffer of the specified _size_ (in bytes) and returns a 21 | ringbuffer reference for subsequent operations. The returned reference 22 | is an integer which may be passed as argument to <>. + 23 | If _mlock=true_, the buffer is locked in memory. + 24 | If _usepipe=true_, a pipe is associated with the ringbuffer allowing its 'read' end to be 25 | turned in an object compatible with 26 | https://github.com/diegonehab/luasocket[sockect.select]() 27 | and thus to be used in conjunction with sockets or other fd-based 28 | communication mechanisms (of course, you don't want to do this in an 29 | audio processing realtime thread...). + 30 | This function is only available in the <> and must be 31 | used before the client is <>.# 32 | 33 | 34 | [[jack.ringbuffer_write]] 35 | * _ok_ = *ringbuffer_write*( _rbuf_, _tag_ [, _data_ ] ) _MPT_ + 36 | [small]#Write a <> to the ringbuffer _rbuf_. + 37 | Returns _true_ on success, or _false_ if there was not enough space for the message.# 38 | 39 | 40 | [[jack.ringbuffer_read]] 41 | * _tag_, _data_ = *ringbuffer_read*( _rbuf_ ) _MPT_ + 42 | [small]#Read a <> from the ringbuffer _rbuf_. 43 | Returns _tag_=_nil_ if there are no messages available. + 44 | If the message is composed of the _tag_ only, then _data_ is returned as an 45 | empty string (so that _data:len=0_).# 46 | 47 | 48 | [[jack.ringbuffer_reset]] 49 | * *ringbuffer_reset*( _rbuf_ ) _MPT_ + 50 | [small]#Resets the ringbuffer _rbuf_.# 51 | 52 | 53 | [[jack.ringbuffer_peek]] 54 | * _tag_, _data_ = *ringbuffer_peek*( _rbuf_ ) _MPT_ + 55 | [small]#Same as <>, but does not advance the message pointer.# 56 | 57 | [[jack.ringbuffer_read_advance]] 58 | * _ok_ = *ringbuffer_read_advance*( _rbuf_ ) _MPT_ + 59 | [small]#Advances the message read pointer without reading the message.# 60 | 61 | [[jack.ringbuffer_getfd]] 62 | * _fd_ = *ringbuffer_getfd*( _rbuf_ ) _MPT_ + 63 | [small]#Returns the file descriptor of the pipe associated with the ringbuffer _rbuf_, 64 | or _nil_ if it was <> without pipe.# 65 | 66 | //// 67 | - RINGBUFFER_HDRLEN header length in bytes @@ 68 | 69 | //// 70 | 71 | === Selectable ringbuffers 72 | 73 | //^luajack/selectable.lua 74 | @@ TODO 75 | 76 | -------------------------------------------------------------------------------- /doc/session.adoc: -------------------------------------------------------------------------------- 1 | 2 | === Session API 3 | 4 | [small]#Rfr: link:++http://jackaudio.org/api/group__SessionClientFunctions.html++[Session API for clients]# 5 | 6 | Session clients are expected to use <>() to 7 | register a session callback in order to listen to events sent by the session manage, 8 | which instead uses the functions described below in this section. 9 | 10 | 11 | [[jack.session_notify]] 12 | * _{ reply1, ..., replyN }_ = *session_notify*( _client_, _target_, _type_, _path_ ) _M_ + 13 | [small]#Sends an event to clients that registered a session callback. + 14 | The _target_ argument (a string) is the name of the target client, or _nil_ for 'all'. + 15 | The event _type_ must be one amongst _'save'_, _'save_and_quit'_ and _'save_template'_. + 16 | The _path_ argument is the session directory path. + 17 | The function returns a table with the replies from the session clients. 18 | Each entry of the table is a subtable containing the following elements: + 19 | *reply.uuid* (a string): the client's UUID; + 20 | *reply.name* (a string): the client's name; + 21 | *reply.command* (a string): the command line needed to restore the client; + 22 | *reply.save_error* (_true_ or _nil_): session flag; + 23 | *reply.need_terminal* (_true_ or _nil_): session flag.# 24 | 25 | 26 | [[jack.has_session_callback]] 27 | * _boolean_ = *has_session_callback*( _client_, _clientname_ ) _M_ + 28 | [small]#Returns _true_ if the client named _clientname_ has registered a session callback, 29 | _false_ otherwise.# 30 | 31 | 32 | [[jack.reserve_client_name]] 33 | * *reserve_client_name*( _client_, _clientname_ ) _M_ + 34 | [small]#Reserves the client name _clientname_ and associates it with the UUID _uuid_.# 35 | 36 | -------------------------------------------------------------------------------- /doc/statistics.adoc: -------------------------------------------------------------------------------- 1 | 2 | === Statistics 3 | 4 | [small]#Rfr: link:++http://jackaudio.org/api/statistics_8h.html++[statistics.h File Reference]# 5 | 6 | [[jack.max_delayed_usecs]] 7 | * _delay_ = *max_delayed_usecs*( _client_ ) _M_ + 8 | [small]#Returns the maximum delay reported by the backend since startup or reset.# 9 | 10 | 11 | [[jack.xrun_delayed_usecs]] 12 | * _delay_ = *xrun_delayed_usecs*( _client_ ) _M_ + 13 | [small]#Returns the delay in microseconds due to the most recent xrun occurrence.# 14 | 15 | 16 | [[jack.reset_max_delayed_usecs]] 17 | * *reset_max_delayed_usecs*( _client_ ) _M_ + 18 | [small]#Resets the maximum delay counter.# 19 | 20 | 21 | [[jack.profile]] 22 | * *profile*( _client_, _what_ ) + 23 | _n_, _min_, _max_, _mean_, _var_ = *profile*( _client_ ) + 24 | [small]#Profile the real-time callbacks for _client_. + 25 | The _what_ parameter may be one amongst 26 | _start_ (reset the counters and start profiling), 27 | _stop_ (stop profiling), and 28 | _restart_ (start profiling, but do not reset the counters). 29 | If the _what_ parameter is _nil_ (or none), the function returns 30 | the number of profiled callbacks, followed by the minimum, maximum, 31 | mean and variance of the time (in seconds) they consumed. 32 | Please note that these are only rough estimates.# 33 | 34 | -------------------------------------------------------------------------------- /doc/threads.adoc: -------------------------------------------------------------------------------- 1 | 2 | === Client threads 3 | 4 | [small]#Rfr: link:++http://jackaudio.org/api/group__ClientThreads.html++[Creating and managing client threads].# 5 | 6 | 7 | [[jack.thread_load]] 8 | * _thread_ = *thread_load*( _client_, _chunk_, _..._ ) _M_ + 9 | [small]#Creates a client thread with its dedicated <> and 10 | returns a reference for subsequent operations. 11 | The _chunk_ argument is a string containing the Lua code to be executed in the 12 | thread. 13 | Additional arguments in the variadic part (_..._), if any, are passed as arguments 14 | to the chunk and may be of the following types only: _nil_, _boolean_, _number_, _string_ 15 | (references to LuaJack objects are allowed, since their type is _number_). + 16 | A <> is automatically 17 | pre-loaded in the thread context, with the subset of JACK functionalities that 18 | can be accessed directly by the thread chunk.# 19 | 20 | 21 | [[jack.thread_loadfile]] 22 | * _thread_ = *thread_loadfile*( _client_, _filename_, _..._ ) _M_ + 23 | [small]#Same as <>(), with the only difference that it 24 | loads the chunk from the file specified by _filename_. The file is searched for using 25 | the same mechanism used by Lua's 26 | http://www.lua.org/manual/5.3/manual.html#pdf-require[require]() 27 | to search for modules.# 28 | 29 | 30 | [[jack.self]] 31 | * _client_, _thread_ = *self*( ) _T_ + 32 | [small]#Returns the client and thread references of the calling thread. 33 | This function is available only to thread chunks (i.e. in thread contexts).# 34 | 35 | 36 | [[jack.signal]] 37 | * *signal*( _client_, _thread_ ) _MPT_ + 38 | [small]#Signals the pthread condition variable associated with _thread_.# 39 | 40 | 41 | [[jack.wait]] 42 | * *wait*( ) _T_ + 43 | [small]#Waits on the pthread condition variable associated with the calling thread. 44 | This function is available only to thread chunks (i.e. in thread contexts).# 45 | 46 | 47 | [[jack.testcancel]] 48 | * *testcancel*( ) _T_ + 49 | [small]#Creates a cancellation point in the calling thread. 50 | This function is available only to thread chunks (i.e. in thread contexts).# 51 | 52 | 53 | [[jack.real_time_priority]] 54 | * _priority_ = *real_time_priority*( _client_ ) _MPT_ + 55 | [small]#If JACK is running with realtime priority, returns the priority that any thread 56 | created by JACK will run at. Otherwise it returns _nil_.# 57 | 58 | 59 | [[jack.max_real_time_priority]] 60 | * _priority_ = *max_real_time_priority*( _client_ ) _MPT_ + 61 | [small]#If JACK is running with realtime priority, returns the maximum priority that a 62 | realtime client thread should use. Otherwise it returns _nil_.# 63 | 64 | 65 | [[jack.acquire_real_time_scheduling]] 66 | * *acquire_real_time_scheduling*( _priority_ ) _MPT_ + 67 | [small]#Enables realtime scheduling, with the specified _priority_, for the calling thread.# 68 | 69 | 70 | [[jack.drop_real_time_scheduling]] 71 | * *drop_real_time_scheduling*( ) _MPT_ + 72 | [small]#Drops realtime scheduling for the calling thread.# 73 | 74 | 75 | -------------------------------------------------------------------------------- /doc/time.adoc: -------------------------------------------------------------------------------- 1 | 2 | === Time 3 | 4 | [small]#Rfr: link:++http://jackaudio.org/api/group__TimeFunctions.html++[Handling time]# 5 | 6 | [[jack.time]] 7 | * _useconds_ = *time*( ) _MPT_ + 8 | [small]#Returns JACK's current system time in microseconds.# 9 | 10 | 11 | [[jack.frame]] 12 | * _frameno_ = *frame*( _client_ ) alias *frame_time* + 13 | [small]#Returns the estimated current time in frames (to be used outside the process callback).# 14 | 15 | 16 | [[jack.since]] 17 | * _elapsed_ = *since*( _useconds_ ) _MPT_ + 18 | [small]#Returns the time, in microseconds, elapsed since JACK's system time _useconds_.# 19 | 20 | 21 | [[jack.since_frame]] 22 | * _nframes_ = *since_frame*( _client_, _frameno_ ) _MPT_ + 23 | [small]#Returns the number of frames elapsed since the estimated time _frameno_, in frames 24 | (to be used outside the process callback).# 25 | 26 | 27 | [[jack.frames_to_time]] 28 | * _useconds_ = *frames_to_time*( _client_, _nframes_ ) _MPT_ + 29 | [small]#Returns the estimated time in microseconds of the specified time in frames.# 30 | 31 | 32 | [[jack.time_to_frames]] 33 | * _nframes_ = *time_to_frames*( _client_, _useconds_ ) _MPT_ + 34 | [small]#Returns the estimated time in frames of the specified time in microseconds.# 35 | 36 | 37 | [[jack.frames_since_cycle_start]] 38 | * _nframes_ = *frames_since_cycle_start*( _client_ ) _MPT_ + 39 | [small]#Returns the estimated time in frames that has passed since the JACK server began the 40 | current process cycle.# 41 | 42 | 43 | [[jack.last_frame_time]] 44 | * _nframes_ = *last_frame_time*( _client_ ) _P_ + 45 | [small]#Returns the time, in frames, at the start of the current process cycle. 46 | Available in the process callback only, to be used to interpret timestamps generated with 47 | <>() in other threads.# 48 | 49 | 50 | [[jack.cycle_times]] 51 | * _current_frames_, _current_usecs_, _next_usecs_, _period_usecs_ = *cycle_times*( _client_ ) _P_ + 52 | [small]#Available in the process callback only, returns internal cycle timing information 53 | (refer to http://jackaudio.org/api/group__TimeFunctions.html[jack_get_cycle_time]() 54 | in the JACK documentation for more details).# 55 | 56 | 57 | -------------------------------------------------------------------------------- /doc/transport.adoc: -------------------------------------------------------------------------------- 1 | 2 | === Transport and timebase 3 | 4 | [small]#Rfr: link:++http://jackaudio.org/api/group__TransportControl.html++[Transport and Timebase control] - 5 | link:++http://jackaudio.org/api/transport-design.html++[JACK Transport Design].# 6 | 7 | //^ url with underscores... 8 | :jack_position_url: http://jackaudio.org/api/structjack__position__t.html 9 | 10 | 11 | [[jack.current_transport_frame]] 12 | * _frameno_ = *current_transport_frame*( _client_ ) _MPT_ + 13 | [small]#Returns an estimate of the current transport frame.# 14 | 15 | 16 | [[jack.transport_state]] 17 | * _state_ = *transport_state*( _client_ ) _MPT_ + 18 | [small]#Returns the current transport _state_ (a string that may have one of the following values: 19 | _'starting'_, _'rolling'_, or _'stopping'_).# 20 | 21 | 22 | [[jack.transport_query]] 23 | * _state_, _position_ = *transport_query*( _client_ ) _MPT_ + 24 | [small]#Returns the current transport _state_ (see <>()) 25 | and the current transport _position_. + 26 | The transport position is a Lua-table representation of the 27 | {jack_position_url}[jack_position_t] struct. 28 | Its elements have the same name and meaning as the fields of the struct, except for the 29 | _valid_ field, which in the table representation is not used (optional elements which 30 | are not present are set to _nil_, i.e. not set at all).# 31 | 32 | 33 | [[jack.transport_start]] 34 | * *transport_start*( _client_ ) _MPT_ + 35 | [small]#Starts the JACK transport rolling.# 36 | 37 | 38 | [[jack.transport_stop]] 39 | * *transport_stop*( _client_ ) _MPT_ + 40 | [small]#Stops the JACK transport rolling.# 41 | 42 | 43 | [[jack.transport_locate]] 44 | * *transport_locate*( _client_, _frameno_ ) _MPT_ + 45 | [small]#Repositions the JACK transport to the frame number _frameno_ 46 | (realtime safe, may be used in the <>).# 47 | 48 | 49 | [[jack.transport_reposition]] 50 | * *transport_reposition*( _client_, _position_ ) _MPT_ + 51 | [small]#Request a new transport _position_ 52 | (realtime safe, may be used in the <>).# 53 | 54 | 55 | [[jack.set_sync_timeout]] 56 | * *set_sync_timeout*( _client_, _timeout_ ) _MPT_ + 57 | [small]#Sets the _timeout_ value (in microseconds) for 58 | http://jackaudio.org/api/transport-design.html#slowsyncclients[slow-sync clients].# 59 | 60 | -------------------------------------------------------------------------------- /examples/alias.lua: -------------------------------------------------------------------------------- 1 | -- LuaJack example: alias.lua 2 | -- 3 | -- LuaJack version of JACK's alias.c example. 4 | 5 | jack = require("luajack") 6 | 7 | getopt = require("luajack.getopt") 8 | fmt = string.format 9 | 10 | my_name = arg[0] 11 | 12 | USAGE = 13 | "Usage: lua " .. my_name .. " [options] portname alias" .. 14 | "\nWith no options, set 'alias' as alias for 'portname'".. 15 | "\nOptions:" .. 16 | "\n -u, --unalias remove 'alias' as an alias for 'portname'" .. 17 | "\n -l, --list list aliases for port and exit" .. 18 | "\n -h, --help display this help message" .. 19 | "\n -v, --version display version information and exit" .. 20 | "\n\n" 21 | 22 | function ShowVersion() 23 | print(fmt("%s: %s, %s", my_name, jack._VERSION, jack._JACK_VERSION)) 24 | end 25 | 26 | function ShowUsage(errmsg) 27 | if errmsg then print(errmsg, "\n") end 28 | print(USAGE) 29 | if errmsg then os.exit() end 30 | end 31 | 32 | short_opts = "luhv" 33 | long_opts = { 34 | list = 'l', 35 | unalias = 'u', 36 | help = 'h', 37 | version = 'v', 38 | } 39 | 40 | optarg, optind, opterr = getopt(arg, short_opts, long_opts) 41 | 42 | if opterr then ShowUsage(opterr) end 43 | 44 | if optarg.h then ShowUsage() os.exit(true) end 45 | 46 | if optarg.v then ShowVersion() os.exit(true) end 47 | 48 | portname = arg[optind] 49 | alias = arg[optind+1] 50 | 51 | nargs = #arg -optind +1 52 | 53 | if (optarg.l and nargs < 1) or (not optarg.l and nargs < 2 ) then 54 | ShowUsage("Not enough arguments") 55 | end 56 | 57 | c = jack.client_open(my_name, { no_start_server=true }) 58 | 59 | if not jack.nport_exists(c, portname) then 60 | error("No port named '".. portname .."'") 61 | end 62 | 63 | if optarg.l then 64 | alias1, alias2 = jack.nport_aliases(c, portname) 65 | print(fmt("Aliases for %s = %s %s", portname, alias1 or "", alias2 or "")) 66 | ok = true 67 | elseif optarg.u then 68 | print("Unsetting "..alias.." as alias for "..portname.." ...") 69 | ok = pcall(jack.nport_unset_alias, c, portname, alias) 70 | else 71 | print("Setting "..alias.." as alias for "..portname.." ...") 72 | ok = pcall(jack.nport_set_alias, c, portname, alias) 73 | end 74 | 75 | if not ok then print("Failed") end 76 | os.exit(ok) 77 | -------------------------------------------------------------------------------- /examples/bufsize.lua: -------------------------------------------------------------------------------- 1 | -- LuaJack example: bufsize.lua 2 | -- 3 | -- LuaJack version of JACK's bufsize.c example. 4 | 5 | local jack = require("luajack") 6 | 7 | local USAGE = 8 | "Usage: lua " .. arg[0] .. " [bufsize]" .. 9 | "\nPrints the buffer size and the sample rate.\n" .. 10 | "Optionally sets the buffer size (bufsize range: 1-8192).\n\n" 11 | 12 | local function Usage(errmsg) 13 | if errmsg then print(errmsg, "\n") end 14 | print(USAGE) 15 | os.exit() 16 | end 17 | 18 | if #arg > 0 then 19 | bufsize = math.tointeger(arg[1]) 20 | if not bufsize then Usage() end 21 | if bufsize < 1 or bufsize > 8192 then Usage("Invalid bufsize") end 22 | end 23 | 24 | c = jack.client_open(arg[0]) 25 | 26 | print("Buffer size = " .. jack.buffer_size(c)) 27 | print("Sample rate = " .. jack.sample_rate(c)) 28 | 29 | if bufsize then 30 | print("Setting buffer size to " .. bufsize) 31 | jack.set_buffer_size(c, bufsize) 32 | end 33 | 34 | 35 | -------------------------------------------------------------------------------- /examples/c_api/Makefile: -------------------------------------------------------------------------------- 1 | 2 | clean : 3 | @cd example1; $(MAKE) -s $@ 4 | @cd example2; $(MAKE) -s $@ 5 | @cd example3; $(MAKE) -s $@ 6 | @cd example4; $(MAKE) -s $@ 7 | @cd example5; $(MAKE) -s $@ 8 | -------------------------------------------------------------------------------- /examples/c_api/example1/Makefile: -------------------------------------------------------------------------------- 1 | 2 | Tgt := mymodule 3 | Src := $(wildcard *.c) 4 | Objs := $(Src:.c=.o) 5 | 6 | INCDIR = -I/usr/include 7 | LIBDIR = -L/usr/lib 8 | LIBS = -ljack -lluajack -lpthread 9 | 10 | COPT += -O2 11 | COPT += -Wall -Wextra -Wpedantic 12 | COPT += -std=gnu99 13 | COPT += -fpic 14 | 15 | override CFLAGS = $(COPT) $(INCDIR) 16 | 17 | default: build 18 | 19 | clean: 20 | @-rm -f *.so *.dll *.o *.err *.map *.S *~ *.log 21 | 22 | build: clean $(Tgt) 23 | 24 | $(Tgt): $(Objs) 25 | @-$(CC) -shared -o $(Tgt).so $(Objs) $(LIBDIR) $(LIBS) 26 | @-rm -f $(Objs) 27 | @echo 28 | 29 | -------------------------------------------------------------------------------- /examples/c_api/example1/README.md: -------------------------------------------------------------------------------- 1 | 2 | ####LuaJack C-API - Example 1 3 | 4 | This is a simple example showing how to use the LuaJack C-API (luajack.h) to 5 | implement the RT callbacks in C, which may be beneficial for computational 6 | intensive audio processing. 7 | 8 | The idea is to implement the RT callbacks in a Lua module written in C 9 | (mymodule.c in this example), and to load and initialize it in the process 10 | chunk. The main and the process chunk are written in Lua, as usual. 11 | 12 | The C module exports an initialization function that receives the LuaJack 13 | objects it will operate on (clients, ports, threads, ringbuffers), and 14 | registers its RT callback(s) using the LuaJack C-API. 15 | 16 | By doing so, when the client is active and an RT event occurs, LuaJack 17 | executes the module's C callbacks instead of executing Lua code as it would 18 | do in the usual case where the process chunk registers Lua callbacks. 19 | 20 | #####Running the example 21 | 22 | The example creates two audio ports (in and out) and just copies the input 23 | samples to the output. We can test it using the metronome example (metro.lua) 24 | to generate tones, connecting its output to our 'in', and our 'out' to a 25 | system output port. In order to do so: 26 | 27 | 1. Compile our example: 28 | 29 | ```sh 30 | $ make 31 | ``` 32 | 33 | This will generate the mymodule.so library. Lua searches for this library when 34 | the process chunk executes _require("mymodule")_. 35 | 36 | 37 | 2. Start the JACK server, using [QjackCtl](http://qjackctl.sourceforge.net/). 38 | 39 | 3. Launch the metronome: 40 | 41 | ```sh 42 | $ lua metro.lua 43 | ``` 44 | 45 | 4. Launch our example: 46 | 47 | ```sh 48 | $ lua main.lua 49 | ``` 50 | 51 | (You may need to tell the linker the path to libluajack.so. See the configure.sh 52 | script.) 53 | 54 | 5. Using the QjackCtl GUI, connect the ports as shown here: 55 | 56 | ``` 57 | metro.lua/120_bpm ----> mymodule/in 58 | mymodule/in ----------> system/playback_1 59 | ``` 60 | 61 | If everything goes fine, you should hear the tone from the speaker corresponding 62 | to system/playback-1. 63 | 64 | 65 | -------------------------------------------------------------------------------- /examples/c_api/example1/configure.sh: -------------------------------------------------------------------------------- 1 | 2 | # Where ld can find libluajack.so: 3 | LibDir=/usr/local/lib 4 | 5 | case :$LD_LIBRARY_PATH: in 6 | *:$LibDir:*) ;; # already in 7 | *) export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$LibDir;; 8 | esac 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/c_api/example1/main.lua: -------------------------------------------------------------------------------- 1 | 2 | jack = require("luajack") 3 | 4 | -- Create a client and two audio ports: 5 | c = jack.client_open("mymodule", { no_start_server=true }) 6 | port_in = jack.input_audio_port(c, "in") 7 | port_out = jack.output_audio_port(c, "out") 8 | 9 | 10 | -- The process chunk just loads the C module implementing the RT callbacks, 11 | -- and initializes it, passing it the Luajack objects (and possibly other 12 | -- parameters). 13 | -- The C module will register its RT callbacks using the luajack.h C API. 14 | PROCESS_CHUNK = [[ 15 | c, port_in, port_out = table.unpack(arg) 16 | require("mymodule").init(c, port_in, port_out) 17 | ]] 18 | 19 | jack.process_load(c, PROCESS_CHUNK, c, port_in, port_out) 20 | 21 | jack.shutdown_callback(c, function(_,code,reason) error(reason) end) 22 | 23 | jack.activate(c) 24 | 25 | jack.sleep() 26 | 27 | 28 | -------------------------------------------------------------------------------- /examples/c_api/example1/mymodule.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2015 Stefano Trettel 4 | * 5 | * Software repository: LuaJack, https://github.com/stetre/luajack 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include "luajack.h" 28 | 29 | #define nframes_t jack_nframes_t 30 | #define sample_t jack_default_audio_sample_t 31 | 32 | /* LuaJack objects */ 33 | luajack_t *client = NULL; 34 | luajack_t *port_in = NULL; 35 | luajack_t *port_out = NULL; 36 | 37 | /*----------------------------------------------------------------------* 38 | | RT Callbacks | 39 | *----------------------------------------------------------------------*/ 40 | 41 | static int Process(nframes_t nframes, void *arg) 42 | /* This is the process callback (JackProcessCallback). 43 | * It just copies the input port buffer to the output port buffer. 44 | */ 45 | { 46 | (void)arg; /* not used */ 47 | sample_t *buf_in, *buf_out; 48 | 49 | /* Retrieve the buffers */ 50 | buf_in = luajack_get_buffer(port_in); 51 | buf_out = luajack_get_buffer(port_out); 52 | 53 | /* Copy the input to the output */ 54 | if(buf_in && buf_out) 55 | memcpy(buf_out, buf_in, nframes *sizeof(sample_t)); 56 | 57 | return 0; 58 | } 59 | 60 | /*----------------------------------------------------------------------* 61 | | Initialization | 62 | *----------------------------------------------------------------------*/ 63 | 64 | static int Init(lua_State *L) 65 | /* This is the module initialization function. It must be called by 66 | * The process chunk must call it when loaded (before the client is 67 | * activated) and pass it the LuaJack objects (in this case a client 68 | * and two ports) and any other relevant parameters. 69 | * 70 | * Since this function is called when the client is not active, it need 71 | * not be real-time safe. 72 | */ 73 | { 74 | client = luajack_checkclient(L, 1); 75 | port_in = luajack_checkport(L, 2); 76 | port_out = luajack_checkport(L, 3); 77 | 78 | /* Register the RT process callback */ 79 | if(luajack_set_process_callback(client, Process, NULL/*arg*/) != 0) 80 | return luaL_error(L, "Cannot register process callback"); 81 | return 0; 82 | } 83 | 84 | static const struct luaL_Reg Functions[] = 85 | { 86 | { "init", Init }, 87 | { NULL, NULL } /* sentinel */ 88 | }; 89 | 90 | int luaopen_mymodule(lua_State *L) 91 | /* Lua calls this function when the module is require()d. 92 | * It creates the 'mymodule' table and add its function to it 93 | * (in this case we only have the mymodule.init() function). 94 | */ 95 | { 96 | lua_newtable(L); 97 | luaL_setfuncs(L, Functions, 0); 98 | return 1; 99 | } 100 | 101 | -------------------------------------------------------------------------------- /examples/c_api/example2/Makefile: -------------------------------------------------------------------------------- 1 | 2 | Tgt := mymodule 3 | Src := $(wildcard *.c) 4 | Objs := $(Src:.c=.o) 5 | 6 | INCDIR = -I/usr/include 7 | LIBDIR = -L/usr/lib 8 | LIBS = -ljack -lluajack -lpthread 9 | 10 | COPT += -O2 11 | COPT += -Wall -Wextra -Wpedantic 12 | COPT += -std=gnu99 13 | COPT += -fpic 14 | 15 | override CFLAGS = $(COPT) $(INCDIR) 16 | 17 | default: build 18 | 19 | clean: 20 | @-rm -f *.so *.dll *.o *.err *.map *.S *~ *.log 21 | 22 | build: clean $(Tgt) 23 | 24 | $(Tgt): $(Objs) 25 | @-$(CC) -shared -o $(Tgt).so $(Objs) $(LIBDIR) $(LIBS) 26 | @-rm -f $(Objs) 27 | @echo 28 | 29 | -------------------------------------------------------------------------------- /examples/c_api/example2/README.md: -------------------------------------------------------------------------------- 1 | 2 | ####LuaJack C-API - Example 2 3 | 4 | This example enhances Example 1 by adding a client thread (thread.lua) and 5 | a bidirectional communication channel between the process context and the 6 | thread context. 7 | 8 | The communication channel is implemented with a couple of ringbuffers, and in 9 | this example it is used just to exchange pings and pongs between the two contexts. 10 | 11 | In a real life application the thread context would typically implement some sort 12 | of GUI, and the bidirectional channel would be used to carry control commands 13 | (in the thread to process direction) and notifications (in the process to thread 14 | directions). 15 | 16 | #####Running the example 17 | 18 | Same as Example 1. 19 | 20 | -------------------------------------------------------------------------------- /examples/c_api/example2/configure.sh: -------------------------------------------------------------------------------- 1 | 2 | # Where ld can find libluajack.so: 3 | LibDir=/usr/local/lib 4 | 5 | case :$LD_LIBRARY_PATH: in 6 | *:$LibDir:*) ;; # already in 7 | *) export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$LibDir;; 8 | esac 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/c_api/example2/main.lua: -------------------------------------------------------------------------------- 1 | 2 | RBUFSIZE = 1000 3 | 4 | jack = require("luajack") 5 | 6 | -- Create a client and two audio ports: 7 | c = jack.client_open("mymodule", { no_start_server=true }) 8 | port_in = jack.input_audio_port(c, "in") 9 | port_out = jack.output_audio_port(c, "out") 10 | rbuf1 = jack.ringbuffer(c, RBUFSIZE, true) -- process to thread 11 | rbuf2 = jack.ringbuffer(c, RBUFSIZE, true) -- thread to process 12 | 13 | 14 | -- The process chunk just loads the C module implementing the RT callbacks, 15 | -- and initializes it, passing it the Luajack objects (and possibly other 16 | -- parameters). 17 | -- The C module will register its RT callbacks using the luajack.h C API. 18 | PROCESS_CHUNK = [[ 19 | require("mymodule").init( table.unpack(arg)) 20 | ]] 21 | 22 | t = jack.thread_loadfile(c, "thread", rbuf1, rbuf2) 23 | jack.process_load(c, PROCESS_CHUNK, c, port_in, port_out, t, rbuf2, rbuf1) 24 | 25 | jack.shutdown_callback(c, function(_,code,reason) error(reason) end) 26 | 27 | jack.activate(c) 28 | 29 | jack.sleep() 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/c_api/example2/mymodule.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2015 Stefano Trettel 4 | * 5 | * Software repository: LuaJack, https://github.com/stetre/luajack 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include "luajack.h" 28 | 29 | #define nframes_t jack_nframes_t 30 | #define sample_t jack_default_audio_sample_t 31 | 32 | /* LuaJack objects */ 33 | luajack_t *client = NULL; 34 | luajack_t *port_in = NULL; 35 | luajack_t *port_out = NULL; 36 | luajack_t *thread = NULL; /* the client thread to communicate with */ 37 | luajack_t *rbuf_in = NULL; /* thread to process ringbuffer */ 38 | luajack_t *rbuf_out = NULL; /* process to thread ringbuffer */ 39 | 40 | 41 | /*----------------------------------------------------------------------* 42 | | RT Callbacks | 43 | *----------------------------------------------------------------------*/ 44 | 45 | #define MAXDATA 256 /* max length of data in a ringbuffer message */ 46 | 47 | static int CheckRingbuffer(void) 48 | /* Checks if a ping is available in the ringbuffer, and if so replies 49 | * with a pong. */ 50 | { 51 | uint32_t tag; 52 | char data[MAXDATA]; 53 | size_t len; 54 | 55 | if(!luajack_ringbuffer_read(rbuf_in, &tag, data, MAXDATA, &len)) 56 | return 0; /* no messages available to read */ 57 | 58 | /* ping received */ 59 | if(len < 4) 60 | return luajack_error("invalid message from thread"); 61 | 62 | /* turn 'ping' into 'pong' and send it back with the same tag */ 63 | data[1]='o'; 64 | luajack_ringbuffer_write(rbuf_out, tag, data, len); 65 | 66 | /* signal the thread */ 67 | luajack_signal(client, thread); 68 | return 0; 69 | } 70 | 71 | 72 | 73 | static int Process(nframes_t nframes, void *arg) 74 | /* This is the process callback (JackProcessCallback). 75 | * It just copies the input port buffer to the output port buffer. 76 | */ 77 | { 78 | (void)arg; /* not used */ 79 | sample_t *buf_in, *buf_out; 80 | 81 | CheckRingbuffer(); /* read messages from the ringbuffer */ 82 | 83 | /* Retrieve the buffers */ 84 | buf_in = luajack_get_buffer(port_in); 85 | buf_out = luajack_get_buffer(port_out); 86 | 87 | /* Copy the input to the output */ 88 | if(buf_in && buf_out) 89 | memcpy(buf_out, buf_in, nframes *sizeof(sample_t)); 90 | 91 | return 0; 92 | } 93 | 94 | /*----------------------------------------------------------------------* 95 | | Initialization | 96 | *----------------------------------------------------------------------*/ 97 | 98 | static int Init(lua_State *L) 99 | /* This is the module initialization function. It must be called by 100 | * The process chunk must call it when loaded (before the client is 101 | * activated) and pass it the LuaJack objects (in this case a client 102 | * and two ports) and any other relevant parameters. 103 | * 104 | * Since this function is called when the client is not active, it need 105 | * not be real-time safe. 106 | */ 107 | { 108 | client = luajack_checkclient(L, 1); 109 | port_in = luajack_checkport(L, 2); 110 | port_out = luajack_checkport(L, 3); 111 | thread = luajack_checkthread(L, 4); 112 | rbuf_in = luajack_checkringbuffer(L, 5); 113 | rbuf_out = luajack_checkringbuffer(L, 6); 114 | 115 | /* Register the RT process callback */ 116 | if(luajack_set_process_callback(client, Process, NULL/*arg*/) != 0) 117 | return luaL_error(L, "Cannot register process callback"); 118 | return 0; 119 | } 120 | 121 | static const struct luaL_Reg Functions[] = 122 | { 123 | { "init", Init }, 124 | { NULL, NULL } /* sentinel */ 125 | }; 126 | 127 | int luaopen_mymodule(lua_State *L) 128 | /* Lua calls this function when the module is require()d. 129 | * It creates the 'mymodule' table and add its function to it 130 | * (in this case we only have the mymodule.init() function). 131 | */ 132 | { 133 | lua_newtable(L); 134 | luaL_setfuncs(L, Functions, 0); 135 | return 1; 136 | } 137 | 138 | -------------------------------------------------------------------------------- /examples/c_api/example2/thread.lua: -------------------------------------------------------------------------------- 1 | 2 | -- This is the thread chunk, loaded by the main chunk with jack.thread_load(). 3 | -- It just sends pings and waits for pongs using the ringbuffers created by the 4 | -- main chunk for process<->thread communication. 5 | 6 | rbuf_in = arg[1] -- process to thread ringbuffer 7 | rbuf_out = arg[2] -- thread to process ringbuffer 8 | 9 | client, thread = jack.self() 10 | 11 | count = 1 -- message counter (we will use it as tag) 12 | next_receive = 1 -- next expected tag 13 | 14 | 15 | function send_ping() 16 | -- Sends a 'ping' to the process context via the 'out' ringbuffer. 17 | jack.ringbuffer_write(rbuf_out, count, "ping"..count) 18 | count = count + 1 19 | end 20 | 21 | 22 | function check_ringbuffer() 23 | -- Retrieves all the messages available in the 'in' ringbuffer, 24 | -- checks them, and sends another ping. 25 | while true do 26 | local tag, data = jack.ringbuffer_read(rbuf_in) 27 | if not tag then return end -- no more messages available 28 | 29 | if (tag % 100) == 0 then -- print only every 100 ping-pong exchanges 30 | print("[thread] received tag="..tag.." data='"..data.."'") 31 | end 32 | 33 | -- Check that the pong is correct 34 | if tag ~= next_receive then 35 | error("[thread] unexpected tag="..tag.." from process chunk") 36 | end 37 | if data ~= "pong"..tag then 38 | error("[thread] unexpected data='"..data.."' from process chunk") 39 | end 40 | 41 | next_receive = next_receive + 1 42 | 43 | send_ping() 44 | end 45 | end 46 | 47 | 48 | function main_loop1() 49 | while true do 50 | jack.wait() -- block until someone invokes jack.signal() on this thread. 51 | check_ringbuffer() 52 | end 53 | end 54 | 55 | 56 | function main_loop2() 57 | while true do 58 | jack.testcancel() -- cancellation point 59 | check_ringbuffer() 60 | -- ... 61 | -- check here if a GUI event occurred, and if so, process it 62 | -- ... 63 | end 64 | end 65 | 66 | 67 | -- Send the first ping and enter the thread's main loop: 68 | main_loop = main_loop1 -- See NOTE below. 69 | print("using main_loop" .. (main_loop==main_loop1 and 1 or 2)) 70 | send_ping() 71 | main_loop() 72 | 73 | 74 | -- NOTE: This example shows two alternative main loops, implemented in the 75 | -- main_loop1() and main_loop2() functions, respectively (to select the main 76 | -- loop, just change the assignment of the main_loop variable above). 77 | -- 78 | -- In the main_loop1() alternative, the thread blocks on jack.wait() when it 79 | -- has read all the available messages. When the process context sends a ping, 80 | -- it also calls jack.signal() on the thread, which causes jack.wait() to 81 | -- return. 82 | -- 83 | -- The use of jack.wait(), however, may be unacceptable if, for example, the 84 | -- thread needs to continuosly listen for GUI events (or for messages on a 85 | -- socket, data on a pipe, or whatever). 86 | -- 87 | -- In such cases an implementation such as the main_loop2() alternative is 88 | -- more appropriate. It avoids blocking on jack.wait(), and continuosly 89 | -- checks for incoming messages in the ringbuffer and then for other kind 90 | -- of events (GUI, sockets, etc). 91 | -- 92 | -- Notice the use of jack.testcancel() to insert a cancellation point (it is 93 | -- just a binding to pthread_testcancel()). This is needed to ensure that LuaJack 94 | -- can cancel the thread if the main exits for some reason. 95 | -- It is not needed if jack.wait() or any other function that is itself a 96 | -- cancellation point (e.g. socket.select()) is already used in the main loop. 97 | -- (If you are not sure that this is the case, use it). 98 | -- 99 | 100 | -------------------------------------------------------------------------------- /examples/c_api/example3/Makefile: -------------------------------------------------------------------------------- 1 | 2 | Tgt := mymodule 3 | Src := $(wildcard *.c) 4 | Objs := $(Src:.c=.o) 5 | 6 | INCDIR = -I/usr/include 7 | LIBDIR = -L/usr/lib 8 | LIBS = -ljack -lluajack -lpthread 9 | 10 | COPT += -O2 11 | COPT += -Wall -Wextra -Wpedantic 12 | COPT += -std=gnu99 13 | COPT += -fpic 14 | 15 | override CFLAGS = $(COPT) $(INCDIR) 16 | 17 | default: build 18 | 19 | clean: 20 | @-rm -f *.so *.dll *.o *.err *.map *.S *~ *.log 21 | 22 | build: clean $(Tgt) 23 | 24 | $(Tgt): $(Objs) 25 | @-$(CC) -shared -o $(Tgt).so $(Objs) $(LIBDIR) $(LIBS) 26 | @-rm -f $(Objs) 27 | @echo 28 | 29 | -------------------------------------------------------------------------------- /examples/c_api/example3/README.md: -------------------------------------------------------------------------------- 1 | 2 | ####LuaJack C-API - Example 3 3 | 4 | This example enhances Example 2 by adding, in the client thread, a simple GUI 5 | that displays the number of pings and pongs exchanged with the process thread 6 | via the ringbuffers. 7 | 8 | The GUI is implemented using [MoonFLTK](https://github.com/stetre/moonfltk). 9 | 10 | #####Running the example 11 | 12 | Same as Example 1. 13 | 14 | -------------------------------------------------------------------------------- /examples/c_api/example3/configure.sh: -------------------------------------------------------------------------------- 1 | 2 | # Where ld can find libluajack.so: 3 | LibDir=/usr/local/lib 4 | 5 | case :$LD_LIBRARY_PATH: in 6 | *:$LibDir:*) ;; # already in 7 | *) export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$LibDir;; 8 | esac 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/c_api/example3/main.lua: -------------------------------------------------------------------------------- 1 | 2 | RBUFSIZE = 1000 3 | 4 | jack = require("luajack") 5 | 6 | -- Create a client and two audio ports: 7 | c = jack.client_open("mymodule", { no_start_server=true }) 8 | port_in = jack.input_audio_port(c, "in") 9 | port_out = jack.output_audio_port(c, "out") 10 | rbuf1 = jack.ringbuffer(c, RBUFSIZE, true) -- process to thread 11 | rbuf2 = jack.ringbuffer(c, RBUFSIZE, true) -- thread to process 12 | 13 | 14 | -- The process chunk just loads the C module implementing the RT callbacks, 15 | -- and initializes it, passing it the Luajack objects (and possibly other 16 | -- parameters). 17 | -- The C module will register its RT callbacks using the luajack.h C API. 18 | PROCESS_CHUNK = [[ 19 | require("mymodule").init( table.unpack(arg)) 20 | ]] 21 | 22 | t = jack.thread_loadfile(c, "thread", rbuf1, rbuf2) 23 | jack.process_load(c, PROCESS_CHUNK, c, port_in, port_out, t, rbuf2, rbuf1) 24 | 25 | jack.shutdown_callback(c, function(_,code,reason) error(reason) end) 26 | 27 | jack.activate(c) 28 | 29 | jack.sleep() 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/c_api/example3/mymodule.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2015 Stefano Trettel 4 | * 5 | * Software repository: LuaJack, https://github.com/stetre/luajack 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include "luajack.h" 28 | 29 | #define nframes_t jack_nframes_t 30 | #define sample_t jack_default_audio_sample_t 31 | 32 | /* LuaJack objects */ 33 | luajack_t *client = NULL; 34 | luajack_t *port_in = NULL; 35 | luajack_t *port_out = NULL; 36 | luajack_t *thread = NULL; /* the client thread to communicate with */ 37 | luajack_t *rbuf_in = NULL; /* thread to process ringbuffer */ 38 | luajack_t *rbuf_out = NULL; /* process to thread ringbuffer */ 39 | 40 | 41 | /*----------------------------------------------------------------------* 42 | | RT Callbacks | 43 | *----------------------------------------------------------------------*/ 44 | 45 | #define MAXDATA 256 /* max length of data in a ringbuffer message */ 46 | 47 | static int CheckRingbuffer(void) 48 | /* Checks if a ping is available in the ringbuffer, and if so replies 49 | * with a pong. */ 50 | { 51 | uint32_t tag; 52 | char data[MAXDATA]; 53 | size_t len; 54 | 55 | if(!luajack_ringbuffer_read(rbuf_in, &tag, data, MAXDATA, &len)) 56 | return 0; /* no messages available to read */ 57 | 58 | /* ping received */ 59 | if(len < 4) 60 | return luajack_error("invalid message from thread"); 61 | 62 | /* turn 'ping' into 'pong' and send it back with the same tag */ 63 | data[1]='o'; 64 | luajack_ringbuffer_write(rbuf_out, tag, data, len); 65 | 66 | /* signal the thread */ 67 | luajack_signal(client, thread); 68 | return 0; 69 | } 70 | 71 | 72 | 73 | static int Process(nframes_t nframes, void *arg) 74 | /* This is the process callback (JackProcessCallback). 75 | * It just copies the input port buffer to the output port buffer. 76 | */ 77 | { 78 | (void)arg; /* not used */ 79 | sample_t *buf_in, *buf_out; 80 | 81 | CheckRingbuffer(); /* read messages from the ringbuffer */ 82 | 83 | /* Retrieve the buffers */ 84 | buf_in = luajack_get_buffer(port_in); 85 | buf_out = luajack_get_buffer(port_out); 86 | 87 | /* Copy the input to the output */ 88 | if(buf_in && buf_out) 89 | memcpy(buf_out, buf_in, nframes *sizeof(sample_t)); 90 | 91 | return 0; 92 | } 93 | 94 | /*----------------------------------------------------------------------* 95 | | Initialization | 96 | *----------------------------------------------------------------------*/ 97 | 98 | static int Init(lua_State *L) 99 | /* This is the module initialization function. It must be called by 100 | * The process chunk must call it when loaded (before the client is 101 | * activated) and pass it the LuaJack objects (in this case a client 102 | * and two ports) and any other relevant parameters. 103 | * 104 | * Since this function is called when the client is not active, it need 105 | * not be real-time safe. 106 | */ 107 | { 108 | client = luajack_checkclient(L, 1); 109 | port_in = luajack_checkport(L, 2); 110 | port_out = luajack_checkport(L, 3); 111 | thread = luajack_checkthread(L, 4); 112 | rbuf_in = luajack_checkringbuffer(L, 5); 113 | rbuf_out = luajack_checkringbuffer(L, 6); 114 | 115 | /* Register the RT process callback */ 116 | if(luajack_set_process_callback(client, Process, NULL/*arg*/) != 0) 117 | return luaL_error(L, "Cannot register process callback"); 118 | return 0; 119 | } 120 | 121 | static const struct luaL_Reg Functions[] = 122 | { 123 | { "init", Init }, 124 | { NULL, NULL } /* sentinel */ 125 | }; 126 | 127 | int luaopen_mymodule(lua_State *L) 128 | /* Lua calls this function when the module is require()d. 129 | * It creates the 'mymodule' table and add its function to it 130 | * (in this case we only have the mymodule.init() function). 131 | */ 132 | { 133 | lua_newtable(L); 134 | luaL_setfuncs(L, Functions, 0); 135 | return 1; 136 | } 137 | 138 | -------------------------------------------------------------------------------- /examples/c_api/example3/thread.lua: -------------------------------------------------------------------------------- 1 | 2 | -- This is the thread chunk, loaded by the main chunk with jack.thread_load(). 3 | -- It just sends pings and waits for pongs using the ringbuffers created by the 4 | -- main chunk for process<->thread communication. 5 | 6 | fl = require("moonfltk") 7 | 8 | rbuf_in = arg[1] -- process to thread ringbuffer 9 | rbuf_out = arg[2] -- thread to process ringbuffer 10 | 11 | client, thread = jack.self() 12 | 13 | next_send = 1 -- message counter (we will use it as tag) 14 | next_receive = 1 -- next expected tag 15 | 16 | 17 | function send_ping() 18 | -- Sends a 'ping' to the process context via the 'out' ringbuffer. 19 | jack.ringbuffer_write(rbuf_out, next_send, "ping"..next_send) 20 | next_send = next_send + 1 21 | end 22 | 23 | 24 | function check_ringbuffer() 25 | -- Retrieves all the messages available in the 'in' ringbuffer, 26 | -- checks them, and sends another ping. 27 | while true do 28 | local tag, data = jack.ringbuffer_read(rbuf_in) 29 | if not tag then return end -- no more messages available 30 | 31 | if (tag % 100) == 0 then -- print only every 100 ping-pong exchanges 32 | print("[thread] received tag="..tag.." data='"..data.."'") 33 | end 34 | 35 | o_ping:value(next_send-1) 36 | o_pong:value(next_receive-1) 37 | 38 | -- Check that the pong is correct 39 | if tag ~= next_receive then 40 | error("[thread] unexpected tag="..tag.." from process chunk") 41 | end 42 | if data ~= "pong"..tag then 43 | error("[thread] unexpected data='"..data.."' from process chunk") 44 | end 45 | 46 | next_receive = next_receive + 1 47 | 48 | send_ping() 49 | end 50 | end 51 | 52 | 53 | win = fl.window(340, 180, "LuaJack C-API - Example 3") 54 | o_ping = fl.output(60, 40, 260, 30, "pings") 55 | o_pong = fl.output(60, 100, 260, 30, "pongs") 56 | win:done() 57 | win:show() 58 | 59 | function main_loop() 60 | while true do 61 | jack.testcancel() -- cancellation point 62 | check_ringbuffer() 63 | if not fl.check() then os.exit() end 64 | end 65 | end 66 | 67 | 68 | send_ping() 69 | main_loop() 70 | 71 | -------------------------------------------------------------------------------- /examples/c_api/example4/Makefile: -------------------------------------------------------------------------------- 1 | 2 | Tgt := myecho 3 | Src := $(wildcard *.c) 4 | Objs := $(Src:.c=.o) 5 | 6 | INCDIR = -I/usr/include 7 | LIBDIR = -L/usr/lib 8 | LIBS = -ljack -lluajack -lpthread 9 | 10 | COPT += -O2 11 | COPT += -Wall -Wextra -Wpedantic 12 | COPT += -std=gnu99 13 | COPT += -fpic 14 | 15 | override CFLAGS = $(COPT) $(INCDIR) 16 | 17 | default: build 18 | 19 | clean: 20 | @-rm -f *.so *.dll *.o *.err *.map *.S *~ *.log 21 | 22 | build: clean $(Tgt) 23 | 24 | $(Tgt): $(Objs) 25 | @-$(CC) -shared -o $(Tgt).so $(Objs) $(LIBDIR) $(LIBS) 26 | @-rm -f $(Objs) 27 | @echo 28 | 29 | -------------------------------------------------------------------------------- /examples/c_api/example4/README.md: -------------------------------------------------------------------------------- 1 | 2 | ####LuaJack C-API - Example 4 3 | 4 | Similar to examples 2 and 3, implements an echo effect with controllable parameters 5 | (gain and delay) and a fancy [MoonFLTK](https://github.com/stetre/moonfltk) GUI. 6 | 7 | -------------------------------------------------------------------------------- /examples/c_api/example4/gui.lua: -------------------------------------------------------------------------------- 1 | -- This is the GUI script. It is loaded by the main chunk with 2 | -- jack.thread_loadfile() and runs in a separate thread. 3 | 4 | fl = require("moonfltk") 5 | 6 | rbuf_in = arg[1] -- process to gui ringbuffer 7 | rbuf_out = arg[2] -- gui to process ringbuffer 8 | 9 | client, gui = jack.self() 10 | 11 | ------------------------------------------------------------------ 12 | -- GUI <-> process protocol 13 | ------------------------------------------------------------------ 14 | 15 | TAG_ON = 1 16 | TAG_OFF = 2 17 | 18 | function send_onoff() 19 | -- Sends a control message to the process, via the 'out' ringbuffer. 20 | local on, g, d = onoff:value(), gain:value(), delay:value() 21 | if not on or g==0 or d==0 then 22 | jack.ringbuffer_write(rbuf_out, TAG_OFF) 23 | else 24 | jack.ringbuffer_write(rbuf_out, TAG_ON, string.pack("ff",g,d)) 25 | end 26 | end 27 | 28 | function check_ringbuffer() 29 | -- Retrieves all the messages available in the 'in' ringbuffer. 30 | while true do 31 | local tag, data = jack.ringbuffer_read(rbuf_in) 32 | if not tag then return end -- no more messages available 33 | -- ... STATUS_RSP ... 34 | end 35 | end 36 | 37 | ------------------------------------------------------------------ 38 | -- GUI implementation 39 | ------------------------------------------------------------------ 40 | 41 | W, H = 300, 400 -- window width and height 42 | M = math.floor(W/6) -- margin (dials) 43 | M1 = math.floor(W/20) -- margin (button) 44 | D = math.floor((W - 3*M)/2) -- dial side 45 | 46 | win = fl.double_window(100, 100, W, H, "Echo") 47 | 48 | o = fl.dial(M, M, D, D, "Delay") 49 | o:type('line') 50 | o:align('top') 51 | o:color(0xff007700) 52 | o:range(0, 4.0) 53 | o:step(0.01) 54 | o:value(0) 55 | o:callback(function(o) 56 | local val = o:value() 57 | o:label(string.format("Delay\n%.2f",val)) 58 | send_onoff() 59 | end) 60 | delay = o 61 | 62 | 63 | o = fl.dial(M+D+M, M, D, D, "Gain") 64 | o:type('line') 65 | o:align('top') 66 | o:color(0x7700ff00) 67 | o:range(0, 1.0) 68 | o:step(.1) 69 | o:value(0) 70 | o:callback(function(o) 71 | local val = o:value() 72 | o:label(string.format("Gain\n%.1f",val)) 73 | send_onoff() 74 | end) 75 | gain = o 76 | 77 | o = fl.button(M1, M+D+M1, W-2*M1, H-D-M-2*M1) 78 | o:value(true) 79 | o:down_box('thin down box') 80 | o:color(fl.BLACK) 81 | o:selection_color(0x77007700) 82 | o:labelsize(40) 83 | o:callback(function(o) 84 | local val = not o:value() 85 | o:value(val) 86 | o:label(val and "On" or "Off") 87 | send_onoff() 88 | end) 89 | onoff = o 90 | 91 | gain:do_callback() 92 | delay:do_callback() 93 | onoff:do_callback() 94 | 95 | win:done() 96 | win:resizable(win) 97 | win:show() 98 | 99 | ------------------------------------------------------------------ 100 | -- Main loop 101 | ------------------------------------------------------------------ 102 | 103 | while true do 104 | jack.testcancel() -- cancellation point 105 | check_ringbuffer() 106 | rv = fl.wait(0.1) 107 | if rv==nil or not win:shown() then os.exit() end 108 | if rv then -- an event occurred 109 | -- local ev = fl.event() 110 | -- ... 111 | end 112 | end 113 | 114 | -------------------------------------------------------------------------------- /examples/c_api/example4/main.lua: -------------------------------------------------------------------------------- 1 | 2 | jack = require("luajack") 3 | 4 | RBUFSIZE = 1000 5 | 6 | MIDI_CONTROLLER_OUT = "jack-keyboard:midi_out" 7 | SYNTH_MIDI_IN = "sinesynth.lua:midi_in" 8 | SYNTH_OUT = "sinesynth.lua:audio_out" 9 | CAPTURE_1 = "system:capture_1" 10 | PLAYBACK_1 = "system:playback_1" 11 | PLAYBACK_2 = "system:playback_2" 12 | NAME = "myecho" 13 | 14 | auto_connect = arg[1] or "manual" 15 | 16 | -- Create a client and two audio ports: 17 | c = jack.client_open(NAME, { no_start_server=true }) 18 | port_in = jack.input_audio_port(c, "audio_in") 19 | port_out = jack.output_audio_port(c, "audio_out") 20 | rbuf1 = jack.ringbuffer(c, RBUFSIZE, true) -- process to gui 21 | rbuf2 = jack.ringbuffer(c, RBUFSIZE, true) -- gui to process 22 | 23 | 24 | -- The process chunk just loads the C module implementing the RT callbacks, 25 | -- and initializes it, passing it the Luajack objects (and possibly other 26 | -- parameters). 27 | -- The C module will register its RT callbacks using the luajack.h C API. 28 | PROCESS_CHUNK = [[ 29 | require("myecho").init( table.unpack(arg)) 30 | ]] 31 | 32 | t = jack.thread_loadfile(c, "gui", rbuf1, rbuf2) 33 | jack.process_load(c, PROCESS_CHUNK, c, port_in, port_out, t, rbuf2, rbuf1) 34 | 35 | jack.shutdown_callback(c, function(_,code,reason) error(reason) end) 36 | 37 | jack.activate(c) 38 | 39 | function connect(c, port1, port2) 40 | if not jack.nport_connected(c, port1, port2) then 41 | jack.connect(c, port1, port2) 42 | end 43 | end 44 | 45 | if auto_connect=="auto" then 46 | connect(c, MIDI_CONTROLLER_OUT, SYNTH_MIDI_IN) 47 | connect(c, SYNTH_OUT, NAME..":audio_in") 48 | connect(c, NAME..":audio_out", PLAYBACK_1) 49 | connect(c, NAME..":audio_out", PLAYBACK_2) 50 | elseif auto_connect=="capture" then 51 | connect(c, CAPTURE_1, NAME..":audio_in") 52 | connect(c, NAME..":audio_out", PLAYBACK_1) 53 | connect(c, NAME..":audio_out", PLAYBACK_2) 54 | --elseif auto_connect=="myoption" then 55 | -- ... 56 | -- add your preferred connections here 57 | -- ... 58 | elseif auto_connect=="manual" then 59 | -- do not connect 60 | else error("invalid option '"..auto_connect.."'") 61 | end 62 | 63 | jack.sleep() 64 | 65 | 66 | -------------------------------------------------------------------------------- /examples/c_api/example5/Makefile: -------------------------------------------------------------------------------- 1 | 2 | Tgt := myecho 3 | Src := $(wildcard *.c) 4 | Objs := $(Src:.c=.o) 5 | 6 | INCDIR = -I/usr/include 7 | LIBDIR = -L/usr/lib 8 | LIBS = -ljack -lluajack -lpthread 9 | 10 | COPT += -O2 11 | COPT += -Wall -Wextra -Wpedantic 12 | COPT += -std=gnu99 13 | COPT += -fpic 14 | 15 | override CFLAGS = $(COPT) $(INCDIR) 16 | 17 | default: build 18 | 19 | clean: 20 | @-rm -f *.so *.dll *.o *.err *.map *.S *~ *.log 21 | 22 | build: clean $(Tgt) 23 | 24 | $(Tgt): $(Objs) 25 | @-$(CC) -shared -o $(Tgt).so $(Objs) $(LIBDIR) $(LIBS) 26 | @-rm -f $(Objs) 27 | @echo 28 | 29 | -------------------------------------------------------------------------------- /examples/c_api/example5/README.md: -------------------------------------------------------------------------------- 1 | 2 | ####LuaJack C-API - Example 5 3 | 4 | This example also implements an echo effect, as Example 4, but using a 5 | different approach where the process callback is implemented as a Lua 6 | function, while the only part implemented in C is the signal processing. 7 | 8 | This approach is less efficient than the one used in the previous examples, 9 | because it involves executing Lua code in the RT thread. On the other hand, 10 | it is easier to handle things such as ringbuffers or MIDI messages in Lua, so 11 | this approach is maybe the most suitable for applications where the only 12 | computational demanding part of the callback is really the audio signal processing. 13 | 14 | The example is composed of the following parts: 15 | 16 | - **myecho.c**: this is the Lua module that implements the signal processing 17 | in C. It exports a few functions for initializations and parameters control, 18 | and a signal processing function (_myecho.process_()) to be called by the 19 | Lua RT callback at every round. This function receives the pointers to the 20 | input and output buffers to operate on (plus their dimension in number of frames). 21 | The Lua callback can retrieve the pointers by means of the _jack.raw_buffer_() 22 | function. The module is to be _require_()d and used only by the process chunk 23 | (controlling it directly also from other threads, e.g. from the GUI, is a very 24 | very very bad idea). 25 | 26 | - **process.lua**: this is the process chunk, implementing the process callback 27 | in Lua. The callback's job is to handle the communication with the GUI (via 28 | ringbuffers), control the above mentioned C module on behalf of the GUI, and 29 | invoke the signal processing function. 30 | 31 | - **gui.lua**: this script implements the GUI, using [MoonFLTK](https://github.com/stetre/moonfltk) 32 | (the use of MoonFLTK is not mandatory). 33 | The script is executed in a separate (non RT) client thread, and communicates with the 34 | process thread via ringbuffers. 35 | 36 | - **main.lua**: this is the main chunk that glues all together. It creates the JACK 37 | client, the ringbuffers, registers the audio ports (and optionally connects them), 38 | executes the process and GUI threads, and finally enters the _jack.sleep_() endless loop. 39 | 40 | -------------------------------------------------------------------------------- /examples/c_api/example5/gui.lua: -------------------------------------------------------------------------------- 1 | -- This is the GUI script. It is loaded by the main chunk with 2 | -- jack.thread_loadfile() and runs in a separate thread. 3 | 4 | fl = require("moonfltk") 5 | 6 | rbuf_in = arg[1] -- process to gui ringbuffer 7 | rbuf_out = arg[2] -- gui to process ringbuffer 8 | 9 | client, gui = jack.self() 10 | 11 | ------------------------------------------------------------------ 12 | -- GUI <-> process protocol 13 | ------------------------------------------------------------------ 14 | 15 | TAG_ON = 1 16 | TAG_OFF = 2 17 | 18 | function send_onoff() 19 | -- Sends a control message to the process, via the 'out' ringbuffer. 20 | local on, g, d = onoff:value(), gain:value(), delay:value() 21 | if not on or g==0 or d==0 then 22 | jack.ringbuffer_write(rbuf_out, TAG_OFF) 23 | else 24 | jack.ringbuffer_write(rbuf_out, TAG_ON, string.pack("ff",g,d)) 25 | end 26 | end 27 | 28 | function check_ringbuffer() 29 | -- Retrieves all the messages available in the 'in' ringbuffer. 30 | while true do 31 | local tag, data = jack.ringbuffer_read(rbuf_in) 32 | if not tag then return end -- no more messages available 33 | -- ... STATUS_RSP ... 34 | end 35 | end 36 | 37 | ------------------------------------------------------------------ 38 | -- GUI implementation 39 | ------------------------------------------------------------------ 40 | 41 | W, H = 300, 400 -- window width and height 42 | M = math.floor(W/6) -- margin (dials) 43 | M1 = math.floor(W/20) -- margin (button) 44 | D = math.floor((W - 3*M)/2) -- dial side 45 | 46 | win = fl.double_window(100, 100, W, H, "Echo") 47 | 48 | o = fl.dial(M, M, D, D, "Delay") 49 | o:type('line') 50 | o:align('top') 51 | o:color(0xff007700) 52 | o:range(0, 4.0) 53 | o:step(0.01) 54 | o:value(0) 55 | o:callback(function(o) 56 | local val = o:value() 57 | o:label(string.format("Delay\n%.2f",val)) 58 | send_onoff() 59 | end) 60 | delay = o 61 | 62 | 63 | o = fl.dial(M+D+M, M, D, D, "Gain") 64 | o:type('line') 65 | o:align('top') 66 | o:color(0x7700ff00) 67 | o:range(0, 1.0) 68 | o:step(.1) 69 | o:value(0) 70 | o:callback(function(o) 71 | local val = o:value() 72 | o:label(string.format("Gain\n%.1f",val)) 73 | send_onoff() 74 | end) 75 | gain = o 76 | 77 | o = fl.button(M1, M+D+M1, W-2*M1, H-D-M-2*M1) 78 | o:value(true) 79 | o:down_box('thin down box') 80 | o:color(fl.BLACK) 81 | o:selection_color(0x77007700) 82 | o:labelsize(40) 83 | o:callback(function(o) 84 | local val = not o:value() 85 | o:value(val) 86 | o:label(val and "On" or "Off") 87 | send_onoff() 88 | end) 89 | onoff = o 90 | 91 | gain:do_callback() 92 | delay:do_callback() 93 | onoff:do_callback() 94 | 95 | win:done() 96 | win:resizable(win) 97 | win:show() 98 | 99 | ------------------------------------------------------------------ 100 | -- Main loop 101 | ------------------------------------------------------------------ 102 | 103 | while true do 104 | jack.testcancel() -- cancellation point 105 | check_ringbuffer() 106 | rv = fl.wait(0.1) 107 | if rv==nil or not win:shown() then os.exit() end 108 | if rv then -- an event occurred 109 | -- local ev = fl.event() 110 | -- ... if we want to handle other events ... 111 | end 112 | end 113 | 114 | -------------------------------------------------------------------------------- /examples/c_api/example5/main.lua: -------------------------------------------------------------------------------- 1 | 2 | jack = require("luajack") 3 | 4 | RBUFSIZE = 1000 5 | 6 | MIDI_CONTROLLER_OUT = "jack-keyboard:midi_out" 7 | SYNTH_MIDI_IN = "sinesynth.lua:midi_in" 8 | SYNTH_OUT = "sinesynth.lua:audio_out" 9 | CAPTURE_1 = "system:capture_1" 10 | PLAYBACK_1 = "system:playback_1" 11 | PLAYBACK_2 = "system:playback_2" 12 | NAME = "myecho" 13 | 14 | auto_connect = arg[1] or "manual" 15 | 16 | -- Create a client and two audio ports: 17 | c = jack.client_open(NAME, { no_start_server=true }) 18 | port_in = jack.input_audio_port(c, "audio_in") 19 | port_out = jack.output_audio_port(c, "audio_out") 20 | rbuf1 = jack.ringbuffer(c, RBUFSIZE, true) -- process to gui 21 | rbuf2 = jack.ringbuffer(c, RBUFSIZE, true) -- gui to process 22 | 23 | 24 | t = jack.thread_loadfile(c, "gui", rbuf1, rbuf2) 25 | jack.process_loadfile(c, "process", c, port_in, port_out, t, rbuf2, rbuf1) 26 | 27 | jack.shutdown_callback(c, function(_,code,reason) error(reason) end) 28 | 29 | jack.activate(c) 30 | 31 | function connect(c, port1, port2) 32 | if not jack.nport_connected(c, port1, port2) then 33 | jack.connect(c, port1, port2) 34 | end 35 | end 36 | 37 | if auto_connect=="auto" then 38 | connect(c, MIDI_CONTROLLER_OUT, SYNTH_MIDI_IN) 39 | connect(c, SYNTH_OUT, NAME..":audio_in") 40 | connect(c, NAME..":audio_out", PLAYBACK_1) 41 | connect(c, NAME..":audio_out", PLAYBACK_2) 42 | elseif auto_connect=="capture" then 43 | connect(c, CAPTURE_1, NAME..":audio_in") 44 | connect(c, NAME..":audio_out", PLAYBACK_1) 45 | connect(c, NAME..":audio_out", PLAYBACK_2) 46 | --elseif auto_connect=="myoption" then 47 | -- ... 48 | -- add your preferred connections here 49 | -- ... 50 | elseif auto_connect=="manual" then 51 | -- do not connect 52 | else error("invalid option '"..auto_connect.."'") 53 | end 54 | 55 | jack.sleep() 56 | 57 | 58 | -------------------------------------------------------------------------------- /examples/c_api/example5/myecho.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2016 Stefano Trettel 4 | * 5 | * Software repository: LuaJack, https://github.com/stetre/luajack 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include "luajack.h" /* automatically includes */ 28 | 29 | #define nframes_t jack_nframes_t 30 | #define sample_t jack_default_audio_sample_t 31 | /* usually a float, but JACK may have been compiled defining it as a double */ 32 | 33 | #define MAX_DELAY 10.0 /* max echo delay (seconds) */ 34 | static nframes_t Fs; /* sample rate */ 35 | static nframes_t Mmax; /* max delay line length */ 36 | static nframes_t M; /* current delay line length */ 37 | static sample_t Delay = 0; /* current delay (seconds) */ 38 | static sample_t Gain = 0; /* current gain */ 39 | static sample_t *Dline = NULL; /* delay line */ 40 | static nframes_t Ptr1, Ptr2; /* read and write pointers */ 41 | 42 | static int Off(lua_State *L) 43 | { 44 | // printf("myecho Off\n"); 45 | (void)L; 46 | Gain = 0; 47 | Delay = 0; 48 | return 0; 49 | } 50 | 51 | static int On(lua_State *L) 52 | { 53 | sample_t gain = luaL_checknumber(L, 1); 54 | sample_t delay = luaL_checknumber(L, 2); 55 | if((gain <= 0) || (delay <= 0)) 56 | return Off(L); 57 | 58 | Gain = gain > 1.0 ? 1.0 : gain; 59 | Delay = delay > MAX_DELAY ? MAX_DELAY : delay; 60 | M = Delay*Fs; 61 | Ptr2 = (Ptr1 + M) % Mmax; 62 | // printf("myecho On: Delay=%g M=%u Gain=%g\n", Delay, M, Gain); 63 | return 0; 64 | } 65 | 66 | static int Status(lua_State *L) 67 | { 68 | lua_pushnumber(L, Gain); 69 | lua_pushnumber(L, Delay); 70 | return 2; 71 | } 72 | 73 | static sample_t DelayLine(sample_t x) 74 | { 75 | sample_t y = Dline[Ptr1]; 76 | Dline[Ptr1] = 0; /* to keep the delay line clear */ 77 | Dline[Ptr2] = x; 78 | Ptr1 = (Ptr1 + 1)%Mmax; 79 | Ptr2 = (Ptr2 + 1)%Mmax; 80 | return y; 81 | } 82 | 83 | static void *checklightuserdata(lua_State *L, int arg) 84 | { 85 | if(!lua_islightuserdata(L, arg)) 86 | { luaL_argerror(L, arg, "not a lightuserdata"); return NULL; } 87 | return lua_touserdata(L, arg); 88 | } 89 | 90 | 91 | static int Process(lua_State *L) 92 | { 93 | nframes_t nframes, i; 94 | sample_t x; 95 | sample_t *buf_in, *buf_out; 96 | 97 | buf_in = checklightuserdata(L, 1); 98 | buf_out = checklightuserdata(L, 2); 99 | nframes = luaL_checkinteger(L, 3); 100 | 101 | if(buf_in && buf_out) 102 | { 103 | if(Gain==0) /* no echo */ 104 | memcpy(buf_out, buf_in, nframes *sizeof(sample_t)); 105 | else 106 | { 107 | for(i=0; i process protocol ------------------------------- 29 | local tag, data = jack.ringbuffer_read(rbuf_in) 30 | if tag then 31 | if tag==TAG_ON then 32 | gain, delay = string.unpack("ff", data) 33 | myecho.on(gain, delay) 34 | elseif tag==TAG_OFF then 35 | myecho.off() 36 | --[[ 37 | elseif tag==TAG_STATUS_REQ then 38 | gain, delay = myecho.status() 39 | jack.ringbuffer_write(rbuf_out, TAG_STATUS_RSP, ......) 40 | jack.signal(client, gui) 41 | --]] 42 | else -- ignore 43 | end 44 | end 45 | 46 | -- Audio processing ---------------------------------------- 47 | jack.get_buffer(port_in) 48 | jack.get_buffer(port_out) 49 | -- Retrieve the raw buffer pointers, so that we can pass them 50 | -- to our signal processing function implemented in C: 51 | buf_in = jack.raw_buffer(port_in) 52 | buf_out = jack.raw_buffer(port_out) 53 | -- Do the signal processing: 54 | myecho.process(buf_in, buf_out, nframes) 55 | 56 | end 57 | 58 | jack.process_callback(client, process) 59 | 60 | -------------------------------------------------------------------------------- /examples/connect.lua: -------------------------------------------------------------------------------- 1 | -- LuaJack example: connect.lua 2 | -- 3 | -- LuaJack version of JACK's connect.c example. 4 | 5 | jack = require("luajack") 6 | getopt = require("luajack.getopt") 7 | fmt = string.format 8 | 9 | my_name = arg[0] 10 | 11 | USAGE = 12 | "Usage: lua " .. my_name .. " [options] portname1 portname2" .. 13 | "\nConnects two ports together.".. 14 | "\nOptions:" .. 15 | "\n -s, --server connect to the jack server named " .. 16 | "\n -d, --disconnect disconnect ports" .. 17 | "\n -h, --help display this help message" .. 18 | "\n -v, --version display version information and exit" .. 19 | "\n\n" 20 | 21 | function ShowVersion() 22 | print(fmt("%s: %s, %s", my_name, jack._VERSION, jack._JACK_VERSION)) 23 | end 24 | 25 | function ShowUsage(errmsg) 26 | if errmsg then print(errmsg, "\n") end 27 | print(USAGE) 28 | if errmsg then os.exit() end 29 | end 30 | 31 | 32 | short_opts = "ds:hv" 33 | long_opts = { 34 | disconnect = 'd', 35 | server = 's', 36 | help = 'h', 37 | version = 'v', 38 | } 39 | 40 | optarg, optind, opterr = getopt(arg, short_opts, long_opts) 41 | 42 | if opterr then ShowUsage(opterr) end 43 | 44 | if optarg.h then ShowUsage() os.exit(true) end 45 | 46 | if optarg.v then ShowVersion() os.exit(true) end 47 | 48 | server_name = optarg.s 49 | 50 | nargs = #arg -optind +1 51 | if nargs < 2 then 52 | ShowUsage("Not enough arguments") 53 | end 54 | 55 | portname1 = arg[optind] 56 | portname2 = arg[optind+1] 57 | 58 | c = jack.client_open(my_name, { no_start_server=true, server_name=server_name} ) 59 | 60 | -- Set the port connection callback: 61 | jack.port_connect_callback(c, function(_, srcport, dstport, con) 62 | if ((srcport == portname1) and (dstport == portname2)) 63 | or ((srcport == portname2) and (dstport == portname1)) 64 | then 65 | done = true 66 | end 67 | end) 68 | 69 | -- Activate the client: 70 | jack.activate(c) 71 | 72 | -- Connect (or disconnects) the ports: 73 | if optarg.d then 74 | jack.disconnect(c, portname1, portname2) 75 | else 76 | jack.connect(c, portname1, portname2) 77 | end 78 | 79 | while not done do 80 | jack.sleep(0) 81 | end 82 | 83 | print("Done") 84 | os.exit(true) 85 | -------------------------------------------------------------------------------- /examples/control.lua: -------------------------------------------------------------------------------- 1 | -- LuaJack example: control.lua 2 | -- 3 | -- LuaJack version of JACK's control.c example. 4 | 5 | jack = require("luajack") 6 | 7 | c = jack.client_open(arg[0], { no_start_server=true }) 8 | 9 | function printports(type_pattern, direction) 10 | local ports = jack.get_ports(c, { type_pattern=type_pattern, direction=direction } ) 11 | for i, name in ipairs(ports) do 12 | print("port: " .. name .. " (" .. jack.nport_flags(c, name) .. ")") 13 | end 14 | end 15 | 16 | reorder = 0 17 | function list_ports() 18 | reorder = reorder + 1 19 | print("Graph reorder count = " .. reorder) 20 | printports("audio", "input") 21 | printports("audio", "output") 22 | printports("midi", "input") 23 | printports("midi", "output") 24 | end 25 | 26 | jack.shutdown_callback(c, function(_, code, reason) 27 | error(reason .." (".. code ..")") 28 | end) 29 | 30 | jack.graph_order_callback(c, list_ports) 31 | 32 | jack.activate(c) 33 | 34 | jack.sleep() 35 | -------------------------------------------------------------------------------- /examples/cpu_load.lua: -------------------------------------------------------------------------------- 1 | -- LuaJack example: cpu_load.lua 2 | -- 3 | -- LuaJack version of JACK's cpu_load.c example. 4 | 5 | jack = require("luajack") 6 | 7 | c = jack.client_open(arg[0], { no_start_server=true }) 8 | 9 | jack.shutdown_callback(c, function(_, code, reason) 10 | error(reason .." (".. code ..")") 11 | end) 12 | 13 | jack.activate(c) 14 | 15 | while true do 16 | cpu = jack.cpu_load(c) 17 | print("jack cpu load: ".. (cpu - cpu%0.01) .. " %") 18 | jack.sleep(1) 19 | end 20 | -------------------------------------------------------------------------------- /examples/echo.lua: -------------------------------------------------------------------------------- 1 | -- LuaJack example: echo.lua 2 | -- 3 | -- Implements a simple echo effect. 4 | -- 5 | -- x --.--------------------->(+)--> y 6 | -- | ^ 7 | -- | ________ g | 8 | -- '----|__z^-M__|---|>----' 9 | -- 10 | -- 11 | -- To try it out, launch jackd (e.g, using QjackCtl), then in different shells: 12 | -- 13 | -- $ jack-keyboard # the MIDI controller 14 | -- $ lua sinesynth.lua # the synthesizer 15 | -- $ lua echo.lua auto 0.4 0.8 # echo with gain=0.4 and delay of 0.8 seconds 16 | -- 17 | -- With the 'auto' option, the script automatically connects jack-keyboard to 18 | -- sinesynth, sinesynth with the echo, and the echo with the system playback. 19 | -- By playing notes on the keyboard you should then hear tones with echo. 20 | -- 21 | -- An alternative way of running the exaample is with the 'capture' option, e.g. 22 | -- $ lua echo.lua capture 0.4 0.8 23 | -- This connects system:capture_1 to the echo input, and the echo output to the 24 | -- system playback, thus applying the echo to whatever sound source is plugged 25 | -- into system:capture_1. 26 | -- 27 | -- With the "manual" option, no automatic connection is performed. 28 | -- 29 | 30 | jack = require("luajack") 31 | 32 | MIDI_CONTROLLER_OUT = "jack-keyboard:midi_out" 33 | SYNTH_MIDI_IN = "sinesynth.lua:midi_in" 34 | SYNTH_OUT = "sinesynth.lua:audio_out" 35 | CAPTURE_1 = "system:capture_1" 36 | PLAYBACK_1 = "system:playback_1" 37 | PLAYBACK_2 = "system:playback_2" 38 | 39 | NAME = arg[0] 40 | 41 | auto_connect = arg[1] or "manual" 42 | 43 | gain = tonumber(arg[2]) or 0.3 -- 44 | delay = tonumber(arg[3]) or 0.5 -- seconds 45 | if gain > 1 and gain < 0 then error("invalid gain="..gain) end 46 | if delay < 0 then error("invalid delay="..delay) end 47 | 48 | --jack.verbose("on") 49 | 50 | --------------------------------------------------------------------- 51 | PROCESS = [[ 52 | c, in_port, out_port, gain, delay = table.unpack(arg) 53 | 54 | fs = jack.sample_rate(c) 55 | bufsz = jack.buffer_size(c) 56 | M = math.floor(delay*fs) -- delay line length 57 | print("echo parameters: delay="..delay.." s"..", gain="..gain.." (delay line length: "..M..")") 58 | 59 | D = { } -- delay line 60 | for i = 1, M do D[i] = 0 end 61 | ptr = 1 -- delay line pointer 62 | 63 | function delayline(x) 64 | local y = D[ptr] 65 | D[ptr] = x 66 | ptr = ptr + 1 67 | if ptr > M then ptr = 1 end 68 | return y 69 | end 70 | 71 | input = {} -- output samples 72 | output = {} -- output samples 73 | 74 | for i = 1, bufsz do output[i] = 0 input[i] = 0 end 75 | 76 | function process(nframes) 77 | jack.get_buffer(in_port) 78 | jack.get_buffer(out_port) 79 | input = { jack.read(in_port, nframes) } 80 | --assert(#input == nframes) 81 | for i = 1, nframes do 82 | local x = input[i] 83 | output[i] = x + delayline(x)*gain 84 | end 85 | jack.write(out_port, table.unpack(output)) 86 | end 87 | 88 | jack.process_callback(c, process) 89 | ]] 90 | 91 | --------------------------------------------------------------------- 92 | c = jack.client_open(NAME) 93 | 94 | fs = jack.sample_rate(c) 95 | bs = jack.buffer_size(c) 96 | period = bs/fs*1e6 97 | print(string.format("fs=%d bufsz=%d (period=%.0f us)", fs, bs, period)) 98 | 99 | in_port = jack.input_audio_port(c, "audio_in") 100 | out_port = jack.output_audio_port(c, "audio_out") 101 | 102 | jack.process_load(c, PROCESS, c, in_port, out_port, gain, delay) 103 | 104 | jack.shutdown_callback(c, function(code, reason) error("shutdown from server") end) 105 | 106 | jack.activate(c) 107 | 108 | function connect(c, port1, port2) 109 | if not jack.nport_connected(c, port1, port2) then 110 | jack.connect(c, port1, port2) 111 | end 112 | end 113 | 114 | if auto_connect=="auto" then 115 | connect(c, MIDI_CONTROLLER_OUT, SYNTH_MIDI_IN) 116 | connect(c, SYNTH_OUT, NAME..":audio_in") 117 | connect(c, NAME..":audio_out", PLAYBACK_1) 118 | connect(c, NAME..":audio_out", PLAYBACK_2) 119 | elseif auto_connect=="capture" then 120 | connect(c, CAPTURE_1, NAME..":audio_in") 121 | connect(c, NAME..":audio_out", PLAYBACK_1) 122 | connect(c, NAME..":audio_out", PLAYBACK_2) 123 | --elseif auto_connect=="myoption" then 124 | -- ... 125 | -- add your preferred connections here 126 | -- ... 127 | elseif auto_connect=="manual" then 128 | -- do not connect 129 | else error("invalid option '"..auto_connect.."'") 130 | end 131 | 132 | jack.sleep() 133 | -------------------------------------------------------------------------------- /examples/evmon.lua: -------------------------------------------------------------------------------- 1 | -- LuaJack example: evmon.lua 2 | -- 3 | -- LuaJack version of JACK's evmon.c example. 4 | 5 | jack = require("luajack") 6 | getopt = require("luajack.getopt") 7 | fmt = string.format 8 | 9 | my_name = arg[0] 10 | 11 | USAGE = 12 | "Usage: lua " .. my_name .. " [options]" .. 13 | "\nWith no options, monitor JACK server events".. 14 | "\nOptions:" .. 15 | "\n -h, --help display this help message" .. 16 | "\n -v, --version display version information and exit" .. 17 | "\n\n" 18 | 19 | function ShowVersion() 20 | print(fmt("%s: %s, %s", my_name, jack._VERSION, jack._JACK_VERSION)) 21 | end 22 | 23 | function ShowUsage(errmsg) 24 | if errmsg then print(errmsg, "\n") end 25 | print(USAGE) 26 | if errmsg then os.exit() end 27 | end 28 | 29 | short_opts = "hv" 30 | long_opts = { 31 | help = 'h', 32 | version = 'v', 33 | } 34 | 35 | optarg, optind, opterr = getopt(arg, short_opts, long_opts) 36 | if opterr then ShowUsage(opterr) end 37 | if optarg.h then ShowUsage() os.exit(true) end 38 | if optarg.v then ShowVersion() os.exit(true) end 39 | 40 | -- Create the client: 41 | c = jack.client_open(my_name, { no_start_server=true }) 42 | 43 | -- Register the callbacks: 44 | jack.port_registration_callback(c, function (_, portname, reg) 45 | print(fmt("Port %s %s",portname, reg)) 46 | end) 47 | 48 | jack.port_connect_callback(c, function (_, portname1, portname2, con) 49 | print(fmt("Ports %s and %s %s", portname1, portname2, con)) 50 | end) 51 | jack.client_registration_callback(c, function (_, clientname, reg) 52 | print(fmt("Client %s %s", clientname, reg)) 53 | end) 54 | 55 | jack.graph_order_callback(c, function() print("Graph reordered") end) 56 | 57 | -- Activate the client: 58 | jack.activate(c) 59 | 60 | -- Endless loop: 61 | jack.sleep() 62 | 63 | -------------------------------------------------------------------------------- /examples/getopt/getopt_example.lua: -------------------------------------------------------------------------------- 1 | 2 | local getopt = require("luajack.getopt") 3 | 4 | local short_opts = "hVvo:n:S:" 5 | local long_opts = { 6 | verbose = "v", 7 | help = "h", 8 | output = "o", 9 | set_value = "S", 10 | ["set-output"] = "o" 11 | } 12 | 13 | local progname = arg[0] 14 | local USAGE = 15 | "Usage: lua " .. progname .. " [options] ARG1 [ ARG2 ... ]" .. 16 | "\n\nOptions:" .. 17 | "\n -h, --help" .. 18 | "\n -v, --help" .. 19 | "\n -o FILE, --output=FILE" .. 20 | "\n -S VALUE, --set_value=VALUE".. 21 | "\n\n" 22 | 23 | local function Usage() print(USAGE) os.exit(true) end 24 | 25 | local optarg, optind, opterr = getopt(arg, short_opts, long_opts) 26 | 27 | if opterr then print(opterr,"\n") Usage() end 28 | 29 | if optarg.h then Usage() end 30 | 31 | if #arg < optind then print("Not enough arguments\n") Usage() end 32 | 33 | -- options: 34 | print("Opt:", "Value:") 35 | for k,v in pairs(optarg) do print(k,v) end 36 | print("") 37 | 38 | -- arguments that follow: 39 | print("Arg:", "value:") 40 | for i=optind, #arg do print(i, arg[i]) end 41 | print("") 42 | 43 | local nargs = #arg - optind + 1 44 | print("nargs = " .. nargs) 45 | 46 | -------------------------------------------------------------------------------- /examples/getopt/getopt_example.sh: -------------------------------------------------------------------------------- 1 | lua getopt_example.lua -o 1 -n 5 -S 3 --set-output 6 1 2 3 2 | -------------------------------------------------------------------------------- /examples/gui_thru.lua: -------------------------------------------------------------------------------- 1 | -- LuaJack example: gui_thru.lua 2 | -- 3 | -- This example implements an audio 'thru' with a simple GUI that displays 4 | -- the number of processed frames in its title, and has a button to 5 | -- mute/unmute the output. 6 | -- 7 | -- The GUI is implemented in a separate thread using MoonFLTK 8 | -- ( https://github.com/stetre/moonfltk/ ). 9 | -- 10 | -- The GUI thread and the process thread are connected with a couple of 11 | -- ringbuffers. This bidirectional channel carries a trivial 'control protocol' 12 | -- between the GUI and the audio processing thread. The protocol is summarized 13 | -- in the table below. 14 | -- 15 | -- tag data direction message meaning 16 | -- --------------------------------------------------- 17 | -- 1 nil gui->process mute 18 | -- 2 nil gui->process unmute 19 | -- 3 nil gui->process frames count request 20 | -- 4 nframes process->gui frames count response 21 | -- 22 | -- 23 | -- To run the example, launch qjackctl, start the JACK server and execute: 24 | -- $ lua gui_thread.lua 25 | -- 26 | -- Then execute the metronome example: 27 | -- $ lua metro.lua 28 | -- 29 | -- and via the qjackctl GUI connect the metro.lua 'out' to the gui_thru 'in' 30 | -- and the gui_thru 'out' to a system 'in'. 31 | -- 32 | 33 | ----------------------------------------------------------------------- 34 | -- This is the inline script implementing the GUI. 35 | -- It runs in a separate client thread. 36 | 37 | GUI = [[ 38 | rbuf_in, rbuf_out = table.unpack(arg) 39 | c, gui = jack.self() 40 | 41 | fl = require("moonfltk") 42 | 43 | timer = require("moonfltk.timer") 44 | timer.init() 45 | 46 | -- The T1 timer is used to send frame count requests at regular intervals 47 | REQ_INTERVAL = 1.0 -- seconds 48 | T1 = timer.create(REQ_INTERVAL, function() 49 | jack.ringbuffer_write(rbuf_out, 3) 50 | timer.start(T1) 51 | end) 52 | 53 | function mute_cb(b) -- callback for the 'mute' button 54 | local tag = b:value() and 1 or 2 -- mute/unmute 55 | jack.ringbuffer_write(rbuf_out, tag) 56 | end 57 | 58 | win = fl.window(340, 180, "thru") 59 | b = fl.toggle_button(20, 40, 300, 100, "MUTE") 60 | b:callback(mute_cb) 61 | b:labelfont(fl.BOLD + fl.ITALIC) 62 | b:labelsize(36) 63 | b:labeltype('shadow') 64 | win:done() 65 | win:show() 66 | 67 | timer.start(T1) 68 | 69 | -- GUI thread main loop: 70 | while true do 71 | jack.testcancel() -- cancellation point 72 | if not fl.check() then os.exit() end -- check for GUI events 73 | 74 | repeat 75 | local tag, data = jack.ringbuffer_read(rbuf_in) 76 | if tag == 4 then -- frames count response 77 | win:label("frames: " .. data) 78 | end 79 | until not tag 80 | 81 | end 82 | ]] 83 | 84 | ----------------------------------------------------------------------- 85 | -- This is the inline script implementing the audio processing. 86 | -- It runs in the client's process thread. 87 | 88 | PROCESS = [[ 89 | client, port_in, port_out, gui, rbuf_in, rbuf_out = table.unpack(arg) 90 | 91 | tot_frames = 0 92 | mute = false 93 | 94 | function process(nframes) 95 | 96 | tot_frames = tot_frames + nframes 97 | 98 | -- GUI <-> process protocol 99 | local tag, data = jack.ringbuffer_read(rbuf_in) 100 | if tag then 101 | if tag==1 then mute=true -- mute 102 | elseif tag==2 then mute=false -- unmute 103 | elseif tag==3 then -- frames count request 104 | jack.ringbuffer_write(rbuf_out, 4, tostring(tot_frames)) 105 | jack.signal(client, gui) 106 | else -- ignore 107 | end 108 | end 109 | 110 | -- Audio processing 111 | jack.get_buffer(port_in) 112 | jack.get_buffer(port_out) 113 | 114 | if mute then -- write 0's 115 | jack.clear(port_out) 116 | else 117 | jack.copy(port_out, port_in) 118 | end 119 | 120 | end 121 | 122 | jack.process_callback(client, process) 123 | ]] 124 | 125 | ----------------------------------------------------------------------- 126 | -- This is the main script. 127 | 128 | jack = require("luajack") 129 | getopt = require("luajack.getopt") 130 | fmt = string.format 131 | 132 | my_name = arg[0] 133 | 134 | USAGE = 135 | "Usage: lua " .. my_name .. " [options]" .. 136 | "\nRedirects input to output'".. 137 | "\nOptions:" .. 138 | "\n -h, --help display this help message" .. 139 | "\n -v, --version display version information and exit" .. 140 | "\n\n" 141 | 142 | function ShowVersion() 143 | print(fmt("%s: %s, %s", my_name, jack._VERSION, jack._JACK_VERSION)) 144 | end 145 | 146 | function ShowUsage(errmsg) 147 | if errmsg then print(errmsg, "\n") end 148 | print(USAGE) 149 | if errmsg then os.exit() end 150 | end 151 | 152 | short_opts = "hv" 153 | long_opts = { 154 | help = 'h', 155 | version = 'v', 156 | } 157 | 158 | optarg, optind, opterr = getopt(arg, short_opts, long_opts) 159 | if opterr then ShowUsage(opterr) end 160 | if optarg.h then ShowUsage() os.exit(true) end 161 | if optarg.v then ShowVersion() os.exit(true) end 162 | 163 | 164 | c = jack.client_open(my_name, { no_start_server=true }) 165 | 166 | jack.verbose("on") 167 | 168 | port_in = jack.input_audio_port(c, "in") 169 | port_out = jack.output_audio_port(c, "out") 170 | 171 | rbuf1 = jack.ringbuffer(c, 1000, true) -- process -> GUI 172 | rbuf2 = jack.ringbuffer(c, 1000, true) -- GUI -> process 173 | 174 | gui = jack.thread_load(c, GUI, rbuf1, rbuf2) 175 | 176 | jack.process_load(c, PROCESS, c, port_in, port_out, gui, rbuf2, rbuf1) 177 | 178 | jack.shutdown_callback(c, function(_, code, reason) 179 | error(reason .." (".. code..")") 180 | end) 181 | 182 | jack.activate(c) 183 | 184 | jack.sleep() -- endless loop 185 | 186 | -------------------------------------------------------------------------------- /examples/gui_thru2.lua: -------------------------------------------------------------------------------- 1 | -- LuaJack example: gui_thru.lua 2 | -- 3 | -- This example implements an audio 'thru' with a simple GUI that displays 4 | -- the number of processed frames in its title, and has a button to 5 | -- mute/unmute the output. 6 | -- 7 | -- The MoonFLTK based GUI is running in the main thread where also the jack client 8 | -- is created. ( https://github.com/stetre/moonfltk/ ). 9 | -- 10 | -- The main thread and the process thread are connected with a couple of 11 | -- ringbuffers. This bidirectional channel carries a trivial 'control protocol' 12 | -- between the GUI and the audio processing thread. The protocol is summarized 13 | -- in the table below. 14 | -- 15 | -- tag data direction message meaning 16 | -- --------------------------------------------------- 17 | -- 1 nil gui->process mute 18 | -- 2 nil gui->process unmute 19 | -- 3 nil gui->process frames count request 20 | -- 4 nframes process->gui frames count response 21 | -- 22 | -- 23 | -- To run the example, launch qjackctl, start the JACK server and execute: 24 | -- $ lua gui_thread.lua 25 | -- 26 | -- Then execute the metronome example: 27 | -- $ lua metro.lua 28 | -- 29 | -- and via the qjackctl GUI connect the metro.lua 'out' to the gui_thru 'in' 30 | -- and the gui_thru 'out' to a system 'in'. 31 | -- 32 | 33 | 34 | ----------------------------------------------------------------------- 35 | -- This is the inline script implementing the audio processing. 36 | -- It runs in the client's process thread. 37 | 38 | PROCESS = [[ 39 | client, port_in, port_out, to_proc_rbuf, from_proc_rbuf = table.unpack(arg) 40 | 41 | local fl = require("moonfltk.background") 42 | 43 | tot_frames = 0 44 | mute = false 45 | 46 | function process(nframes) 47 | 48 | tot_frames = tot_frames + nframes 49 | 50 | -- GUI <-> process protocol 51 | local tag, data = jack.ringbuffer_read(to_proc_rbuf) 52 | if tag then 53 | if tag==1 then mute=true -- mute 54 | elseif tag==2 then mute=false -- unmute 55 | elseif tag==3 then -- frames count request 56 | jack.ringbuffer_write(from_proc_rbuf, 4, tostring(tot_frames)) 57 | fl.awake() 58 | else -- ignore 59 | end 60 | end 61 | 62 | -- Audio processing 63 | jack.get_buffer(port_in) 64 | jack.get_buffer(port_out) 65 | 66 | if mute then -- write 0's 67 | jack.clear(port_out) 68 | else 69 | jack.copy(port_out, port_in) 70 | end 71 | 72 | end 73 | 74 | jack.process_callback(client, process) 75 | ]] 76 | 77 | ----------------------------------------------------------------------- 78 | -- This is the main script. 79 | 80 | jack = require("luajack") 81 | getopt = require("luajack.getopt") 82 | fmt = string.format 83 | 84 | my_name = arg[0] 85 | 86 | USAGE = 87 | "Usage: lua " .. my_name .. " [options]" .. 88 | "\nRedirects input to output'".. 89 | "\nOptions:" .. 90 | "\n -h, --help display this help message" .. 91 | "\n -v, --version display version information and exit" .. 92 | "\n\n" 93 | 94 | function ShowVersion() 95 | print(fmt("%s: %s, %s", my_name, jack._VERSION, jack._JACK_VERSION)) 96 | end 97 | 98 | function ShowUsage(errmsg) 99 | if errmsg then print(errmsg, "\n") end 100 | print(USAGE) 101 | if errmsg then os.exit() end 102 | end 103 | 104 | short_opts = "hv" 105 | long_opts = { 106 | help = 'h', 107 | version = 'v', 108 | } 109 | 110 | optarg, optind, opterr = getopt(arg, short_opts, long_opts) 111 | if opterr then ShowUsage(opterr) end 112 | if optarg.h then ShowUsage() os.exit(true) end 113 | if optarg.v then ShowVersion() os.exit(true) end 114 | 115 | 116 | c = jack.client_open(my_name, { no_start_server=true }) 117 | 118 | jack.verbose("on") 119 | 120 | port_in = jack.input_audio_port(c, "in") 121 | port_out = jack.output_audio_port(c, "out") 122 | 123 | from_proc_rbuf = jack.ringbuffer(c, 1000, true) -- process -> GUI 124 | to_proc_rbuf = jack.ringbuffer(c, 1000, true) -- GUI -> process 125 | 126 | jack.process_load(c, PROCESS, c, port_in, port_out, to_proc_rbuf, 127 | from_proc_rbuf) 128 | 129 | jack.shutdown_callback(c, function(_, code, reason) 130 | error(reason .." (".. code..")") 131 | end) 132 | 133 | jack.activate(c) 134 | 135 | ----------------------------------------------------------------------- 136 | -- GUI 137 | 138 | fl = require("moonfltk") 139 | 140 | timer = require("moonfltk.timer") 141 | timer.init() 142 | 143 | -- The T1 timer is used to send frame count requests at regular intervals 144 | REQ_INTERVAL = 1.0 -- seconds 145 | T1 = timer.create(REQ_INTERVAL, function() 146 | jack.ringbuffer_write(to_proc_rbuf, 3) 147 | timer.start(T1) 148 | end) 149 | 150 | function mute_cb(b) -- callback for the 'mute' button 151 | local tag = b:value() and 1 or 2 -- mute/unmute 152 | jack.ringbuffer_write(to_proc_rbuf, tag) 153 | end 154 | 155 | win = fl.window(340, 180, "thru") 156 | b = fl.toggle_button(20, 40, 300, 100, "MUTE") 157 | b:callback(mute_cb) 158 | b:labelfont(fl.BOLD + fl.ITALIC) 159 | b:labelsize(36) 160 | b:labeltype('shadow') 161 | win:done() 162 | win:show() 163 | 164 | timer.start(T1) 165 | 166 | -- GUI thread main loop: 167 | while fl.wait() do 168 | 169 | repeat 170 | local tag, data = jack.ringbuffer_read(from_proc_rbuf) 171 | if tag == 4 then -- frames count response 172 | win:label("frames: " .. data) 173 | end 174 | until not tag 175 | 176 | jack.sleep(0) -- just check for errors 177 | 178 | end 179 | 180 | 181 | -------------------------------------------------------------------------------- /examples/interactive.lua: -------------------------------------------------------------------------------- 1 | -- LuaJack example: JACK transport interactive monitoring. 2 | -- 3 | 4 | jack = require("luajack") 5 | 6 | print(jack._VERSION .. ", " .. jack._JACK_VERSION) 7 | jack.verbose("on") 8 | 9 | c = jack.client_open("luajack", { no_start_server=true }) 10 | 11 | jack.shutdown_callback(c, function(_, code, reason) 12 | print(reason .. " (" .. code .. ")") 13 | os.exit() 14 | end) 15 | 16 | -- define a few commands ... 17 | quit = function() os.exit() end 18 | 19 | start = function() jack.transport_start(c) end 20 | 21 | stop = function() jack.transport_stop(c) end 22 | 23 | state = function() print(jack.transport_state(c)) end 24 | 25 | query = function() 26 | local state, position = jack.transport_query(c) 27 | print("state: " .. state) 28 | print("frame: " .. position.frame .. " (current: " .. jack.frame(c) .. ")") 29 | print("position fields: ") 30 | for k, v in pairs(position) do print(k, v) end 31 | end 32 | 33 | help = function() 34 | print([[ 35 | Available commands: 36 | start - start JACK transport rolling 37 | stop - stop JACK transport rolling 38 | state - query JACK transport state 39 | query - query JACK transport state and position 40 | help - print this help 41 | quit - exit this program 42 | ]]) end 43 | 44 | print([[Type 'help' for a list of available commands.]]) 45 | 46 | 47 | function extract_command(s) 48 | local s = string.match(s, [[%b""]]) 49 | if s then return string.sub(s, 2, #s-1) end 50 | end 51 | 52 | function load_command(err) 53 | local s = extract_command(err) 54 | if type(s) == "string" then 55 | return load(s .. "()") 56 | else 57 | return nil, err 58 | end 59 | end 60 | 61 | 62 | while true do 63 | io.write("luajack> ") 64 | local f, err = load(io.read()) 65 | if type(f) ~= "function" then 66 | f, err = load_command(err) 67 | end 68 | if f then 69 | pcall(f) 70 | else 71 | print(err) 72 | end 73 | jack.sleep(0) 74 | end 75 | 76 | -------------------------------------------------------------------------------- /examples/latency.lua: -------------------------------------------------------------------------------- 1 | -- LuaJack example: latency.lua 2 | -- 3 | -- Modified version of thru.lua, to show the use of the latency API. 4 | 5 | jack = require("luajack") 6 | getopt = require("luajack.getopt") 7 | fmt = string.format 8 | 9 | my_name = arg[0] 10 | 11 | USAGE = 12 | "Usage: lua " .. my_name .. " [options]" .. 13 | "\nRedirects input to output'".. 14 | "\nOptions:" .. 15 | "\n -h, --help display this help message" .. 16 | "\n -v, --version display version information and exit" .. 17 | "\n\n" 18 | 19 | function ShowVersion() 20 | print(fmt("%s: %s, %s", my_name, jack._VERSION, jack._JACK_VERSION)) 21 | end 22 | 23 | function ShowUsage(errmsg) 24 | if errmsg then print(errmsg, "\n") end 25 | print(USAGE) 26 | if errmsg then os.exit() end 27 | end 28 | 29 | short_opts = "hv" 30 | long_opts = { 31 | help = 'h', 32 | version = 'v', 33 | } 34 | 35 | optarg, optind, opterr = getopt(arg, short_opts, long_opts) 36 | if opterr then ShowUsage(opterr) end 37 | if optarg.h then ShowUsage() os.exit(true) end 38 | if optarg.v then ShowVersion() os.exit(true) end 39 | 40 | c = jack.client_open(my_name, { no_start_server=true }) 41 | 42 | port_in = jack.input_audio_port(c, "in") 43 | port_out = jack.output_audio_port(c, "out") 44 | 45 | jack.process_load(c, [[ 46 | c, port_in, port_out = table.unpack(arg) 47 | 48 | function process(nframes) 49 | -- just copy the frames from in to out: 50 | jack.get_buffer(port_in) 51 | jack.get_buffer(port_out) 52 | jack.copy(port_out, port_in) 53 | end 54 | 55 | jack.process_callback(c, process) 56 | ]], c, port_in, port_out) 57 | 58 | jack.shutdown_callback(c, function(_, code, reason) 59 | error(reason .." (".. code..")") 60 | end) 61 | 62 | function latency(_, mode) 63 | local port = mode == "capture" and port_out or port_in 64 | -- Notice that, although the client gets samples from port_in and writes 65 | -- them to port_out, from the JACK server's perspective port_out is a 66 | -- 'capture' port because it captures samples from it, and port_in is a 67 | -- 'playback' port, because it sends samples to it. 68 | -- So, if mode is 'capture', we must recompute latencies for port_out, 69 | -- otherwise for port_in. 70 | -- Since this is just an example, we just add 1 frame to both min and max, 71 | -- just to see that we can actually set the latency range. 72 | local min, max = jack.latency_range(port, mode) 73 | print("latency callback: "..mode.." min="..min.." max="..max) 74 | jack.set_latency_range(port, mode, min+1, max+1) 75 | end 76 | 77 | jack.latency_callback(c, latency) 78 | 79 | jack.activate(c) 80 | 81 | jack.sleep(3) 82 | 83 | -- this will trigger the latency callback for all ports: 84 | jack.recompute_total_latencies(c) 85 | 86 | jack.sleep(3) 87 | 88 | -- let's do it once more... 89 | jack.recompute_total_latencies(c) 90 | 91 | jack.sleep(3) 92 | 93 | print("bye!") 94 | -------------------------------------------------------------------------------- /examples/metro.lua: -------------------------------------------------------------------------------- 1 | -- LuaJack example: metro.lua 2 | -- 3 | -- LuaJack version of JACK's metro.c example (simplified, with no amplitude envelope). 4 | 5 | jack = require("luajack") 6 | getopt = require("luajack.getopt") 7 | fmt = string.format 8 | 9 | my_name = arg[0] 10 | 11 | USAGE = 12 | "" .. my_name .. " - A JACK-based metronome.".. 13 | "\nUsage: lua " .. my_name .. " [options]" .. 14 | "\nOptions:" .. 15 | "\n -b, --bpm beats per minute (def: 120)" .. 16 | "\n -f, --frequency the frequency of the tone (def: 880 hz)" .. 17 | "\n -a, --amplitude maximum amplitude (between 0 and 1, def: 0.5)" .. 18 | "\n -d, --duration duration of the tone (def: 0.1 s)" .. 19 | "\n -n, --name name for metronome client (def: '".. my_name .. "')" .. 20 | "\n -t, --transport transport aware" .. 21 | "\n -p, --playback connect to playback port" .. 22 | "\n -h, --help display this help message" .. 23 | "\n -v, --version display version information and exit" .. 24 | "\n\n" 25 | 26 | function ShowVersion() 27 | print(fmt("%s: %s, %s", my_name, jack._VERSION, jack._JACK_VERSION)) 28 | end 29 | 30 | function ShowUsage(errmsg) 31 | if errmsg then print(errmsg, "\n") end 32 | print(USAGE) 33 | if errmsg then os.exit() end 34 | end 35 | 36 | function Error(errmsg) 37 | print("Error: " .. errmsg) 38 | print("(type 'lua " .. my_name .. " -h' for help)") 39 | os.exit() 40 | end 41 | 42 | short_opts = "b:f:a:d:n:tp:hv" 43 | long_opts = { 44 | bpm ='b', 45 | frequency ='f', 46 | amplitude = 'a', 47 | duration = 'd', 48 | name = 'n', 49 | transport = 't', 50 | playback = 'p', 51 | help = 'h', 52 | version = 'v', 53 | } 54 | 55 | -- get arguments and options: 56 | optarg, optind, opterr = getopt(arg, short_opts, long_opts) 57 | if opterr then ShowUsage(opterr) end 58 | if optarg.h then ShowUsage() os.exit(true) end 59 | if optarg.v then ShowVersion() os.exit(true) end 60 | 61 | local name = optarg.n or my_name 62 | 63 | local bpm = optarg.b or 120 64 | bpm = math.tointeger(bpm) 65 | if not bpm or bpm < 0 then 66 | Error("bpm must be a positive integer") 67 | end 68 | 69 | local freq = optarg.f or 880 70 | freq = tonumber(freq) 71 | if not freq or freq < 0 then 72 | Error("frequency must be a positive number") 73 | end 74 | 75 | local max_amp = optarg.a or 0.5 76 | max_amp = tonumber(max_amp) 77 | if not max_amp or max_amp < 0 or max_amp > 1 then 78 | Error("amplitude must be a number between 0 and 1") 79 | end 80 | 81 | local dur = optarg.d or 0.1 82 | dur = tonumber(dur) 83 | if not dur or dur < 0 then 84 | Error("duration must be a positive number") 85 | end 86 | 87 | local transport_aware = optarg.t and true or false 88 | dur = tonumber(dur) 89 | if not dur or dur < 0 or dur >= 60/bpm then 90 | Error("duration must be a positive number and smaller than 60/bpm") 91 | end 92 | 93 | local playback_port = optarg.p 94 | 95 | local port_name = tostring(bpm) .. "_bpm" 96 | 97 | print("client name = " .. name) 98 | print("bpm = " .. bpm) 99 | print("output port = " .. port_name) 100 | print("playback port = " .. (playback_port or "none")) 101 | print("tone frequency = " .. freq) 102 | print("tone amplitude = " .. max_amp) 103 | print("tone duration = " .. dur) 104 | print("transport aware = " .. (transport_aware and "yes" or "no")) 105 | 106 | -- open the client: 107 | c = jack.client_open(name, { no_start_server=true }) 108 | 109 | -- create the output port: 110 | out = jack.output_audio_port(c, port_name) 111 | 112 | -- load the process script: 113 | jack.process_load(c, [[ 114 | local c = arg[1] 115 | local out = arg[2] 116 | local bpm = arg[3] 117 | local freq = arg[4] 118 | local max_amp = arg[5] 119 | local dur = arg[6] 120 | local transport_aware = arg[7] 121 | 122 | local offset = 1 123 | local wave_length 124 | local wave_table = {} 125 | 126 | local function round(x) 127 | local y = math.floor(x) 128 | return x%1 > 0.5 and y+1 or y 129 | end 130 | 131 | -- get the sample rate and build the wave table 132 | local fs = jack.sample_rate(c) 133 | wave_length = round(60 * fs / bpm) 134 | tone_length = round(fs * dur) 135 | scale = 2 * math.pi * freq / fs 136 | for i = 1, tone_length do 137 | wave_table[i] = max_amp * math.sin(scale*(i-1)) 138 | end 139 | for i = tone_length+1, wave_length do 140 | wave_table[i] = 0 141 | end 142 | 143 | local function process_silence(nframes) 144 | jack.get_buffer(out) 145 | jack.clear(out) 146 | end 147 | 148 | local function process_audio(nframes) 149 | jack.get_buffer(out) 150 | local frames_left = nframes 151 | while (wave_length - offset) < frames_left do 152 | jack.write(out, table.unpack(wave_table, offset, wave_length)) 153 | frames_left = frames_left - (wave_length - offset + 1) 154 | offset = 1 155 | end 156 | if frames_left > 0 then 157 | jack.write(out, table.unpack(wave_table, offset, offset + frames_left)) 158 | offset = offset + frames_left 159 | end 160 | end 161 | 162 | local function process(nframes) 163 | if transport_aware then 164 | local state, pos = jack.transport_query(c) 165 | if state ~= "rolling" then 166 | process_silence(nframes) 167 | return 168 | end 169 | end 170 | process_audio(nframes) 171 | end 172 | 173 | jack.process_callback(c, process) 174 | 175 | ]], c, out, bpm, freq, max_amp, dur, transport_aware) 176 | 177 | -- register other callbacks: 178 | jack.shutdown_callback(c, function(_, code, reason) 179 | error(reason .." (".. code ..")") 180 | end) 181 | 182 | -- activate the client: 183 | jack.activate(c) 184 | 185 | -- optionally connect to playback port: 186 | if playback_port then 187 | jack.port_connect(out, playback_port) 188 | end 189 | 190 | -- sleep while waiting for jack to call back: 191 | jack.sleep() 192 | 193 | -------------------------------------------------------------------------------- /examples/midi_dump.lua: -------------------------------------------------------------------------------- 1 | -- LuaJack example: midi_dump.lua 2 | -- 3 | -- LuaJack version of JACK's midi_dump.c example. 4 | 5 | jack = require("luajack") 6 | midi = require("luajack.midi") 7 | getopt = require("luajack.getopt") 8 | fmt = string.format 9 | 10 | my_name = arg[0] 11 | 12 | USAGE = 13 | "Usage: lua " .. my_name .. " [options]" .. 14 | "\nListens for MIDI events on a jack MIDI port and prints them to stdout.".. 15 | "\nOptions:" .. 16 | "\n -a, --abstime use absolute timestamps relative to application start" .. 17 | "\n -p, --play play notes on an output audio port" .. 18 | "\n -v, --version display version information and exit" .. 19 | "\n\n" 20 | 21 | function ShowVersion() 22 | print(fmt("%s: %s, %s", my_name, jack._VERSION, jack._JACK_VERSION)) 23 | end 24 | 25 | function ShowUsage(errmsg) 26 | if errmsg then print(errmsg, "\n") end 27 | print(USAGE) 28 | if errmsg then os.exit() end 29 | end 30 | 31 | short_opts = "aphv" 32 | long_opts = { 33 | abstime = 'a', 34 | help = 'h', 35 | version = 'v', 36 | } 37 | 38 | optarg, optind, opterr = getopt(arg, short_opts, long_opts) 39 | if opterr then ShowUsage(opterr) end 40 | if optarg.h then ShowUsage() os.exit(true) end 41 | if optarg.v then ShowVersion() os.exit(true) end 42 | 43 | -- create the client: 44 | c = jack.client_open(my_name) 45 | 46 | jack.shutdown_callback(c, function(_, code, reason) 47 | error(reason .." (".. code..")") 48 | end) 49 | 50 | -- create the input port: 51 | midi_in = jack.input_midi_port(c, "midi_in") 52 | 53 | -- create a ringbuffer for process to thread communication: 54 | rbuf = jack.ringbuffer(c, 1000, true) 55 | 56 | -- create the thread and load the thread script in it: 57 | t = jack.thread_load(c, [[ 58 | jack = assert(jack) 59 | midi = require("luajack.midi") 60 | 61 | rbuf = arg[1] 62 | 63 | while true do 64 | jack.wait() 65 | while true do 66 | local tag, msg = jack.ringbuffer_read(rbuf) 67 | if not tag then break end 68 | print(midi.tostring(tag, msg)) 69 | end 70 | end 71 | ]], rbuf) 72 | 73 | -- load the process script: 74 | jack.process_load(c, [[ 75 | c, t, midi_in, rbuf, abstime = table.unpack(arg) 76 | 77 | local n = 0 -- absolute frame number 78 | 79 | function process(nframes) 80 | local ec, lc = jack.get_buffer(midi_in) 81 | if ec > 0 then 82 | local time, msg = jack.read(midi_in) 83 | while time do 84 | -- write to ringbuffer 85 | local tag = abstime and time+n or time 86 | jack.ringbuffer_write(rbuf, tag, msg) 87 | jack.signal(c, t) 88 | time, msg = jack.read(midi_in) 89 | end 90 | end 91 | n = n + nframes 92 | end 93 | 94 | jack.process_callback(c, process) 95 | 96 | ]], c, t, midi_in, rbuf, optarg.a) 97 | 98 | -- activate client and enter the sleep loop: 99 | jack.activate(c) 100 | jack.sleep() 101 | 102 | -------------------------------------------------------------------------------- /examples/oscillator.lua: -------------------------------------------------------------------------------- 1 | -- LuaJack example: oscillator.lua 2 | -- 3 | -- Generates a pure sinusoid at a given frequency and sends it to 4 | -- a playback port. 5 | 6 | jack = require("luajack") 7 | getopt = require("luajack.getopt") 8 | fmt = string.format 9 | 10 | my_name = arg[0] 11 | 12 | USAGE = 13 | "Usage: lua " .. my_name .. " [options]" .. 14 | "\nGenerates a pure sinusoid and sends it to a playback port.".. 15 | "\nOptions:" .. 16 | "\n -f, --frequency the frequency of the sinusoid (def: 440 hz)" .. 17 | "\n -d, --duration duration of the sinusoid (def: 3 s)" .. 18 | "\n -o, --output the playback port (def: system:playback_1)" .. 19 | "\n -h, --help display this help message" .. 20 | "\n -v, --version display version information and exit" .. 21 | "\n\n" 22 | 23 | function ShowVersion() 24 | print(fmt("%s: %s, %s", my_name, jack._VERSION, jack._JACK_VERSION)) 25 | end 26 | 27 | function ShowUsage(errmsg) 28 | if errmsg then print(errmsg, "\n") end 29 | print(USAGE) 30 | if errmsg then os.exit() end 31 | end 32 | 33 | short_opts = "f:d:o:hv" 34 | long_opts = { 35 | frequency ='f', 36 | duration = 'd', 37 | output = 'o', 38 | help = 'h', 39 | version = 'v', 40 | } 41 | 42 | optarg, optind, opterr = getopt(arg, short_opts, long_opts) 43 | if opterr then ShowUsage(opterr) end 44 | if optarg.h then ShowUsage() os.exit(true) end 45 | if optarg.v then ShowVersion() os.exit(true) end 46 | 47 | local playback_port = optarg.o or "system:playback_1" 48 | local f = optarg.f or 440 -- frequency (Hz) 49 | local dur = optarg.d or 3 -- duration (s) 50 | 51 | 52 | -- open the 'oscillator' client: 53 | c = jack.client_open("oscillator") 54 | 55 | -- register the output port: 56 | out = jack.output_audio_port(c, "out") 57 | 58 | jack.process_load(c, [[ 59 | local pi = math.pi 60 | local sin = math.sin 61 | 62 | local c, out, f = table.unpack(arg) 63 | 64 | local y = {} -- output samples 65 | local n = 0 -- frame number 66 | 67 | -- get the sample rate and compute the radian frequency: 68 | fs = jack.sample_rate(c) 69 | wT = 2*pi*f/fs -- radian frequency (rad/sample) = 2*pi*f/fs 70 | print("sample rate = " .. fs .. " hz") 71 | print("frequency = " .. f .. " hz") 72 | print("radian frequency = " .. wT .. " rad/sample") 73 | 74 | function process(nframes) 75 | -- get the port buffer 76 | jack.get_buffer(out) 77 | -- compute the samples 78 | for i=1,nframes do 79 | y[i] = .5*sin(wT*n) 80 | n = n+1 81 | end 82 | -- write the samples to the output port 83 | jack.write(out, table.unpack(y)) 84 | end 85 | 86 | jack.process_callback(c, process); 87 | 88 | ]], c, out, f) 89 | 90 | -- register other callbacks: 91 | jack.shutdown_callback(c, function(_, code, reason) 92 | error(reason .." (".. code ..")") 93 | end) 94 | 95 | jack.xrun_callback(c, function() print("xrun") end) 96 | 97 | -- activate the client: 98 | jack.activate(c) 99 | 100 | -- connect the output port to the playback port: 101 | jack.port_connect(out, playback_port) 102 | 103 | -- sleep while waiting for jack to call back: 104 | jack.sleep(dur) 105 | 106 | -- disconnect the port (superfluous because client:close() does it) 107 | jack.port_disconnect(out) 108 | -- close the client (superfluous because it is automatically closed at exit) 109 | jack.client_close(c) 110 | 111 | print("bye!") 112 | 113 | -------------------------------------------------------------------------------- /examples/ringbuffers/pingpong.lua: -------------------------------------------------------------------------------- 1 | -- LuaJack example: pingpong.lua 2 | -- 3 | -- Shows the use of ringbuffers for bi-directional communication 4 | -- between the rt process thread and a non rt client thread. 5 | 6 | jack = require("luajack") 7 | 8 | jack.verbose("on") 9 | 10 | 11 | opt = arg[1] 12 | if not opt or opt=="wait" then WAIT=true 13 | elseif opt=="poll" then WAIT=false 14 | else error("invalid argument") 15 | end 16 | 17 | 18 | THREAD = [[ 19 | rbuf_in, rbuf_out, wait = table.unpack(arg) 20 | 21 | local function receive() 22 | local tag, data = jack.ringbuffer_read(rbuf_in) 23 | if not tag then return end 24 | print("thread received: tag=".. tag .. " data=".. data) 25 | jack.ringbuffer_write(rbuf_out, tag, "pong" .. tag) 26 | end 27 | 28 | if wait then 29 | -- Alternative 1: wait until the process thread signals. 30 | print("Blocking") 31 | while true do 32 | jack.wait() 33 | receive() 34 | end 35 | else 36 | -- Alternative 2: don't wait, and repeatedly poll the ringbuffer instead. 37 | -- We need to add a cancellation point to avoid hanging up when the client exits. 38 | print("Non-blocking") 39 | while true do 40 | jack.testcancel() -- test if the client is exiting (cancellation point) 41 | receive() 42 | end 43 | end 44 | ]] 45 | 46 | PROCESS = [[ 47 | c, thread, rbuf_in, rbuf_out = table.unpack(arg) 48 | 49 | local m = 0 50 | local tag = 0 51 | function process(nframes) 52 | -- check if the user thread wrote some data to rbuf_in 53 | -- (since this is the rt thread, it can not block on wait()): 54 | local rtag, rdata = jack.ringbuffer_read(rbuf_in) 55 | if rtag then -- some data is available 56 | -- we should not print in rt threads, but this is just an example: 57 | print("main received: tag=".. rtag .. " data=".. rdata) 58 | end 59 | m = m + 1 60 | if m == 100 then 61 | -- write some data to rbuf_out and signal the user thread 62 | jack.ringbuffer_write(rbuf_out, tag, "ping" .. tag ) 63 | jack.signal(c, thread) 64 | tag = tag + 1 65 | m = 0 66 | end 67 | end 68 | 69 | jack.process_callback(c, process) 70 | ]] 71 | 72 | -- Create the client: 73 | c = jack.client_open("pingpong") 74 | 75 | -- Create the ringbuffers: 76 | local rbuf1 = jack.ringbuffer(c, 8000, true) -- thread to client 77 | local rbuf2 = jack.ringbuffer(c, 8000, true) -- client to thread 78 | 79 | -- Create the thread and process contexts: 80 | thread = jack.thread_load(c, THREAD, rbuf2, rbuf1, WAIT) 81 | jack.process_load(c, PROCESS, c, thread, rbuf1, rbuf2) 82 | 83 | jack.shutdown_callback(c, function(_, code, reason) 84 | error(reason .." (".. code ..")") 85 | end) 86 | 87 | jack.activate(c) 88 | 89 | jack.sleep(10) 90 | print("exiting") 91 | 92 | -------------------------------------------------------------------------------- /examples/ringbuffers/select.lua: -------------------------------------------------------------------------------- 1 | -- LuaJack example: select.lua 2 | -- 3 | -- Shows the use of ringbuffers with select() in client threads. 4 | 5 | jack = require("luajack") 6 | 7 | jack.verbose("on") 8 | 9 | THREAD = [[ 10 | selectable = require("luajack.selectable") 11 | socket = require("socket") 12 | 13 | rbuf = arg[1] 14 | -- create the 'selectable' ringbuffer: 15 | r = selectable(jack, rbuf) 16 | 17 | -- select() loop: 18 | while true do 19 | local rlist, wlist, errmsg = socket.select({ r }, nil, .5) 20 | if errmsg then print("select:", errmsg) end 21 | if rlist then 22 | for _, s in ipairs(rlist) do 23 | if s == r then 24 | local tag, data = jack.ringbuffer_read(rbuf) 25 | print("thread received: tag=".. tag .. " data=".. data) 26 | end 27 | end 28 | end 29 | end 30 | ]] 31 | 32 | PROCESS = [[ 33 | c, rbuf = table.unpack(arg) 34 | 35 | local m = 0 36 | local tag = 1 37 | function process(nframes) 38 | m = m + 1 39 | if m == 100 then 40 | -- Write some data to the ringbuffer. This will automatically write 41 | -- a byte to the associated pipe, so select() will detect it and will 42 | -- wake up the user thread: 43 | jack.ringbuffer_write(rbuf, tag, "message #" .. tag ) 44 | tag = tag + 1 45 | m = 0 46 | end 47 | end 48 | 49 | jack.process_callback(c, process) 50 | ]] 51 | 52 | -- Create the client: 53 | c = jack.client_open("select_example") 54 | 55 | -- Create the ringbuffers: 56 | local rbuf = jack.ringbuffer(c, 100, true, true) -- main to thread 57 | -- Notice that the ringbuffer is created with usepipe=true so that the thread 58 | -- can use it with socket.select(). 59 | 60 | -- Create the thread and process contexts: 61 | thread = jack.thread_load(c, THREAD, rbuf) 62 | jack.process_load(c, PROCESS, c, rbuf) 63 | 64 | jack.shutdown_callback(c, function(_, code, reason) 65 | error(reason .." (".. code ..")") 66 | end) 67 | 68 | jack.activate(c) 69 | 70 | jack.sleep(10) 71 | print("exiting") 72 | 73 | -------------------------------------------------------------------------------- /examples/ringbuffers/selectpingpong.lua: -------------------------------------------------------------------------------- 1 | -- LuaJack example: selectpingpong.lua 2 | -- 3 | -- Shows the use of ringbuffers with select() in client threads. 4 | 5 | jack = require("luajack") 6 | 7 | jack.verbose("on") 8 | 9 | THREAD = [[ 10 | selectable = require("luajack.selectable") 11 | socket = require("socket") 12 | 13 | rbuf_in, rbuf_out = table.unpack(arg) 14 | 15 | -- create the 'selectable' object: 16 | r = selectable(jack, rbuf_in) 17 | 18 | local function receive() 19 | local tag, data = jack.ringbuffer_read(rbuf_in) 20 | print("thread received: tag=".. tag .. " data=".. data) 21 | jack.ringbuffer_write(rbuf_out, tag, "pong" .. tag) 22 | end 23 | 24 | -- select() loop: 25 | while true do 26 | local rlist, wlist, errmsg = socket.select({ r }, nil, .5) 27 | if errmsg then print("select:", errmsg) end 28 | if rlist then 29 | for _, s in ipairs(rlist) do 30 | if s == r then receive() end 31 | end 32 | end 33 | end 34 | ]] 35 | 36 | PROCESS = [[ 37 | c, thread, rbuf_in, rbuf_out = table.unpack(arg) 38 | 39 | local m = 0 40 | local tag = 0 41 | function process(nframes) 42 | -- check if the user thread wrote some data to rbuf_in: 43 | local rtag, rdata = jack.ringbuffer_read(rbuf_in) 44 | if rtag then 45 | -- we should not print in rt threads, but this is just an example: 46 | print("main received: tag=".. rtag .. " data=".. rdata) 47 | end 48 | m = m + 1 49 | if m == 100 then 50 | -- write some data to rbuf_out (this will automatically write a byte 51 | -- to the associated pipe, so select() will detect it and will wake up 52 | -- the user thread): 53 | jack.ringbuffer_write(rbuf_out, tag, "ping" .. tag ) 54 | tag = tag + 1 55 | m = 0 56 | end 57 | end 58 | 59 | jack.process_callback(c, process) 60 | ]] 61 | 62 | -- Create the client: 63 | c = jack.client_open("select_example") 64 | 65 | -- Create the ringbuffers: 66 | local rbuf1 = jack.ringbuffer(c, 8000, true) -- thread to client 67 | local rbuf2 = jack.ringbuffer(c, 8000, true, true) -- client to thread 68 | -- Note: rbuf2 is created with usepipe=true so that the thread can use it with 69 | -- socket.select(). 70 | -- rbuf1, on the other hand, is created with usepipe=nil (i.e. false) because 71 | -- its reader is the process() callback's thread, which cannot use socket.select(), 72 | -- being a real-time thread. Iinstead, it checks for data in the ringbuffer at the 73 | -- beginning of the callback. 74 | 75 | -- Create the thread and process contexts: 76 | thread = jack.thread_load(c, THREAD, rbuf2, rbuf1) 77 | jack.process_load(c, PROCESS, c, thread, rbuf1, rbuf2) 78 | 79 | jack.shutdown_callback(c, function(_, code, reason) 80 | error(reason .." (".. code ..")") 81 | end) 82 | 83 | jack.activate(c) 84 | 85 | jack.sleep(10) 86 | print("exiting") 87 | 88 | -------------------------------------------------------------------------------- /examples/showtime.lua: -------------------------------------------------------------------------------- 1 | -- LuaJack example: showtime.lua 2 | -- 3 | -- LuaJack version of JACK's showtime.c example. 4 | 5 | jack = require("luajack") 6 | getopt = require("luajack.getopt") 7 | fmt = string.format 8 | 9 | my_name = arg[0] 10 | 11 | USAGE = 12 | "Usage: lua " .. my_name .. " [options]" .. 13 | "\nWith no options, displays jack time information at intervals of 1 second.".. 14 | "\nOptions:" .. 15 | "\n -i, --interval display time at intervals of seconds" .. 16 | "\n -h, --help display this help message" .. 17 | "\n -v, --version display version information and exit" .. 18 | "\n\n" 19 | 20 | function ShowVersion() 21 | print(fmt("%s: %s, %s", my_name, jack._VERSION, jack._JACK_VERSION)) 22 | end 23 | 24 | function ShowUsage(errmsg) 25 | if errmsg then print(errmsg, "\n") end 26 | print(USAGE) 27 | if errmsg then os.exit() end 28 | end 29 | 30 | 31 | short_opts = "i:hv" 32 | long_opts = { 33 | interval = 'i', 34 | help = 'h', 35 | version = 'v', 36 | } 37 | 38 | optarg, optind, opterr = getopt(arg, short_opts, long_opts) 39 | if opterr then ShowUsage(opterr) end 40 | if optarg.h then ShowUsage() os.exit(true) end 41 | if optarg.v then ShowVersion() os.exit(true) end 42 | interval = tonumber(optarg.i) or 1 43 | 44 | function showtime(c) 45 | local state, position = jack.transport_query(c) 46 | print("------------------------") 47 | print("state: " .. state) 48 | print("frame: " .. position.frame .. " (current: " .. jack.frame_time(c) .. ")") 49 | print("position fields: ") 50 | for k, v in pairs(position) do print(k, v) end 51 | -- print("state:", jack.transport_state(c)) 52 | end 53 | 54 | c = jack.client_open(my_name) 55 | 56 | jack.shutdown_callback(c, function(_, code, reason) 57 | error(reason .." (".. code ..")") 58 | end) 59 | 60 | while true do 61 | jack.sleep(interval) 62 | showtime(c) 63 | end 64 | -------------------------------------------------------------------------------- /examples/sinesynth.lua: -------------------------------------------------------------------------------- 1 | -- LuaJack example: sinesynth.lua 2 | -- 3 | -- Naive synthesizer, receives MIDI events from a MIDI input port 4 | -- and generates boring sinusoidal tones. 5 | -- 6 | -- To try it, with the JACK virtual keyboard, launch jackd (eg. using 7 | -- QjackCtl), then from two different shells execute: 8 | -- $ jack-keyboard 9 | -- $ lua sinesynth.lua auto 10 | -- 11 | -- This should connect the MIDI out of jack-keyboard with the MIDI in of the synth, 12 | -- and the audio out of the synth with the first 2 system playback ports. 13 | -- If everything is alright, playing the jack-keyboard should result in sinusoidal 14 | -- tones coming out from the system speakers, corresponding to the notes played on 15 | -- the keyboard. The synth reacts only to note on/off events. 16 | -- 17 | -- By executing without the 'auto' argument (i.e.: $ lua sinesynth.lua ) 18 | -- the ports are not automatically connected and must be connected manually 19 | -- using QjackCtl or by other means. 20 | -- 21 | jack = require("luajack") 22 | 23 | NAME = arg[0] 24 | auto_connect = arg[1] == "auto" and true or false 25 | MIDI_CONTROLLER_OUT = "jack-keyboard:midi_out" 26 | 27 | jack.verbose("on") 28 | 29 | --------------------------------------------------------------------- 30 | PROCESS = [[ 31 | midi = require("luajack.midi") 32 | c, in_port, out_port = table.unpack(arg) 33 | 34 | pi = math.pi 35 | sin = math.sin 36 | active = {} -- active[m] = velocity (if key m is on) or nil (if off) 37 | 38 | fs = jack.sample_rate(c) 39 | 40 | local wT = {} -- wT[m] = radian frequency for MIDI key = m 41 | for m = 1, 120 do -- piano keyboard: 21 to 108 42 | wT[m] = midi.note_frequency(m)*2*pi/fs 43 | end 44 | 45 | function set_velocity(m, v) 46 | if v == 0 then 47 | active[m] = nil 48 | else 49 | active[m] = v/127/2 50 | end 51 | end 52 | 53 | local y = {} -- output samples 54 | n = 0 -- frame number 55 | function compute_output(nframes) 56 | for i = 1, nframes do 57 | y[i] = 0 58 | for m,v in pairs(active) do -- add active notes 59 | y[i] = y[i] + v * sin(wT[m]*n) 60 | end 61 | n = n+1 62 | end 63 | end 64 | 65 | function get_midi_events() 66 | while true do 67 | time, msg = jack.read(in_port) 68 | if not time then return end 69 | ev, chan, par = midi.decode(msg) 70 | if ev == "note on" then 71 | --print(time, ev, par.key, par.velocity) -- should not print in an RT callback ... 72 | set_velocity(par.key, par.velocity) 73 | elseif ev == "note off" then 74 | --print(time, ev, par.key, par.velocity) 75 | set_velocity(par.key, 0) 76 | else 77 | --print(time, ev) 78 | end 79 | end 80 | end 81 | 82 | function process(nframes) 83 | ec, lc = jack.get_buffer(in_port) 84 | if ec > 0 then 85 | get_midi_events() 86 | end 87 | jack.get_buffer(out_port) 88 | compute_output(nframes) 89 | jack.write(out_port, table.unpack(y)) 90 | end 91 | 92 | jack.process_callback(c, process) 93 | ]] 94 | 95 | --------------------------------------------------------------------- 96 | c = jack.client_open(NAME) 97 | 98 | in_port = jack.input_midi_port(c, "midi_in") 99 | out_port = jack.output_audio_port(c, "audio_out") 100 | 101 | jack.process_load(c, PROCESS, c, in_port, out_port) 102 | 103 | jack.shutdown_callback(c, function(code, reason) error("shutdown from server") end) 104 | 105 | jack.activate(c) 106 | 107 | if auto_connect then 108 | jack.connect(c, MIDI_CONTROLLER_OUT, NAME..":midi_in") 109 | jack.connect(c, NAME..":audio_out", "system:playback_1") 110 | jack.connect(c, NAME..":audio_out", "system:playback_2") 111 | end 112 | 113 | fs = jack.sample_rate(c) 114 | bs = jack.buffer_size(c) 115 | period = bs/fs*1e6 116 | 117 | jack.sleep(1) 118 | jack.profile(c, "start") 119 | print(string.format("fs=%d bufsz=%d (period=%.0f us)", fs, bs, period)) 120 | 121 | while true do 122 | jack.sleep(3) 123 | local n, min, max, mean, var = jack.profile(c) 124 | print(string.format("n=%d min=%.1f max=%.1f mean=%.1f dev=%.1f (us) period/max=%.0f", 125 | n, min*1e6, max*1e6, mean*1e6, math.sqrt(var)*1e6, period/(max*1e6))) 126 | end 127 | 128 | -------------------------------------------------------------------------------- /examples/thru.lua: -------------------------------------------------------------------------------- 1 | -- LuaJack example: thru.lua 2 | -- 3 | -- Just copies data from an input port to an output port 4 | -- (works with audio ports as well as with MIDI ports). 5 | 6 | jack = require("luajack") 7 | getopt = require("luajack.getopt") 8 | fmt = string.format 9 | 10 | my_name = arg[0] 11 | 12 | USAGE = 13 | "Usage: lua " .. my_name .. " [options]" .. 14 | "\nRedirects input to output'".. 15 | "\nOptions:" .. 16 | "\n -m, --midi use MIDI ports" .. 17 | "\n -h, --help display this help message" .. 18 | "\n -v, --version display version information and exit" .. 19 | "\n\n" 20 | 21 | function ShowVersion() 22 | print(fmt("%s: %s, %s", my_name, jack._VERSION, jack._JACK_VERSION)) 23 | end 24 | 25 | function ShowUsage(errmsg) 26 | if errmsg then print(errmsg, "\n") end 27 | print(USAGE) 28 | if errmsg then os.exit() end 29 | end 30 | 31 | short_opts = "mhv" 32 | long_opts = { 33 | midi = 'm', 34 | help = 'h', 35 | version = 'v', 36 | } 37 | 38 | optarg, optind, opterr = getopt(arg, short_opts, long_opts) 39 | if opterr then ShowUsage(opterr) end 40 | if optarg.h then ShowUsage() os.exit(true) end 41 | if optarg.v then ShowVersion() os.exit(true) end 42 | 43 | c = jack.client_open(my_name, { no_start_server=true }) 44 | 45 | jack.verbose("on") 46 | 47 | if optarg.m then 48 | port_in = jack.input_midi_port(c, "in") 49 | port_out = jack.output_midi_port(c, "out") 50 | else 51 | port_in = jack.input_audio_port(c, "in") 52 | port_out = jack.output_audio_port(c, "out") 53 | end 54 | 55 | jack.process_load(c, [[ 56 | c, port_in, port_out = table.unpack(arg) 57 | 58 | function process(nframes) 59 | jack.get_buffer(port_in) 60 | jack.get_buffer(port_out) 61 | jack.copy(port_out, port_in) 62 | end 63 | 64 | jack.process_callback(c, process) 65 | ]], c, port_in, port_out) 66 | 67 | jack.shutdown_callback(c, function(_, code, reason) 68 | error(reason .." (".. code..")") 69 | end) 70 | 71 | jack.activate(c) 72 | 73 | jack.sleep() -- endless loop 74 | -------------------------------------------------------------------------------- /luajack/getopt.lua: -------------------------------------------------------------------------------- 1 | -- luajack/getopt.lua 2 | -- 3 | -- This code is a slight modification of lua-alt-getopt 4 | -- (http://sourceforge.net/projects/lua-alt-getopt/ ) 5 | -- The copyright notice of the original version is included below. 6 | -- 7 | -- Stefano Trettel 8 | -- 9 | ------------------------------------------------------------------------ 10 | -- Original version copyright notice 11 | -- Copyright (c) 2009 Aleksey Cheusov 12 | -- 13 | -- Permission is hereby granted, free of charge, to any person obtaining 14 | -- a copy of this software and associated documentation files (the 15 | -- "Software"), to deal in the Software without restriction, including 16 | -- without limitation the rights to use, copy, modify, merge, publish, 17 | -- distribute, sublicense, and/or sell copies of the Software, and to 18 | -- permit persons to whom the Software is furnished to do so, subject to 19 | -- the following conditions: 20 | -- 21 | -- The above copyright notice and this permission notice shall be 22 | -- included in all copies or substantial portions of the Software. 23 | -- 24 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | -- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | -- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | -- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | -- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | -- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | -- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | 32 | local type, pairs, ipairs, io, os = type, pairs, ipairs, io, os 33 | 34 | -- module ("alt_getopt") @@ 35 | 36 | local function convert_short2long (opts) 37 | local i = 1 38 | local len = #opts 39 | local ret = {} 40 | 41 | for short_opt, accept_arg in opts:gmatch("(%w)(:?)") do 42 | ret[short_opt]=#accept_arg 43 | end 44 | 45 | return ret 46 | end 47 | 48 | local opterr = nil 49 | 50 | local function exit_with_error(msg, exit_status) 51 | opterr = msg 52 | return nil 53 | --[[ 54 | io.stderr:write (msg) 55 | os.exit (exit_status) 56 | --]] 57 | end 58 | 59 | local function err_unknown_opt(opt) 60 | opterr = "Unknown option `-" .. (#opt > 1 and "-" or "") .. opt .. "'" 61 | end 62 | 63 | local function canonize(options, opt) 64 | if not options [opt] then 65 | err_unknown_opt(opt) 66 | return opt 67 | end 68 | 69 | while type (options [opt]) == "string" do 70 | opt = options [opt] 71 | if not options [opt] then 72 | err_unknown_opt(opt) 73 | return opt 74 | end 75 | end 76 | return opt 77 | end 78 | 79 | local function get_ordered_opts(arg, sh_opts, long_opts) 80 | local i = 1 81 | local count = 1 82 | local opts = {} 83 | local optarg = {} 84 | 85 | local options = convert_short2long (sh_opts) 86 | for k,v in pairs (long_opts) do 87 | options [k] = v 88 | end 89 | 90 | while i <= #arg do 91 | local a = arg [i] 92 | 93 | if a == "--" then i = i + 1 break 94 | elseif a == "-" then break 95 | elseif a:sub (1, 2) == "--" then 96 | local pos = a:find ("=", 1, true) 97 | if pos then 98 | local opt = a:sub (3, pos-1) 99 | opt = canonize(options, opt) 100 | if not opt or opterr then return nil end 101 | if options [opt] == 0 then 102 | return exit_with_error("Bad usage of option `" .. a .. "'", 1) 103 | end 104 | 105 | optarg [count] = a:sub (pos+1) 106 | opts [count] = opt 107 | else 108 | local opt = a:sub (3) 109 | 110 | opt = canonize (options, opt) 111 | if not opt or opterr then return nil end 112 | 113 | if options [opt] == 0 then 114 | opts [count] = opt 115 | else 116 | if i == #arg then 117 | return exit_with_error ("Missed value for option `" .. a .. "'", 1) 118 | end 119 | 120 | optarg [count] = arg [i+1] 121 | opts [count] = opt 122 | i = i + 1 123 | end 124 | end 125 | count = count + 1 126 | 127 | elseif a:sub (1, 1) == "-" then 128 | local j 129 | for j=2,a:len () do 130 | local opt = canonize (options, a:sub (j, j)) 131 | if not opt or opterr then return nil end 132 | 133 | if options [opt] == 0 then 134 | opts [count] = opt 135 | count = count + 1 136 | elseif a:len () == j then 137 | if i == #arg then 138 | return exit_with_error ("Missed value for option `-" .. opt .. "'", 1) 139 | end 140 | 141 | optarg [count] = arg [i+1] 142 | opts [count] = opt 143 | i = i + 1 144 | count = count + 1 145 | break 146 | else 147 | optarg [count] = a:sub (j+1) 148 | opts [count] = opt 149 | count = count + 1 150 | break 151 | end 152 | end 153 | else 154 | break 155 | end 156 | 157 | i = i + 1 158 | end 159 | 160 | return opts,i,optarg 161 | end 162 | 163 | 164 | local function get_opts(arg, sh_opts, long_opts) 165 | local ret = {} 166 | 167 | local opts,optind,optarg = get_ordered_opts(arg, sh_opts, long_opts) 168 | 169 | if not opts then return nil, nil, opterr end 170 | 171 | for i,v in ipairs(opts) do 172 | if optarg [i] then 173 | ret[v] = optarg [i] 174 | else 175 | ret[v] = true --@@ 176 | end 177 | end 178 | return ret, optind, nil 179 | end 180 | 181 | return get_opts --@@ 182 | 183 | -------------------------------------------------------------------------------- /luajack/selectable.lua: -------------------------------------------------------------------------------- 1 | -- The MIT License (MIT) 2 | -- 3 | -- Copyright (c) 2015 Stefano Trettel 4 | -- 5 | -- Software repository: LuaJack, https://github.com/stetre/luajack 6 | -- 7 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 8 | -- of this software and associated documentation files (the "Software"), to deal 9 | -- in the Software without restriction, including without limitation the rights 10 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | -- copies of the Software, and to permit persons to whom the Software is 12 | -- furnished to do so, subject to the following conditions: 13 | -- 14 | -- The above copyright notice and this permission notice shall be included in all 15 | -- copies or substantial portions of the Software. 16 | -- 17 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | -- SOFTWARE. 24 | 25 | ------------------------------------------------------------------------------- 26 | -- LuaJack selectable ringbuffer 27 | ------------------------------------------------------------------------------- 28 | -- Turns the read end of a LuaJack ringbuffer into an object which is 29 | -- compatible with LuaSocket's socket.select() and eith LunaSDL's reactor. 30 | 31 | local function create_object(jack, ref) 32 | local self = {} 33 | local ref = ref 34 | local jack = jack 35 | local fd = jack.ringbuffer_getfd(ref) 36 | return setmetatable(self, 37 | { 38 | __index = { 39 | 40 | reference = function(self) return ref end, 41 | 42 | getfd = function(self) return fd end, 43 | 44 | dirty = function(self) return jack.ringbuffer_peek(ref) end, 45 | 46 | settimeout = function(self, seconds) return true end, 47 | 48 | read = function(self) return jack.ringbuffer_read(ref) end, 49 | 50 | write = function(self, tag, data) return jack.ringbuffer_write(ref, tag, data) end, 51 | 52 | reset = function(self) return jack.ringbuffer_reset(ref) end, 53 | 54 | }, -- __index 55 | 56 | __tostring = function(self) return string.format("ringbuffer{%d}",ref) end, 57 | 58 | -- __gc = function(self) print("collected") end, 59 | 60 | }) 61 | end 62 | 63 | return create_object 64 | 65 | -------------------------------------------------------------------------------- /luajack/shell.lua: -------------------------------------------------------------------------------- 1 | -- The MIT License (MIT) 2 | -- 3 | -- Copyright (c) 2016 Stefano Trettel 4 | -- 5 | -- Software repository: LuaJack, https://github.com/stetre/luajack 6 | -- 7 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 8 | -- of this software and associated documentation files (the "Software"), to deal 9 | -- in the Software without restriction, including without limitation the rights 10 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | -- copies of the Software, and to permit persons to whom the Software is 12 | -- furnished to do so, subject to the following conditions: 13 | -- 14 | -- The above copyright notice and this permission notice shall be included in all 15 | -- copies or substantial portions of the Software. 16 | -- 17 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | -- SOFTWARE. 24 | 25 | ------------------------------------------------------------------------------- 26 | -- LuaJack Bash Utilities 27 | ------------------------------------------------------------------------------- 28 | 29 | local shell = {} 30 | 31 | function shell.exec(command) 32 | -- Executes a shell command in the background and returns its pid 33 | local fp = io.popen(command .. ' & echo $!') 34 | assert(fp) 35 | local pid = fp:read('n') 36 | assert(pid) 37 | fp:close() 38 | return math.floor(pid) 39 | end 40 | 41 | function shell.term(pid) 42 | -- Sends a TERM signal to process 43 | os.execute("kill -TERM " .. pid) 44 | end 45 | 46 | function shell.kill(pid) 47 | -- Sends a KILL signal to process 48 | os.execute("kill -KILL " .. pid) 49 | end 50 | 51 | function shell.exists(pid) 52 | -- Checks if the process exists 53 | if os.execute("ps -p ".. pid .." -o pid= > /dev/null") then 54 | return true 55 | end 56 | return false 57 | end 58 | 59 | return shell 60 | 61 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | 2 | ifdef MINGW_PREFIX 3 | $(error "not available on this platform") 4 | MINGW=1 5 | else 6 | LINUX=1 7 | endif 8 | 9 | # Lua version 10 | LUAVER?=$(shell lua -e 'print(string.match(_VERSION, "%d+%.%d+") or "5.3")') 11 | ifeq ($(LUAVER),) 12 | # lua-interpreter not found 13 | LUAVER=5.3 14 | endif 15 | 16 | # Base install directory 17 | ifdef LINUX 18 | PREFIX?=/usr/local 19 | endif 20 | ifdef MINGW 21 | PREFIX?=$(MINGW_PREFIX) 22 | endif 23 | 24 | # Directory where to install Lua modules 25 | L_DIR=$(PREFIX)/share/lua/$(LUAVER) 26 | # Directory where to install Lua C modules 27 | C_DIR=$(PREFIX)/lib/lua/$(LUAVER) 28 | # Directory where to install C headers 29 | H_DIR=$(PREFIX)/include 30 | # Directory where to install C libraries 31 | S_DIR=$(PREFIX)/lib 32 | 33 | ifeq ($(D),1) 34 | DEBUG=1 35 | endif 36 | 37 | ifdef LINUX 38 | INCDIR = -I/usr/include -I/usr/include/lua$(LUAVER) 39 | LIBDIR = -L/usr/lib 40 | LIBS = -ljack -lpthread 41 | endif 42 | ifdef MINGW 43 | LIBS = 44 | endif 45 | 46 | Tgt := luajack 47 | Src := $(wildcard *.c) 48 | Objs := $(Src:.c=.o) 49 | 50 | 51 | COPT += -O2 52 | #COPT += -O0 -g 53 | COPT += -Wall -Wextra -Wpedantic 54 | COPT += -std=gnu99 55 | COPT += -DLUAVER=$(LUAVER) 56 | ifdef LINUX 57 | COPT += -fpic 58 | COPT += -DLINUX 59 | endif 60 | ifdef MINGW 61 | COPT += -DMINGW 62 | endif 63 | ifdef DEBUG 64 | COPT += -DDEBUG 65 | COPT += -Wshadow -Wsign-compare -Wundef -Wwrite-strings 66 | COPT += -Wdisabled-optimization -Wdeclaration-after-statement 67 | COPT += -Wmissing-prototypes -Wstrict-prototypes -Wnested-externs 68 | COPT += -Wc++-compat -Wold-style-definition 69 | endif 70 | 71 | override CFLAGS = $(COPT) $(INCDIR) 72 | 73 | default: build 74 | 75 | where: 76 | @echo "PREFIX="$(PREFIX) 77 | @echo "LUAVER="$(LUAVER) 78 | @echo $(L_DIR) 79 | @echo $(C_DIR) 80 | @echo $(H_DIR) 81 | @echo $(S_DIR) 82 | 83 | clean: 84 | @-rm -f *.so *.dll *.o *.err *.map *.S *~ *.log 85 | @-rm -f $(Tgt).symbols 86 | 87 | install: 88 | @-mkdir -pv $(H_DIR) 89 | @-mkdir -pv $(C_DIR) 90 | @-mkdir -pv $(S_DIR) 91 | @-mkdir -pv $(L_DIR) 92 | @-cp -fpv $(Tgt).h $(H_DIR) 93 | @-cp -fpvr ../$(Tgt) $(L_DIR) 94 | ifdef LINUX 95 | @-cp -fpv $(Tgt).so $(C_DIR) 96 | @-ln -fsv $(C_DIR)/$(Tgt).so $(S_DIR)/lib$(Tgt).so 97 | endif 98 | ifdef MINGW 99 | @-cp -fpv $(Tgt).dll $(C_DIR) 100 | endif 101 | 102 | uninstall: 103 | @-rm -f $(H_DIR)/$(Tgt).h 104 | @-rm -f $(C_DIR)/$(Tgt).so 105 | @-rm -f $(S_DIR)/lib$(Tgt).so 106 | @-rm -fr $(L_DIR)/$(Tgt) 107 | @-rm -f $(C_DIR)/$(Tgt).dll 108 | 109 | build: clean $(Tgt) 110 | 111 | symbols: build 112 | @objdump -T $(Tgt).so > $(Tgt).symbols 113 | 114 | $(Tgt): $(Objs) 115 | ifdef LINUX 116 | @-$(CC) -shared -o $(Tgt).so $(Objs) $(LIBDIR) $(LIBS) 117 | endif 118 | ifdef MINGW 119 | @-$(CC) -shared -o $(Tgt).dll $(Objs) $(LIBDIR) $(LIBS) 120 | endif 121 | @-rm -f $(Objs) 122 | @echo 123 | 124 | -------------------------------------------------------------------------------- /src/_make: -------------------------------------------------------------------------------- 1 | make D=$1 && sudo make install 2 | -------------------------------------------------------------------------------- /src/alloc.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2015 Stefano Trettel 4 | * 5 | * Software repository: LuaJack, https://github.com/stetre/luajack 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | /**************************************************************************** 27 | * Memory allocator * 28 | ****************************************************************************/ 29 | 30 | #include "internal.h" 31 | 32 | /* LuaJack does not use malloc(), free() etc directly, but it inherits the 33 | * memory allocator from the main Lua state instead (see lua_getallocf in 34 | * the Lua manual). 35 | * 36 | * By doing so, one can use an alternative malloc() implementation without 37 | * recompiling LuaJack (one needs to recompile lua only, or execute it with 38 | * LD_PRELOAD set to the path to the malloc library). 39 | * 40 | */ 41 | static lua_Alloc alloc = NULL; 42 | static void* ud = NULL; 43 | static lua_Alloc rt_alloc = NULL; 44 | static void* rt_ud = NULL; 45 | 46 | lua_Alloc luajack_allocf(void **ud_) 47 | { 48 | /* @@ change this if you want a different memory allocator for the rt threads */ 49 | *ud_ = ud; 50 | return alloc; 51 | } 52 | 53 | void luajack_malloc_init(lua_State *L) 54 | { 55 | if(alloc) 56 | luaL_error(L, UNEXPECTED_ERROR); 57 | alloc = lua_getallocf(L, &ud); 58 | rt_alloc = luajack_allocf(&rt_ud); 59 | if(!rt_alloc) 60 | luajack_error("cannot initialize luajack_malloc()"); 61 | } 62 | 63 | void* luajack_main_malloc(size_t size) 64 | { return alloc ? alloc(ud, NULL, 0, size) : NULL; } 65 | 66 | void luajack_main_free(void *ptr) 67 | { 68 | if(alloc) 69 | alloc(ud, ptr, 0, 0); 70 | } 71 | 72 | void* luajack_malloc(size_t size) 73 | { return rt_alloc ? rt_alloc(rt_ud, NULL, 0, size) : NULL; } 74 | 75 | void luajack_free(void *ptr) 76 | { 77 | if(rt_alloc) 78 | rt_alloc(rt_ud, ptr, 0, 0); 79 | } 80 | 81 | -------------------------------------------------------------------------------- /src/cud.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2015 Stefano Trettel 4 | * 5 | * Software repository: LuaJack, https://github.com/stetre/luajack 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | /**************************************************************************** 27 | * Clients database * 28 | ****************************************************************************/ 29 | 30 | #include "internal.h" 31 | 32 | static int cmp(cud_t *cud1, cud_t *cud2) /* the compare function */ 33 | { return (cud1->key < cud2->key ? -1 : cud1->key > cud2->key); } 34 | 35 | static RB_HEAD(cudtree_s, cud_s) Head = RB_INITIALIZER(&Head); 36 | 37 | RB_PROTOTYPE_STATIC(cudtree_s, cud_s, entry, cmp) 38 | RB_GENERATE_STATIC(cudtree_s, cud_s, entry, cmp) 39 | 40 | static cud_t *cud_remove(cud_t *cud) 41 | { return RB_REMOVE(cudtree_s, &Head, cud); } 42 | static cud_t *cud_insert(cud_t *cud) 43 | { return RB_INSERT(cudtree_s, &Head, cud); } 44 | cud_t *cud_search(uintptr_t key) 45 | { cud_t tmp; tmp.key = key; return RB_FIND(cudtree_s, &Head, &tmp); } 46 | cud_t *cud_first(uintptr_t key) 47 | { cud_t tmp; tmp.key = key; return RB_NFIND(cudtree_s, &Head, &tmp); } 48 | cud_t *cud_next(cud_t *cud) 49 | { return RB_NEXT(cudtree_s, &Head, cud); } 50 | #if 0 51 | cud_t *cud_prev(cud_t *cud) 52 | { return RB_PREV(cudtree_s, &Head, cud); } 53 | cud_t *cud_min(void) 54 | { return RB_MIN(cudtree_s, &Head); } 55 | cud_t *cud_max(void) 56 | { return RB_MAX(cudtree_s, &Head); } 57 | cud_t *cud_root(void) 58 | { return RB_ROOT(&Head); } 59 | #endif 60 | 61 | cud_t *cud_new(void) 62 | { 63 | cud_t *cud; 64 | if((cud = (cud_t*)Malloc(sizeof(cud_t))) == NULL) return NULL; 65 | memset(cud, 0, sizeof(cud_t)); 66 | cud->key = (uintptr_t)cud; 67 | if(cud_search(cud->key)) 68 | { Free(cud); luajack_error(UNEXPECTED_ERROR); return NULL; } 69 | cud->obj.type = LUAJACK_TCLIENT; 70 | cud->obj.xud = (void*)cud; 71 | cud->SampleRate = LUA_NOREF; 72 | cud->Xrun = LUA_NOREF; 73 | cud->GraphOrder = LUA_NOREF; 74 | cud->Freewheel = LUA_NOREF; 75 | cud->ClientRegistration = LUA_NOREF; 76 | cud->PortRegistration = LUA_NOREF; 77 | cud->PortRename = LUA_NOREF; 78 | cud->PortConnect = LUA_NOREF; 79 | cud->Shutdown = LUA_NOREF; 80 | cud->Latency = LUA_NOREF; 81 | cud->Session = LUA_NOREF; 82 | cud->Process = LUA_NOREF; 83 | cud->BufferSize = LUA_NOREF; 84 | cud->Sync = LUA_NOREF; 85 | cud->Timebase = LUA_NOREF; 86 | cud->TimebaseConditional = LUA_NOREF; 87 | SIMPLEQ_INIT(&(cud->fifo)); 88 | luajack_stat_reset(&(cud->stat)); 89 | cud_insert(cud); 90 | MarkCudValid(cud); 91 | return cud; 92 | } 93 | 94 | static void cud_free(cud_t* cud) 95 | { 96 | if(cud_search(cud->key) == cud) 97 | cud_remove(cud); 98 | Free(cud); 99 | DBG("cud_free()\n"); 100 | } 101 | 102 | void cud_free_all(void) 103 | { 104 | cud_t *cud; 105 | while((cud = cud_first(0))) 106 | cud_free(cud); 107 | } 108 | 109 | cud_t* cud_check(lua_State *L, int arg) 110 | { 111 | uintptr_t key = luaL_checkinteger(L, arg); 112 | cud_t *cud = cud_search(key); 113 | if(!cud || !IsCudValid(cud)) 114 | luaL_error(L, "invalid client reference"); 115 | return cud; 116 | } 117 | 118 | void cud_fifo_insert(cud_t* cud, pud_t *pud) 119 | { 120 | pud->cud = cud; 121 | SIMPLEQ_INSERT_TAIL(&(cud->fifo), pud, cudfifoentry); 122 | } 123 | 124 | void cud_fifo_remove(pud_t *pud) 125 | { 126 | #define cud ((cud_t*)(pud->cud)) 127 | pud_t *node, *prev; 128 | node = SIMPLEQ_FIRST(&(cud->fifo)); 129 | if(node) 130 | { 131 | if(node == pud) 132 | { 133 | SIMPLEQ_REMOVE_HEAD(&(cud->fifo), cudfifoentry); 134 | return; 135 | } 136 | prev = node; 137 | while((node = SIMPLEQ_NEXT(prev, cudfifoentry))) 138 | { 139 | if(node == pud) 140 | { 141 | SIMPLEQ_REMOVE_AFTER(&(cud->fifo), prev, cudfifoentry); 142 | return; 143 | } 144 | prev = node; 145 | } 146 | } 147 | luajack_error(UNEXPECTED_ERROR); 148 | #undef cud 149 | } 150 | 151 | -------------------------------------------------------------------------------- /src/evt.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2015 Stefano Trettel 4 | * 5 | * Software repository: LuaJack, https://github.com/stetre/luajack 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | /**************************************************************************** 27 | * Non-rt callbacks queue * 28 | ****************************************************************************/ 29 | 30 | #include "internal.h" 31 | 32 | static pthread_mutex_t Lock = PTHREAD_MUTEX_INITIALIZER; 33 | #define evt_lock() pthread_mutex_lock(&Lock) 34 | #define evt_unlock() pthread_mutex_unlock(&Lock) 35 | 36 | static SIMPLEQ_HEAD(myfifo_s, evt_s) head = SIMPLEQ_HEAD_INITIALIZER(head); 37 | static unsigned int counter = 0; 38 | 39 | evt_t* evt_new(void) 40 | { 41 | evt_t *evt = (evt_t*)Malloc(sizeof(evt_t)); 42 | if(!evt) return NULL; 43 | memset(evt, 0, sizeof(evt_t)); 44 | return evt; 45 | } 46 | 47 | unsigned int evt_count(void) 48 | { return counter; } 49 | 50 | void evt_free(evt_t *evt) 51 | { 52 | if(evt->arg1) Free(evt->arg1); 53 | if(evt->arg2) Free(evt->arg2); 54 | if(evt->session_event) jack_session_event_free(evt->session_event); 55 | Free(evt); 56 | } 57 | 58 | void evt_insert(evt_t *evt) 59 | { 60 | evt_lock(); 61 | SIMPLEQ_INSERT_TAIL(&head, evt, entry); 62 | counter++; 63 | syncpipe_write(luajack_evtpipe[1]); /* to make pselect() return */ 64 | evt_unlock(); 65 | } 66 | 67 | static evt_t* remove_nolock(void) 68 | { 69 | evt_t *evt = SIMPLEQ_FIRST(&head); 70 | if(evt) 71 | { 72 | SIMPLEQ_REMOVE_HEAD(&head, entry); 73 | syncpipe_read(luajack_evtpipe[0]); 74 | counter--; 75 | } 76 | return evt; 77 | } 78 | 79 | evt_t* evt_remove(void) 80 | { 81 | evt_t *evt; 82 | evt_lock(); 83 | evt = remove_nolock(); 84 | evt_unlock(); 85 | return evt; 86 | } 87 | 88 | evt_t* evt_first(void) 89 | { 90 | evt_t *evt; 91 | evt_lock(); 92 | evt = SIMPLEQ_FIRST(&head); 93 | evt_unlock(); 94 | return evt; 95 | } 96 | 97 | evt_t* evt_next(evt_t *evt) 98 | { 99 | evt_t *evt1; 100 | evt_lock(); 101 | evt1 = SIMPLEQ_NEXT(evt, entry); 102 | evt_unlock(); 103 | return evt1; 104 | } 105 | 106 | void evt_free_all(void) 107 | { 108 | evt_t *evt; 109 | /* called only at exit, from the main thread and with all other threads inactive */ 110 | /* evt_lock(); */ 111 | while((evt = remove_nolock()) != NULL) 112 | evt_free(evt); 113 | /* evt_lock(); */ 114 | } 115 | 116 | -------------------------------------------------------------------------------- /src/latency.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2015 Stefano Trettel 4 | * 5 | * Software repository: LuaJack, https://github.com/stetre/luajack 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | /**************************************************************************** 27 | * Managing and determining latency * 28 | ****************************************************************************/ 29 | 30 | #include "internal.h" 31 | 32 | int latency_pushmode(lua_State *L, jack_latency_callback_mode_t mode) 33 | { 34 | switch(mode) 35 | { 36 | case JackCaptureLatency: lua_pushstring(L, "capture"); break; 37 | case JackPlaybackLatency: lua_pushstring(L, "playback"); break; 38 | default: 39 | luaL_error(L, "invalid callback mode %u", mode); 40 | } 41 | return 1; 42 | } 43 | 44 | static jack_latency_callback_mode_t CheckMode(lua_State *L, int arg) 45 | { 46 | const char* mode = luaL_checkstring(L, arg); 47 | if(strncmp(mode, "capture", strlen("capture")) == 0) return JackCaptureLatency; 48 | if(strncmp(mode, "playback", strlen("playback")) == 0) return JackPlaybackLatency; 49 | return (jack_latency_callback_mode_t)luaL_error(L, "invalid latency callback mode '%s'", mode); 50 | } 51 | 52 | /*--------------------------------------------------------------------------* 53 | | Functions | 54 | *--------------------------------------------------------------------------*/ 55 | 56 | static int RecomputeTotalLatencies(lua_State *L) 57 | { 58 | cud_t *cud = cud_check(L, 1); 59 | int rc = jack_recompute_total_latencies(cud->client); 60 | if(rc!=0) 61 | return luajack_strerror(L, rc); 62 | return 0; 63 | } 64 | 65 | static int LatencyRange(lua_State *L) 66 | { 67 | jack_latency_range_t range; 68 | pud_t *pud = pud_check(L, 1); 69 | jack_latency_callback_mode_t mode = CheckMode(L, 2); 70 | jack_port_get_latency_range(pud->port, mode, &range); 71 | lua_pushinteger(L, range.min); 72 | lua_pushinteger(L, range.max); 73 | return 2; 74 | } 75 | 76 | static int SetLatencyRange(lua_State *L) 77 | { 78 | jack_latency_range_t range; 79 | pud_t *pud = pud_check(L, 1); 80 | jack_latency_callback_mode_t mode = CheckMode(L, 2); 81 | range.min = luaL_checkinteger(L, 3); 82 | range.max = luaL_checkinteger(L, 4); 83 | jack_port_set_latency_range(pud->port, mode, &range); 84 | return 0; 85 | } 86 | 87 | /*--------------------------------------------------------------------------* 88 | | Registration | 89 | *--------------------------------------------------------------------------*/ 90 | 91 | static const struct luaL_Reg MFunctions[] = 92 | { 93 | { "recompute_total_latencies", RecomputeTotalLatencies }, 94 | { "latency_range", LatencyRange }, 95 | { "set_latency_range", SetLatencyRange }, 96 | { NULL, NULL } /* sentinel */ 97 | }; 98 | 99 | int luajack_open_latency(lua_State *L, int state_type) 100 | { 101 | switch(state_type) 102 | { 103 | case ST_MAIN: luaL_setfuncs(L, MFunctions, 0); break; 104 | default: 105 | break; 106 | } 107 | return 1; 108 | } 109 | 110 | -------------------------------------------------------------------------------- /src/pud.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2015 Stefano Trettel 4 | * 5 | * Software repository: LuaJack, https://github.com/stetre/luajack 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | /**************************************************************************** 27 | * Ports database * 28 | ****************************************************************************/ 29 | 30 | #include "internal.h" 31 | 32 | static int cmp(pud_t *pud1, pud_t *pud2) /* the compare function */ 33 | { return (pud1->key < pud2->key ? -1 : pud1->key > pud2->key); } 34 | 35 | static RB_HEAD(pudtree_s, pud_s) Head = RB_INITIALIZER(&Head); 36 | 37 | RB_PROTOTYPE_STATIC(pudtree_s, pud_s, entry, cmp) 38 | RB_GENERATE_STATIC(pudtree_s, pud_s, entry, cmp) 39 | 40 | static pud_t *pud_remove(pud_t *pud) 41 | { return RB_REMOVE(pudtree_s, &Head, pud); } 42 | static pud_t *pud_insert(pud_t *pud) 43 | { return RB_INSERT(pudtree_s, &Head, pud); } 44 | static pud_t *pud_search(uintptr_t key) 45 | { pud_t tmp; tmp.key = key; return RB_FIND(pudtree_s, &Head, &tmp); } 46 | pud_t *pud_first(uintptr_t key) 47 | { pud_t tmp; tmp.key = key; return RB_NFIND(pudtree_s, &Head, &tmp); } 48 | pud_t *pud_next(pud_t *pud) 49 | { return RB_NEXT(pudtree_s, &Head, pud); } 50 | #if 0 51 | pud_t *pud_prev(pud_t *pud) 52 | { return RB_PREV(pudtree_s, &Head, pud); } 53 | pud_t *pud_min(void) 54 | { return RB_MIN(pudtree_s, &Head); } 55 | pud_t *pud_max(void) 56 | { return RB_MAX(pudtree_s, &Head); } 57 | pud_t *pud_root(void) 58 | { return RB_ROOT(&Head); } 59 | #endif 60 | pud_t *pud_new(cud_t *cud) 61 | { 62 | pud_t *pud; 63 | if((pud = (pud_t*)Malloc(sizeof(pud_t))) == NULL) return NULL; 64 | memset(pud, 0, sizeof(pud_t)); 65 | pud->key = (uintptr_t)pud; 66 | if(pud_search(pud->key)) 67 | { Free(pud); luajack_error(UNEXPECTED_ERROR); return NULL; } 68 | cud_fifo_insert(cud, pud); 69 | pud->obj.type = LUAJACK_TPORT; 70 | pud->obj.xud = (void*)pud; 71 | pud_insert(pud); 72 | MarkPudValid(pud); 73 | return pud; 74 | } 75 | 76 | static void pud_free(pud_t* pud) 77 | { 78 | if(pud_search(pud->key) == pud) 79 | pud_remove(pud); 80 | cud_fifo_remove(pud); 81 | Free(pud); 82 | DBG("pud_free()\n"); 83 | } 84 | 85 | void pud_free_all(void) 86 | { 87 | pud_t *pud; 88 | while((pud = pud_first(0))) 89 | pud_free(pud); 90 | } 91 | 92 | 93 | pud_t* pud_check(lua_State *L, int arg) 94 | { 95 | uintptr_t key = luaL_checkinteger(L, arg); 96 | pud_t *pud = pud_search(key); 97 | if(!pud || !IsPudValid(pud)) 98 | luaL_error(L, "invalid port reference"); 99 | return pud; 100 | } 101 | 102 | -------------------------------------------------------------------------------- /src/rud.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2015 Stefano Trettel 4 | * 5 | * Software repository: LuaJack, https://github.com/stetre/luajack 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | /**************************************************************************** 27 | * Ringbuffers database * 28 | ****************************************************************************/ 29 | 30 | #include "internal.h" 31 | 32 | static int cmp(rud_t *rud1, rud_t *rud2) /* the compare function */ 33 | { return (rud1->key < rud2->key ? -1 : rud1->key > rud2->key); } 34 | 35 | static RB_HEAD(rudtree_s, rud_s) Head = RB_INITIALIZER(&Head); 36 | 37 | RB_PROTOTYPE_STATIC(rudtree_s, rud_s, entry, cmp) 38 | RB_GENERATE_STATIC(rudtree_s, rud_s, entry, cmp) 39 | 40 | static rud_t *rud_remove(rud_t *rud) 41 | { return RB_REMOVE(rudtree_s, &Head, rud); } 42 | static rud_t *rud_insert(rud_t *rud) 43 | { return RB_INSERT(rudtree_s, &Head, rud); } 44 | static rud_t *rud_search(uintptr_t key) 45 | { rud_t tmp; tmp.key = key; return RB_FIND(rudtree_s, &Head, &tmp); } 46 | rud_t *rud_first(uintptr_t key) 47 | { rud_t tmp; tmp.key = key; return RB_NFIND(rudtree_s, &Head, &tmp); } 48 | rud_t *rud_next(rud_t *rud) 49 | { return RB_NEXT(rudtree_s, &Head, rud); } 50 | #if 0 51 | rud_t *rud_prev(rud_t *rud) 52 | { return RB_PREV(rudtree_s, &Head, rud); } 53 | rud_t *rud_min(void) 54 | { return RB_MIN(rudtree_s, &Head); } 55 | rud_t *rud_max(void) 56 | { return RB_MAX(rudtree_s, &Head); } 57 | rud_t *rud_root(void) 58 | { return RB_ROOT(&Head); } 59 | #endif 60 | 61 | rud_t *rud_new(void) 62 | { 63 | rud_t *rud; 64 | if((rud = (rud_t*)Malloc(sizeof(rud_t))) == NULL) return NULL; 65 | memset(rud, 0, sizeof(rud_t)); 66 | rud->key = (uintptr_t)rud; 67 | if(rud_search(rud->key)) 68 | { Free(rud); luajack_error(UNEXPECTED_ERROR); return NULL; } 69 | rud->obj.type = LUAJACK_TRINGBUFFER; 70 | rud->obj.xud = (void*)rud; 71 | rud_insert(rud); 72 | MarkRudValid(rud); 73 | return rud; 74 | } 75 | 76 | static void rud_free(rud_t* rud) 77 | { 78 | if(rud_search(rud->key) == rud) 79 | rud_remove(rud); 80 | Free(rud); 81 | DBG("rud_free()\n"); 82 | } 83 | 84 | void rud_free_all(void) 85 | { 86 | rud_t *rud; 87 | while((rud = rud_first(0))) 88 | rud_free(rud); 89 | } 90 | 91 | 92 | rud_t* rud_check(lua_State *L, int arg) 93 | { 94 | uintptr_t key = luaL_checkinteger(L, arg); 95 | rud_t *rud = rud_search(key); 96 | if(!rud || !IsRudValid(rud)) 97 | luaL_error(L, "invalid ringbuffer reference"); 98 | return rud; 99 | } 100 | 101 | -------------------------------------------------------------------------------- /src/srvctl.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2015 Stefano Trettel 4 | * 5 | * Software repository: LuaJack, https://github.com/stetre/luajack 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | /**************************************************************************** 27 | * Controlling & querying JACK server operation 28 | ****************************************************************************/ 29 | 30 | #include "internal.h" 31 | 32 | /*--------------------------------------------------------------------------* 33 | | Client methods | 34 | *--------------------------------------------------------------------------*/ 35 | 36 | static int Freewheel(lua_State *L) 37 | { 38 | cud_t *cud = cud_check(L, 1); 39 | int onoff = luajack_checkonoff(L, 2); 40 | int rc = jack_set_freewheel(cud->client, onoff); 41 | if(rc != 0) 42 | return luajack_strerror(L, rc); 43 | return 0; 44 | } 45 | 46 | static int SetBufferSize(lua_State *L) 47 | { 48 | cud_t *cud = cud_check(L, 1); 49 | nframes_t nframes = luaL_checkinteger(L, 2); 50 | int rc = jack_set_buffer_size(cud->client, nframes); 51 | if(rc != 0) 52 | return luajack_strerror(L, rc); 53 | return 0; 54 | } 55 | 56 | static int BufferSize(lua_State *L) 57 | { 58 | cud_t *cud = cud_check(L, 1); 59 | lua_pushinteger(L, cud->buffer_size); 60 | /* jack_get_buffer_size() should only be used when the client is activated */ 61 | return 1; 62 | } 63 | 64 | static int SampleRate(lua_State *L) 65 | { 66 | cud_t *cud = cud_check(L, 1); 67 | lua_pushinteger(L, jack_get_sample_rate(cud->client)); 68 | return 1; 69 | } 70 | 71 | static int CpuLoad(lua_State *L) 72 | { 73 | cud_t *cud = cud_check(L, 1); 74 | lua_pushnumber(L, jack_cpu_load(cud->client)); 75 | return 1; 76 | } 77 | 78 | 79 | /*--------------------------------------------------------------------------* 80 | | Registration | 81 | *--------------------------------------------------------------------------*/ 82 | 83 | static const struct luaL_Reg MFunctions[] = 84 | { 85 | { "freewheel", Freewheel }, 86 | { "set_buffer_size", SetBufferSize }, 87 | { "buffer_size", BufferSize }, 88 | { "sample_rate", SampleRate }, 89 | { "cpu_load", CpuLoad }, 90 | { NULL, NULL } /* sentinel */ 91 | }; 92 | 93 | #define PFunctions MFunctions 94 | 95 | static const struct luaL_Reg TFunctions[] = 96 | { 97 | { "buffer_size", BufferSize }, 98 | { "sample_rate", SampleRate }, 99 | { "cpu_load", CpuLoad }, 100 | { NULL, NULL } /* sentinel */ 101 | }; 102 | 103 | 104 | int luajack_open_srvctl(lua_State *L, int state_type) 105 | { 106 | switch(state_type) 107 | { 108 | case ST_MAIN: luaL_setfuncs(L, MFunctions, 0); break; 109 | case ST_PROCESS: luaL_setfuncs(L, PFunctions, 0); break; 110 | case ST_THREAD: luaL_setfuncs(L, TFunctions, 0); break; 111 | default: 112 | break; 113 | } 114 | return 1; 115 | } 116 | 117 | -------------------------------------------------------------------------------- /src/statistics.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2015 Stefano Trettel 4 | * 5 | * Software repository: LuaJack, https://github.com/stetre/luajack 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | /**************************************************************************** 27 | * Monitoring the performance of a running JACK server * 28 | ****************************************************************************/ 29 | 30 | #include "internal.h" 31 | #include 32 | 33 | /*--------------------------------------------------------------------------* 34 | | Functions | 35 | *--------------------------------------------------------------------------*/ 36 | 37 | static int MaxDelayedUsecs(lua_State *L) 38 | /* delay = max_delayed_usecs(client) */ 39 | { 40 | float dly; 41 | cud_t *cud = cud_check(L, 1); 42 | dly = jack_get_max_delayed_usecs(cud->client); 43 | lua_pushnumber(L, dly); 44 | return 1; 45 | } 46 | 47 | static int XrunDelayedUsecs(lua_State *L) 48 | /* delay = xrun_delayed_usecs(client) */ 49 | { 50 | cud_t *cud = cud_check(L, 1); 51 | lua_pushnumber(L, jack_get_xrun_delayed_usecs(cud->client)); 52 | return 1; 53 | } 54 | 55 | static int ResetMaxDelayedUsecs(lua_State *L) 56 | /* reset_max_delayed_usecs(client) */ 57 | { 58 | cud_t *cud = cud_check(L, 1); 59 | jack_reset_max_delayed_usecs(cud->client); 60 | return 0; 61 | } 62 | 63 | /*--------------------------------------------------------------------------* 64 | | Registration | 65 | *--------------------------------------------------------------------------*/ 66 | 67 | static const struct luaL_Reg MFunctions[] = 68 | { 69 | { "max_delayed_usecs", MaxDelayedUsecs }, 70 | { "xrun_delayed_usecs", XrunDelayedUsecs }, 71 | { "reset_max_delayed_usecs", ResetMaxDelayedUsecs }, 72 | { NULL, NULL } /* sentinel */ 73 | }; 74 | 75 | int luajack_open_statistics(lua_State *L, int state_type) 76 | { 77 | switch(state_type) 78 | { 79 | case ST_MAIN: luaL_setfuncs(L, MFunctions, 0); break; 80 | default: 81 | break; 82 | } 83 | return 1; 84 | } 85 | 86 | -------------------------------------------------------------------------------- /src/syncpipe.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2015 Stefano Trettel 4 | * 5 | * Software repository: LuaJack, https://github.com/stetre/luajack 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | /**************************************************************************** 27 | * Synchronization pipes * 28 | ****************************************************************************/ 29 | 30 | #include "internal.h" 31 | #include 32 | 33 | static sigset_t Sigpipe; /* constant */ 34 | 35 | int syncpipe_new(int pipefd[2]) 36 | { 37 | int flags, rc; 38 | if(pipe(pipefd) == -1) 39 | return -1; 40 | 41 | /* make both ends non-blocking */ 42 | flags = fcntl(pipefd[0], F_GETFL, 0); 43 | rc = fcntl(pipefd[0], F_SETFL, flags | O_NONBLOCK); 44 | flags = fcntl(pipefd[1], F_GETFL, 1); 45 | rc += fcntl(pipefd[1], F_SETFL, flags | O_NONBLOCK); 46 | if(rc < 0) 47 | return -1; 48 | return 0; 49 | } 50 | 51 | int syncpipe_write(int writefd) 52 | /* write a byte to the pipe so to make select() return */ 53 | { 54 | int rc; 55 | sigset_t oldset; 56 | char zero = 0; 57 | /* mask SIGPIPE otherwise it would cause disasters if received in a thread... */ 58 | pthread_sigmask(SIG_BLOCK, &Sigpipe, &oldset); 59 | rc = write(writefd, &zero, 1); 60 | /* rc = 1 : ok, 1 byte written, rc = 0: nothing was written (don't bother) */ 61 | if(rc<0) 62 | { 63 | if(errno==EAGAIN) /* (EWOULDBLOCK) the write would block (skip it) */ 64 | rc = 0; 65 | else /* EPIPE, EBADF, ... : all fatal errors */ 66 | rc = -1; 67 | } 68 | pthread_sigmask(SIG_SETMASK, &oldset, NULL); 69 | return rc; 70 | } 71 | 72 | int syncpipe_read(int readfd) 73 | /* read a byte from the pipe to avoid filling it and because otherwise 74 | * the readfd would remain 'ready' for select() */ 75 | { 76 | int rc; 77 | char zero; 78 | rc = read(readfd, &zero, 1); 79 | if(rc>0) return 0; /* ok */ 80 | if(rc==0) return -1; /* pipe is closed */ 81 | /* rc<0 */ 82 | if(errno==EAGAIN) return 0; /* (EWOULDBLOCK) the read would block (skip it) */ 83 | return -1; /* EPIPE, EBADF, ... : all fatal errors */ 84 | } 85 | 86 | int syncpipe_init(void) 87 | { 88 | sigemptyset(&Sigpipe); 89 | sigaddset(&Sigpipe, SIGPIPE); 90 | return 1; 91 | } 92 | 93 | -------------------------------------------------------------------------------- /src/time.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2015 Stefano Trettel 4 | * 5 | * Software repository: LuaJack, https://github.com/stetre/luajack 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | /**************************************************************************** 27 | * Handling time * 28 | ****************************************************************************/ 29 | 30 | #include "internal.h" 31 | 32 | #define CheckProcess(L, cud) do { \ 33 | if(!IsProcessCallback((cud))) \ 34 | luaL_error((L), "function available only in process callback"); \ 35 | } while(0) 36 | 37 | /*--------------------------------------------------------------------------* 38 | | Functions | 39 | *--------------------------------------------------------------------------*/ 40 | 41 | static int Time(lua_State *L) 42 | { 43 | lua_pushinteger(L, jack_get_time()); 44 | return 1; 45 | } 46 | 47 | static int FrameTime(lua_State *L) 48 | { 49 | cud_t *cud = cud_check(L, 1); 50 | lua_pushinteger(L, jack_frame_time(cud->client)); 51 | return 1; 52 | } 53 | 54 | static int Since(lua_State *L) /* */ 55 | { 56 | jack_time_t t1 = luaL_checkinteger(L, 1); 57 | lua_pushinteger(L, jack_get_time() - t1); 58 | return 1; 59 | } 60 | 61 | static int SinceFrame(lua_State *L) /* */ 62 | { 63 | cud_t *cud = cud_check(L, 1); 64 | nframes_t t1 = luaL_checkinteger(L, 2); 65 | lua_pushinteger(L, jack_frame_time(cud->client) - t1); 66 | return 1; 67 | } 68 | 69 | static int FramesToTime(lua_State *L) 70 | { 71 | cud_t *cud = cud_check(L, 1); 72 | nframes_t nframes = luaL_checkinteger(L, 2); 73 | lua_pushinteger(L, jack_frames_to_time(cud->client, nframes)); 74 | return 1; 75 | } 76 | 77 | static int TimeToFrames(lua_State *L) 78 | { 79 | cud_t *cud = cud_check(L, 1); 80 | jack_time_t time = luaL_checkinteger(L, 2); 81 | lua_pushinteger(L, jack_time_to_frames(cud->client, time)); 82 | return 1; 83 | } 84 | 85 | static int FramesSinceCycleStart(lua_State *L) 86 | { 87 | cud_t *cud = cud_check(L, 1); 88 | lua_pushinteger(L, jack_frames_since_cycle_start(cud->client)); 89 | return 1; 90 | } 91 | 92 | static int LastFrameTime(lua_State *L) 93 | { 94 | cud_t *cud = cud_check(L, 1); 95 | CheckProcess(L, cud); 96 | lua_pushinteger(L, jack_last_frame_time(cud->client)); 97 | return 1; 98 | } 99 | 100 | static int CycleTimes(lua_State *L) 101 | { 102 | nframes_t current_frames; 103 | jack_time_t current_usecs, next_usecs; 104 | float period_usecs; 105 | int rc; 106 | cud_t *cud = cud_check(L, 1); 107 | CheckProcess(L, cud); 108 | rc = jack_get_cycle_times(cud->client, 109 | ¤t_frames, ¤t_usecs,&next_usecs, &period_usecs); 110 | if(rc) 111 | luaL_error(L, "jack_get_cycle_times() returned %d", rc); 112 | lua_pushinteger(L, current_frames); 113 | lua_pushinteger(L, current_usecs); 114 | lua_pushinteger(L, next_usecs); 115 | lua_pushnumber(L, period_usecs); 116 | return 4; 117 | } 118 | 119 | 120 | /*--------------------------------------------------------------------------* 121 | | Registration | 122 | *--------------------------------------------------------------------------*/ 123 | 124 | #define COMMON_FUNCTIONS \ 125 | { "time", Time }, \ 126 | { "since", Since }, \ 127 | { "since_frame", SinceFrame }, \ 128 | { "frames_to_time", FramesToTime }, \ 129 | { "time_to_frames", TimeToFrames }, \ 130 | { "frames_since_cycle_start", FramesSinceCycleStart }, \ 131 | { "frame_time", FrameTime }, \ 132 | { "frame", FrameTime } 133 | 134 | 135 | static const struct luaL_Reg MFunctions[] = 136 | { 137 | COMMON_FUNCTIONS, 138 | { NULL, NULL } /* sentinel */ 139 | }; 140 | 141 | #define TFunctions MFunctions 142 | 143 | static const struct luaL_Reg PFunctions[] = 144 | { 145 | COMMON_FUNCTIONS, 146 | { "last_frame_time", LastFrameTime }, 147 | { "cycle_times", CycleTimes }, 148 | { NULL, NULL } /* sentinel */ 149 | }; 150 | 151 | 152 | int luajack_open_time(lua_State *L, int state_type) 153 | { 154 | switch(state_type) 155 | { 156 | case ST_MAIN: luaL_setfuncs(L, MFunctions, 0); break; 157 | case ST_PROCESS: luaL_setfuncs(L, PFunctions, 0); break; 158 | case ST_THREAD: luaL_setfuncs(L, TFunctions, 0); break; 159 | default: 160 | break; 161 | } 162 | return 1; 163 | } 164 | 165 | -------------------------------------------------------------------------------- /src/tud.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2015 Stefano Trettel 4 | * 5 | * Software repository: LuaJack, https://github.com/stetre/luajack 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | /**************************************************************************** 27 | * Ports database * 28 | ****************************************************************************/ 29 | 30 | #include "internal.h" 31 | 32 | static int cmp(tud_t *tud1, tud_t *tud2) /* the compare function */ 33 | { return (tud1->key < tud2->key ? -1 : tud1->key > tud2->key); } 34 | 35 | static RB_HEAD(tudtree_s, tud_s) Head = RB_INITIALIZER(&Head); 36 | 37 | RB_PROTOTYPE_STATIC(tudtree_s, tud_s, entry, cmp) 38 | RB_GENERATE_STATIC(tudtree_s, tud_s, entry, cmp) 39 | 40 | static tud_t *tud_remove(tud_t *tud) 41 | { return RB_REMOVE(tudtree_s, &Head, tud); } 42 | static tud_t *tud_insert(tud_t *tud) 43 | { return RB_INSERT(tudtree_s, &Head, tud); } 44 | static tud_t *tud_search(uintptr_t key) 45 | { tud_t tmp; tmp.key = key; return RB_FIND(tudtree_s, &Head, &tmp); } 46 | tud_t *tud_first(uintptr_t key) 47 | { tud_t tmp; tmp.key = key; return RB_NFIND(tudtree_s, &Head, &tmp); } 48 | tud_t *tud_next(tud_t *tud) 49 | { return RB_NEXT(tudtree_s, &Head, tud); } 50 | #if 0 51 | tud_t *tud_prev(tud_t *tud) 52 | { return RB_PREV(tudtree_s, &Head, tud); } 53 | tud_t *tud_min(void) 54 | { return RB_MIN(tudtree_s, &Head); } 55 | tud_t *tud_max(void) 56 | { return RB_MAX(tudtree_s, &Head); } 57 | tud_t *tud_root(void) 58 | { return RB_ROOT(&Head); } 59 | #endif 60 | 61 | tud_t *tud_new(void) 62 | { 63 | tud_t *tud; 64 | if((tud = (tud_t*)Malloc(sizeof(tud_t))) == NULL) return NULL; 65 | memset(tud, 0, sizeof(tud_t)); 66 | tud->key = (uintptr_t)tud; 67 | if(tud_search(tud->key)) 68 | { Free(tud); luajack_error(UNEXPECTED_ERROR); return NULL; } 69 | tud->obj.type = LUAJACK_TTHREAD; 70 | tud->obj.xud = (void*)tud; 71 | tud_insert(tud); 72 | MarkTudValid(tud); 73 | return tud; 74 | } 75 | 76 | static void tud_free(tud_t* tud) 77 | { 78 | if(tud_search(tud->key) == tud) 79 | tud_remove(tud); 80 | Free(tud); 81 | DBG("tud_free()\n"); 82 | } 83 | 84 | void tud_free_all(void) 85 | { 86 | tud_t *tud; 87 | while((tud = tud_first(0))) 88 | tud_free(tud); 89 | } 90 | 91 | 92 | tud_t* tud_check(lua_State *L, int arg) 93 | { 94 | uintptr_t key = luaL_checkinteger(L, arg); 95 | tud_t *tud = tud_search(key); 96 | if(!tud || !IsTudValid(tud)) 97 | luaL_error(L, "invalid thread reference"); 98 | return tud; 99 | } 100 | 101 | -------------------------------------------------------------------------------- /thirdparty/asciidoctor-styles-license: -------------------------------------------------------------------------------- 1 | The CSS used for LuaJack Reference Manual is taken from the 2 | Asciidoctor styles project 3 | (https://github.com/asciidoctor/asciidoctor-stylesheet-factory ). 4 | 5 | Below is a copy of the Asciidoctor styles copyright notice. 6 | 7 | Stefano Trettel 8 | 9 | ======================================================================== 10 | 11 | Asciidoctor styles 12 | ------------------ 13 | 14 | Copyright (c) 2013 Dan Allen 15 | 16 | MIT License 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining 19 | a copy of this software and associated documentation files (the 20 | "Software"), to deal in the Software without restriction, including 21 | without limitation the rights to use, copy, modify, merge, publish, 22 | distribute, sublicense, and/or sell copies of the Software, and to 23 | permit persons to whom the Software is furnished to do so, subject to 24 | the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be 27 | included in all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 30 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 31 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 32 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 33 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 34 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 35 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 36 | 37 | 38 | Other licensed work 39 | ------------------- 40 | 41 | - Foundation 4 by Zurb, on which the themes are built, is licensed under the 42 | Apache License, v2.0: 43 | 44 | http://apache.org/licenses/LICENSE-2.0 45 | http://foundation.zurb.com 46 | 47 | - The riak theme is derived from the Riak documentation theme by Basho, 48 | licensed under the Creative Commons Attribution 3.0 Unported License: 49 | 50 | http://creativecommons.org/licenses/by/3.0/us 51 | http://docs.basho.org 52 | 53 | - The iconic theme is inspired by O'Reilly typography and Atlas manual. 54 | 55 | http://oreilly.com 56 | -------------------------------------------------------------------------------- /thirdparty/lua-alt-getopt-license: -------------------------------------------------------------------------------- 1 | The luajack/getopt.lua script is taken from: 2 | http://sourceforge.net/projects/lua-alt-getopt/ 3 | 4 | Below is a copy of the copyright notice included in the original script. 5 | 6 | Stefano Trettel 7 | 8 | =========================================================================== 9 | -- Copyright (c) 2009 Aleksey Cheusov 10 | -- 11 | -- Permission is hereby granted, free of charge, to any person obtaining 12 | -- a copy of this software and associated documentation files (the 13 | -- "Software"), to deal in the Software without restriction, including 14 | -- without limitation the rights to use, copy, modify, merge, publish, 15 | -- distribute, sublicense, and/or sell copies of the Software, and to 16 | -- permit persons to whom the Software is furnished to do so, subject to 17 | -- the following conditions: 18 | -- 19 | -- The above copyright notice and this permission notice shall be 20 | -- included in all copies or substantial portions of the Software. 21 | -- 22 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | -- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | -- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 25 | -- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 26 | -- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 27 | -- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 28 | -- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | 30 | -------------------------------------------------------------------------------- /thirdparty/openbsd-queue-license: -------------------------------------------------------------------------------- 1 | The src/queue.h module is taken from: 2 | http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/sys/sys/queue.h 3 | 4 | Below is a copy of the copyright notice included in the module. 5 | 6 | Stefano Trettel 7 | 8 | =========================================================================== 9 | /* 10 | * Copyright (c) 1991, 1993 11 | * The Regents of the University of California. All rights reserved. 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that the following conditions 15 | * are met: 16 | * 1. Redistributions of source code must retain the above copyright 17 | * notice, this list of conditions and the following disclaimer. 18 | * 2. Redistributions in binary form must reproduce the above copyright 19 | * notice, this list of conditions and the following disclaimer in the 20 | * documentation and/or other materials provided with the distribution. 21 | * 3. Neither the name of the University nor the names of its contributors 22 | * may be used to endorse or promote products derived from this software 23 | * without specific prior written permission. 24 | * 25 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 | * SUCH DAMAGE. 36 | * 37 | * @(#)queue.h 8.5 (Berkeley) 8/20/94 38 | */ 39 | 40 | -------------------------------------------------------------------------------- /thirdparty/openbsd-tree-license: -------------------------------------------------------------------------------- 1 | The src/tree.h module is taken from: 2 | http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/sys/sys/tree.h 3 | 4 | Below is a copy of the copyright notice included in the module. 5 | 6 | Stefano Trettel 7 | 8 | =========================================================================== 9 | /* 10 | * Copyright 2002 Niels Provos 11 | * All rights reserved. 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that the following conditions 15 | * are met: 16 | * 1. Redistributions of source code must retain the above copyright 17 | * notice, this list of conditions and the following disclaimer. 18 | * 2. Redistributions in binary form must reproduce the above copyright 19 | * notice, this list of conditions and the following disclaimer in the 20 | * documentation and/or other materials provided with the distribution. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | -------------------------------------------------------------------------------- /thirdparty/powered-by-lua-license: -------------------------------------------------------------------------------- 1 | The 'powered by Lua' logo (doc/powered-by-lua.gif ) is taken from: 2 | http://www.lua.org/images . 3 | 4 | Below is a copy of the copyright notice from the same web-page. 5 | 6 | Stefano Trettel 7 | 8 | =========================================================================== 9 | Copyright © 1998 Lua.org. Graphic design by Alexandre Nakonechnyj. 10 | 11 | Permission is hereby granted, without written agreement and without license 12 | or royalty fees, to use, copy, and distribute this logo for any purpose, 13 | including commercial applications, subject to the following conditions: 14 | 15 | - The origin of this logo must not be misrepresented; you must not claim that 16 | you drew the original logo. 17 | - The only modification you can make is to adapt the orbiting text to your 18 | product name. 19 | - The logo can be used in any scale as long as the relative proportions of its 20 | elements are maintained. 21 | --------------------------------------------------------------------------------