// this example uses SwingOSC to

// build a simple GUI for SuperCollider.

// It does so using "OSC-messaging style",

// that is it controls SwingOSC directly,

// not using custom SC-lang classes.


(

s = Server.local;

s.waitForBoot({

var cleanUp, autoPilot, butResp, winResp, resonFreq = 400;

cleanUp = {

"byebye".postln;

autoPilot.stop;

butResp.remove;

winResp.remove;

n.sendBundle( nil,

[ "/method", \myWindowAc, \remove ],

[ "/method", \myDustButtonAc, \remove ],

[ "/method", \myKlangButtonAc, \remove ],

[ "/method", \myPlingButtonAc, \remove ],

[ "/method", \myDustSliderAc, \remove ],

[ "/method", \myResonSliderAc, \remove ],

[ "/method", \myReverbSliderAc, \remove ],

[ "/method", \myDumpOSCAc, \remove ],

[ "/method", \myAutoPilotAc, \remove ],

[ "/free", \myWindowAc, \myDustButtonAc, \myKlangButtonAc, \myPlingButtonAc,

\myDustSliderAc, \myResonSliderAc, \myReverbSliderAc, \myDumpOSCAc,

\myAutoPilotAc ],

[ "/method", \myWindow, \dispose ],

[ "/free", \myWindow, \myDustSlider, \myResonSlider, \myLab,

\myReverbSlider, \myPlingButton, \myKlangButton, \myDustButton,

\myAutoPilot, \myCenterPanel, \myBottomPanel, \myEastPanel, \myCP ]

);

a.clear;

};


autoPilot = Task({

inf.do({

3.0.wait;

resonFreq = clip( resonFreq + (exprand( 1, 200 ) * (2.rand - 0.5).sign), 50, 10000 );

// a.set( \resonFreq, resonFreq );

n.sendMsg( "/set", \myResonSlider, \value, resonFreq );

});

});


SynthDef( "help-Dust", { arg out=0;

Out.ar( out, Pan2.ar( Dust.ar( XLine.kr( 20000, 2, 5, doneAction: 2 ), 0.25 ), -0.5 ));

}).send( s );


n = NetAddr( "127.0.0.1", 57111 );

try { n.connect };  // required for TCP!

n.sendMsg( "/dumpOSC", 1, 1 );

n.sendMsg( "/set",

'[', "/local", \myWindow, '[', "/new", "javax.swing.JFrame", "SwingOSC Test", ']', ']',

\bounds, '[', "/new", "java.awt.Rectangle", 80, 80, 560, 240, ']', \defaultCloseOperation, 0 );


// n.sendMsg( "/a_add", \myWindow, \myAction );


winResp = OSCresponderNode( n, "/window", { arg resp, time, msg;

var source = msg[1].asSymbol, what = msg[2].asSymbol;

if( (source == \myWindow) && (what == \closing), {

cleanUp.value;

});

}).add;

butResp = OSCresponderNode( n, "/action", { arg resp, time, msg;

var source = msg[1].asSymbol;

// msg.postln;

case { source == \myDustButton }

{

Synth.new( "help-Dust", [\out, a.bus.index], s );

}

{ source == \myKlangButton }

{

if( msg[4] == 1, {

a.fadeTime_( 0.1 );

a[ 0 ] = { 

{ Mix.fill(8, { SinOsc.ar( 500 + 500.0.rand, 0, 0.05) })}.dup;

};

}, {

a.fadeTime_( 0.6 );

a[ 0 ] = nil;

});

}

{ source == \myPlingButton }

{

if( msg[4] == 1, {

a.fadeTime_( 0.1 );

a[ 1 ] = { |dustRate = 10.0, resonFreq = 400|

Ringz.ar( Dust.ar( [1,1] * dustRate, 0.1 ), resonFreq );

};

}, {

a.fadeTime_( 0.6 );

a[ 1 ] = nil;

});

}

{ source == \myDustSlider }

{

a.set( \dustRate, msg[4].sqrt );

}

{ source == \myResonSlider }

{

resonFreq = msg[4];

a.set( \resonFreq, resonFreq );

}

{ source == \myReverbSlider }

{

a.set( \reverbMix, msg[4] / 100 );

}

{ source == \myDumpOSC }

{

n.sendMsg( "/dumpOSC", msg[4], msg[4]);

}

{ source == \myAutoPilot }

{

if( msg[4] == 1, {

autoPilot.start;

}, {

autoPilot.stop;

});

};

}).add;

CmdPeriod.doOnce( cleanUp );


a = NodeProxy.audio( s, 2 );

a.play;

a[ 2 ] = \filter -> { arg in, reverbMix = -1.0;

var verb;

verb = in;

8.do({ verb = AllpassN.ar( verb, 1.0, [ 1.0.rand, 1.0.rand ], 1) });

XFade2.ar( in, LPF.ar( verb, mul: 2.0 ), reverbMix );

};


n.sendBundle( nil,

[ "/set", '[', "/local", \myDustSlider, '[', "/new", "de.sciss.swingosc.Slider", 1, 500, 100, ']', ']',

\minorTickSpacing, 20, \majorTickSpacing, 100, \paintTicks, true ],

[ "/set", '[', "/local", \myResonSlider, '[', "/new", "de.sciss.swingosc.Slider", 100, 2000, 400, ']', ']',

\minorTickSpacing, 20, \majorTickSpacing, 100, \paintTicks, true ],

[ "/set", '[', "/local", \myLab, '[', "/new", "javax.swing.JLabel", "hey guantanamo cha cha cha!", 0, ']', ']',

\foreground, '[', "/new", "java.awt.Color", 0x40, 0x00, 0x80, ']',

\font, '[', "/new", "java.awt.Font", "Helvetica Neue Light Italic", 0, 24, ']', ],

[ "/set", '[', "/local", \myReverbSlider, '[', "/new", "de.sciss.swingosc.Slider", 1, -100, 100, -100, ']', ']',

\paintTicks, true ],

[ "/local", \myDumpOSC, '[', "/new", "javax.swing.JCheckBox", "Dump OSC", true, ']' ],

[ "/local", \myPlingButton, '[', "/new", "javax.swing.JToggleButton", "Pling", ']' ],

[ "/local", \myKlangButton, '[', "/new", "javax.swing.JToggleButton", "Klang", ']' ],

[ "/local", \myDustButton, '[', "/new", "javax.swing.JButton", "Dust", ']' ],

[ "/local", \myAutoPilot, '[', "/new", "javax.swing.JCheckBox", "Auto Pilot", ']' ],


[ "/local", \myCenterPanel, '[', "/new", "javax.swing.JPanel",

'[', "/new", "java.awt.GridLayout", 3, 1, ']', ']' ],

[ "/method", \myCenterPanel, \add, '[', "/ref", \myLab, ']' ],

[ "/method", \myCenterPanel, \add, '[', "/ref", \myDustSlider, ']' ],

[ "/method", \myCenterPanel, \add, '[', "/ref", \myResonSlider, ']' ],


[ "/local", \myBottomPanel, '[', "/new", "javax.swing.JPanel",

'[', "/new", "java.awt.FlowLayout", ']', ']' ],

[ "/method", \myBottomPanel, \add, '[', "/ref", \myDumpOSC, ']' ],

[ "/method", \myBottomPanel, \add, '[', "/ref", \myPlingButton, ']' ],

[ "/method", \myBottomPanel, \add, '[', "/ref", \myKlangButton, ']' ],

[ "/method", \myBottomPanel, \add, '[', "/ref", \myDustButton, ']' ],

[ "/method", \myBottomPanel, \add, '[', "/ref", \myAutoPilot, ']' ],


[ "/local", \myEastPanel, '[', "/new", "javax.swing.JPanel",

'[', "/new", "java.awt.GridLayout", 1, 2, ']', ']' ],

[ "/method", \myEastPanel, \add, '[', "/ref", \myReverbSlider, ']' ],

[ "/method", \myEastPanel, \add, '[', "/new", "javax.swing.JSeparator", ']' ],


[ "/local", \myCP, '[', "/method", \myWindow, \getContentPane, ']' ],

[ "/method", \myCP, \add, '[', "/ref", \myCenterPanel, ']', "Center" ],

[ "/method", \myCP, \add, '[', "/ref", \myBottomPanel, ']', "South" ],

[ "/method", \myCP, \add, '[', "/ref", \myEastPanel, ']', "East" ]

);


n.sendMsg( "/local",

\myDustSliderAc, '[', "/new", "de.sciss.swingosc.ActionResponder", \myDustSlider, \value, ']',

\myResonSliderAc, '[', "/new", "de.sciss.swingosc.ActionResponder", \myResonSlider, \value, ']',

\myReverbSliderAc, '[', "/new", "de.sciss.swingosc.ActionResponder", \myReverbSlider, \value, ']',

\myPlingButtonAc, '[', "/new", "de.sciss.swingosc.ActionResponder", \myPlingButton, \selected, ']',

\myKlangButtonAc, '[', "/new", "de.sciss.swingosc.ActionResponder", \myKlangButton, \selected, ']',

\myDustButtonAc, '[', "/new", "de.sciss.swingosc.ActionResponder", \myDustButton, ']',

\myAutoPilotAc, '[', "/new", "de.sciss.swingosc.ActionResponder", \myAutoPilot, \selected, ']',

\myDumpOSCAc, '[', "/new", "de.sciss.swingosc.ActionResponder", \myDumpOSC, \selected, ']',

\myWindowAc, '[', "/new", "de.sciss.swingosc.WindowResponder", \myWindow, ']'

);

n.sendMsg( "/set", \myWindow, \visible, true );

});

)



