X32

I made the acquisition of a Behringer X32 standard console in June 2014 for my music studio and gig needs.
Great unit! is all I can say. No problem with it, a pretty good sound, easy to manage and learn, with a comprehensive list of effects and a lot of capabilities, including being controllable through OSC protocol.

X32 OSC Protocol Document

I decided to spend a little time (well... initially :-)) understanding OSC programming aspects to interface with the X32, and have it do things for me. This work resulted in an updated, unofficial X32 OSC protocol document which I hope will help many others. [updated Sep.8, 2018]

Over time, I developed utilities for my own use and help other users. These utilities can run under different operating systems. Raspberry and Mac/OSX versions are available for some of them. Click on the links below to download the versions of the utilities corresponding to your platform.

   


 

  
 

All utilities below are distributed as Freeware for non-commercial users.

Commercial users should contact me before using or distributing these tools.










And XAir devices?
Go to a site from Ken Mitchell who has ported some of the tools here and also created new utilities specifically aimed at XAir devices.


Standard disclaimers apply!

Here's a quick list (with links to Windows versions of the tools). I do invite you to read the description/examples below.
A small "hardware" project is proposed at the very end of this page.


Windows Utilities

Terminal/Command Line tools:
  • X32 emulator: The X32 you can carry in your pocket for developing apps or testing on the go. [updated Sep 23, 2018]
  • X32PlayScript: A script file for Theater play-scripts. [Updated May 5, 2016]
  • X32USB: A simple tool to list, play, load, execute files contained in the USB drive attached to X32. [updated Dec. 17, 2017]
  • X32GetScene: Get a Scene/Snippet file directly out of your X32. [updated Feb 18, 2018]
  • X32SetScene: Interprets a Scene/Snippet file and set your X32 accordingly. [updated  Feb 18, 2018]
  • X32GEQ2cpy: Copies one side of GEQ2 or TEQ2 to the other. [updated Oct. 31, 2016]
  • X32Replay: Record and play back a show (respecting time... of course). [updated May 1, 2016]
  • X32_Command: A simple, yet very powerful tool for sending OSC commands and listening to X32 OSC data. [updated Aug 1, 2018]
  • IPScan: A simple utility program to scan the network for connected X32 devices.Source code included in zip file. [updated Nov12, 2017]

Windows Applications

Graphical Interface tools:
  • X32Reaper: 2-way communication/control X32<->Reaper - see details below. [updated Dec 30, 2017]
  • X32GetShow: Get and save on your system the current show, cues, and all associated scenes and snippets. [updated Nov 17, 2017]
  • X32SetShow: Load in X32 a given show, cues, and all associated scenes and snippets from a PC location. [updated Nov 17, 2017]
  • X32Fade: A program to manage fade in and fade out of multiple levels / faders of the X32 - time controlled! [updated Feb 21, 2015]
  • X32Tap: "tap in" (manually or automatically) the time value of a delay effect of the X32. [updated Aug 4, 2018]
  • X32SetPreset: Load in X32 a Channel, Effect or Routing Preset from a PC location. [updated Nov 17, 2017]
  • X32SetLib: Load in X32 library,  Channel, Effect or Routing Presets from a PC location. [updated Nov 17, 2017]
  • X32GetLib: Retrieve your X32 preset libraries and save them to a PC directory. [updated Nov 17, 2017]
  • X32Ssaver: An X32 LCD & LEDs "screen-saver"& "SOF" utility. See below for POSIX, cmd, or GUI versions. [updated Sep 24, 2016]
  • X32AutoMix: A simple approach to Automatic Mixing tool for up to 32 channels. [updated Oct. 28, 2017]
  • X32CustomLayer: A utility to create and manage custom channel layers easily. [updated July 8, 2016]
  • X32DeskSave/X32DeskRestore: A utility to Save/Restore X32 State, Scene, Routing or Pattern based data sets. [updated Sep. 9, 2018]
  • X32SafeMutes: A utility to prevent unwanted changes to X32/M32 Mutes. [updated July 31, 2016]
  • X32PunchControl: Control your DAW while playing audio, recording or playing back X32 commands. [updated June 19, 2018]
  • X32Commander: Have X32 commands control other devices through MIDI or OSC. [updated July 21, 2018]
  • X32Midi2OSCr: Have X32 receive OSC data from MIDI devices. [updated June 24, 2018]
  • X32CopyFX: Reset or Copy sides or full parameters set from an effect to itself or another. [updated Nov. 25, 2016]
  • X32Wav_Xlive: Merging single WAV files into X-Live! compatible multi-channel WAV files. [updated Feb 17, 2018]
  • X32Xlive_Wav: Split / Explode X-Live! multi-channel WAV files from a recorded session into individual, single channel WAV files. [updated Mar. 3, 2018]
  • X32cpXLiveMarkers: Import XLive! Session markers into a REAPER project. [updated Feb 12, 2018]
  • X32Jog4Xlive: Enabling audio Jog and Shuttle for X-Live! while using SD card audio. [updated Nov. 25, 2017]
  • X32USBW: A utility to play/load/execute files contained in the X32 USB stick [updated Mar. 25, 2018]
  • X32VoiceTrax: A utility to let users manage their channel gain during recording sessions [May 11, 2018]

Other Platforms

Apple versions of all command-line mode programs running in a terminal window, compiled on a Mac book pro OS X HighSierra: X32mac. [updated May 17, 2018]

Ubuntu/Linux versions of some of the utilities above: ubuntu/Linux [updated May 17, 2018]

ARM versions of the first 7 utilities above, compiled on a Raspberry PI: X32arm. [updated Apr.. 8 2015]

Source code
of X32 utilities (C code) are available on git repositories at  https://github.com/pmaillot/X32-Behringer. Feel free to clone and contribute (ideas, bug fixes / improvements).

I'll progressively populate with the source code of the utilities, based on your requests and feedback.



Detailed List of Applications & Utilities

X32 emulator


The X32 you can carry in your pocket for developing apps or testing.
"32 channels, 16 bus, 8 simultaneous effects, 8 Aux,  6 Matrix, L/R, Mono, 8 DCA, 50+ internal effects, multi-client support"

sounds familiar? If you don't have your X32 with you, this X32 emulator can be helpful. With no particular attempt at optimization, this tool parses and manages X32 commands (as a real X32 would), keeps up to 4 xremote clients updated - Of course no sound, and even if all 32 Channels, 16 Sends, 8 FXreturns, 8 Aux, 6 Matrix, 8 DCA, Main and all FX parameters are fully implemented along with multi-client xremote update and many more, not all X32 commands are supported (and that's not the goal), but the emulator is handy for developing X32 applications.

Starting with version 0.22, the emulator will save and read its internal state (about 10k parameters). A file is used to this effect (.X32res.rc), and the non-Behringer /shutdown command enables saving data before closing the application. To create or re-init the file (necessary when moving from one version to another one), simply delete the old one, run the emulator and quit using a /shutdown command (using X32_Command for example); a new file is then created.

version 0.41 (May12, 2016) now correctly accepts an empty OSC Type Tag string (",\0\0\0") following the OSC Address Pattern as per OSC 1.0 specification. FXrtrn parameters have also been corrected, they were numerous typos preventing certain commands to execute correctly.

The latest version (0.70) supports X32 FW ver. 3.08 OSC commands. It also enables synchronization both ways with X32-Edit.

Windows version,
RaspPI version. Linux version with Linux/Ubuntu utilities above; Mac version with Mac utilities above.





Tools: Detailed description and examples:



X32PlayScript

A script file for Theater play-scripts

[Updated May 5, 2016]
A Small utility for venues, theaters, musicals where a show is rehearsed before being played during one or more seasons with pretty much the same settings, changing during the show.
This is a bash script, optimally running X32SetScene from one show act to the next, following play-scripts settings.

During rehearsals:
The FOH engineer sets all X32 scene parameters fit for a play act/scene, and saves the X32 scene in such a way the act and scene are listed along with a common name part, possibly followed by a meaningful text, for example in the case of the play "ALEX" ©2014 by J. Johnson:
A1S1_ALEX_The_Fond_Household, ..., A1S8_ALEX_The_Ink_Tank, ..., A2S1_ALEX_DAKs_Pad, ...

Saved scenes can be exported to USB then copied to a PC or directly exported to a PC using the X32GetScene utility proposed here.

During plays:
The FOH Engineer groups all scene files in a single directory.
MyMac:misc pm$ ls -l
-rwxr--r--  1 pm  staff  63740 Apr 24 10:55 A01S00_ALEX_init state.scn
-rwxr--r--  1 pm  staff  63740 May  4 08:26 A01S01_ALEX_The_Fond_Household.scn
        [...]
-rwxr--r--  1 pm  staff  63740 May  4 08:27 A01S08_ALEX_DAKs_Pad.scn
        [...]
-rwxr-xr-x  1 pm  staff   4071 May  5 09:21 X32PlayScript
MyMac:misc pm$


The FOH Engineer can the run the batch file below, placed in the same directory:
 MyMac:misc pm$ ./X32PlayScript ALEX


The utility (a shell script) will ask for the X32 IP address, list all the files in the directory and proposes to go through each of them, waiting for the FOH engineer input to set scene details by executing data contained in the scene file corresponding to the current play act and scene in an optimal way. As the script runs, you can enter the following (lowercase/uppercase):
y -> accept proposed scene updates to current state
k -> same as above but will send full scene
f -> skip proposed scene
b -> go back one scene
s -> early stop (exits the program)

any other character: do nothing





X32USB

A simple command line tool to list, play, load, execute files contained in the USB drive attached to X32.

The utility connects to the X32 (default IP is 192.168.1.62 and can be changed with option -i), displays a prompt and waits on <stdin> to read command and display the results to <stdout>. The prompt is either >: if no USB drive is present or $:[dir name] if a USB drive is detected and readable.
X32USB lists / executes files contained in the USB drive. An error is reported if no USB drive is present. Files are listed in two columns, with a preceding file id and their associated type, helping in the commands below.

The known commands are as follow:
help: Display command reminder help
ls List directory contents (with id and type)
dir List directory contents (with id and type)
cd <id> | <name>: Change directory (prompt is updated)
load <id> | <name>: Load or Run file (scene, snippet, etc.)
run <id> | <name>: Load or Run file (scene, snippet, etc.)
umount: Unmount the USB drive (no longer accessible)
exit | quit: Exists program

For wave files:
play <id> | <name>: Play WAV file
stop: Stops a currently playing wav
pause: Pauses a wav file currently playing
resume: Resumes playing the current wav file





X32USBW

A simple Windows tool to list, play, load, execute files contained in the USB drive attached to X32.

