Linux – Command line multitrack audio looper for Linux

audiocommand linelinux

Given a number of audio files, (say, *.wav), I'd like a command line way to mix these files as tracks with individual volumes, and define global loop points, so the audio mix loops seamlessly.

Could anyone suggest any alternatives?

Many thanks in advance,
Cheers!


In fact, a "command line looper" can almost be achieved with melt and the file below – the problem is that: the looping is not really seamless (there are audible clicks); I cannot see a way to mix more than two channels; and in spite of adding the -audio-track | -hide-video switch on the command line, a blank screen is still shown. (some more on looping in melt here: Re: [Mlt-devel] loop for producer)

This command must be used to call melt in "looping" mode:

melt eof=loop loop.mlt

… while the loop.mlt file (calling upon audio files test.wav and test2.wav) is this (it will be 70 frames long during loop playback in total):

<mlt>
  <producer id="producer0">
    <property name="aspect_ratio">0.000000</property>
    <property name="resource">test.wav</property>
    <property name="audio_index">0</property>
    <property name="video_index">-1</property>
    <property name="global_feed">1</property>
  </producer>
  <producer id="producer1">
    <property name="aspect_ratio">0.000000</property>
    <property name="resource">test2.wav</property>
    <property name="audio_index">0</property>
    <property name="video_index">-1</property>
    <property name="global_feed">1</property>
  </producer>
  <playlist id="playlist0">
    <entry producer="producer0" in="0" out="30"/>
    <blank length="10"/>
    <entry producer="producer0" in="0" out="30"/>
  </playlist>
  <playlist id="playlist1">
    <blank length="30"/>
    <entry producer="producer1" in="0" out="10"/>
  </playlist>
  <tractor id="tractor0" title="loop.mlt" global_feed="1" in="0" out="71">
    <multitrack id="multitrack0">
      <track producer="playlist0" hide="video"/>
      <track producer="playlist1" hide="video"/>
    </multitrack>
    <transition id="transition1" in="0" out="71">
      <property name="a_track">0</property>
      <property name="b_track">1</property>
      <property name="mlt_service">mix</property>
      <property name="start">0.5</property>
      <property name="end">0.5</property>
      <property name="combine">1</property>
      <property name="always_active">1</property>
    </transition>
  </tractor>
</mlt>

Best Answer

Well, I think I found a solution for a Linux command-line multitrack audio looper - and that is to use a ChucK => Strongly-timed, On-the-fly Audio Programming Language script.

First of all, ChucK can be installed via sudo apt-get install chuck in Debian/Ubuntu. However, after you install, try to type chuck and then TAB in terminal; you should get something like:

$ chuck
chuck       chuck.alsa  chuck.oss

... that is to say - note that there are three ChucK executables; each for a separate Linux audio backend - and note that the default executable, chuck, actually refers to JACK (the other executables, obviously, refer to ALSA and OSS).

Since most typical users on Linux (also myself) do not actually have jack installed - running the chuck executable may cause dissapointment, as it won't result with sound (given that jack is not present on the system); see, for instance:

You may want to run chuck --probe and see what is reported for different backends - expect a complaint from chuck if JACK is not present and running, but chuck.alsa should pass:

$ chuck --probe
[chuck]: (via rtaudio): no devices found for compiled audio APIs!
[chuck]:
# .....
$ chuck.alsa --probe
[chuck]: found 4 device(s) ...
[chuck]: ------( chuck -- dac1 )---------------
[chuck]: device name = "hw:SB,0"
[chuck]: probe [success] ...
[chuck]: # output channels = 6
[chuck]: # input channels  = 2
[chuck]: # duplex Channels = 2
# .....

