MP3 Read an MP3 file or stream, or write an MP3 file (can also read Ogg)


This class is a wrapper for the *nix command-line tools curl and lame, making it easy to access MP3 files and MP3 (shoutcast/icecast) internet streams. This implies two caveats:


 * It will work on Mac OSX, Linux, and other unix-like systems, but is extremely unlikely to work on Windows.

 * You must have curl and lame installed on your system. curl is a generic tool for internet downloading, and is available on most systems. lame is a tool commonly used for MP3 encoding/decoding.


To tell this class where your system's lame and curl programs are, you can check/set the class variables (you could add a line to your startup file to set them):


MP3.lamepath;

MP3.curlpath;

// To check that there's a file at the expected path

File.exists(MP3.lamepath)

File.exists(MP3.curlpath)


In order to use this class you create an instance of it, passing a path or URL to the MP3 data as the first argument. The second constructor argument is the mode, either \readfile to read from a file (this is the default), or \readurl to read from a URL, or \writefile to write to a file. You can then call the start method when you want the MP3 data to begin queueing up (i.e. before you start your SC DiskIn/DiskOut synth or whatever). You then use the fifo variable as the path to stream into the buffer.


Additional arguments can be passed to the lame executable, passed in as a string as the first argument to .start.


For file writing, the class tells lame to expect 16-bit raw audio data at 44.1 kHz, so this is what you must output - see the example below. (The reason for writing as raw data is that the "fifo" trick excludes the possibility of the fileseeking which is required for writing soundfile headers.)


The .playing flag can tell you whether the MP3 is currently (still) playing - i.e. it is true if you have started the MP3 and not yet stopped it. (It will revert back to false if the MP3 file finishes, even if you haven't called the .stop method.)


Ogg format files can be read (despite the name of the class!) as long as you have the oggdec command-line tool installed. To tell the class that it should expect an ogg file rather than an MP3, set the constructor's "format" argument (the third argument) to \ogg .


Examples


// Let's read in an internet radio stream and warp it a bit.

// First we need a synthdef:

s.boot;

(

SynthDef("help_mp3_01", { |bufnum = 0|

var son, wibble;

son = DiskIn.ar(2, bufnum);

wibble = LFPar.kr(0.1).range(0.5, 2.0);

son = PitchShift.ar(son, pitchRatio: wibble);

Out.ar(0, son);

}).load(s);

)


// Now let's create the MP3 object and cue it into a Buffer.


// Choose one of these two:

// (a) a stream URL - note the use of the second argument to indicate a remote stream

m = MP3("http://icecast.commedia.org.uk:8000/resonance.mp3", \readurl);

// (b) a local file

m = MP3("/Users/dan/Music/SqueezeTheTrigger(Version1).mp3");

m = MP3("/Users/danstowell/Music/ManiAyer.mp3");


m.start;

// Now you can use it almost like any other file, by reading from m.fifo

b = Buffer.cueSoundFile(s, m.fifo, 0, 2);

x = Synth("help_mp3_01", [\bufnum, b.bufnum], addAction:\addToTail);

m.playing;

// You can stop and restart the piping (with a bit of a delay) - note what happens

m.stop;

m.playing;

m.start;

m.playing;

// Please remember to tidy up after yourself:

x.free;

b.close; b.free;

m.finish;



/////////////////////////////////////////////////////


// Reading into a buffer is possible, but you *must* specify the number of (uncompressed) frames to read.

m = MP3("http://icecast.commedia.org.uk:8000/resonance.mp3", \readurl);

m.start;

b= Buffer.read(s, m.fifo, 0, 50000);

// After a second or two, should be able to play a snatch of the stream

b.play;


// Please remember to tidy up after yourself:

b.close; b.free;

m.finish;



/////////////////////////////////////////////////////


// Alternatively, a method is provided for reading a local MP3 file more easily (won't work on streams)

s.boot;

b = MP3.readToBuffer(s, "/Users/dan/Music/SqueezeTheTrigger(Version1).mp3")

b.play;


b.free;



/////////////////////////////////////////////////////


// Writing an MP3 file using DiskOut. See the DiskOut helpfile for more on this.

(

SynthDef("help_mp3_02", { |bufnum = 0|

var son;

son = SinOsc.ar(SinOsc.ar(Line.kr(1, 100, 10, doneAction:2)).range(220, 550)) * 0.1;

son = son.dup;

Out.ar(0, son);

DiskOut.ar(bufnum, son);

}).load(s);

)

// Create an MP3 object for writing

m = MP3("recordings/mp3test.mp3", \writefile);

m.start;

// allocate a disk i/o buffer

b = Buffer.alloc(s, 65536, 2);

// Start writing

b.write(m.fifo, "raw", "int16", 0, 0, true);


x = Synth("help_mp3_02", [\bufnum, b.bufnum], addAction:\addToTail);


// once the writing has stopped, tidy up

b.close; b.free;

m.finish;