The utility is somewhat the equivalent of X32USB above, but a Windows application version. It starts with connecting to the X32 by specifying the IP address of the X32 and clicking on Connect. When this happens, a second window opens with the list of directories and files contained in the current directory of the USB stick.
From this window, one can select a file or directory and click on an action (PLAY, PAUSE or STOP for wav files, Dir for entering and listing a selected directory, and Load for loading an X32 scene, snippet, or preset.





The main window reflects the file currently playing, if any, as well as the PLAY, PAUSE or STOP status. At the right of the displayed filename is a decreasing counter showing the remaining time (in seconds) to the end of the file, before automatically jumping to the next file in the list.
Automatic playing of the file next to the current one is limited to the current directory.
The user can navigate to any other directory while a file is playing.





X32GetScene

Get a Scene/Snippet file directly out of your X32

The utility connects to the X32 (default IP is 192.168.0.64 and can be changed with option -i) reads <stdin> to read the elements you want to get from the X32, and saves the result to <stdout>.
The elements to get from the X32 are described/coded as they are in a typical .scn file. As a facility, you can provide an existing (partial or complete) scene file to get the elements from your X32. The values from the input scene file will be ignored.

Example:
X32GetScene -i 192.168.1.32 -s name1 -n note1 <Default.scn >myscene.scn

Will take all lines from Default.scn as requests to the X32, and generate a scene file with the current X32 values respective of the requests into a file called myscene.scn, with name and note values name1 and note1 respectively.

When not providing a file as input, one has to type in requests one line at a time. Typing "exit" will terminate the program. For example (without -s or -n, the utility will ask for a name an notes):
X32GetScene -i 192.168.1.32 -s name1 -n note1 >myscene.scn


Typing in:
/ch/01/config
/ch/01/delay
exit

creates a file myscene.scn contaning (actual values will depend on the state of your X32):
#2.1# "name1" "note1" %000000000 1 X32GetScene V1.3 ©2014 Patrick-Gilles Maillot

/ch/01/config "" 1 YE 1
/ch/01/delay OFF   0.3





X32SetScene

Interprets a Scene/Snippet file and set your X32 accordingly

The utility connects to the X32 (default IP is 192.168.0.64 and can be changed with option -i) reads <stdin> to read the scene file elements and transform them into OSC commands to the X32.
The elements to send to the X32 are described/coded as in typical .scn files.

Example:
X32SetScene -i 192.168.1.32 < myscene.scn

Will take all lines from myscene.scn, interpret them and set the X32 state accordingly.

When not providing a file as input, one has to type in requests one line at a time. Typing "exit" will terminate the program. For example:
X32SetScene -i 192.168.1.32


With typing in:
/ch/01/mix OFF   0 ON +0 OFF -oo
/ch/01/config "MyVox" 1 GN 1
exit


will set Channel 1 to: muted, fader to position 0db, pan to center, mono OFF and mono level to -infinity,
channel 1 scribble screen will light in green, display "MyVox", with icon 1 selected (i.e. blank), and channel 1 will have "IN 1" as source.


Watch a video of X32SetScene in action.





X32GEQ2cpy

Copies one side of GEQ2 or TEQ2 to the other, and more

The utility connects to the X32 (default IP is 192.168.0.64 and can be changed with option -i) reads faders for A or B side of G/T EQ2 and updates B or A with the values just read. It can also be used to copy all EQ fader values from one EQ slot to another. It can also be used to reset both sides of G/T EQ2 to 0db for all faders. Master levels can be treated separately.

Example,  assuming your X32 is at IP 192.168.1.32:
X32GEQ2cpy -i 192.168.1.32

Will copy all A side fader data to B side of G/T EQ2, which is expected to be at FX slot #1.

The FX slot where to find G/T EQ2 can be set with option -f <n> n: [1..8] - default = 1

The default direction for copy is A->B; This can be set with option -d which will take A>B, B>A, or R
Entering option -d R will reset all faders to 0db.
With version 1.3, -d C enables copying all faders from FX slot set with option -f to FX slot set with option -g

The master level faders are part of the copy or reset operation by default. To disable this: use -l 0 or -l n

Examples:

X32GEQ2cpy -i 192.168.1.32 -d r

will reset all faders and master levels for A and B of G/T EQ2 effect at FX slot #1

X32GEQ2cpy -i 192.168.1.32 -f 3 -l 0

will copy A to B all fader levels of G/T EQ2 effect at FX slot #3, not changing master levels

new in v 1.3:
X32GEQ2cpy -i 192.168.1.32  -d c -f 3 -g 4 -l 1

will copy all fader levels of G/T EQ2 effect at FX slot #3 to G/T EQ2 effect at FX slot #4, including master levels


Watch a video of X32GEQ2cpy in action





X32CopyFX

Reset or Copy sides or full parameters set from an effect to itself or another.

This small utility is the Windows GUI equivalent to the command line X32GEQ2cpy tool above.

As one launches X32CopyFX, the tool displays 5 fields the user has to fill;


The first field to fill is for the X32 IP address. As soon a the IP address is entered and the user clicks on "Connect", the tool requests from the X32 its set of currently loaded effects in the X32. The list of available effects is loaded into the two combo-boxes left of the APPLY button. The user then selects a source (FROM) and destination (TO) effect slot. A third combo-box lists the possible operations: reset, copy side A to side B, or the opposite (applies to the FROM effect), or copy the FROM effect data to the TO effect.

Starting with version 1.20, The tool offers two RESET types of effects settings to their respective default values. Choices are to reset only the effect in the FROM: box, or all effects from the FROM: to the TO: boxes; each effect will receive their respective default values.

Starting with ver 1.30, X32CopyFX will accept an optional "-f <file>" argument to the main program. If such option is passed on, the <file> provided by the user (a text file) is expected to hold pairs of lines in the form:
{
  effect name
  effect parameters values

}


This enables to set default values for effects to user preferences rather than default X32 values, for example the file below will overwrite HALL and PIT default values, all other effects will keep the X32 standard default values.
<myfx.txt>:
HALL
40 1.57 60 5k74 25 0.0 83 7k2 0.95 25 50 40
PIT
-12. 0 5.0 52 15k8 80

The APPLY button starts the selected operation. The button momentarily (1 second) displays "DONE" as the operation progresses.

NOTE: Microsoft Defender seems to inappropriately report this program as potential malware: X32CopyFX uses (arrays of) pointers and can overwrite some of these pointers (managing memory correctly) to replace X32 effects parameters default values by user sets. While a very effective way of doing this (from a memory management point of view), it can be something considered “suspicious”.
 




X32Reaper

X32 <-> REAPER controls.

The main idea is to use the X32 as a playback unit and a control surface to REAPER. Digital audio is stored and managed on a computer via REAPER. REAPER controls translate to the X32 and vice-verse, changes made at the X32 deck are reflected in REAPER.
An OSC config file and a daemon program enable Reaper actions to be reflected on the X32. Simultaneously, the daemon program listens to the X32 and translates X32 actions and changes to REAPER changes on the computer. The videos below are showing the different situations
I tested this on all 32 channels and no problem!, just be careful in selecting the X32 inputs from the card, and setting up the REAPER routing matrix correctly. It's simple but a little tedious at first: in 1->track 1->out 1, etc... until in 32 -> track 32 -> out 32. Nice diagonals.

Do read the documentation that comes with program, read REAPER documentation on setting up a control surface, this can / will be useful. Cut&paste the OSC config file provided in the X32Reaper documentation to install your config file. Be careful in selecting and matching ports and IP addresses, and you'll be just fine.


 Windows Graphical User Interface
  Raspberry Graphical User Interface

 


Notes
  • If you are a user of a previous version, you most likely need to update the REAPER OSC configuration and .X32Reaper.ini resource files to benefit from the new features and avoid errors. Please follow instructions in the documentation for managing this.
  • For the Windows-GUI version, two additional files need to be with the utility. There's a graphics .bmp file of 115x140 pixels you can replace with your preferred design, and a resource file to keep and reuse the panel values from the last run.
  • Starting with version 1.2.1 X32Reaper will automatically select user bank C when the Connect/Run button is clicked.
  • Starting with version 1.2.2 X32Reaper enables a jog-style navigation (forward or backwards by beat, measure, marker or item) from encoders when in "play" mode.
  • Starting with version 1.4, X32Reaper offers a lot more features in its Windows GUI version. Please favor this over older versions.
  • Version 1.5 corrects a mis-ordered set of DCA min and max vs. Bus min and max in the config file.
  • Version 1.8 adds a setting for managing REAPER Track Send Offset; indeed REAPER Track sends are logical values; Physical bus send tracks numbers are offset by the number of HW outputs the track is assigned to.
  • Version 1.9 fixes a weird behavior when selecting several REAPER tracks at once and moving one of the faders (REAPER or X32).
  • Version 2.0 introduces the notion of DCA for REAPER tracks. Of course this is already possible within REAPER, but the idea is to easily control multiple REAPER tracks from an X32 DCA (which can still control its REAPER associated track and X32 internal channels. Setting all associated REAPER tracks volumes to the value set by the respective X32 DCA. Also applies to tracks ON/OFF. REAPER track numbers are set in groups of min-max for each DCA. This is typically useful when controlling REAPER tracks beyond X32 capabilities, routed (withing REAPER) to stereo buses and having the bus pairs controlled by a single X32 DCA fader controlling the overall mix of all associated REAPER tracks, without cannibalizing X32 channels.
  • Version 2.10 provides (virtual) bank selection for REAPER tracks so they can be controlled from your X32, acting as a pure control surface. This is exclusive from the Transport Loop control as two buttons are reserved for bank up/down management. It also covers X32 → REAPER direction, for REAPER tracks, banks are in 32 tracks blocks. Useful for managing large numbers of REAPER tracks using X32 as a surface control, the actual mix being performed by REAPER.
  • Version 2.22 (2.20 & 2.21 had a small bugs) enables switching back and forth between virtual banks, keeping the settings that have been set with the X32 or with REAPER. Also, X32 scribbles icons and colors can be set from REAPER using a specific syntax for REAPER tracks.
  • Version 2.4 enables bank selection capability without having to set transport. When only bank selection is on, the user can choose the two buttons assigned to the bank up and down actions (still user set C only, buttons 5 to 12). When used in conjunctions with transport, the two buttons are 9 and 10 and cannot be changed. No change to the user interface, although I may enable changing buttons assignments from the tool GUI in a near future.
  • Version 2.41 corrects small things in the way channel  banks up and down buttons are using internal features.
  • Versions 2.5 & 2.51 enable the user to set the bank up/down buttons from the Windows graphical user interface.
  • Version 2.52 fixes a small rounding function and enables filtering what is actually sent to X32 from REAPER.
  • Version 2.61 offers the possibility to set REAPER markers on the fly directly from the X32 during recording or playback; Bank C color can also be chosen in the resource file.

Ready? 

... download
X32Reaper, unzip, and give it a run! You can also watch Michael's video, setting up his X32 with X32Reaper; Another video from Michael shows/explains using transport section.


REAPER Template

You'll find here a template I often use to start a project using my X32 and REAPER. This results in the project screen shown here and the routing shown here. It includes all currently available controls, and maps to the default X32Reaper resource file provided with the program. All 16 bus mixes are setup to enable a classical routing of input channels, mapped to the X32 capabilities. Setting up your X-USB card to 32in/32out will provide you with the capability to control REAPER Tracks and use the X32 capabilities to mix your work, listening to the result directly with the X32. A 32 channels sound "card". Make sure you toggle your X32 channel input routing between In and Out in order to mix or record with REAPER, respectively.

I have set all Channel, Aux, Bus[1-12], and DCA to their minimum as a precaution; all SENDs are also set to -oo. Better keep your audio levels safe when you start a new project. REAPER master track is not used; mixing/mastering is done only on the X32 as this is what I use for mixing audio.


[June 5] A Raspberry version 1.3 with a GUI is available too :-). Find it here.

The latest of X32Reaper for Mac and Linux is available as a command line (terminal) application. It includes all controls as mentioned in X32Reaper for Windows, reading all needed parameters from a configuration file (.X32Reaper.ini); below is an example of config file. The two first integers are ignored.

A typical resource file for the program (should be placed in the directory where the program is)

400 260 0 0 1 1
1 32 33 40 41 48 49 64 65 72 1
192.168.1.62
192.168.1.65
10025
10027

This would result in the following when running the program:
Showing the X32 and REAPER IPs and all parameters used for the running session. Use ^C to kill the program.





X32Replay

Record and play back a show

This is work in progress, but you may find it quite useful as it already is.

Start the utility... If record is on, all commands and modifications made on the X32 are time stamped (with a precision close to 10µs) and recorded to a file. if needed, recording can be paused, and started again.

When recording ends, the file is closed.

The file can then be played back, the time stamps associated with each recorded command ensure the commands will be played back at correct times, relative to the start of the play command.

The utility runs in a terminal window (Windows cmd); Example,  assuming your X32 is at IP 192.168.1.32:
 X32Replay -i 192.168.1.3


Additional option is-v 0/1; Attention, this option will display a lot of additional data and this can impact the responsiveness of the tool.

Usage: X32Replay [-i X32 console ipv4 address] -default: 192.168.0.64
                 [-v 0/1, verbose option] -default: 0
                 [-f file name] -default: X32ReplayFile.txt

  known commands:
    stop:         stops recording or playing and closes file
    record:       reports recording state
    record off:   stops recording
    record on:    starts or resumes recording
    record pause: pauses recording and keeps file opened
    pause:        pauses recording and keeps file opened
    play:         reports playing state
    play off:     stops playing
    play on:      starts playing
    # typed line: during recording, records the typed line as a user tag


download the X32Replay utility, unzip, and give it a run! (use "X32Replay -h" for command-line options)





X32GetShow

Get/Save on your PC the X32 current show, cues, and all associated scenes and snippets:


This program connects to the X32 (using the IP provided in the dedicated label). Once connected to X32, it reads the current show from X32, and saves the show as a *.shw file on your PC (in a directory you select using the "save To" button); all cues, scenes, and snippets are listed and saved with the show.

There are two options for saving the show: "Save To" and "Save As". Both are quite similar and will use the directory you point at in the opened dialog as destination directory for the saved data (show files). In the first option (Save To), the X32 show and saved files are keeping the name as set in the X32. The second option (Save As) enables renaming the X32 show files to a new show name as data is saved.

For all scenes and snippets listed in the show file, the utility creates the respective files (*.scn or *.snp) and saves all data associated with each scene or snippet. The saved files can then be kept for later or offline use, manual edit, or reload in a X32 from a USB drive.

The different scenes and snippets names will appear under the "GetSHOW" button as the program progresses in saving files. When finished, "Complete" will be displayed. In the event something does not complete as expected; "Error" will be displayed and the log file will have more information.

Example: on a X32 system with show name "show1", containing 2 scenes "scene1" & "scene2" at positions 00 and 01 respectively, a snippet "snippet1" with Parameter Filter "Preamp (HA)", channel "Ch 1", return "Aux 1" and  main/matrix/group "Matrix 1" selected,

X32GetShow will connect to the X32 and read data from it. The following files (actual data values may vary) will be created in the specified directory:

c:/test/show1.shw:
#2.1#
show "show1" 0 0 0 0 0 0 0 0 0 0 "X32-Edit 2.30"
cue/000 100 "cue1" 0 0 0 0 1 0 0
cue/001 101 "cue101" 0 1 0 0 1 0 0
scene/000 "scene1" "" %000000000 1
scene/001 "scene2" "" %000000000 1
snippet/000 "snippet1" 1 1 1 1 1

c:/test/show1.000.scn
... a 2000+ lines file

c:/test/show1.001.scn
... a 2000+ lines file

c:/test/show1.000.snp
#2.1# "snippet1" 1 1 1 1 1
/ch/01/preamp -0.5 OFF OFF 24  61
/ch/01/delay OFF   0.3
/auxin/01/preamp +0.0 OFF
/mtx/01/preamp OFF


A log file will also be created in a dedicated directory. It enables listing the different actions taken by the program (if the "list of actions" option is checked) and the full set of OSC commands sent and received (if the "Verbose" option is checked).

Additional options enable restoring or not Show Safes after running the program, Use of not the Master Level Safe X32 feature or not when saving scenes, and keeping or not Scene Safes when saving scenes.

Finally, a delay value (in milliseconds) can be adjusted to allow for scenes and snippets to load safely before additional commands are sent to X32 (tested with 0ms delay with a wired 100Mbit/s Ethernet connection is OK)

All options are saved in a file: .X32GetShow.ini which also contains the Width, Height and Window loop Refresh factor as the first 3 parameters to be adjusted to fit your Windows settings, as shown below:

540 200 300 0 1 0 0 0 0     <W>  <H> <R> < options >
192.168.1.77               
<    IP    >
.                          
<log directory>

Finally, the top left corner bitmap is kept in .X32.bmp and can be changed to any bitmap of your choice, as long as it complies to Windows .bmp format and is 115 pixels by 95 pixels.

Notes: Tested with Firmware 2.08, 2.10 and 2.12. This is a Windows only tool.
Special thanks to John Nourse for his valuable testing on X32 Core and X32 Standard systems.





X32SetShow

Load to X32 a new show, cues, and all associated scenes and snippets from a directory/file




This program connects to the X32 (using the IP provided in the dedicated label). Once connected to X32, enables you to browse (opens a directory browsing window) your PC files for a location where a show file and associated scenes&snippets are located. It reads the show file, and saves all data to your X32 as the current show; all cues, scenes, and snippets are loaded and installed as well.
For all scenes and snippets associated to the show, the utility reads the respective files (*.scn or *.snp) and loads all data from each scene or snippet. The show is then fully available from your X32.

The different scenes and snippets names will appear under the "SetSHOW" button as the program progresses in loading data from files. When finished, "Complete" will be displayed. In the event something does not complete as expected; "Error" will be displayed and the log file will have more information. Note: the scene and snippet files are expected to be in the same directory as the show file they report to.

Example: on a PC with show name "show1", referencing 2 scenes "scene1" & "scene2" files, a snippet "snippet1" file,
X32SetShow will connect to the X32 and read data from it. The following files (actual data values may vary) will be accessed and their respective data loaded to X32:

c:/test/show1.shw:
#2.1#
show "show1" 0 0 0 0 0 0 0 0 0 0 "X32-Edit 2.30"
cue/000 100 "cue1" 0 0 0 0 1 0 0
cue/001 101 "cue101" 0 1 0 0 1 0 0
scene/000 "scene1" "" %000000000 1
scene/001 "scene2" "" %000000000 1
snippet/000 "snippet1" 1 1 1 1 1


c:/test/show1.000.scn
... a 2000+ lines file

c:/test/show1.001.scn
... a 2000+ lines file

c:/test/show1.000.snp
#2.1# "snippet1" 1 1 1 1 1
/ch/01/preamp -0.5 OFF OFF 24  61
/ch/01/delay OFF   0.3
/auxin/01/preamp +0.0 OFF
/mtx/01/preamp OFF


A log file will also be created in a dedicated directory. It enables listing the different actions taken by the program (if the "list of actions" option is checked) and the OSC commands sent and received (if the "Verbose" option is checked).

Additional options enable updating or not Show Safes from the show file data after running the program, Set of not the Master Level Safe X32 feature or not when saving scenes, and Update or not Scene Safes when loading scenes. The tool also enables a full erase of the show in the console prior to load a new one or replace some of the scenes/snippets only with the Reset Show Data flag.

Finally, a delay value (in milliseconds) can be adjusted to allow for scenes and snippets to load safely before additional commands are sent to X32 [a value between 1 (verbose on) and 2 (verbose off) a wired 100Mbit/s Ethernet connection is OK].

All options are saved in a file: .X32SetShow.ini which also contains the Width, Height as the first 2 parameters to be adjusted to fit your Windows settings, as shown below:

540 200 1 1 0 0 1 1 4                    <W>  <H> < 6x options ><delay>
192.168.0.64                             <X32 IP address>
MyShow                                   <Name of last Show>
.                                        <Path to log file>
C:\test1                                 <path to last show file

Finally, the top left corner bitmap is kept in .X32.bmp and can be changed to any bitmap of your choice, as long as it complies to Windows .bmp format and is 115pixels by 95 pixels.

Notes: Tested with Firmware 2.08,2.10, and 2.12. This is a Windows only tool.
Make sure you respect 2.08 files with 2.08 FW and 2.10 files with 2.10 FW, or you'll run into problems.
Special thanks to John Nourse for his valuable testing on X32 Core and X32 Standard systems.
May 29: X32SetShow ver. 1.0: 1/3 the size, no more dependency on other programs and better use of X32 resources.



An automated Fade-IN / Fade-OUT tool for X32

The program reads user commands from a GUI to select which X32 Faders (or levels) should be controlled. It opens a communication stream and generates the appropriate commands to change selected level values down (Fade-out) or up (Fade-in) over a given amount of time.



Watch a video of X32Fade in action. Please read the documentation to decide if this is for you. Then download and use the program!

Please note: this is a Windows only tool for now. A linux version may become available later.
Feb. 21th: The program has been updated to a new version; there was a bug where Fade-out would result in a Fade-in, unless one clicks on STOP first. I also updated the documentation, and added the linux/ubuntu version.





X32Tap

An tool to "tap-set" delay time values for a Delay X32 effect.

Especially useful to Core and Rack Users, this program "reads" user taps from a GUI or from an X32 audio Channel to set time values used in a Delay effect at a given effect slot (1-4) of the X32. X32 Standard users can use the feature from the surface control in the User Controllable area, if it is set as the default calls for, although not very responsive, so even for X32 Standard users, X32Tap can be quite handy.
Valid time values [0...3000ms] are automatically computed (withing the limits of what a non-real time OS can offer) and sent to the X32 using appropriate OSC commands.




Tempo Taps can be set manually (tapping twice in a row on the TAP button), by entering the BPM value directly, or by using an X32 audio Channel; the tool reads the GATE meter for the respective channel to detect threshold data which is used as a tempo tick. There's also the possibility to average 5 consecutive taps to set a final value. Please read the documentation that comes with the program on setting GATE parameters to desired values.






X32SetPreset

Manage your Presets (Channel, Effect, Routing) on your PC and set them to X32

No more limits and a lot of flexibility to change settings and values for your preset files.






Connect to X32, select the channel or effect slot the preset will apply to if needed.

Browse your PC for Preset files (Channel, Effect or Routing) and chose a file to send to X32. Channel Presets features are displayed as a preview with icons showing if a preset section is present, and active. Before sending to X32, safes can be applied to prevent some preset sections to interact with the X32. Master levels can also automatically be lowered to avoid unwanted pops or larsen arising when a new preset is loading.
Hit the "Set Preset" button and you're done. A status will display when your preset file is complete.



Manage your Presets (Channel, Effect, Routing) on your PC and send them to X32.
No more limits and a lot of flexibility to change settings and values for your preset files.



Connect to X32, browse your PC for Preset files (Channel, Effect or Routing) and select one or multiple presets to transfer to X32 Presets library.

Set the position for the first preset in the library, positions will increment automatically, as presets are saved in the X32 library.

Hit the "Xfer Presets" button to start the transfer. A status will display as the transfer progresses and when transfer is complete.

Important note: All your preset files should be version 2.1; You can easily check this with a text editor (file begins with a #2.1# header) if not, you can actually set the header manually, as this does the trick in most situations.



Manage your Presets (Channel, Effect, Routing) on your PC from your X32.
No more limits and a lot of flexibility to change settings and values for your preset files.




Connect to X32, select a destination directory on your PC for Preset files. Select the preset library you want to retrieve (Channel, Effects or Routing). Hit the "Xfer Presets" button to start the transfer. A status will display as the transfer progresses and when transfer is complete.

Starting with ver. 0.20, X32GetLib enables to save ALL presets (Channel, Effects, and Routing) in a single click.





X32Ssaver

A utility to lower brightness of the X32 LCD and LEDs, screen-saver-style, with user settable delay.
Same as above, but a Windows version (still a command line tool).

GUI versions:

X32SsaverGW - Windows version
 X32SsaverRPi - Raspberry Pi version


 

This is a simple command-line or GUI utility. Source included for the command-line versions to port it to other platforms if you wish.
For the command-line versions:
Usage: X32Ssaver [-i IP address][-d delay value]

Defaults are: 192.168.0.64 for the IP address and
        5 seconds for the delay

The GUI versions will reset light for LEDs and LCD to their respective initial levels when switching OFF, disconnecting, or exiting the app.

Starting with version 0.20, the Windows GUI version also manages a timer to automatically switch OFF after a user settable delay, the Sends On Faders functionality, preventing unwanted changes at the desk if you forget to switch OFF SOF.

Watch a small video of the utility in action.





X32AutoMix

A simplistic approach to Automatic Mixing Tool for X32 - Up to 32 Channels.

[ Or should I name this utility "X32Toy-a-Mix"?   :-) ]

This [Windows only for now] utility enables automatic action of level Faders based on the actual signal present at X32 Inputs.

Note: this tool/utility does not claim providing professional
AutoMixing functions such as found in Dugan Automixing products.

It will nevertheless drive input levels based on the presence of a signal at a given channel. Optionally, the global mix level will also be adjusted automatically based on the number of active inputs, a feature called NOM [Number of Open Mics]: each time the number of active inputs doubles, the global mix drops 3dB, readjusting up 3dB when appropriate.

Global mixing typically takes place at the channel faders, the L/R bus being used (by default) as global mix. This can be inconvenient when other sources (out of the AutoMix feature) are to be used as well, without going through AutoMix. This version enables to use a mix bus rather than L/R. In that case you can select a bus number (01...16), and use the bus send rotary button to adjust the minimum/nominal level values. The global mix will take place on the bus you selected, using the bus fader as global mix level.

It is a good idea to unselect L/R from the Automixed channels when Automixing through a bus, Automixed input signals would otherwise go through both L/R and the bus. Another (important!) benefit of using a mix bus rather than the L/R bus is to avoid unnecessary wear on channel motorized faders.


For all active channels:
No signal:The channel level goes to its minimum position after a predefined delay (Delay to Fader Down).
A signal above a predefined level: The channel level moves to its nominal position, after an adjustable delay (Delay to Fader Up).

For all active channels, the minimum (Default: -oo) and nominal (Default: 0db) channel level positions can be changed at anytime, and will be remembered for the current session.




The Channels controlled by the tools are selected within a range (Channel numbering is from 01 to 32). A single channel can be set by using the same channel number for both values on each side of the "to:" , as in the picture above.

Delay to Fader Up: is set by the frequency at which data is read from X32. Typical values are from 0ms to 1000ms. The value granularity  is around 50ms. This is a global value (applies to all channels).

Delay to Fader Down: is controlled by a timer within the utility, and is set in seconds.  A  channel fader will stay Up if a signal is present, and will go Down as soon as no signal has been sensed on that channel for x seconds.

Sensitivity: is a threshold value above which a signal is considered present. It is based on the X32 preamp input. practical values are above 0.001 and  1.000 is the max value (fully saturated/clipping preamp). This is a global value (applies to all channels).

You can decide to use a mix Bus (check the button to that effect) and set the mix Bus number accordingly. Automixing will go through that bus  and make all corrections on that bus rather than the L/R bus (the default).

The NOM feature is optional and can be enabled by pressing the NOM OFF/NOM ON button.

The utility panel values are reloaded from the utility each time the tool is set into a running state by pressing the OFF button (it will then display "ON")

Starting with ver 0.23, the tool saves the Fader Up and Fader Down values from one session to the next, so you don't have to reset these when starting a new Automix session.

Watch a small video of the tool (then ver 0.10) in action (Sorry for the poor quality).



Sends OSC commands to X32, allows listening to X32 too...

This command line Windows tool enables sending and receiving OSC data in many ways.
usage: X32_command [-i X32 console ipv4 address]
                   [-d 0/1, [0], debug option]
                   [-v 0/1  [1], verbose option]
                   [-k 0/1  [1], keyboard mode on]
                   [-t int  [10], delay between batch commands in ms]
                   [-s file, reads X32node (scene, snippet, presets) formatted data lines from 'file']
                   [-f file, sets batch mode on, getting input data from 'file']
                     default IP is 192.168.0.64

 If option -s file is used, the program reads data from the provided file
 until EOF has been reached, and exits after that.

 If option -f file is used, the program runs in batch mode, taking data from
 the provided file until EOF has been reached, or 'exit' or 'kill' entered.

Note: make sure the batch file is respecting unix standards (i.e. use notepad++ to create the file under Windows so EOL are made of only "\n" and not "\r\n").

 While executing, the following commands can be used (without the quotes):
   '#line of text.....': will print out the input line as a comment line
   'exit' | 'quit': will quit the current mode
   'kill': [batch mode only] will exit the program
   'time <value>':
[batch mode only] will change the delay between batch commands to <value>
   'verbose <on|off>': will change the verbose mode
   'verbose': will return the current verbose mode
   'xremote <on|off>': will change the xremote mode
   'xremote': will return the current xremote mode
   '' (empty line) [standard mode only]: will repeat the last entered command


All other commands are parsed and sent to X32.
 Typical X32 OSC command structure:
   <command> <format> [<data> [<data> [...]]], where for example:
      command: /info, /status, /ch/00/mix/fader, ...
      format: ',' ',i' ',f' ',s' or a combination: ',siss' ',ffiss' ...
      data: a list of int, float or string types separated by a space char...


Note: Per OSC spec and in the format above OSC Type Tag String is "mandatory", therefore one should always provide at least a ',' to complete the OSC Address Pattern. This is nevertheless not necessary for X32 as the system accepts "older" notations where empty OSC Type Tag Strings are not present (see examples below)
       
Examples:

/info                             - "old" notation sending an info request to X32
/info ,                           - "correct" (i.e. OSC compliant) notation sending an info request to X32

/ch/01/config/name ,s "My Guitar" - set name of channel 01 to My Guitar (note: space in the name)
/ch/01/config/name ,s MyGuitar    - set name of channel 01 to MyGuitar (note: no space)

/ ,s 'ch/01/config "" 1 YE 1'     - erase channel 01 name, set icon to index 1,
                                    color to Yellow and source to IN01

/ch/01/mix/fader ,f 0.5           - set mixing fader of channel 01 to mid-position
/node ,s fx/01/par                - retrieve the 64 parameters of effect at FX slot 1

Please refer to the unofficial X32 OSC protocol document for a list of commands.





X32CustomLayer

A simple utility to create and manage custom channel layers easily.

Starting from a standard or current X32 setup (that is Input channels 01 to 32 and Aux inputs 01 to 08), X32CustomLayer enables you to reorganize the way “channels” are set (or layered).

Basically this enables you to virtually move a “channel” to a different position, keeping the actual input assigned to that “channel”; For example, say you have your singer microphone set at “channel” 01 and using XLR input 01. For ease of use, you need or would prefer to have this microphone handled by fader 25, and no time to unplug everything, and change all “channel” 25 settings to values that were used for “channel” 01. You enter 01 in the box under “channel” 25, and voila! the program does the rest in copying config, eq, dyn,…, sends sections and in reassigning the input source, in a snap!



X32CustomLayer offers many options you can use to rearrange your X32 channel strips layout. Please check out the documentation for a complete list of functions.
Save and Restore your X32 Preferences and State, Scene, Routing, Commands

Say you want to be able to save those parameters that are not handled  by shows, scenes, snippets, cues, presets, routing or effects files... So when you connect to your desk, it is exactly as you want it: screen, light brightness, view, etc.

That's what the "DeskSave" option of the tool will offer... but wait! there's more: You can also save scenes, routing presets or any type of file or settings based on a pattern file (a scene, any set of commands, etc.); what the "Pattern" option does is read a file you selected by pressing on the "Pat.File" button, extract the X32 commands from this file and send the extracted commands to X32 to retrieve the values from your X32. These are saved in the file you select using the "Save As" button. Of course you don't need to select a pattern file if you choose "Scene" or "Routing" options.

Saving a full X32 Scene takes about 3 seconds (over WiFi), saving Routing or State/Preferences takes about 1.5 second.



The restore utility below looks rather similar and enables you to select a file as a source of commands and values to be restored to your X32. It can be any of the files you saved using X32DeskSave, but it can also be an actual X32 Scene or an X32 Snippet file you saved directly on your system and imported you your PC.





For both utilities, you need to connect to your X32, select the files or options you want to use. The status line under the "SaveDeskData" or "RestoreDeskData" buttons will indicate "Ready" meaning the utility has the information it needs to perform an operation. "Complete" will indicate the completion of the save or restore action. Other status lines will indicate actions you should take prior to using the utility.

Versions 1.00 and above of X32DeskSave and X32DeskRestore include a command-line version for Mac and Linux users in the respective Mac or Linx files proposed at the top of this page.

Attention!,
This will not save/restore your current X32 show; for this you will need to use X32GetShow/X32SetShow.
This will not save/restore all X32 current libraries types; for this you will need to use X32GetLib/X32SetLib
Prevent unwanted changes to X32/M32 Mutes

This utility will prevent unwanted changes to Mutes; If desirable, load an initial state, select your X32/M32 and connect. Changed if needed the configuration (channels Mutes you want to protect), save the new configuration if you want to use it again later. When connected to X32/M32, click on the "MUTES SAFE OFF" button to engage Mutes safe (the button changes to "MUTES SAFE ON"). Any attempt to change a mute on a protected (selected) channel, aux, fxrtn, bus, dca, mtx,.. will instantaneously be cancelled by the utility, until you disengage the Mutes Safe button, or change the configuration.


In the example above, L/R and FxRtn01&FxRtn02 Mutes are safe.

version 0.02 manages Mute Groups; there can be issues with linked channels being part of a Mute group.





Control your DAW while playing audio, recording or playing back X32 commands.

Many DAW software offer “automation”; a way to record and play DAW changes (volume, pan, effect settings, etc.) as audio is recorded to or played from the DAW. Nevertheless, this is limited to the DAW capabilities, and does not offer the physical comfort of a control surface for managing mix changes.

Many users with an X32 will want to use the large palette of audio capabilities the X32 offers, from basic multi-track mixing, FX assignments and changes, along with their DAW software.

X32PunchControl enables the missing link between your DAW software and X32, under the DAW timing control:

  • X32PunchControl will record all X32 OSC commands respective of mixing, channels and FX controls, routing, etc. into a file of time-stamped records.
  • These records can later be played again, in sync with the audio from DAW to X32, thanks to the recorded time stamp and the MIDI time code from the DAW to X32PunchControl.
  • These records can also be modified in real time when playing DAW audio and the already recorded data, via standard punch in/out operations. This way you can incrementally enhance your mix, by applying successive modifications.
  • When re-positioning the DAW song cursor prior to the current position or playing with successive REW commands, X32PunchControl will manage keeping up with the DAW software, replaying (catching up) OSC commands to ensure the X32 state is what it should be at the cursor position, so your mix, controls, effects are all correctly set. When re-positioning the DAW song cursor further to the current position or playing with successive FF commands, similar computation takes place, but the control is simpler to handle.

In all cases the data is always recorded in real time to a file. X32PunchControl typically uses 2 files, one for reading only (if a previous recording has taken place) and one for writing only. When done, the “writing” file becomes the “reading” file for the next update or play X32PunchControl session.

In order to start from a known state of settings, routing, etc., X32PunchControl enables loading an X32 scene file from the X32 scene library section.

Most transport options and commands needed during normal workflow are under the control of X32PunchControl. This can be done from the program GUI, clicking on transport buttons (REW, PLAY, PAUSE, PUNCH, RECORD, STOP, FF), or using a user selectable set of buttons, directly from the X32. X32PunchControl enables assigning transport buttons to a set of X32 Buttons of the User Assign Section.
Have X32 commands control other devices through MIDI or OSC.

!!! Big change with version 1.10 and later: The calculator code uses RPN notation, and shares that code with X32Midi2OSC.

X32Commander scans for a user selected subset of X32 Commands. If a matching command is found, the tool can fire a MIDI or OSC command to another device, optionally using parameters sent with the matching command.


When using the X32 Command parameters, a formula can be applied:
for MIDI or OSC, the reverse polish notation calculator supports the following operators on numbers, (possibly preceded with a $ to represent an incoming parameter), in decimal or hexadecimal data:
Standard operations (+) (-) (*) (/), 
Boolean logical and shift operators (~ > < & ^ |),
Modulo (%) on ints,
3 operand test operator (?),
Equal comparison (=), Different comparison (!),
Exp (e), Log_n conversion (l), Log_10 conversion (L),
Truncate to int (i), and
Absolute value (a).
A 1 memory slot enables passing results from one calculation block to another if needed [starting with ver 1.11]
    (M) pops from RPN stack and adds to memory
    (m) pushes memory to RPN stack
    (Z) zeroes memory

for example:
    [$11 Z 0xb = M -1 M m 127 0 ?] [10 m +]
will return 0 and 10, respectively if the value 11 is in the parameter $11, and
will return 127 and 9, respectively if a value other than 11 is in the parameter $11

Some operators (such as boolean and modulo) will naturally convert to int before being applied. The result of the operation is converted to the OSC destination format (int or float), or int (1 byte) for MIDI.

The program can be launched directly, in that case it will attempt to open ./X32Commander.txt, a file that contains the list of commands to match and replace with user selected commands. X32Commander can also be launched from a terminal window, with a -f <file> option, enabling you to create and keep a set of files to fit different situations.

Below is an example of user command file, used at program startup used to have a second X32 mimic what the first one does; Make sure to use with version1.05 or later.

# X32Commander translation file ©Patrick-Gilles Maillot
# These two first lines must be kept part of the this file
#
# Describes and lists MIDI or OSC command to send, corresponding to the X32
# OSC command the program scans for, listed below.
# A line starting with M means the expected command to send will be a MIDI command
#                      O means the expected command to send will be an OSC command
# In the command to send, a '$0' string element will be replaced by the parameter value
# of the respective OSC command 0: first parameter, 1: 2nd parameter, and so on
# '$n' parameters or their calculated data must be enclosed within '[' and ']' characters
#   
#    The reverse polish notation calculator supports the following operators, on numbers,
#    (possibly preceded with a $ to represent a MIDI parameter), or hexadecimal data:
#    (+) (-) (*) (/)  Boolean operators (~ >> << & ^ |), modulo (%) on ints, test operator (?),
#    equal comparison (=), different comparison (!), exp (e), log_n conversion (l),
#    log_10 conversion (L), and truncate to int (i).
#
# Once all M (for Midi) or O (for OSC) commands are detailed, a wildcard line as below can be
# set to mention that any other OSC command should just be copied to OSC output
# The line should be (without quotes) "O   *   "
#
# comment line below if only one instance of a line can match
scan all
O   *   # Just copy everything to output --- WARNING! Do not use if OSC in and
#         out IP addresses are the same or you'll end-up with infinite loops
#
# end of file


Below is an example of user command file, used at program startup and playing with a few OSC and MIDI commands for buttons and faders with operators:

# X32Commander translation file ©Patrick-Gilles Maillot
# These two first lines must be kept part of the this file
#
# Describes and lists MIDI or OSC command to send, corresponding to the X32
# OSC command the program scans for, listed below.
# A line starting with M means the expected command to send will be a MIDI command
#                      O means the expected command to send will be an OSC command
# In the command to send, a '$0' string element will be replaced by the parameter value
# of the respective OSC command 0: first parameter, 1: 2nd parameter, and so on
# '$n' parameters or their calculated data must be enclosed within '[' and ']' characters
#   
#    The reverse polish notation calculator supports the following operators, on numbers,
#    (possibly preceded with a $ to represent a MIDI parameter), or hexadecimal data:
#    (+) (-) (*) (/)  Boolean operators (~ >> << & ^ |), modulo (%) on ints, test operator (?),
#    equal comparison (=), different comparison (!), exp (e), log_n conversion (l),
#    log_10 conversion (L), and truncate to int (i).
#
# Once all M (for Midi) or O (for OSC) commands are detailed, a wildcard line as below can be
# set to mention that any other OSC command should just be copied to OSC output
# The line should be (without quotes) "O   *   "
#
# comment line below if only one instance of a line can match
scan all
#
M   /-stat/selidx ,i 0        | F0 7F 00 [$0] 02 F7    # first command select channel 1
#                                                 ... sends F0 7F 02 00 02 F7 for chan 1
#                                                 ... sends F0 7F 02 01 02 F7 for chan 2
# Channel Faders [0., 1.]-> [0..127] etc.
O   /ch/01/mix/fader ,f 0     | /ch/05/mix/fader ,f [1 $0 -]    # for fader moves on channel 1
O   /ch/02/mix/fader ,f 0     | /ch/06/mix/fader ,f [2 $0 *]    # for fader moves on channel 2
O   /ch/03/mix/fader ,f 0     | /ch/07/mix/fader ,f [1 $0 -]    # for fader moves on channel 3
O   /ch/03/mix/fader ,f 0     | /ch/08/mix/fader ,f [2 $0 *]    # for fader moves on channel 3
M   /ch/03/mix/fader ,f 0     | F0 7F 04 [$0 127 *] 02 F7       # for fader moves on channel 3
M   /ch/04/mix/fader ,f 0     | F0 7F 04 [1 $0 - 127 *] 02 F7   # for fader moves on channel 4
#
# end of file


The following file, also included in the download zip gives an example of user command file, used at program startup enabling CH10 to act as the stereo pair of CH01 (no need to force stereo pairs to adjacent X32 channels anymore). Make sure to use with version1.10 or later.

# X32Commander translation file ©Patrick-Gilles Maillot
# These two first lines must be kept part of the this file
#
# Describes and lists MIDI or OSC command to send, corresponding to the X32
# OSC command the program scans for, listed below.
# A line starting with M means the expected command to send will be a MIDI command
#                      O means the expected command to send will be an OSC command
# In the command to send, a '$0' string element will be replaced by the parameter value
# of the respective OSC command 0: first parameter, 1: 2nd parameter, and so on
# '$n' parameters or their calculated data must be enclosed within '[' and ']' characters
#   
#    The reverse polish notation calculator supports the following operators, on numbers,
#    (possibly preceded with a $ to represent a MIDI parameter), or hexadecimal data:
#    (+) (-) (*) (/)  Boolean operators (~ >> << & ^ |), modulo (%) on ints, test operator (?),
#    equal comparison (=), different comparison (!), exp (e), log_n conversion (l),
#    log_10 conversion (L), and truncate to int (i).
#
# Once all M (for Midi) or O (for OSC) commands are detailed, a wildcard line as below can be
# set to mention that any other OSC command should just be copied to OSC output
# The line should be (without quotes) "O   *   "
#
# comment line below if only one instance of a line can match
scan all
#

# This example file sets ch10 as the stereo pair of ch01
# Commands may be added or removed in order to adjust or optimize what parts of the audio path
# are actually used. Note that pan functions are always set as [1 $0 -] to move in opposite direction
#
# General settings
O   /ch/01/config/color ,i 0            | /ch/10/config/color ,i [$0]
O   /ch/01/delay/on ,i 0                | /ch/10/delay/on ,i [$0]
O   /ch/01/delay/time ,f 0              | /ch/10/delay/time ,i [$0]
# Preamp section - may adjust to adapt to uneven levels
O   /ch/01/preamp/trim ,f 0             | /ch/10/preamp/trim ,f [$0]
O   /ch/01/preamp/invert ,i 0           | /ch/10/preamp/invert ,i [$0]
O   /ch/01/preamp/hpon ,i 0             | /ch/10/preamp/hpon ,i [$0]
O   /ch/01/preamp/hpslope ,i 0          | /ch/10/preamp/hpslope ,i [$0]
O   /ch/01/preamp/hpf ,f 0              | /ch/10/preamp/hpf ,f [$0]
# Gate
O   /ch/01/gate/on ,i 0                 | /ch/10/gate/on ,i [$0]
O   /ch/01/gate/mode ,i 0               | /ch/10/gate/mode ,i [$0]
O   /ch/01/gate/thr ,f 0                | /ch/10/gate/thr ,f [$0]
O   /ch/01/gate/range ,f 0              | /ch/10/gate/range ,f [$0]
O   /ch/01/gate/attack ,f 0             | /ch/10/gate/attack ,f [$0]
O   /ch/01/gate/hold ,f 0               | /ch/10/gate/hold ,f [$0]
O   /ch/01/gate/release ,f 0            | /ch/10/gate/release ,f [$0]
O   /ch/01/gate/keysrc ,i 0             | /ch/10/gate/keysrc ,i [$0]
O   /ch/01/gate/filter/on ,i 0          | /ch/10/gate/filter/on ,i [$0]
O   /ch/01/gate/filter/type ,i 0        | /ch/10/gate/filter/type ,i [$0]
O   /ch/01/gate/filter/f ,f 0           | /ch/10/gate/filter/f ,f [$0]
# Comp
O   /ch/01/dyn/on ,i 0                  | /ch/10/dyn/on ,i [$0]
O   /ch/01/dyn/mode ,i 0                | /ch/10/dyn/mode ,i [$0]
O   /ch/01/dyn/det ,i 0                 | /ch/10/dyn/det ,i [$0]
O   /ch/01/dyn/env ,i 0                 | /ch/10/dyn/env ,i [$0]
O   /ch/01/dyn/thr ,f 0                 | /ch/10/dyn/thr ,f [$0]
O   /ch/01/dyn/ratio ,i 0               | /ch/10/dyn/ratio ,i [$0]
O   /ch/01/dyn/knee ,f 0                | /ch/10/dyn/knee ,f [$0]
O   /ch/01/dyn/mgain ,f 0               | /ch/10/dyn/mgain ,f [$0]
O   /ch/01/dyn/attack ,f 0              | /ch/10/dyn/attack ,f [$0]
O   /ch/01/dyn/hold ,f 0                | /ch/10/dyn/hold ,f [$0]
O   /ch/01/dyn/release ,f 0             | /ch/10/dyn/release ,f [$0]
O   /ch/01/dyn/pos ,i 0                 | /ch/10/dyn/pos ,i [$0]
O   /ch/01/dyn/keysrc ,i 0              | /ch/10/dyn/keysrc ,i [$0]
O   /ch/01/dyn/mix ,f 0                 | /ch/10/dyn/mix ,f [$0]
O   /ch/01/dyn/auto ,i 0                | /ch/10/dyn/auto ,i [$0]
O   /ch/01/dyn/filter/on ,i 0           | /ch/10/dyn/filter/on ,i [$0]
O   /ch/01/dyn/filter/type ,i 0         | /ch/10/dyn/filter/type ,i [$0]
O   /ch/01/dyn/filter/f ,f 0            | /ch/10/dyn/filter/f ,f [$0]
# Insert
O   /ch/01/insert/on ,i 0               | /ch/10/insert/on ,i [$0]
O   /ch/01/insert/pos ,i 0              | /ch/10/insert/pos ,i [$0]
O   /ch/01/insert/sel ,i 0              | /ch/10/insert/sel ,i [$0]
# EQ
O   /ch/01/eq/on ,i 0                   | /ch/10/eq/on ,i [$0]
O   /ch/01/eq/1/type ,i 0               | /ch/10/eq/1/type ,i [$0]
O   /ch/01/eq/1/f ,f 0                  | /ch/10/eq/1/f ,f [$0]
O   /ch/01/eq/1/g ,f 0                  | /ch/10/eq/1/f ,g [$0]
O   /ch/01/eq/1/q ,f 0                  | /ch/10/eq/1/f ,q [$0]
O   /ch/01/eq/2/type ,i 0               | /ch/10/eq/2/type ,i [$0]
O   /ch/01/eq/2/f ,f 0                  | /ch/10/eq/2/f ,f [$0]
O   /ch/01/eq/2/g ,f 0                  | /ch/10/eq/2/f ,g [$0]
O   /ch/01/eq/2/q ,f 0                  | /ch/10/eq/2/f ,q [$0]
O   /ch/01/eq/3/type ,i 0               | /ch/10/eq/3/type ,i [$0]
O   /ch/01/eq/3/f ,f 0                  | /ch/10/eq/3/f ,f [$0]
O   /ch/01/eq/3/g ,f 0                  | /ch/10/eq/3/f ,g [$0]
O   /ch/01/eq/3/q ,f 0                  | /ch/10/eq/3/f ,q [$0]
O   /ch/01/eq/4/type ,i 0               | /ch/10/eq/4/type ,i [$0]
O   /ch/01/eq/4/f ,f 0                  | /ch/10/eq/4/f ,f [$0]
O   /ch/01/eq/4/g ,f 0                  | /ch/10/eq/4/f ,g [$0]
O   /ch/01/eq/4/q ,f 0                  | /ch/10/eq/4/f ,q [$0]
# SENDS - remove unnecessary/unwanted ones if needed
O   /ch/01/mix/on ,i 0                  | /ch/10/mix/on ,i [$0]
O   /ch/01/mix/fader ,f 0               | /ch/10/mix/fader ,f [$0]
O   /ch/01/mix/st ,i 0                  | /ch/10/mix/st ,i [$0]
O   /ch/01/mix/pan ,f 0                 | /ch/10/mix/pan ,f [1 $0 -]
O   /ch/01/mix/mono ,i 0                | /ch/10/mix/mono ,i [$0]
O   /ch/01/mix/mlevel ,f 0              | /ch/10/mix/mlevel ,f [$0]
O   /ch/01/mix/01/on ,i 0               | /ch/10/mix/01/on ,i [$0]
O   /ch/01/mix/01/level ,f 0            | /ch/10/mix/01/level ,f [$0]
O   /ch/01/mix/01/pan ,f 0              | /ch/10/mix/01/pan ,f [1 $0 -]
O   /ch/01/mix/01/type ,i 0             | /ch/10/mix/01/type ,i [$0]
O   /ch/01/mix/02/on ,i 0               | /ch/10/mix/02/on ,i [$0]
O   /ch/01/mix/02/level ,f 0            | /ch/10/mix/02/level ,f [$0]
O   /ch/01/mix/03/on ,i 0               | /ch/10/mix/03/on ,i [$0]
O   /ch/01/mix/03/level ,f 0            | /ch/10/mix/03/level ,f [$0]
O   /ch/01/mix/03/pan ,f 0              | /ch/10/mix/03/pan ,f [1 $0 -]
O   /ch/01/mix/03/type ,i 0             | /ch/10/mix/03/type ,i [$0]
O   /ch/01/mix/04/on ,i 0               | /ch/10/mix/04/on ,i [$0]
O   /ch/01/mix/04/level ,f 0            | /ch/10/mix/04/level ,f [$0]
O   /ch/01/mix/05/on ,i 0               | /ch/10/mix/05/on ,i [$0]
O   /ch/01/mix/05/level ,f 0            | /ch/10/mix/05/level ,f [$0]
O   /ch/01/mix/05/pan ,f 0              | /ch/10/mix/05/pan ,f [1 $0 -]
O   /ch/01/mix/05/type ,i 0             | /ch/10/mix/05/type ,i [$0]
O   /ch/01/mix/06/on ,i 0               | /ch/10/mix/06/on ,i [$0]
O   /ch/01/mix/06/level ,f 0            | /ch/10/mix/06/level ,f [$0]
O   /ch/01/mix/07/on ,i 0               | /ch/10/mix/07/on ,i [$0]
O   /ch/01/mix/07/level ,f 0            | /ch/10/mix/07/level ,f [$0]
O   /ch/01/mix/07/pan ,f 0              | /ch/10/mix/07/pan ,f [1 $0 -]
O   /ch/01/mix/07/type ,i 0             | /ch/10/mix/07/type ,i [$0]
O   /ch/01/mix/08/on ,i 0               | /ch/10/mix/08/on ,i [$0]
O   /ch/01/mix/08/level ,f 0            | /ch/10/mix/08/level ,f [$0]
O   /ch/01/mix/09/on ,i 0               | /ch/10/mix/09/on ,i [$0]
O   /ch/01/mix/09/level ,f 0            | /ch/10/mix/09/level ,f [$0]
O   /ch/01/mix/09/pan ,f 0              | /ch/10/mix/09/pan ,f [1 $0 -]
O   /ch/01/mix/09/type ,i 0             | /ch/10/mix/09/type ,i [$0]
O   /ch/01/mix/10/on ,i 0               | /ch/10/mix/10/on ,i [$0]
O   /ch/01/mix/10/level ,f 0            | /ch/10/mix/10/level ,f [$0]
O   /ch/01/mix/11/on ,i 0               | /ch/10/mix/11/on ,i [$0]
O   /ch/01/mix/11/level ,f 0            | /ch/10/mix/11/level ,f [$0]
O   /ch/01/mix/11/pan ,f 0              | /ch/10/mix/11/pan ,f [1 $0 -]
O   /ch/01/mix/11/type ,i 0             | /ch/10/mix/11/type ,i [$0]
O   /ch/01/mix/12/on ,i 0               | /ch/10/mix/12/on ,i [$0]
O   /ch/01/mix/12/level ,f 0            | /ch/10/mix/12/level ,f [$0]
O   /ch/01/mix/13/on ,i 0               | /ch/10/mix/13/on ,i [$0]
O   /ch/01/mix/13/level ,f 0            | /ch/10/mix/13/level ,f [$0]
O   /ch/01/mix/13/pan ,f 0              | /ch/10/mix/13/pan ,f [1 $0 -]
O   /ch/01/mix/13/type ,i 0             | /ch/10/mix/13/type ,i [$0]
O   /ch/01/mix/14/on ,i 0               | /ch/10/mix/14/on ,i [$0]
O   /ch/01/mix/14/level ,f 0            | /ch/10/mix/14/level ,f [$0]
O   /ch/01/mix/15/on ,i 0               | /ch/10/mix/15/on ,i [$0]
O   /ch/01/mix/15/level ,f 0            | /ch/10/mix/15/level ,f [$0]
O   /ch/01/mix/15/pan ,f 0              | /ch/10/mix/15/pan ,f [1 $0 -]
O   /ch/01/mix/15/type ,i 0             | /ch/10/mix/15/type ,i [$0]
O   /ch/01/mix/16/on ,i 0               | /ch/10/mix/16/on ,i [$0]
O   /ch/01/mix/16/level ,f 0            | /ch/10/mix/16/level ,f [$0]
# DCA/Mute group
O   /ch/01/grp/dca ,i 0                 | /ch/10/grp/dca ,i [$0]
O   /ch/01/grp/mute ,i 0                | /ch/10/grp/mute ,i [$0]
# Automix
O   /ch/01/automix/group ,i 0           | /ch/10/automix/group ,i [$0]
O   /ch/01/automix/weight ,i 0          | /ch/10/automix/weight ,i [$0]
#    
# end of file


Such tool can enable you to control for example a lighting system, certain features of a DAW connected to X32, or even another X32 family member.

Starting with version 1.7, The source IP address as well as the destination IP,Port can be set as command line arguments; Same for the OSC/Midi commands file. Please type in X32Commander -h to get a quick/printed help. X32Commander can also be used in a batch file such as shown below, listing the file "commander.bat"

C:\Users\p\X32Commander\Release> more commander.bat
.\X32Commander.exe -i 192.168.1.62  -o 192.168.1.62,10023 -f command.txt

C:\Users\p\X32Commander\Release>


See X32Commander in action: https://youtu.be/W_UQt0YdnhU

Have X32 receive OSC commands from a MIDI device or program.

X32Midi2OSC connects to your X32, and to a MIDI source. It intercepts MIDI specific data (user selected) messages, and can send a user selected OSC command to the X32 upon reception of the MIDI data. The program also provides MIDI thru capability to sort of act as "man in the middle" in a MIDI connection. The list of MIDI commands and their respective OSC messages are saved in a configuration file, the basic parameters for the program (such as IP address, etc.) are also kept in a separate file.




MIDI commands are typically 3 bytes (some are 2 and SYSEX data can be a lot more): Each command comes with the following data: <channel number>, <command type>, <data value>

The program can apply operations to any of the MIDI command attributes to modulate the OSC message parameters that will be sent. To this end, an RPN calculator is used to apply operations to the incoming MIDI data before applying it to OSC message parameters. Midi thru is untouched.

The "Dbg" button enables easy debugging of MIDI commands that are received by the program, Each recognized MIDI message will be displayed in hexa form. Use only when debugging specific patterns! as this blocks the program until the displayed message is acknowledged.

The following convention sand operators apply:
$0, $1, $2 represent the incoming MIDI <channel number>, <command type>, <data value>, respectively.

At this time, only short MIDI messages are considered. Only short SYSEX messages are considered. Long SYSEX is ignored.
The list of accepted MIDI commands is listed below in the conversion file example.

The reverse polish notation calculator supports the following operators, on numbers, (possibly preceded with a $ to represent a MIDI parameter), or hexadecimal data:
(+) (-) (*) (/)  Boolean operators (~ >> << & ^ |), modulo (%) on ints, test operator (?), equal comparison (=), different comparison (!), exp (e), log_n conversion (l), log_10 conversion (L), and truncate to int (i).

Formulas should be saved in a file with a ".m2o" extension, as shown below:

#
#
# Translation file for Midi2OSC
#
# ©2018 Patrick-Gilles Maillot
#
#
# Expected format uses space or tab character as separator and separates
# MIDI and respective OSC command by a '|' character:
#
#    {A0..F0} ch d1 d2 | <OSC message>
#
#   - B0 means MIDI control message, where:
#        ch is the channel number [1..16]
#        d1 is the control event number [0..127]
#        d2 is the value associated to the command [0..127]
#
# OSC messages consist of an OSC Address Pattern followed by an
# OSC Type Tag String followed by zero or more OSC Arguments.
#
# Possible OSC Type Tags are i or f
#
# OSC Arguments are listed between brakets '[]' and can be a fixed value,
# such as 0, 1 or an expressionsuch as $n to make reference to data issued
# from the incoming MIDI command, where
#        $0 represents the channel number
#        $1 represents the control event number
#        $2 represents the value data (the value 0 or 127 here will
#                be replaced during execution with the actual data
#                from the MIDI command
#
# Accepted MIDI messages:
#    Message                  Type        Data 1                 Data 2
#    Note Off                  8n        Note Number            Velocity
#    Note On                   9n        Note Number            Velocity
#    Polyphonic Aftertouch     An        Note Number            Pressure
#    Control Change            Bn        Controller Number      Data
#    Program Change            Cn        Program Number         from file
#    Channel Aftertouch        Dn        Pressure               from file
#    Pitch Wheel               En        LSB                    MSB
#    System Message            Fn        depends on message     depends on message
#
#    Key
#    n is the MIDI Channel Number (0-F)
#    LSB is the Least Significant Byte
#    MSB is the Least Significant Byte
#   
#   
#    The reverse polish notation calculator supports the following operators, on numbers,
#    (possibly preceded with a $ to represent a MIDI parameter), or hexadecimal data:
#    (+) (-) (*) (/)  Boolean operators (~ >> << & ^ |), modulo (%) on ints, test operator (?),
#    equal comparison (=), different comparison (!), exp (e), log_n conversion (l),
#    log_10 conversion (L), and truncate to int (i).
#
# Lines starting with a # are comment lines
#
# some examples below (uncomment if you want to test)
#B0  1 10 127 | /ch/10/mix/fader ,f [3 1 & 2 < / 3]     # testing simple operations
#B0  1 11 127 | /ch/11/mix/fader ,f [$1]                # testing simple access to parameter
#B0  1 12 127 | /ch/12/mix/fader ,f [127 $2 -]          # equivalent to inversing effect
#B0  1 13 127 | /ch/12/mix/fader ,fi [127 $2 -] [$2]    # not a valid command, but for test purpose
#90  1 56 127 | /ch/26/mix/fader ,f [$2 2 * i 60 /]     # () force an int value
#
B0 3 12 127 | /fxrtn/01/mix/on ,i [$2 0 =]              # if $2 is 0 then result is 1 (true), 0 (false) otherwise
B0 4 12 127 | /ch/01/mix/fader ,f [$2 3 ! 0.7 0.5 ?]    # if $2 equals 3 then result is 0.5, 0.7 otherwise
#



Merging single WAV files into X-Live! compatible multi-channel WAV files.


This X32 utility aims at creating X-Live! compatible files from standard wave files; The typical use would be to enable playback using the X-Live! expansion board SD card(s) of already available multi-channel wave files such as those recorded from the X-USB interface or exported from a DAW software.




A few rules must be respected in order to merge several wave files in “one” that can be used with X-Live!:
  •  Files to merge must all be named ch_1 to ch_32.
  •  Files to merge must all have the same structure and size.
  •  Files to merge must all be of the same sample rate.
  •  Files to merge must all be 24bits
The long horizontal button labeled “Select Wav files Source Directory” is used to select a directory that contains all the ch_xx files mentioned above, that will be merged to X-Live! format.

With the directory selected, additional settings corresponding to X-Live! capabilities are requested or possible: A session name is expected by X-Live! and can be up to 20 characters long.

Optionally, markers can also be passed on the interface of the utility, to be saved with the created X-Live! files. With the o “List of Markers” buttons, Markers can be given as a comma-separated list of decimal values expressed in seconds, such as “1, 2.5, 5., 30”.

The button “Marker File” can be pressed to select a text (.txt) file that contains the markers; This can be a lot more practical when a large number of markers is provided. X-Live! supports up to 100 markers per session.

With all data ready, it is time to click on the “Merge” button, which will launch the file merging process. The program tests for the different parameters and will report errors if problems are found. Assuming all data are correctly set, the program creates an X-Live! compatible directory containing at least two files: one or more wave files named 00000001.wav to 00000128.wav of up to 4GB each, all 32-bit PCM, and a binary session log file "SE_LOG.bin", which contains information such as the markers applied during merging. All these files will be created under a directory named by the 32-bit timestamp of the recording start as an 8 character hexstring, e.g. "4B728846"


Be patient! Merging large audio files takes time. The program will display the elapsed time at the end of the process. The directory created by the utility can be copied in the X-Live! directory of an SD card to be used with the expansion board, so you can enjoy your mix using the X32.




Mac users:
A command-line version of the tool exists in the X32mac.zip file. Below the -h (help) for the utility, which offers the same options the Windows GUI version does:

pmaillot$ ./X32Wav_Xlive -h
X32Wav_Xlive - ver 1.10 - ©2018 - Patrick-Gilles Maillot
usage: X32Wav_Xlive [-f Marker file []: file containing markers]
                                    [-m marker, [,]: marker time in increasing order values]
                                    <Session dir> <Session name>
            X32Wav_Xlive will take into account all command-line parameter and run its
            'magic', generating XLive! session files from the wav data given as input.
            Many restrictions take place: all wav data must be similar in specs:
                  - same sample rate (48k or 44.1k
                  - same length
                  - 24bit sample size
                  - wav files have to be named ch_1.wav to ch_32.wav
            Example:
            X32Wav_Xlive -m 1.2 -m 15 ./ "new session"
            will create a XLive! session directory in ./ based on the date and time,
            and create a session displayed as <session name> when loaded into XLive! card
            containing a number of multi-channel wav files respective of the number of
            channels found in the source directory. Markers, if present, will be added to
            the created session

pmaillot$

Examples:
pmaillot$ ls
ch_1.wav X32Wav_Xlive ch_2.wav markers.txt

pmaillot$ ./X32Wav_Xlive -m 1 -m 2 ./ name
Done! - Elapsed time: 181ms

pmaillot$ ls
4C4B5674 X32Wav_Xlive.d ch_1.wav X32Wav_Xlive
ch_2.wav markers.txt

pmaillot$ ls 4C*
00000001.wav SE_LOG.BIN



Split / Explode X-Live! multi-channel WAV files from a recorded session into individual, single channel WAV files.



This X32 utility takes multi-channel wave files from a recorded X-Live! session directory and extract an arbitrary amount of channels (user defined names can be assigned) to individual wave files. Audio data can be extracted as 16bit, 24bit [default], or 32 bit samples of data.
X32Xlive_Wav also enables setting/changing the name of sessions displayed on the X32 screen, as shown below:



XLive! sessions are made of 8, 16 or 32 channel wave files. The user selects a Session directory and chooses a directory where single channel files will be generated (or Exploded to use REAPER's notation). One can decide to extract less than the actual number of channels from the Session, as these are sometimes containing empty files to build up to the 8, 16 or 32 channels of XLive! multi-channel session files.
Each destination file can be named per user choice, and destination files can be made of 8, 16, 24, or 32 bits samples; (source files are always 24bit samples in a 32bit container). Destination file names can also be automatically filled using an X32 scene file. Once established, a name can be edited (simple modification) by getting its name, editing the name and setting the name.

Files are exploded following each 4GB session file; if there are for ex. 2 wav files in the session (on 4BG and the next one < 4GB), and the user called for 2 channels to be extracted, naming them "AA" and "BB", this will result in the following file set after extraction:
AA.wav
BB.wav
with AA.wav and BB.wav accumulating the single wave file sections of the two section files used as source.

In case of inconsistencies or errors, messages are reported at the GUI level. Upon termination, the time needed to explode files is provided. Be patient... Exploding multi-channel wav files takes time, even with optimizations for ex ~22s for extracting 5 24bit channels out of a 5.2GB 8-channel XLive! Session

Ver 0.32 of the tool includes an optional progress bar while the explode process runs. The tool processes each XLive! session file as a full progress bar completes. See below, top right corner of the tool. Select (default) or un-select the checkbox left to the progress bar to display or not progress as the tool works.
Ver 0.33 sets a set of default destination file names as the user sets the number of channels. No need to set channel names one by one or to use a scene file.


Ver 0.34 displays the creation time-stamp if the session doesn't have an assigned name, to mimic the X32 behavior. It also includes a Reset button above the Explode button..

Watch a small video of the program in use: https://youtu.be/M_7yO_AAw00

Mac users:
A command-line version of the tool exists in the X32mac.zip file. Below the -h (help) for the utility, which offers the same options the Windows GUI version does:
$ ./X32Xlive_wav -h
X32Xlive_Wav - ver 0.35 - ©2018 - Patrick-Gilles Maillot

usage: X32Xlive_wav [-d dir [./]: Mono wave files path]
                    [-m name []: Sets or Replaces Session name read from source]
                    [-n 1..32 [0]: number of channels to explode to mono wave files]
                    [-c 8/16/24/32 [24]: sample size]
                    [-s file []: optional scene file]
                    [-w #,name, [,]: ch. number ',' followed by respective wave file name]
                    Xlive! Session

       X32Xlive_wav will take into account all command-line parameter and run its
       'magic', generating mono-wave files from the XLive! session given as input.
       Sample size conversion may take place depending on the -c option.
       Channel/Wave or file names can be set all at once if a scene file is provided
       using the -f parameter, or set one at a time or edited if parameters -1...-32
       are used with appropriate names.
      
Note: option -n must appear before any -w or -s options.

       Example:
       X32Xlive_wav -n 3 -d ~ -c 16 -s ~/myscene -w 3,new_name ~/4C4B5674
         will extract as 16bit samples the first 3 channels contained in XLive! session
         4C4B5674 in the home directory, into 3 separate wave files placed in the home
         directory with names taken from the X32 scene file 'myscene', and setting or overriding
         the 3rd wave file name with 'new_name'

       X32Xlive_wav -n 8 -d ~ ~/4C4B5690
         will extract as 24bit samples the first 8 channels contained in XLive! session
         4C4B5690 in the home directory, into 8 separate wave files placed in the home
         directory with names Xlive_Wav_1.wav to Xlive_Wav_8.wav

$




X32cpXliveMarkers

Import XLive! Session markers into a REAPER project

X32cpXliveMarkers copies an XLive! Session set of makers to the clipboard. An XLive! Session can have up to 100 markers. When importing audio to a DAW (I use REAPER), there is no support for importing XLive! Session markers… until now.



With all parameters set, the user just clicks on “Copy” to load all session markers to the clipboard. It is then just a matter of using the proper REAPER function to import these markers into a REAPER project. Under REAPER, this is possible thanks to the Extensions→Marker utilities→Import marker set from clipboard menu which is part of the REAPER SWS extensions.

X32cpXliveMarkers can also generate markers compatible with the Audition DAW software format.
Enabling audio Jog and Shuttle for X-Live! while using SD card audio.

This X32 utility enables a functionality not included with the X-Live! expansion board. I discovered when testing and using the board there was no audio jog/shuttle proposed by the new firmware. This didn’t occur to me at first when recording short audio sequences during my tests. I later wrote another utility (see X32Wav-Xlive) to transfer multiple/separate wav files into an X-Live! compatible session, and realized after transferring a 1h audio session there was no easy way to navigate within the audio session I just copied onto an SD card.



OSC messages are fast and enable real-time remote controls to take place on X32; I wrote a set of transport capabilities to optimize the use of X32 with REAPER, and I’m using the same approach here in changing the destination of one of the User Assign rotary knob into an audio Jog. In the case of REAPER, the knobs control the REAPER slider, and an ingenious zoom function in REAPER enable moving from precisely to very fast within the audio track(s). In the case of X-Live! and X32, there’s no zoom functionality, nor a cursor to move; as a result it was rather painful to move say 10ms at a time. I decided to use a second rotary knob to set the value of the time delta, acting as a zoom factor so to speak. The current version of the application enables a variation between 10ms and 2m41s

You will need to be either in “Play” or “PPause” mode on the X-Live! in order for knob #1 to enable you to navigate in your SD card audio stream; As mentioned earlier, knob #3 will make you move faster within the audio. The explanations as to why I used knob #1 and #3 rather than other values are in the documentation! :)
 


Enabling users to manage their assigned channel gain during a recording session.

This is a commercial tool I developed for VoiceTrax West, a California company providing recording services and studios for recording actors or demos/music/acting. It enables an actor to be autonomous while recording his/her part in managing the gain for the voice channel he/she was assigned.
The X32 IP, a scene number, and a channel number are pre-assigned to the actor. When connection to the X32 is established, the gain for that channel is updated and can simultaneously be updated from the (remote) X32 or the utility the actor controls with the horizontal slider. The gain value in dB shows on the right of the slider. The utility works great under Windows or Wine/Mac.


Note: If you plan to use this utility on a commercial use basis, please contact VoiceTrax West to get their authorization.




Please use and provide feedback on the Behringer X32 Forum!
Thank you!


Please note:
  • Most utilities above run in a cmd window in a Windows XP or later environment, or a terminal window under Unix. They use UDP for communicating to the X32/XR18. Some utilities are proposed as Windows only applications.
  • You may have to prevent your antivirus from killing these programs (AVG versions prior to 2015.0.5751 for example).
  • Windows versions may be a little slower than Linux versions because of the lack of sub-millisecond timing values under Windows.
  • Under Windows only: some utilities require installing cygwin.dll, some don't depending on the development environment I used to write them (i.e. in some case a POSIX environment makes things a lot more portable, therefore the use of Cygwin).
  • I will not accept any responsibility on any problem/failure/mis-operation when making use of the utilities proposed here.



Hardware

X32 Deck Lamp


I wanted to install a light for the X32 Deck, but did not find the one I was looking for at local places; and the ones I found on the internet seemed a little too expensive for what they seemed to be... so I looked for an alternative and found this LED (3W) desk light at Ikea. Cheap enough to play with and with enough goose neck length (2 feet / 60cm) to go with the X32. I also liked the smaller diameter (about 8mm) of the spring, much lighter than the XLR goose neck flexes I ordered on the internet.

Home with my LED lamp, I realized it works on 3.3 Volts when the X32 provides 12 Volts! :-<

I still looked for and ordered and 4 pins XLR to see it it would fit at the other end of the light. Good (almost) fit. Once I removed (using a Dremel tool) the plastic ending, I was left with a 9.5mm diameter fileted tube. The Neutrik 4-pin XLR top can take this, with the little help of a drill bit. The two (XLR top and lamp footing) are now secured with 2component epoxy glue.
an Ikea lamp I&#39;ll modify to fit my needs


After modifications
I ordered a small 3W LED lamp that worked on 12V. When it arrived, I found it was connected to a buck power converter (12Vac->whatever volt) that fit on a tiny PCB, but still too big to go either in the top part of the lamp or in the XLR itself. Fortunately that PCB had extra components I did not need, I removed the 4 diodes that are used to convert the AC to 12V DC before the power converter. I also cut the PCB to its minimal size (a mere 12x8 mm), I also removed the XLR pins parts that are used for soldering the wires so they became flush with the XLR plug itself, freeing room inside the XLR cavity.

With flex wires in place, the PCB fit (barely) in the top cavity of the XLR, and I could screw back the XLR jack/pins in place after testing and making sure there were no shorts.


I'm quite happy with result!, a lightweight desk lamp that accepts 4 different useful levels of intensity (not too bad for a buck power converter) giving some dimming function.

And because it's really lightweight, it won't put too much stress on the X32 lamp XLR connector.


 

Overall cost: 12€ for the Ikea lamp, 4.5€ for the 4pin Neutrik XLR, and 3.2€ for the 12V LED bulb and its converter. And 3h or work.

Note: I noted the X32 when being switched OFF, will send a 12V pulse to the lamp, even if the lamp setting is on OFF. This means it's better to remove the lamp BEFORE  switching OFF the console, as it's highly recommended to disconnect output XLRs or switch OFF speakers before switching OFF the console. Nothing bad, but a little annoying.





MIDI stomp box

MIDI stomp box for controlling X32 parameters


There's been a few demands in the X32 and XAir forums for a way to control some parameters using a stomp box (a simple foot pedal) and possible MIDI as a way to send information. I have been looking at different options and decided to use one of these tiny Arduino devices; In my case I opted for a cheap and simple, yet USB programmable Arduino Leonardo device. This is a Chinese made board, using an Atmega32U4 chip at 16MHz, equivalent to the SparkFun pro-micro board - (board Schematics).

After installing the necessary software to play/program the board, I looked at the few examples available and used the MIDI example program as a basis for the job. Before programing, I wanted to ensure the board would work as a pedal, on battery; Typical batteries used in foot pedals are 9V, and powering a 5V board from 9V is possible on the pro-micro but that would mead using the on-board LDO, which would waist in heat about 1/2 of the battery power in going from 9V to 5V. A better option was to use a buck DC/DC converter with an efficiency above 85%. Again a small Chinese made board was used for that. The battery itself is a rechargeable one, with a capacity of 800mA; quite enough for the job.

By the way the pedal works fine when USB is plugged in (no need for a battery in that case).

As this is a pedal board, I have decided to 3D print the pedal enclosure, went through one or two prototypes, and came out with a simple model, using the online TinkerCad software (main box, bottom, battery door).

Back to software:
The source-code is quite simple and presented below; In my case, I want to send SYSEX messages each time I release the foot switch, and I want a LED to blink when the SYSEX message is sent to MIDI out.

Midi Stomp Box

  Creation: 11 Dec 2016
  ©-2017 Patrick-Gilles Maillot
 
  MIDI-X32: a Stomp pedal for sending SYSEX MIDI messages to an X32 digital mixer from Behringer
  (or any other MIDI device).

  The program waits for a HW pin to become active (when the user presses a switch). This event
  is then used to initiate the sending of the SYSEX MIDI bytes array.

  This project started from an Arduino project/sketch file: MIDI note player / created 13 Jun 2006

  The circuit for MIDI out:
    MIDI jack pin 5 connected to Leonardo digital pin 1 (TX0)
    MIDI jack pin 2 connected to ground
    MIDI jack pin 4 connected to +5V through 220-ohm resistor
  Attach a MIDI cable to the jack, then to a MIDI device, ready to use.

  The LED is connected in series with a 100 Ohms resistor between +5V and Leonardo digital pin D7

  The circuit for the switch:
    Pushbutton attached to Leonardo digital pin pin 2 and to ground
*/

unsigned char S_array[] = {                         // MIDI SYSEX or command array data
  0xF0, 0x7F, 0x02, 0x06, 0x02, 0xF7
};
//
const int S_size = sizeof(S_array) / sizeof(char);  // MIDI SYSEX or command array size
const int B_pin = 2;                                // Switch pin (in)
const int L_pin = 7;                                // LED pin (out)
int       D_delay = 100;                            // Debounce time; increase if needed
int       S_state;                                  // state of the switch
int       L_state;                                  // state of the LED


void setup() {
  // called once at program start
  //  Set MIDI baud rate:
  Serial1.begin(31250);
  // Set pins directions
  pinMode(B_pin, INPUT_PULLUP);
  pinMode(L_pin, OUTPUT);
  // Set initial conditions
  S_state = digitalRead(B_pin);
  digitalWrite(L_pin, HIGH);            // switches OFF LED
  L
_state = 0;
}
void loop() {
  // start main loop
  // read the state of the switch, check for state change:
  S_state = digitalRead(B_pin);
  // wait a few milliseconds
  delay(D_delay);
  // stop LED if needed
  if (L_state) {
    digitalWrite(L_pin, HIGH);
    L_state = 0;
  }

  // only send MIDI data when the foot-switch is released
  if ((digitalRead(B_pin) == 1) && (S_state == 0)) {
    // LED on
    digitalWrite(L_pin, LOW);
    L_state = 1;
    // play all bytes from SYSEX data array:
    for (int i = 0; i < S_size; i ++) {
      Serial1.write(S_array[i]);
    }
  } else {
    // wait a little to save CPU power
    delay(10);
  }
}


To program a different SYSEX sequence, just connect the pedal to USB on a PC and flash the program with a different SYSEX byte array.

It is also quite simple to add a choice of SYSEX strings if one decides to have several strings selected based on a switch or a selector that would be read prior the foot switch being activated.


Below some pictures of the project:

Components used
The 3D printed box


All wired (battery removed)




USB powered
 
                  
 The wiring I used


A small video of the system is also available, visit this Youtube link.






X32 MIDI Tap Tempo

X32 MIDI Tap Tempo Stompbox

Using the stompbox described above as Tap Tempo for X32 or Xair devices:

This is quite easy: Just replace the software :-).

There will be two foot switch press actions required: The first one to initialize a timer, and the second one to get the time difference between the two consecutive switch actions.

This time delta is used as a value sent part of a SYSEX MIDI data that contains the OSC command for setting the delay value of an existing effect in X32 or XAir.

A small video illustrates the tap tempo function, how it changes X32 data for delay effects, and the program source is shown below:

Midi Tap Tempo

  Creation: 5 Aug 2017
  ©-2017 Patrick-Gilles Maillot
 
  TtapMIDI-X32: a Stomp pedal for sending Tempo TAP delay as SYSEX MIDI messages to an X32 digital mixer from
  Behringer (or any compatible MIDI devive).

  The program waits for a HW pin to becon=me active (when the user presses a switch). These events
  are used to generate a delay sent as SYSEX MIDI array to the connected device.

  This project started from an Arduino project/sketch file: MIDI note player / created 13 Jun 2006

  The circuit for MIDI out:
    Leonardo digital pin 1 connected to MIDI jack pin 5
    MIDI jack pin 2 connected to ground
    MIDI jack pin 4 connected to +5V through 220-ohm resistor
  Attach a MIDI cable to the jack, then to a MIDI synth, and play music.

  The circuit for the switch:
    Pushbutton attached to Leonardo digital pin pin 2 and to ground
*/

unsigned char S_array[] = {                         // MIDI SYSEX or command array data
  0xF0, 0x00, 0x20, 0x32, 0x32,
  0x2F, 0x66, 0x78, 0x2F,
  0x30,
  0x2F, 0x70, 0x61, 0x72, 0x2F,
  0x30, 0x32,
  0x20,
  0x33, 0x30, 0x30, 0x30,
  0xF7
};
//
const int     S_size = sizeof(S_array) / sizeof(char);  // MIDI SYSEX or command array size
const int     B_pin = 2;                                // Switch pin (in)
const int     L_pin = 7;                                // LED pin (out)
int           D_delay = 20;                             // Debounce time; increase if needed
int           S_state;                                  // state of the switch
int           L_state;                                  // state of the LED
unsigned long Time1;                                    // Start time
unsigned long Dtime;                                    // Delta time
int           Tcount;                                   // Indicate first or secont press for timer calculation

void setup() {
  // called once at program start
  //  Set MIDI baud rate:
  Serial1.begin(31250);
  // Set pins directions
  pinMode(B_pin, INPUT_PULLUP);
  pinMode(L_pin, OUTPUT);
  S_state = digitalRead(B_pin);
  digitalWrite(L_pin, HIGH);
  Time1 = Dtime = Tcount = 0;
}

void loop() {
  // start main loop
  // read the state of the switch, check for state change:
  S_state = digitalRead(B_pin);
  // wait a few milliseconds
  delay(D_delay);
  // stop LED if needed
  if (L_state) {
    digitalWrite(L_pin, HIGH);
    L_state = 0;
  }
  //
  // only send MIDI data if the new button state is LOW
  if ((digitalRead(B_pin) == 1) && (S_state == 0)) {
    if (Tcount == 0) {
      // first time press: Get time
      Time1 = millis();
      Tcount = 1;
    } else {
      // second time press: calculate time difference and send data
      // calculate Dtime in milliseconds
      Dtime = millis() - Time1;
      // Dtime as valid X32 data 0 to 3000 ms
      if (Dtime < 1) Dtime = 0;
      if (Dtime > 3000) Dtime = 3000;
      // set value of FX number in SYSEX buffer
      S_array[9] = 0x32;
      // set value of Ftime into SYSEX buffer
      S_array[18] = 0x30 + Dtime / 1000;
      S_array[19] = 0x30 + (Dtime - (Dtime/1000) * 1000) / 100;
      S_array[20] = 0x30 + (Dtime - (Dtime/100) * 100) / 10;
      S_array[21] = 0x30 + (Dtime - (Dtime/10) * 10);
      // reset time press counter
      Tcount = 0;
      // play all bytes from SYSEX data array:
      for (int i = 0; i < S_size; i ++) {
        Serial1.write(S_array[i]);
      }
      // LED on
      digitalWrite(L_pin, LOW);
      L_state = 1;
    }
  }
}


Changing the FX number for X32 or Xair is easy: just replace the byte data value assigned to S_array[9]. The default/arbitrary value is FX #2, or 0x32. Use 0x31 for FX #1, 0x33 for FX #3, etc.

Usage: It's a matter of taste, but it is also possible to generate a new Dtime value at each press of the footswitch.

To achieve this, just delete the Tcount variable, its initialization in the setup()section and the if statement that uses it.
Within the
loop() section, replace the line Tcount = 0; by  Time1 = millis();
This way the first press will typically be bogus or greater than 3 seconds, but each subsequent press will provide a valid tap tempo
value corresponding to your foot rythm.





X32 Encoder

X32 audio scrub/jog and transport for X-Live


Spot the difference?

     
Replacing the X32 "phone holder" by a Transport and Audio scrub/jog control device for X-Live


I had in mind for a long time the idea of removing the useless phone holder and use that space for additional X32 Controls. But what to choose or implement?
I was glad Behringer finally came out with the XLive board, adding a really great feature and a new set of capabilities to the desk.

This was the trigger for starting [at last] this project. My goal is to provide a set of programmable keys, pretty much like the User Assign Section, except I want to be able set my own colors! :) I also want it to provide a transport section for the XLive board, something I tested with a small Windows application (see X32Jog4Xlive above) and showing it can be done.

I kept a jog/shuttle encode I savaged from a 15 years old Philips VCR remote control. Turns out this is an ALPS encoder such as this one (below) still available from Aliexpress, but other similar devices can be adapted too, such as this one I will try too.
 
 


 
This device combines two functions:

Shuttle (to perform audio scrubbing)
-1- it provides an encoder which gives CW and CCW rotation information from the centre axis


Jog (to set specific action in addition to shuttle)
-2- it provides an indication of the position of the outer ring axis, coded on 5 bits  (+/- 15 positions plus center), which is mounted on a spring so it automatically returns to its center position

I started to work on the actual device after ordering a WIFI enable/capable Arduino (a device known as NodeMCU V3, that combines the ease of use of Arduino and an ESP8266 WIFI device), as shown below. Such device is typically used in IoT developments.
 
   

The characteristics are impressive for such a small device:
16MB flash, 80MHz (or 160MHz) clock, 32bits processor, 17 GPIOs, enough RAM and... integrated WIFI.

It also comes with the ease of use of the Arduino ecosystem, which makes it (very) easy to use for someone who codes in C :).

My idea of the device is therefore a combination of the Jog/Shuttle encoder, the nodeMCU processor, and a series of 8 push buttons I want to be able to program actions for and assign colors to. My idea for this was initially to find buttons with incorporated LEDs. I did find some, but they came with several issues: each color was fixed (and therefore not programmable), and they would require more electronics and IO pins I would have at hand after using some GPIOs for the jog/shuttle and the keypad itself. The 8 separate keys also were a problem by themselves, as I would have to use a de-multiplexer to limit the number of IOs.

I went for a one-wire, analog keypad approach which is documented in several web pages, and there's a dedicated library to manage the resulting device. It is connected to the analog input which I didn't use, so this is a good thing as it doesn't cost me any GPIOs. I've had trouble with the onewirekeypad library, and couldn't get a reliable keypad to work (i.e. just repetitively give me the key I pressed) and ended up writing my own small key selection routine based on the analog voltage I measured. The rest of the library is actually quite good at de-bouncing the signal and managing key events, so I'm keeping this part!

As for the LEDs, I am using WS2811 based chips/modules that are commonly found in LED strips where each LED can be programmed individually. Again, the Arduino ecosystem is great as there are libraries to manage so-called neopixels. In my case I just need 8 of them!. I got single chip devices from Aliexpress I then cut to their strict minimum size and wired together as a string of 8 LEDs. As a result, a single GPIO is used to control the 8 LEDs, providing the capability to animate, change color and intensity of any of the LEDs, using the FastLed library.

Next was to figure out how to combine keys and LEDs so the color of LEDs would be visible with the keys. IF properly isolated, each LED could light its respective key, as long as there is transparent material to let the light shine through. I found these simple and cheap keys that would fit perfect for what I wanted. The plastic enclosure would have to be "special" in order to isolate each key and its associated LED from the other.

The enclosure I designed is made of two parts (bottom and top). The bottom (V2) or (V3) is quite simple and secures the nodeMCU processor board so its power socket can be plugged in with no problems. The top (V2) or (V3) part is more complex as it must:  -1- support the keypad and LEDs, properly isolated, -2- support the ALPS jog/shuttle, -3- be low enough to still fit under the Desksaver protective hardcover (a must for X32 protection). I tested several prototypes before arriving at at design that agrees with the constraints above.

The difference between V2 and V3 is the thickness of the enclosure;2mm and 1.5mm respectively. Pictures here are from V2 enclosure.
 
General view of the enclosure
   
The nodeMCU module secured in place in the bottom part of the enclosure


 
Securing the Jog/Shuttle with the top
   
Preparing LEDs from single modules; but another solution is worth trying too; order placed... waiting for the chips to arrive (chose the IP30, 144LED/m type)
   
Keypad with LEDs



 
Testing LED lighting through the Keypad key caps, animated-1, animated-2, animated-3.
I still need to add pictograms between the keys and their clear caps!
 

Prototype

Below are a few pictures of the current prototype;

I am still moderately happy with the analog keypad which is a little capricious sometimes: I probably need to adjust the 5 resistor values to better distribute the analog output of each key over the 3.3V supply.


 
Last Checks before closing the device
 
The finished prototype still fits under a Decksaver! (The V3 version of the enclosure makes this even easier)
 
Fully attached and communicating with X32; Note the USB power cable on the right.


The hardware is quite simple and a schematic file is available from my website.

Time to write and test some software :) I selected the following functionality for the 8 keys of the device's keypad:
#1 Set the device in audio scrub mode or in wind (FF or RW) the position of the jog wheel ring sets the direction and speed.
#2 STOP transport function
#3 PAUSE (default) or PLAY transport function
#4 RECORD
#5 Set Marker at the current position
#6 Delete Marker
#7 unassigned
#8 1 click to X-Live screen

