MaxPat Max/MSP file parser


Inherits from: Object


This class can convert Max/MSP 5 files to the old format (Max/MSP 4). It can also be used to generate, examine, manipulate and process Max5 patches in different ways.

See the readme.txt for more information on how to install and how the conversion is performed.

*new(name, rect, fontName, fontSize)

create a new empty max5 patch.

*read (path)

parse a max5 file from disk.

<>dict

the parsed max5 patch stored in a supercollider dictionary.

you can manipulate its content and then write back to disk as max5.

although you will need to know a bit how the max5 file format works.

<>name

name of the patch.  usually the current file name (string).

boxes(recursive)

helper method that returns an array of all boxes in a patch.

recursive is a boolean flag for also including subpatches.  the array is flat.  default= true.

lines(recursive)

helper method that returns an array of all patchlines a patch

recursive is a boolean flag for also including subpatches.  the array is flat.  default= true.

boxesClass(maxclass, recursive)

helper method that returns an array of all boxes in a patch matching some maxclass (symbol).

recursive is a boolean flag for also including subpatches.  the array is flat.  default= true.

findBox(id)

helper method that returns a box with an id matching the argument (string).

nextId

helper method that searches all boxes and returns an id + 1 (string).

makeButton(rect, extra)

makeComment(rect, text, extra)

makeFlonum(rect, extra)

makeLed(rect, extra)

makeMessage(rect, text, extra)

makeNumber(rect, extra)

makeObject(rect, text, numInlets, numOutlets, extra)

makePatcher(rect, maxPat, extra)

makePatchLine(srcId, dstId, srcOutlet, dstInlet, extra)

makePatcher(rect, maxPat, extra)

makeToggle(rect, extra)

helper methods to create a new objects.  all except makePatchLine returns an object id (string)

see examples below.

asMax5

convert dict and return a string with a valid max5 patch (json format).

saveAsMax5 (path, overwrite)

convert dict and save to disk.

asMax4

convert dict and return a string with a valid max4 patch.

saveAsMax4 (path, overwrite)

convert and save to disk.

<>max4colors

an array of default max4 colours (0-15).  you might want to change these if you made a custom colour table in max4

<>scaleBoxWidth

asMax4 only.  global scaling of all object boxes.  set this before converting.  default= 1.

<>scalePatWidth

asMax4 only.  global scaling of all patcher windows width.  set this before converting.  default= 1.

<>scalePatHeight

asMax4 only.  global scaling of all patcher windows height.  set this before converting.  default= 1.

<>stripExtension

asMax4 only.  boolean flag.  removes .maxpat extensions for bpatchers.  default= true.



Patch conversion examples (max5 to max4)


//simple example - convert and save on the desktop

a= MaxPat.read("/Applications/Max5/examples/max-tricks/buttonflower.maxpat");

a.saveAsMax4("~/Desktop/buttonflower.pat");



//batch processing

(

var src= "/Applications/Max5/examples/fft-fun/*"; //folder with max5 patches

var dst= "~/Desktop/"; //destination

src.pathMatch.do{|x|

p= PathName(x);

if(p.extension.keep(3)=="max", { //find all maxpat or maxhelp files

("converting..."+p.fileName).postln;

MaxPat.read(x).saveAsMax4((dst++p.fileNameWithoutExtension++".pat").postln);

});

};

'done';

)


//with file dialog.  will create a file with a .pat extension in the same folder

//it is also possible to select multiple files

(

GUI.dialog.getPaths({|paths|

paths.do{|x|

var a= MaxPat.read(x);

{

("saving"+x++".pat").postln;

a.saveAsMax4(x++".pat");

}.defer(0.1);

};

})

)



Patch introspection examples (max5)


//scanning the dictionary posting all comments in the top level of a max5 patch

a= MaxPat.read("/Applications/Max5/examples/sampling/groove-sync.maxpat");

a.dict['patcher']['boxes'].do{|x| if(x['box']['maxclass']=="comment", {x['box']['text'].postln})};""


//recursively scan and post the tree structure of a patch

