// lastmod: 03-Jun-08
(
e = Eisenkraut.default;
e.connect;
fork { e.initTree; e.initSwing };
)
e.swing.dumpOSC(1);
e.swing.dumpOSC(0);
// WARNING : fails if scsynth wasn't yet booted!
// Example of a realtime plug-in:
// A recorder GUI that let's you
// record everything that comes out
// of a certain bus. For simplicity
// you specify bus index and numChannels
// manually instead of hooking it
// up to automatically match the control room's
// output configuration.
//
// Note that a new menu item appears in
// Process -> SuperCollider.
// Choosing this item will execute
// the populateWindowFunc function
// below.
(
var headerSuffix, ggTimer, ggChannelOffset, ggNumChannels, channelOffset = 0, numChannels = 2, headerFormat = "aiff", ggHeaderFormat, ggSampleFormat, sampleFormat = "float", folder, ggFolder, ggFile, file = "", autoFile = true, routTimer;
x = EisKPlugIn.new.name_( "Control Room Recorder" );
e.addProcessPlugIn( x );
fork { e.initTree; };
headerSuffix = IdentityDictionary.new;
headerSuffix.put( \aiff, "aif" );
headerSuffix.put( \next, "au" );
headerSuffix.put( \wav, "wav" );
headerSuffix.put( \ircam, "irc" );
headerSuffix.put( \raw, "raw" );
folder = "~/Desktop".standardizePath ++ "/";
x.populateWindowFunc = { arg plug, win;
var flow, gui, headerFormats, sampleFormats;
("win.server.addr : "++win.server.addr).postln;
gui = GUI.current;
win.bounds_( win.bounds.resizeTo( 368, 156 ));
flow = FlowLayout( win.view.bounds );
win.view.decorator = flow;
GUI.staticText.new( win, Rect( 0, 0, 48, 24 ))
.align_( \right )
.string_( "Folder" );
ggFolder = GUI.dragSink.new( win, Rect( 0, 0, 280, 20 ))
// .resize_( 2 )
.object_( folder )
.action_({ arg b;
folder = b.object;
});
GUI.button.new( win, Rect( 0, 0, 20, 20 ))
// .resize_( 3 )
.states_([[ "..." ]])
.canFocus_( false )
.action_({ arg b;
GUI.use( gui, {
GUI.dialog.savePanel({ arg path;
ggFolder.object = path;
ggFolder.doAction;
});
});
});
flow.nextLine;
GUI.staticText.new( win, Rect( 0, 0, 48, 24 ))
.align_( \right )
.string_( "File" );
ggFile = GUI.textField.new( win, Rect( 0, 0, 260, 20 ))
// .resize_( 2 )
// .object_( file )
.enabled_( autoFile.not )
.action_({ arg b;
file = b.object;
});
GUI.button.new( win, Rect( 0, 0, 40, 20 ))
// .resize_( 3 )
.states_([[ "Auto" ], [ "Auto", Color.white, Color.blue ]])
.canFocus_( false )
.value_( autoFile.binaryValue )
.action_({ arg b;
autoFile = b.value == 1;
ggFile.enabled( autoFile.not );
});
flow.nextLine;
GUI.staticText.new( win, Rect( 0, 0, 48, 24 ))
.align_( \right )
.string_( "Format" );
headerFormats = headerSuffix.keys.asArray.performUnaryOp( \asString ).sort;
ggHeaderFormat = GUI.popUpMenu.new( win, Rect( 0, 0, 80, 24 ))
.canFocus_( false )
.items_( headerFormats )
// .value_( headerFormats.indexOf( headerFormat ))
.value_( headerFormats.collect({ arg item, idx; if( item == headerFormat, idx, -1 )}).maxItem )
.action_({ arg b;
headerFormat = headerFormats[ b.value ];
});
sampleFormats = [ "int16", "int24", "int32", "float" ];
ggSampleFormat = GUI.popUpMenu.new( win, Rect( 0, 0, 80, 24 ))
.canFocus_( false )
.items_( sampleFormats )
// .value_( sampleFormats.indexOf( sampleFormat ))
.value_( sampleFormats.collect({ arg item, idx; if( item == sampleFormat, idx, -1 )}).maxItem )
.action_({ arg b;
sampleFormat = sampleFormats[ b.value ];
});
~ggSampleFormat = ggSampleFormat;
flow.nextLine;
GUI.staticText.new( win, Rect( 0, 0, 48, 24 ))
.align_( \right )
.string_( "Bus" );
ggChannelOffset = GUI.numberBox.new( win, Rect( 0, 0, 36, 24 ))
.align_( \right )
.object_( channelOffset )
.action_({ arg b;
channelOffset = b.value.asInteger;
});
GUI.staticText.new( win, Rect( 0, 0, 48, 24 ))
.align_( \right )
.string_( "Chans" );
ggNumChannels = GUI.numberBox.new( win, Rect( 0, 0, 36, 24 ))
.align_( \right )
.object_( numChannels )
.action_({ arg b;
numChannels = b.value.asInteger;
});
flow.nextLine;
flow.shift( 48, 8 );
~ggRec = GUI.button.new( win, Rect( 0, 0, 80, 24 ))
.states_([[ "Rec" ]])
.action_({ arg b;
var path, target, targetAddAction;
b.enabled = false;
{
target = nil;
targetAddAction = \addToTail;
// folder = "recordings/";
if( autoFile, {
file = "SC_" ++ Date.localtime.stamp ++ "." ++ headerSuffix[ headerFormat.asSymbol ];
ggFile.object = file;
});
path = folder ++ file;
~buf = EisKBuffer.alloc( e.scsynth, 65536, numChannels );
~buf.write( path, headerFormat, sampleFormat, 0, 0, true );
SynthDef( "simpleRecorder" ++ numChannels, { arg i_bus, i_buf;
DiskOut.ar( i_buf, In.ar( i_bus, numChannels ));
}).send( e.scsynth );
e.scsynth.sync;
~node = Synth( "simpleRecorder" ++ ~buf.numChannels, [ \i_buf, ~buf.bufnum,
\i_bus, channelOffset ], target ?? { RootNode( e.scsynth )}, targetAddAction );
~ggStop.enabled = true;
routTimer = fork {
inf.do({ arg secs;
ggTimer.string_( (secs + 360000).asTimeString.copyToEnd( 1 ));
1.wait;
});
};
}.fork( AppClock );
});
~ggStop = GUI.button.new( win, Rect( 0, 0, 80, 24 ))
.states_([[ "Stop" ]])
.enabled_( false )
.action_({ arg b;
b.enabled = false;
~node.free;
~node = nil;
routTimer.stop;
routTimer = nil;
if( ~buf.notNil, {
~buf.close({ arg buf; buf.free; });
~buf = nil;
});
~ggRec.enabled = true;
});
ggTimer = GUI.staticText.new( win, Rect( 0, 0, 72, 24 ))
.string_( "00:00:00" );
};
)