Depending on the state of key #1, the device is in audio scrub mode or wind mode
  • Audio scrub mode: The jog wheel (outer ring) is used as a 1/8 to 128 acceleration factor for the increments set by the center shuttle encoder. Depending on the X-Live being in PLAY or PAUSE, the unit increment is 1000ms or 25ms, respectively.
  • Wind mode: The jog wheel is used as a way to FF or RW transport device with speeds of ±1ms to ±300s every 10ms (an arbitrary delay added to the main loop).
As the device is WIFI connected to the X32, it is possible to get X32 OSC notifications, while this complicates SW development, it enables for example to manage the device's LEDs brightness in sync with the X32 one as the user rotates the brightness knob. This is shown in a short video: animated-3.

The software uses  number of libraries: WiFiUdp.h, OnewireKeypad.h, Encoder.h, and FastLED.h.

The source code for the prototype is given below. Make sure you set your WIFI SSID, password and the X32 IP address in their respective variables.


X32Encoder source code

///////////////////////////////////////////////////////////////////////////////////////////////
//                                    X32Encoder:
//                            ©2018 Patrck-Gilles Maillot
///////////////////////////////////////////////////////////////////////////////////////////////
//
// This project aims at providing a JOG/SHUTTLE encoder device to supplement the X32/M32
// family of digital audio mixers, especially when equiped with th X-Live extension board
// which provides 32 channels digital recording capabilities.
//
// The proposed device includes a central encode for audio scrubbing; an outer multiposition
// jog enables selecting 32 different positions around two center values. The devices also
// includes 8 separate keys with individual color LEDs
// Each Key can set a specific function either for changing functionality of the device or
// can be used for sending a specific OSC command to the X32/M32.
//
// The device connects to X32/M32 using WIFI, and continuously accepts X32/M32 OSC data. It
// is built around a NodeMCU/ESP8266 Arduino device.
//
//
// In the functions below, a lot of calls to Serial.printf() have been left (commented) so
// one can follow what happens or debug the code when performing changes (once uncommented)
//
// History: Ver 0.1 - initial release June 9th 2018
//
//
// Note:
//   Make sure you set WIFI setup values correctly below (ssid, paswword, etc.)
//
///////////////////////////////////////////////////////////////////////////////////////////////
//
// The Keypad:
//     key 1 - toggle: sets the device in audio scrubb or standard shuttle mode (default)
//     key 2 - press:  sends STOP
//     key 3 - press:  sends PLAY/PAUSE
//     key 4 - press:  sends RECORD
//
//     key 5 - press:  sends Add Marker
//     key 6 - press:  sends Delete Marker
//     key 7 - toggle:  No-Op
//     key 8 - press:  jump to SETUP/CARD screen on X32/M32
//
//
// This program runs and has been tested on a nodeMCU/ESP8266 v3 device
//
// [GP]IOs used:
// A0: Keypress (using analog IO)
// D3: LEDs Control
// D1, D2: Inner rotary encoder used for scrubbing
// D7, D6, D5, D9: Outer encoder used for shuttle
// D4 (board blue LED)
// D8 (pull down only)
// D10 (TX)
//
///////////////////////////////////////////////////////////////////////////////////////////////
// Wifi
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
//
const char* ssid = "Your SSID";
const char* password = "Your Password";
const char* X32IP = "Your X32 IP address";
//
///////////////////////////////////////////////////////////////////////////////////////////////
// Keypad: We use the OnewireKeypad design, but with a simplified way of reading our 8 keys
// Indeed I never got the library to return consistent data, compared to my simple test routine.
// Keypad has 8 keys, and are read as a one wire analog keypad.
#include <OnewireKeypad.h>
// Define Library :
//  The library is defined with following two commands, just follow the formula in this
//  comment and you are good to go.
//  OnewireKeypad
//  Keypad(Serial, Char values, #Rows, #Cols, Arduino Pin, Row_resistor, Columns_resistor)
char KEYS[] = { };                    // empty array as we use our own way of identifying keys
int KeySate[8];                       // array to keep keys states
#define KEY_HOLD_Time         100     // time to consider key held down
#define KEY_BOUNCE_Time       400     // debounce time for keypad keys
OnewireKeypad <Print, 8> Keypad(Serial, KEYS, 4, 2, A0, 2200, 680);
//
///////////////////////////////////////////////////////////////////////////////////////////////
// Macro to send UDP messages
#define send_udp(message, len) \
  Udp.beginPacket(X32IP, 10023); \
  Udp.write(message, len); \
  Udp.endPacket();