(

var func;

a= MaxPat.read("/Applications/Max5/examples/fft-fun/convolution-workshop.maxpat");

func= {|d, i|

var ind= Char.tab.dup(i).join; //indentation

d['boxes'].do{|dd|

if(dd['box']['patcher'].notNil, { //sub patch

(ind++"__"++i++":"+dd['box']['text']).postln;

func.value(dd['box']['patcher'], i+1);

}, { //objects, comments, messages

(ind++dd['box']['maxclass']++":"+dd['box']['text']).copyRange(0, 50).postln;

});

};

};

("0:"+a.name).postln;

func.value(a.dict['patcher'], 1); ""

)



Patch manipulation examples (max5)


//make all patchlines in a patch blue and save a copy on the desktop

(

a= MaxPat.read("/Applications/Max5/examples/spatialization/doppler.maxpat");

a.lines.do{|x| x['patchline'].put('color', #[0, 0, 1, 1])};

a.saveAsMax5("~/Desktop/doppler_blueLines.maxpat");

)


//make all objects in a patch red and save a copy on the desktop

(

a= MaxPat.read("/Applications/Max5/examples/spatialization/doppler.maxpat");

a.boxes.do{|x| if(x['box'].maxclass!="comment", {x['box'].put('bgcolor', #[1, 0, 0, 1])})};

a.saveAsMax5("~/Desktop/doppler_redBoxes.maxpat");

)


//shrink fontsize for all comments with 4

(

a= MaxPat.read("/Applications/Max5/examples/spatialization/doppler.maxpat");

a.boxesClass('comment').do{|x| x['box'].put('fontsize', x['box']['fontsize']-4)};

a.saveAsMax5("~/Desktop/doppler_smallerComments.maxpat");

)



//move around all objects at random.  all lines with 1 central midpoint

(

a= MaxPat.read("/Applications/Max5/examples/spatialization/doppler.maxpat");

a.boxes.do{|x| x['box']['patching_rect'].put(0, 500.rand).put(1, 500.rand)};

a.lines.do{|x| x['patchline'].put('midpoints', [300, 200])};

a.saveAsMax5("~/Desktop/doppler_random.maxpat");

)


//on toplevel - hide all patchlines, unhide and place all objects in a column

(

var y= 0;

a= MaxPat.read("/Applications/Max5/examples/spatialization/doppler.maxpat");

a.lines(false).do{|x| x['patchline'].put('hidden', 1)};

a.boxes(false).do{|x, i|

x['box'].put('hidden', 0);

x['box']['patching_rect'].put(0, 10).put(1, y);

y= y+x['box']['patching_rect'][3];

};

a.dict['patcher']['rect'].put(2, 250).put(3, 750);

a.dict['patcher']['defrect'].put(2, 250).put(3, 750);

a.saveAsMax5("~/Desktop/doppler_column.maxpat");

)


//hide all patchlines in all subpatches

(

a= MaxPat.read("/Applications/Max5/examples/spatialization/doppler.maxpat");

a.lines.do{|x| x['patchline'].put('hidden', 1)};

a.saveAsMax5("~/Desktop/doppler_hidden.maxpat");

)


//make curly patchlines

(

var recursive= true;

var a= MaxPat.read("/Applications/Max5/examples/synths/cheby.maxpat");

var boxes= a.boxes(recursive);

a.lines(recursive).do{|x|

var pnt, mid, num, curl= 6, amp= 12, f;

var src= boxes.detect{|y| y['box']['id']==x['patchline']['source'][0]}['box'];

var dst= boxes.detect{|y| y['box']['id']==x['patchline']['destination'][0]}['box'];

var start= src['patching_rect'].asPoint

+Point(0, src['patching_rect'][3])

+Point(src['patching_rect'][2]/((src['numoutlets']-1).max(1))*x['patchline']['source'][1], 0);

var end= dst['patching_rect'].asPoint

+Point(dst['patching_rect'][2]/((dst['numinlets']-1).max(1))*x['patchline']['destination'][1], 0);

var k= (end-start).asPolar;

if(k.magnitude>45, {

num= k.magnitude.min(255).div(curl);

f= k.magnitude.min(255).div(curl*8);

mid= [];

pnt= start;

num.do{|i|

pnt= pnt+Point(sin(f*i/num*2pi)*amp, cos(f*i/num*2pi)*amp);

pnt= pnt+k.scale(1/(num+1)).asPoint;

mid= mid++pnt.asArray.asInteger;

};

x['patchline'].put('midpoints', mid.flat);

}, {

x['patchline'].put('midpoints', []);

});

x['patchline'].put('hidden', 0);

};

a.saveAsMax5("~/Desktop/cheby_curlt.maxpat", true);

)



Patch generation examples (max5)


//generate 100 comments at random positions

(

a= MaxPat.new;

100.do{|i| a.makeComment(Rect(450.rand, 450.rand, 60, 20), "com"++i, ('fontname': "Geneva", 'fontsize': 9.rrand(50)))};

a.saveAsMax5("~/Desktop/generatedComments.maxpat");

)



//video matrix of toggles - requires jitter. takes a little bit of time to build

(

var w= 40, h= 30, size= 20, ids;

a= MaxPat("srcmovie", Rect(848, 168, 330, 388)); //subpatch

ids= [

a.makeObject(Rect(26, 20, 60, 20), "loadbang", 1, 1),

a.makeToggle(Rect(26, 46, 20, 20)),

a.makeObject(Rect(26, 71, 72, 20), "qmetro 100", 2, 1),

a.makeObject(Rect(26, 97, 136, 20), "jit.qt.movie @dim"+w+h, 2, 1),

a.makeObject(Rect(26, 141, 73, 20), "jit.unpack", 1, 5),

a.makeObject(Rect(40, 193, 133, 20), "jit.op @op >= @val 0.5", 2, 2),

a.makeObject(Rect(40, 245, 46, 20), "jit.iter", 1, 3),

a.makeObject(Rect(54, 291, 103, 20), "sprintf send parent::t%i%i", 2, 1),

a.makeObject(Rect(40, 330, 75, 20), "pattrforward", 1, 1),

a.makeObject(Rect(100, 20, 190, 20), "loadmess read bball.mov", 1, 1),

a.makeMessage(Rect(100, 44, 35, 18), "read"),

a.makeNumber(Rect(100, 70, 50, 20)),

a.makeFlonum(Rect(118, 147, 50, 20)),

a.makeComment(Rect(118, 360, 140, 20), "patch generated in sc")

];

a.makePatchLine(ids[0], ids[1], 0, 0); //connect loadbang and toggle

a.makePatchLine(ids[1], ids[2], 0, 0); //connect toggle and qmetro

a.makePatchLine(ids[2], ids[3], 0, 0);

a.makePatchLine(ids[3], ids[4], 0, 0);

a.makePatchLine(ids[4], ids[5], 1, 0);

a.makePatchLine(ids[5], ids[6], 0, 0);

a.makePatchLine(ids[6], ids[8], 0, 0);

a.makePatchLine(ids[6], ids[7], 1, 0);

a.makePatchLine(ids[7], ids[8], 0, 0);

a.makePatchLine(ids[9], ids[3], 0, 0); //connect loadmess and jit.qt.movie

a.makePatchLine(ids[10], ids[3], 0, 0);

a.makePatchLine(ids[11], ids[2], 0, 1);

a.makePatchLine(ids[12], ids[5], 0, 1);

b= MaxPat("togglematrix", Rect(25, 69, w*size+20, h*size+20)); //main patch with matrix

b.makePatcher(Rect(20, 20, 60, 20), a, ('hidden' : 1));

w.do{|x|

h.do{|y|

//also try to replace makeToggle with makeLed

b.makeToggle(Rect(x*size+10, y*size+10, size, size), ('varname' : "t"++x++y));

};

};

b.saveAsMax5("~/Desktop/generatedToggleMatrix.maxpat", true);

)



//--ideas:

//harvest patcher from cycling74 forum and put together in random order

//genetic algorithms to breed and mutate patches

//build a complete max patch editor in sc