// last mod: 19-May-08
(
e = Eisenkraut.default;
e.connect;
fork { e.initTree; e.initSwing };
)
// Example of a realtime plug-in:
// adding reverberation (GVerb2).
//
// WARNING : scsynth must be booted
// before executing this code!
//
// After executing this code,
// a new menu item appears in
// Process -> SuperCollider.
// Choosing this item will execute
// the populateWindowFunc function
// below.
//
// WARNING : needs some realtime
// memory, so you should make sure
// to put Preferences -> Audio -> Advanced ->
// Realtime Memory to at least 32 MB.
(
var desktopFolder;
desktopFolder = "~/Desktop".standardizePath ++ "/";
x = EisKPlugIn.new.name_( "GVerb Filter" );
e.addProcessPlugIn( x );
fork { e.initTree; };
x.populateWindowFunc = { arg plug, win;
var flow, gui, synth, ggBypass, ggRender, ggProgress, roomSize = 30, revTime = 3, outDamping = 0.5, inDamping = 0.5, dryLevel = 0.dbamp, earlyLevel = -6.dbamp, tailLevel = -12.dbamp, revTimeSpec, inDampingSpec, outDampingSpec, roomSizeSpec, levelSpec, threshSpec, lbDryLevel, lbEarlyLevel, lbTailLevel, lbOutDamping, lbRevTime, lbRoomSize, lbInDamping, lbThreshold, fStop, fCreateDef, maxRoomSize = 1000, spread = 15 /* XXX fixed for now */;
try {
gui = GUI.current;
win.bounds_( win.bounds.resizeTo( 400, 240 ));
flow = FlowLayout( win.view.bounds );
win.view.decorator = flow;
roomSizeSpec = ControlSpec( 1, maxRoomSize, 'exp' );
gui.staticText.new( win, Rect( 0, 0, 80, 24 ))
.align_( \right )
.string_( "Room Size" );
gui.slider.new( win, Rect( 0, 0, 240, 20 ))
.value_( roomSizeSpec.unmap( roomSize ))
.action_({ arg b;
roomSize = roomSizeSpec.map( b.value );
lbRoomSize.string = roomSizeSpec.map( b.value ).round( 0.1 ).asString ++ " m²";
// synth.set( \roomSize, roomSize );
"WARNING: roomSize cannot be modified in realtime.\nPress bypass twice to make the change effective".postln;
});
lbRoomSize = gui.staticText.new( win, Rect( 0, 0, 48, 24 ))
.string_( "30 m²" );
flow.nextLine;
revTimeSpec = ControlSpec( 0.1, 100, 'exp' );
gui.staticText.new( win, Rect( 0, 0, 80, 24 ))
.align_( \right )
.string_( "Reverb Time" );
gui.slider.new( win, Rect( 0, 0, 240, 20 ))
.value_( revTimeSpec.unmap( revTime ))
.action_({ arg b;
revTime = revTimeSpec.map( b.value );
lbRevTime.string = revTimeSpec.map( b.value ).round( 0.1 ).asString ++ " s";
synth.set( \revTime, revTime );
});
lbRevTime = gui.staticText.new( win, Rect( 0, 0, 48, 24 ))
.string_( "3 s" );
flow.nextLine;
inDampingSpec = ControlSpec( 0.0, 1.0, 'lin' );
gui.staticText.new( win, Rect( 0, 0, 80, 24 ))
.align_( \right )
.string_( "Input Damping" );
gui.slider.new( win, Rect( 0, 0, 240, 20 ))
.value_( inDampingSpec.unmap( inDamping ))
.action_({ arg b;
inDamping = inDampingSpec.map( b.value );
lbInDamping.string = (inDampingSpec.map( b.value ).round( 0.01 ) * 100).asString ++ "%";
synth.set( \inDamping, inDamping );
});
lbInDamping = gui.staticText.new( win, Rect( 0, 0, 48, 24 ))
.string_( "50%" );
flow.nextLine;
outDampingSpec = ControlSpec( 0.0, 1.0, 'lin' );
gui.staticText.new( win, Rect( 0, 0, 80, 24 ))
.align_( \right )
.string_( "Output Damp." );
gui.slider.new( win, Rect( 0, 0, 240, 20 ))
.value_( outDampingSpec.unmap( outDamping ))
.action_({ arg b;
outDamping = outDampingSpec.map( b.value );
lbOutDamping.string = (outDampingSpec.map( b.value ).round( 0.01 ) * 100).asString ++ "%";
synth.set( \outDamping, outDamping );
});
lbOutDamping = gui.staticText.new( win, Rect( 0, 0, 48, 24 ))
.string_( "50%" );
flow.nextLine;
levelSpec = ControlSpec( -inf, 0, 'db' );
gui.staticText.new( win, Rect( 0, 0, 80, 24 ))
.align_( \right )
.string_( "Dry Level" );
gui.slider.new( win, Rect( 0, 0, 240, 20 ))
.value_( levelSpec.unmap( dryLevel.ampdb ))
.action_({ arg b;
dryLevel = levelSpec.map( b.value ).dbamp;
lbDryLevel.string = levelSpec.map( b.value ).round( 0.1 ).asString ++ " dB";
synth.set( \dryLevel, dryLevel );
});
lbDryLevel = gui.staticText.new( win, Rect( 0, 0, 48, 24 ))
.string_( "0 dB" );
flow.nextLine;
gui.staticText.new( win, Rect( 0, 0, 80, 24 ))
.align_( \right )
.string_( "Early Ref. Lvl" );
gui.slider.new( win, Rect( 0, 0, 240, 20 ))
.value_( levelSpec.unmap( earlyLevel.ampdb ))
.action_({ arg b;
earlyLevel = levelSpec.map( b.value ).dbamp;
lbEarlyLevel.string = levelSpec.map( b.value ).round( 0.1 ).asString ++ " dB";
synth.set( \earlyLevel, earlyLevel );
});
lbEarlyLevel = gui.staticText.new( win, Rect( 0, 0, 48, 24 ))
.string_( "-6 dB" );
flow.nextLine;
gui.staticText.new( win, Rect( 0, 0, 80, 24 ))
.align_( \right )
.string_( "Reverb Level" );
gui.slider.new( win, Rect( 0, 0, 240, 20 ))
.value_( levelSpec.unmap( tailLevel.ampdb ))
.action_({ arg b;
tailLevel = levelSpec.map( b.value ).dbamp;
lbTailLevel.string = levelSpec.map( b.value ).round( 0.1 ).asString ++ " dB";
synth.set( \tailLevel, tailLevel );
});
lbTailLevel = gui.staticText.new( win, Rect( 0, 0, 48, 24 ))
.string_( "-12 dB" );
flow.nextLine;
flow.shift( 8, 8 );
fStop = {
synth.free;
synth = nil;
};
fCreateDef = { arg numChannels;
SynthDef( \filterGVerb, { arg bus = 0, roomSize = 30, revTime = 3, outDamping = 0.5, inDamping = 0.5, spread = 15, dryLevel = 0, earlyLevel = 0.5, tailLevel = 0.25;
var inp, filter;
inp = In.ar( bus, numChannels ).asArray;
filter = 0;
numChannels.do({ arg ch;
filter = filter + GVerb.ar( inp[ ch ], roomSize, revTime, outDamping, inDamping, spread, dryLevel, earlyLevel, tailLevel, maxRoomSize );
});
ReplaceOut.ar( bus, filter );
});
};
ggBypass = gui.button.new( win, Rect( 0, 0, 80, 24 ))
.states_([[ "Bypass" ], [ "Bypass", Color.black, Color.yellow ]])
.value_( 1 )
.action_({ arg b;
if( b.value == 0, {
{
var msg, grpInput, diskBus, localThresh, localFFTSize, def;
msg = e.query( '/doc/active/sc', [ \diskBusIndex, \diskBusNumChannels, \inputGroup ]);
if( msg.notNil, {
diskBus = Bus( \audio, msg[ 0 ], msg[ 1 ], e.scsynth );
grpInput = Group.basicNew( e.scsynth, msg[ 2 ]);
def = fCreateDef.value( diskBus.numChannels );
synth = Synth.basicNew( def.name, e.scsynth );
def.send( e.scsynth, synth.newMsg( grpInput, [ \bus, diskBus.index, \roomSize, roomSize, \revTime, revTime, \outDamping, outDamping, \inDamping, inDamping, \spread, spread, \dryLevel, dryLevel, \earlyLevel, earlyLevel, \tailLevel, tailLevel ], \addAfter ));
});
}.fork( AppClock );
}, {
fStop.value;
});
});
ggBypass.onClose = fStop; // XXX NEVER GETS CALLED
ggRender = gui.button.new( win, Rect( 0, 0, 80, 24 ))
.states_([[ "Render" ]])
.action_({ arg b;
{
var msg, msg2, msg3, oscFileName, oscFile, fna, audioFileName, numChannels, rate, totalLen, cmd, prog, newProg,
localThresh, def, oscFileName2, oscRaw;
msg = e.sendMsgSync( '/main', \createTempFile );
msg2 = e.sendMsgSync( '/main', \createTempFile );
msg3 = e.sendMsgSync( '/main', \createTempFile );
if( msg.notNil && msg2.notNil && msg3.notNil, {
oscFileName = msg.first;
oscFileName2 = msg2.first;
audioFileName = msg3.first;
msg = e.query( '/doc/active/sc', [ \diskBusNumChannels ]);
if( msg.notNil, {
numChannels = msg.first;
msg = e.query( '/doc/active/timeline', [ \rate, \selectionStart, \selectionStop ]);
if( msg.notNil, {
rate = msg[ 0 ];
totalLen = (msg[ 2 ] - msg[ 1 ]) / rate;
(oscFileName.asString.asCompileString ++ " (numCh = "++numChannels++"; rate = "++rate++"; duration = "++totalLen++") --> " ++ audioFileName).postln;
"Creating NRT file...".postln;
e.sendMsg( '/doc/active/sc', \createNRTFile, oscFileName, 0, 0, numChannels, rate );
// [ "oscFileName", oscFileName, "oscFileName2", oscFileName2 ].postcs;
// XXX BROKEN
// e.sync;
e.query( '/main', [ \version ]);
"...done".postln;
1.0.wait;
// "Reading NRT file...".postln;
//
// oscFile = OSCFile.read( oscFileName );
// "...done".postln;
//
"Creating merged NRT file...".postln;
// oscFileName2="/tmp/test.osc";
fna = EisKNRTFile.openWrite( oscFileName2.asString );
"-----------3".postln;
def = fCreateDef.value( numChannels );
synth = Synth.basicNew( def.name, e.scsynth );
fna.sendBundle( 0.0, def.recvMsg );
fna.sendBundle( 0.0, synth.newMsg( Group.basicNew( e.scsynth, 0 ), [ \threshold, localThresh, \bus, 0 ], \addToTail ));
// "-----------4".postln;
//("cp " ++ oscFileName ++ " /tmp/test2.osc").systemCmd;
// "-----------4.5".postln;
//oscFileName = "/tmp/test2.osc";
oscFile = File( oscFileName.asString, "rb" );
// "-----------5".postln;
oscRaw = Int8Array.newClear( oscFile.length );
oscFile.read( oscRaw );
// "-----------6".postln;
oscFile.close;
fna.file.write( oscRaw );
// "-----------7".postln;
// oscFile.packets.do({ arg p;
// fna.sendRaw( p.asRawOSC );
// });
fna.closeFile;
"...done".postln;
// audioFileName = desktopFolder ++ "test.aif";
cmd = "./scsynth -i 0 -o "++numChannels++" -N " ++ oscFileName2 ++ " _ " ++ audioFileName ++ " "++rate++" AIFF float";
ggProgress.knobColor_( Color.blue( 0.3 ));
ggProgress.hi = 0.0;
prog = 0.0;
ggProgress.visible = true;
Pipe.do( cmd, { arg line, i;
//line.postcs;
if( line.beginsWith( "nextOSCPacket " ), {
newProg = line.copyToEnd( 14 ).asFloat / totalLen;
if( (newProg - prog) > 0.01, {
prog = newProg;
ggProgress.hi = newProg;
0.yield;
});
}, {
line.postln;
0.yield;
});
});
ggProgress.hi = 1.0;
ggProgress.knobColor = Color.green( 0.75 );
// e.sendMsg( '/doc/active', \editMode, \overwrite );
e.sendMsg( '/doc/active', \replace, audioFileName );
}, {
"timeout".warn;
});
}, {
"timeout".warn;
});
}, {
"timeout".warn;
});
}.fork( AppClock );
});
ggProgress = GUI.rangeSlider.new( win, Rect( 0, 0, 172, 24 ))
.canFocus_( false )
.visible_( false );
} { arg error;
error.reportError;
};
};
)