//
// Print a float with a 2 digits precision;
char str[16];
#define flt(f) \
  sprintf(str, "%d.%02d", (int)f, (int)(f < 0.0 ? -1 : 1) * (int)(f*100)%100);
//
///////////////////////////////////////////////////////////////////////////////////////////////
// WIFI and X32 communications related data
WiFiUDP Udp;
unsigned int localUdpPort = 10023;        // X32 port to listen on
char r_buf[255];                          // buffer for incoming X32/M32 OSC packets
char s_buf[128];                          // buffer for sending OSC packets
int  s_len, r_len;                        // send & receive buffer data lengths
char s_info[8] = "/info";                 // info buffer
char s_remt[12] = "/xremote";             // remote buffer
char s_ustate[20] = "/-stat/urec/state";  // get X-Live state
char s_uetime[20] = "/-stat/urec/etime";  // get X-Live position
char s_uempos[20] = "/-urec/markerpos";   // get X-Live marker current index
union {
  float ff;
  int   ii;
  char  cc[4];
} endian;                                 // used for endian conversions
unsigned long XremoteMillis;              // timer for /xremote operation
//
///////////////////////////////////////////////////////////////////////////////////////////////
// Encoder requires low latency interrupt response.  Available CPU
// time does NOT necessarily prove or guarantee correct performance.
// If another library, like NewSoftSerial, is disabling interrupts
// for lengthy periods of time, Encoder can be prevented from
// properly counting the intput signals while interrupt are disabled.
//
// This optional setting causes Encoder to use more optimized code,
// but the downside is a conflict if any other part of your sketch
// or any other library you're using requires attachInterrupt().
// It must be defined before Encoder.h is included.
//#define ENCODER_DO_NOT_USE_INTERRUPTS
#define ENCODER_OPTIMIZE_INTERRUPTS
#include <Encoder.h>
//
// Encoder Pinout:
// Pin 1: D1 / GPIO 5
// Pin 2: Ground
// Pin 3: D2 / GPIO 4
Encoder myEnc( D1, D2); // this is the inner part of the encoder, used for audio scrubbing
long oldPosition = 0;   // old and new positions
long newPosition;
// Encoder (outer ring) settings:
// SH_table is used for the forward or backward displacement in the audio file when audio
// scrubbing is OFF
// SC_table is used to define the acceleration factor given to each step of audio scrubbing
// (when ON)
// rotation index [-5, 5, -4, 4, -2, 2, -3, 3, -6, 6, -7, 7, -1, 1, center-, center+};
int SH_table[16] = { -30000, 30000, -10000, 10000, -2000, 2000, -5000, 5000, -60000, 60000,
                     -300000, 300000, -1000, 1000, 0, 0 };
