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!
The /b
flag of the copy
command treats the files as binary (i.e., a raw stream of meaningless bytes), and copies them byte for byte instead of the default (or the /a
) behavior which treats them as lines of text (with end-of-line characters, end-of-files, etc.)
You can merge text files with either the default text behavior or the binary switch, but pretty much any binary file will not work. You cannot simply copy the bytes from two binary files and expect them to work because binary files usually have headers, metadata, data structures, etc. that define the format of the file. If you do a binary copy, you will simply be copying all the bytes as is which ends up putting these structures in places that they should not be, so when you open them, the parsing function will have trouble and see what is essentially corrupt data. Some program will ignore the parts that don’t make sense and simply show what they can (which allows for stereography to work), but some will throw an error and complain that the file is corrupt. The ability to detect corruption depends on the file-type.
As an example, let’s invent a simplified PDF format:
Byte(s) Meaning
---------------------
File header:
0-1 # of Pages
2-3 Language
4-5 Font
6-EOF Data (each page encoded separately)
Page data:
0-1 Page number
2-3 # of characters on page
4-#chars Letters contained on the page
As you can see, each file will contain a file-level header with some general information, followed by data blocks for each page containing the page data. If you then take two files, each containing one page and merge them as binary files, you will not be creating one two-page file, but instead one corrupt file that starts out with one page, then has a bunch of junk (the file header makes no sense when the program tries to read page two).
The same thing happens for your MP3s. When you combined them like that, the ID3 tags at the start and/or end of the of the second file are retained, and when the player tries to read the next frame, it is expecting audio data, but is finding the header of the second file which does not match the expected format for audio data, so it doesn’t know what to do. Some players will play the header as audio data (which will likely play as static/noise/pops/etc.), some will cut the sound for until the next correct frame, some may stop playing the song altogether, and some may even crash.
The copy
command knows nothing about file-types other than plain-text (and even then, only ASCII text), so only plain-text can be combined correctly with it. Binary files must be combined using an editor that knows how to parse and interpret the contents correctly.
Best Answer
This is because the RIFF header contains information about the duration of the file. You need to remove the RIFF headers from all files and create a new one describing the new file. You're treating them like RAW files, which they aren't
You can use sox for this, that would be a better approach
http://sox.sourceforge.net/
The windows binary download is at
http://sourceforge.net/projects/sox/files/sox/
You can also use it to render a png file containing the FFT data and stuff like that. It's a very neat tool.
https://stackoverflow.com/questions/9457020/merge-2-audio-files-in-sox