Explanation


[ "/set", '[', "/local", \myDustSlider, '[', "/new", "de.sciss.swingosc.Slider", 1, 500, 100, ']', ']',

\minorTickSpacing, 20, \majorTickSpacing, 100, \paintTicks, true ],


- For convenience, "/local" and "/global" return the reference name when using in message nesting. Therefore, they can be used as the first argument to a "/set" or "/method" call. Note that the boolean value true is converted an integer 1 by SuperCollider automatically when assembling the OSC message. SwingOSC on the contrary will detect that the Slider class has a setter method setPaintTicks( boolean ) and convert the integer back to a boolean.


butResp = OSCresponderNode( n, "/action", { arg resp, time, msg; ... });


- For all buttons, special listener classes (ActionReponder) are created. These fire "/action" messages back to the client when the user adjusts the corresponding component. The first message argument (index 1 because SuperCollider passes the message name in the first array index) is the component's reference name, followed by the keyword "performed", followed by key-value pairs of properties as requested in the ActionResponder construction.


[ "/new", "de.sciss.swingosc.ActionResponder", \myDustSlider, \value ] ...


- The class  ActionResponder which comes with SwingOSC, is a helper to get component feedback back to the client. It is essentially a java.awt.event.ActionListener implementing class whose constructor is called with the target compoent's reference name, optionally followed by a single property name or an array of property names. (To pass an array, use the bracketed expression '[', "/array", \propertyName1, \propertyName2, ..., ']'). Here, when the Slider fires an ActionEvent (this happens whenever the slider value changes), a message is sent back to the client who created the ActionResponder. The message will be [ "/action", \myDustSlider, \performed, \value, <currentSliderValue> ]. Note this Slider is a subclass of javax.swing.JSlider equipped with the possiblity to add ActionListeners. Please refer to the javax.swing.JSlider API to learn about the methods and properties.


winResp = OSCresponderNode( n, "/window", { arg resp, time, msg; ... });


- We wish to be notified about the closing window. In fact, setting the \defaultCloseOperation to zero means that nothing is happening when the user clicks the window's close gadget. (the default action of a Swing JFrame is that the frame is made invisible). We create a separate window responder instance that will send the message back to the client so we will pick it up in the OSCresponderNode and clean up the programme. Here's the message the creates the java window responder:


[ "/new", "de.sciss.swingosc.WindowResponder", \myWindow ]


the "/window" message will have the object identifer (\myWindow) as the first message argument and the type of window event in the second argument. Apart from \closing, there are \opened, \activated, \deactivated, \gainedFocus, \lostFocus.



// last mod: 30-jul-07 sciss