float SC_table[16] = { 1. / 6, 32.0, 1. / 5, 16.0, 1. / 3, 4.0, 1. / 4, 8.0, 1. / 7, 64.0,
                       1. / 8, 128.0, 1. / 2, 2.0, 1.0, 1.0 };
#define PAUSE_DTime     25  // the delta_time (per encoder tick) in ms when in PAUSE
#define PLAY_DTime    1000  // the delta_time (per encoder tick) in ms when in PLAY
//
///////////////////////////////////////////////////////////////////////////////////////////////
#define FASTLED_INTERNAL
#include <FastLED.h>
// LEDs
// LED control on D3 / GPIO 0
// color codes for HSV can be found at https://github.com/FastLED/FastLED/wiki/Pixel-reference#chsv
// S: typicall 255; Avoid setting a V value above 128 (1/2 full) as it consumes a lot of power
#define NUM_LEDS 8          // we have 8 keys -> each with their own LED
#define DATA_PIN 3          // LED control will be driven from Data pin 3
CRGB KeyLeds[NUM_LEDS];     // LED data array
int Cleds[NUM_LEDS];        // used to store the color (Hue value) assigned to each LED
int V_value;                // used to set/update the max luminosity, according to X32 settings
//
//
///////////////////////////////////////////////////////////////////////////////////////////////
//                         ------------- Local Routines -------------
///////////////////////////////////////////////////////////////////////////////////////////////
//
//
// XE_getk(): read keypad (analog IO) after debouncing has been validated by onwirekeypad.h
// functionality; the actual values set here depend on the resistors (and their precision) used
// in the keypad; Adjustments of the values in the tests below may be needed.
int XE_getk() {
  int k;
  if ((k = Keypad.Key_State() == 3)) {
    // not pressed = 0, pressed = 1, released = 2,  held = 3
    k = analogRead(A0);
//    Serial.printf("analog key: %d\n", k);
    if (k > 800)
      return 52;   // '4'
    if (k > 400)
      return 56;   // '8'
    if (k > 220)
      return 51;   // '3'
    if (k > 160)
      return 55;   // '7'
    if (k > 135)
      return 50;   // '2'
    if (k > 120)
      return 54;   // '6'
    if (k > 99)
      return 49;    // '1'
    if (k > 20)
      return 53;    // '5'
  }
  return 0;             // no char
}
//
///////////////////////////////////////////////////////////////////////////////////////////////
// Xsprint(): build UDP packet buffer with OSC data, returns the current/resulting
// buffer length
int Xsprint(char *bd, int index, char format, char* bs) {
  int i;
  // check format
  switch (format) {
  case 's':
    // string : copy characters one at a time until a 0 is found
    if (bs) {
      strcpy(bd + index, bs);
      index += (int) strlen(bs) + 1;
    } else {
      bd[index++] = 0;
    }
    // align to 4 bytes boundary if needed
    while (index & 3) bd[index++] = 0;
    break;
  case 'f':
  case 'i':
    // float or int : copy the 4 bytes of float or int in big-endian order
    i = 4;
    while (i > 0) bd[index++] = (char) (((char*) bs)[--i]);
    break;
  default:
    // don't copy anything
    break;
  }
  return index;
}
//
///////////////////////////////////////////////////////////////////////////////////////////////
// Xfprint(): build UDP packet buffer with OSC data including a specific text,
// returns the current/resulting buffer length
int Xfprint(char *bd, int index, char* text, char format, char* bs) {
  // first copy text
  strcpy(bd + index, text);
  index += (int) strlen(text) + 1;
  // align to 4 bytes boundary if needed
  while (index & 3) bd[index++] = 0;
  // then set format, keeping #4 alignment
  bd[index++] = ',';
  bd[index++] = format;
  bd[index++] = 0;
  bd[index++] = 0;
  // based on format, set value
  return Xsprint(bd, index, format, bs);
}
//
///////////////////////////////////////////////////////////////////////////////////////////////
// XE_scrub_action(): Perform audio scrubbing on X-Live:
// get the current position and card state. Based on play or pause, set a delta that will
// be applied, after a multiplication factor is applied based on the outer ring position
// represented by the parameter passed to the function
// The scrub action is defined by a change in position of the rotary encoder (inner one)
void XE_scrub_action(int k) {
  int len, delta_time, numdo1, numdo2;
  int i, l;
  //
  // Scrub action?
  newPosition = myEnc.read();
//  Serial.printf("KeySate[0]: %d\n", KeySate[0]);
  if (newPosition != oldPosition) {
//    Serial.printf("position = %d\n", newPosition);
    // The scrub wheel has been moved...
    send_udp(s_ustate, 20);
    numdo1 = 0;
    do { // Receive incoming UDP packets: interested in ...state...
      numdo1 += 1;
      if (r_len = Udp.parsePacket()) {
        len = Udp.read(r_buf, 255);
//        Serial.printf("r_len scrub len = %d\n", len);
        if (strcmp(r_buf, s_ustate) == 0) {
//          Serial.printf("got state\n");
          // Only interested in the case where status is play or ppause
          for (i = 4, l = 24; i > 0; l++) endian.cc[--i] = *(r_buf + l);
          if (endian.ii & 3) {
            // Set delta_time according to play state
            delta_time = PAUSE_DTime;                     // pause
            if (endian.ii & 2) delta_time = PLAY_DTime;   // play
            // Apply Shuffle data to enable 1ms to 150 min precision!
//            flt(SC_table[s_i]); Serial.printf("jog index = %d, factor: %s\n", k, str);
            delta_time = (int) ((float) delta_time * SC_table[k]);
//            flt(delta_time); Serial.printf("delta_time: %s, ", str);
            // We're in play or ppause, let's read our current position
            send_udp(s_uetime, 20);
            numdo2 = 0;
            do { // Receive incoming UDP packets: interested in ...etime...
              numdo2 += 1;
              if (r_len = Udp.parsePacket()) {
                len = Udp.read(r_buf, 255);
                if (strcmp(r_buf, s_uetime) == 0) {
                  // Read data from X32 into etim
                  for (i = 4, l = 24; i > 0; l++) endian.cc[--i] = *(r_buf + l);
                  // Move FW or BW depending on encoder movements
                  if (newPosition < oldPosition) {
                    endian.ii -= delta_time;          // Scrub backwards
                    if (endian.ii < 0) endian.ii = 0; // Ensure we can get to 00:00:00:00
                  } else if (newPosition > oldPosition) {
                    endian.ii += delta_time;          // Scrub forward
                  }
                  endian.ii += 1;
                  // Set new time/position
                  s_len = Xfprint(s_buf, 0, (char *)"/-action/setposition", 'i', endian.cc);
                  send_udp(s_buf, s_len);
                  oldPosition = newPosition;
                  return;
                } else {
                  r_len = 0;
                }
              }
            } while ((r_len == 0) && (numdo2 < 20));
          }
        } else {
          r_len = 0;
        }
      }
    } while ((r_len == 0) && (numdo1 < 20));
  }
  return;
}
//
///////////////////////////////////////////////////////////////////////////////////////////////
// XE_schuttle_action(): apply a FF or RW to X-Live session while in pause mode (only),
// based on the amount of twist applied to the shuttle (outer ring of the encoder).
// The amount is contained in SH_table, and is set based on the parameter passed to
// the function.
void XE_schuttle_action(int k) {
  int len, numdo1, numdo2;
  int i, l;
//
  // The shuttle wheel has been moved...
  send_udp(s_ustate, 20);
//  Serial.printf("Shuttle\n");
  numdo1 = 0;
  do { // Receive incoming UDP packets: interested in ...state...
    numdo1 += 1;
    r_len = Udp.parsePacket();
//    Serial.printf("Shuttle =  %d\n", r_len);
    if (r_len > 0) {
      len = Udp.read(r_buf, 255);
//      Serial.printf("Shuttle len = %d\n", len);
      if (strcmp(r_buf, s_ustate) == 0) {
        // Only interested in the case where status is play or ppause
        for (i = 4, l = 24; i > 0; l++) endian.cc[--i] = *(r_buf + l);
        if (endian.ii & 3) {
          // Set delta_time according to shuttle position
          if (endian.ii == 1) {      // only valid in pause;
//            flt(delta_time); Serial.printf("delta_time: %s, ", str);
            // We're in ppause, let's read our current position
            send_udp(s_uetime, 20);
            numdo2 = 0;
            do { // Receive incoming UDP packets: interested in ...etime...
              numdo2 += 1;
              if (r_len = Udp.parsePacket()) {
                len = Udp.read(r_buf, 255);
                if (strcmp(r_buf, s_uetime) == 0) {
                  // Read data from X32 into etim
                  for (i = 4, l = 24; i > 0; l++) endian.cc[--i] = *(r_buf + l);
                  // Move FW or BW depending on shuttle data
                  endian.ii += SH_table[k];
                  endian.ii += 1;
                  // Set new time/position
                  s_len = Xfprint(s_buf, 0, (char *)"/-action/setposition", 'i', endian.cc);
                  send_udp(s_buf, s_len);
                  return;
                } else {
                  r_len = 0;
                }
              }
            } while ((r_len == 0) && (numdo2 < 20));
          }
        }
      } else {
        r_len = 0;
      }
    }
  } while ((r_len == 0) && (numdo1 < 20));
  return;
}
//
///////////////////////////////////////////////////////////////////////////////////////////////
// XE_osc_check(): This function is called to check/test X32 generated OSC commands. This
// enables syncing with X32 events (such as LED brightness below as a first option.
void XE_osc_check() {
  int len, i, l;
  //
  // check for general X32 messages we can be interested in
  if (r_len = Udp.parsePacket()) {
//    Serial.printf("r_len light =  %d\n", r_len);
    len = Udp.read(r_buf, 255);
    if (strcmp(r_buf, "/-prefs/ledbright") == 0) {
      // got a change brightness command, update V_value accordingly
      //
      for (i = 4, l = 24; i > 0; l++) endian.cc[--i] = *(r_buf + l);
      V_value = 32 + (int) (endian.ff * 164);
      for (i = 0; i < NUM_LEDS; i++) {
        if (KeySate[i] == 1) {
          KeyLeds[i] = CHSV(Cleds[i], 255, V_value);
//          Serial.printf("Key %d, bright = %d\n", i, V_value);
        } else {
          KeyLeds[i] = CHSV(Cleds[i], 255, 0);
        }
      }
      FastLED.show();     // update LEDs
    }
  }
  return;
}
//
///////////////////////////////////////////////////////////////////////////////////////////////
// XE_del_marker(): Delete X-Live Marker at the current X-Live card marker position
void XE_del_marker() {
  int i, l, len;
  //
  // Request current marker position...
  send_udp(s_uempos, 20);
//  Serial.printf("Marker index %s\n", s_uempos);
  do { // Receive incoming UDP packets: interested in ...markerpos...
    r_len = Udp.parsePacket();
    if (r_len > 0) {
      len = Udp.read(r_buf, 255);
//      Serial.printf("rec len = %d\n", len);
      if (strcmp(r_buf, s_uempos) == 0) {
        for (i = 4, l = 24; i > 0; l++) endian.cc[--i] = *(r_buf + l);
        // Only interested in "/-urec/markerpos" messages
//        Serial.printf("Marker index = %d\n", endian.ii);
        if (endian.ii > 0) {
          s_len = Xfprint(s_buf, 0, (char *)"/-action/delmarker", 'i', endian.cc);
          send_udp(s_buf, s_len);
        }
        return;
      } else {
        r_len = 0;
      }
    }
  } while (r_len == 0);
  return;
}
//
///////////////////////////////////////////////////////////////////////////////////////////////
// XE_key_toggle(): manage Key toggle states (for those keys you decide to set as toggle
// versus push.
void XE_key_toggle(int i) {
  KeySate[i] ^= 1;       // change state
  // assign color and light
  if (KeySate[i] == 1) {
    KeyLeds[i] = CHSV(Cleds[i], 255, V_value);
  } else {
    KeyLeds[i] = CHSV(Cleds[i], 255, 0);
  }
  FastLED.show();   // update LEDs
  return;
}
//
///////////////////////////////////////////////////////////////////////////////////////////////
// XE_key_blink(): manage Key blink (for those keys you decide to use as push
// versus toggle.
void XE_key_blink(int i) {
  // no change in key state
  // assign color and light
  KeyLeds[i] = CHSV(Cleds[i], 255, V_value);
  FastLED.show();   // update LEDs
  delay (200);
  KeyLeds[i] = CHSV(Cleds[i], 255, 0);
  FastLED.show();   // update LEDs
  return;
}
//
///////////////////////////////////////////////////////////////////////////////////////////////
// XE_key_on(): manage Key light on (for those keys you decide to use as push
// versus toggle.
void XE_key_on(int i) {
  // no change in key state
  // set color and light
  KeyLeds[i] = CHSV(Cleds[i], 255, V_value);
  FastLED.show();   // update LEDs
  return;
}
//
///////////////////////////////////////////////////////////////////////////////////////////////
// XE_key_off(): manage Key light off (for those keys you decide to use as push
// versus toggle.
void XE_key_off(int i) {
  // no change in key state
  // reset color and light
  KeyLeds[i] = CHSV(Cleds[i], 255, 0);
  FastLED.show();   // update LEDs
  return;
}
//
//
///////////////////////////////////////////////////////////////////////////////////////////////
//              -------------------- Setup (Called once) -----------------
///////////////////////////////////////////////////////////////////////////////////////////////
//
void setup() {
  int i;
  //
  Serial.begin(115200);
  //
  // WIFI setup
  Serial.printf("\nConnecting to %s ", ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.printf(" connected!\n");
  Udp.begin(localUdpPort);
  Serial.printf("Now listening to X32 at IP %s, UDP port %d\n", X32IP, localUdpPort);
  //
  // initialize sending /xremote every 9 seconds
  send_udp(s_remt, 12);
  XremoteMillis = millis();
  //
  // Encoder setup
  // An example of jog (outer ring) device pinout found at
  //            http://g7nbp.blogspot.fr/2008/12/jogging-and-shuttling-with-arduino.html
  // Using these inputs:
  pinMode(D6, INPUT_PULLUP); // GPIO12   -> Connector Pin 4
  pinMode(D7, INPUT_PULLUP); // GPIO13   -> Connector Pin 5
  pinMode(D5, INPUT_PULLUP); // GPIO14   -> Connector Pin 3
  pinMode(D9, INPUT_PULLUP); // GPIO3/RX -> Connector Pin 1
  //                            Ground   -> Connector Pin 2
  //  values table
  //  index           0  1   2  3   4  5   6  7   8  9   A  B   C  D     E        F
  // rotation index [-5, 5, -4, 4, -2, 2, -3, 3, -6, 6, -7, 7, -1, 1, center-, center+};
  //
  //
  // LEDs: Declare type
  FastLED.addLeds < WS2811, DATA_PIN, RGB > (KeyLeds, NUM_LEDS);
  // Assign a color to each LED
  Cleds[0] = 0;
  Cleds[1] = 32;
  Cleds[2] = 64;
  Cleds[3] = 96;
  Cleds[4] = 128;
  Cleds[5] = 160;
  Cleds[5] = 192;
  Cleds[7] = 224;
  for (i = 0; i < NUM_LEDS; i++) {
    KeyLeds[i] = CHSV(Cleds[i], 255, 0); // all LEDs black at startup
    KeySate[i] = 0;                      // all keys to state = 0
  }
  V_value = 128;    // average value. 0 = min, 192 = absolute Max (TBD)
  FastLED.show();   // update LEDs
  //
  //
  // Keypad
  // organization of keys               Approx analog values read for 3.27v VCC
  //      1     2     3     4           500   199   125   92
  //
  //      5     6     7     8           1000  243   142   99
  Keypad.SetHoldTime(KEY_HOLD_Time);            // Key held time in ms
  Keypad.SetDebounceTime(KEY_BOUNCE_Time);      // Key Debounce time in ms
}
//
//
//
///////////////////////////////////////////////////////////////////////////////////////////////
//              -------------------- loop (Called always) -----------------
///////////////////////////////////////////////////////////////////////////////////////////////
//
void loop() {
  int s_i, key;
  int b3, b2, b1, b0;
  unsigned long currentMillis;
  int STOP = 0;
  int PAUSE = 1;
  int ONE = 1;
  int PLAY = 2;
  int RECORD = 3;
  int SETUP = 3;
  int CARD = 6;
  //
  //
  // Check is a /xremote OSC command needs to be sent to X32
  currentMillis = millis();
  if ((currentMillis - XremoteMillis) > 9000) {
//    Serial.printf("Sending Remote\n");
    send_udp(s_remt, 12);
    XremoteMillis = currentMillis;
  }
  // Keypad update?
  // Update LEDs according to status
  // also set specific action when a key is pressed. This can be:
  //    - simple toggling of KeyState so it can be used somewhere else
  //    - take into account key press to send a command to X32 (blink LED too)
  if ((key = XE_getk())) {
//    Serial.printf("Key = %d\n", key);
    switch (key - 49) {     // Action based on key pressed
    case 0:                 // Set device encode in scrub or wind (ff or rew) mode
      XE_key_toggle(0);
      break;
    case 1:                 // Set X-Live in STOP state
      XE_key_on(1);
      s_len = Xfprint(s_buf, 0, s_ustate, 'i', (char *)&STOP);
      send_udp(s_buf, s_len);
      if (KeySate[2] == 1) XE_key_toggle(2);    // reset PLAY if needed
      if (KeySate[3] == 1) XE_key_toggle(3);    // reset RECORD if needed
      delay(150);
      XE_key_off(1);
      break;
    case 2:                 // Toggle X-Live between Paused and Play states
      if(KeySate[3] == 0) { // no action while recording
        if (KeySate[2] == 1) {
          s_len = Xfprint(s_buf, 0, s_ustate, 'i', (char *)&PAUSE);
        } else {
          s_len = Xfprint(s_buf, 0, s_ustate, 'i', (char *)&PLAY);
        }
        send_udp(s_buf, s_len);
        delay(1);
        XE_key_toggle(2);
      }
      break;
    case 3:                 // Set X-Live in Record state
      if (KeySate[3] == 0) {
        s_len = Xfprint(s_buf, 0, s_ustate, 'i', (char *)&RECORD);
        send_udp(s_buf, s_len);
        XE_key_toggle(3);
      }
      break;
    case 4:                 // Add Marker at current position (during Pause, Play or Record)
      XE_key_on(4);
      s_len = Xfprint(s_buf, 0, (char *)"/-action/addmarker", 'i', (char *)&ONE);
      send_udp(s_buf, s_len);
      delay(150);
      XE_key_off(4);
      break;
    case 5:                 // Delete Marker at current position (during Pause)
      XE_key_on(5);
      XE_del_marker();
      delay(100);
      XE_key_off(5);
      break;
    case 6:                 // No-OP for now... :)
      XE_key_toggle(6);
      break;
    case 7:                 // Jump to Setup/Card screen
      XE_key_on(7);
      s_len = Xfprint(s_buf, 0, (char *)"/-stat/screen/screen", 'i', (char *)&SETUP);
      send_udp(s_buf, s_len);
      s_len = Xfprint(s_buf, 0, (char *)"/-stat/screen/SETUP/page", 'i', (char *)&CARD);
      send_udp(s_buf, s_len);
      delay(150);
      XE_key_off(7);
      break;
    }
  }
  //
  // get shuffle index
  b3 = digitalRead(D6);
  b2 = digitalRead(D9);
  b1 = digitalRead(D5);
  b0 = digitalRead(D7);
  s_i = b3 << 3 | b2 << 2 | b1 << 1 | b0;
  // Choose how to manage the shuttle/scrub encode, depending on Key 0 state
  // OFF -> suttle only, used for FFW and FRW, based on the shuttle position
  // ON  -> scrub using the central jog , accelerated of slowed based on the outer shuttle
  if (KeySate[0] == 1) {
    XE_scrub_action(s_i);
  } else {
    // case where KeySate[0] = 0
    if (SH_table[s_i]) {
      XE_schuttle_action(s_i);
    }
  }
  // Another OSC command?
  XE_osc_check();
  //
  delay(10);
  // end of loop
}




Note: Interestingly, I found this device after making mine; nice piece of gear and the company has other programmable sets of keys that can be of interest too. My small device as described above is a lot cheaper and was fun to build. I learned a lot in the process, and I can program it in any ways I need.





3D printing fun


I wanted to secure my phone to a well known support: a microphone stand;The use is to take stable videos or to use live the apps that enable controlling my music gear.
I bought a cheap but flexible support from Asia, aimed at securing mobile phones to the air vents of a car. I 3D printed an accessory to adapt the support to the end of the microphone stand. All done! a very stable, and secure support for phones of all sizes.
Steps below:



 
 
 
 
 


Comments