For most users, then, running chuck.alsa instead should do the trick - however, note that this will likely take up the audio device directly (doesn't look like chuck is aware of pulseaudio) - and then you will not be able to run other audio-generating programs (like vlc) in parallel, and have the sound mixed from both applications (the other application will basically stay silent).

 

But this being noted - we can now proceed with a ChucK script for looping.

One of the great things about ChucK is that basially we can define one script that handles the playback and looping of just one sound - and then call multiple instances of that script in parallel - which effectively creates a multitrack audio looper!

After some messing around with ChucK examples - see:

... I managed to cook my own script - primarily based on examples: sndbuf.ck and valueat.ck - which I've called loopsndbuf.ck:

// sound file
// initialize empty string variable for filename
"" => string filename;

// set a default filename to be loaded
"/path/to/freesound.org/100391__dobroide__20100627-creek.wav" => filename;

// if arguments are passed to the script,
// use the first argument as filename instead
if( me.args() ) me.arg(0) => filename;

0.5 => float myvolume;
if( me.args() ) if (me.arg(1) != "") Std.atof(me.arg(1)) => myvolume;

<<< "myvolume: " + myvolume >>>;

SndBuf buf;

filename => buf.read;
myvolume => buf.gain;
1.0 => buf.rate;


// time loop
Impulse i => dac;
while( true )
{
    int pos;

    repeat( buf.samples() )
    {
        buf.valueAt( pos ) => i.next;
        pos++;
        1::samp => now;
    }
}

(see the online version for more comments)

Having saved this script, we can now call it with chuck as interpreter:

chuck.alsa loopsndbuf.ck

... which will start loading the sound as in default settings, and looping it. Or, we can call the script with arguments - note that the character to separate arguments from a chuck script is colon (:):

chuck.alsa loopsndbuf.ck:/path/to/freesound.org/23222__erdie__thunderstorm2.wav:0.4

... or, - finally - we can call multiple instances of the loopsndbuf.ck script, which chuck will run in parallel. For this, I'd rather put everything in a bash script, let's call it loopchuck.sh:

set -x
PTH="/path/to/freesound.org"

chuck.alsa \
loopsndbuf.ck:$PTH/15528__ch0cchi__domestic-cat-purr.wav:1.0 \
loopsndbuf.ck:$PTH/100391__dobroide__20100627-creek.wav:0.05 \
loopsndbuf.ck:$PTH/23222__erdie__thunderstorm2.wav:0.4 \
loopsndbuf.ck:$PTH/2519__rhumphries__rbh-rain-01.wav:0.4 \
loopsndbuf.ck:$PTH/18766__reinsamba__chimney-fire.wav:0.4 \
loopsndbuf.ck:$PTH/53380__eric5335__meadow-ambience.wav:0.4

Running this script should basically load separate instances of the script, each with their own file and volume - chuck will then first load all audio into memory, spitting something like this to stdout:

$ ./loopchuck.sh
++ PTH=/path/to/freesound.org
++ chuck.alsa loopsndbuf.ck:/path/to ....
"myvolume: 1.0000" : (string)
"myvolume: 0.0500" : (string)
"myvolume: 0.4000" : (string)
"myvolume: 0.4000" : (string)
"myvolume: 0.4000" : (string)
"myvolume: 0.4000" : (string)

... and then, it will have all the scripts start at the same time - and then it will loop all the sounds individually at sample accuracy, according to their file length - wrapping the loop for each file, when it concludes, separately. In other words, the phases of the loops are independent (similar to how the GUI program terminatorX treats loops by default) - and that is exactly what I was looking for :) !! Note that you may have to press CTRL+C twice to fully stop the loopchuck.sh script. (and yes, the audio samples used are from Freesound.org)

Note, however, that while chuck loads all these files in memory, it uses quite a bit of CPU reproducing and mixing these loops - and so expect to have drops in the audio, if the OS has to handle some heavy operations (like GUI, say in the case when scrolling a webpage in your browser). In order to avoid this, I guess a jack setup has to be used - but then one has to get involved with Linux real-time priorities and whatnot (which isn't necessarily a straightforward process).

 

Well, I guess I'm happy now - since this is all I wanted (I just wish I could mix this audio output with one from say vlc or youtube in firefox, though - done, check other answer) :); hope it helps others, too,
Cheers!

Related Question